Skip to content

feat(engineer-bot): require a live E2E repro for bug fixes (not unit-only) #106

feat(engineer-bot): require a live E2E repro for bug fixes (not unit-only)

feat(engineer-bot): require a live E2E repro for bug fixes (not unit-only) #106

# Engineer Bot — follow-up.
#
# When someone replies to a review thread on a PR carrying the `engineer-bot`
# label (or the label is applied), the bot re-evaluates the open threads in
# catch-up mode, applies a fixup (or replies / marks blocked), and pushes to the
# PR branch. Driven by this repo's .bot/ prompts.
#
# Like the author, this builds/runs the product (setup-poetry). Shared setup
# (tokens + Node + engine install, PAT-free via an engine-scoped App token) is in
# the local ./.github/actions/bot-prelude composite; this file owns checkout,
# setup-poetry, and the followup run. Engine installed in REF mode pinned to a SHA.
#
# The gate below is the canonical label-gated form copied verbatim from the
# engine dogfood (the single source of truth). It ANDs with the engine-side
# floor; do not weaken it.
name: Engineer Bot — Follow-up
on:
pull_request_review_comment:
types: [created]
# `engineer-bot` label applied → catch-up over pre-existing review threads.
pull_request:
types: [labeled]
permissions:
contents: write # push fixup commits to the PR branch
pull-requests: write # post inline replies
id-token: write # JFrog OIDC exchange for the engine/SDK/CLI install
jobs:
followup:
# SECURITY + scope (CANONICAL label-gated model — verbatim from the engine):
# - skip fork PRs — keep the model + App token out of untrusted code's reach;
# - operate only on non-fork OPEN PRs carrying the `engineer-bot` label
# (maintainer-applied opt-in; label requires triage+);
# - comment path: trusted commenters only
# (OWNER/MEMBER/COLLABORATOR/CONTRIBUTOR, or a bot — reviewer-bot/Copilot),
# never the engineer-bot's own comments, and never a reviewer-bot reconcile
# loopback. CONTRIBUTOR is included because a maintainer with PRIVATE org
# membership is reported as `author_association: CONTRIBUTOR` in the webhook
# payload (even though REST shows MEMBER) — without it, such a maintainer's
# review comments SILENTLY fail this gate and the bot never engages. Low
# risk: this path already requires a non-fork, OPEN, `engineer-bot`-labeled
# PR (a maintainer-applied opt-in);
# - labeled path: the `engineer-bot` label was just applied by a human.
if: >-
github.event.pull_request.head.repo.fork == false
&& github.event.pull_request.state == 'open'
&& (
(
github.event_name == 'pull_request_review_comment'
&& contains(github.event.pull_request.labels.*.name, 'engineer-bot')
&& !startsWith(github.event.comment.user.login, 'peco-engineer-bot')
&& !contains(github.event.comment.body, '<!-- pr-review-bot:v1 reconcile -->')
&& (
contains(fromJson('["OWNER","MEMBER","COLLABORATOR","CONTRIBUTOR"]'), github.event.comment.author_association)
|| (
github.event.comment.user.type == 'Bot'
&& (
startsWith(github.event.comment.user.login, 'peco-review-bot')
|| startsWith(github.event.comment.user.login, 'Copilot')
|| startsWith(github.event.comment.user.login, 'copilot')
)
)
)
)
|| (
github.event_name == 'pull_request'
&& github.event.action == 'labeled'
&& github.event.label.name == 'engineer-bot'
&& github.event.sender.type != 'Bot'
&& !startsWith(github.event.sender.login, 'peco-engineer-bot')
)
)
environment: azure-prod # DATABRICKS_HOST / DATABRICKS_TOKEN live here
runs-on:
group: databricks-protected-runner-group
labels: [linux-ubuntu-latest]
timeout-minutes: 30
concurrency:
group: engineer-bot-followup-pr-${{ github.event.pull_request.number }}
cancel-in-progress: false
steps:
# Checkout FIRST (remote action) so the local `./` composites resolve.
# Check out the PR head BRANCH (not the merge ref) so fixups push back to
# it. persist-credentials:false; the run step below sets an authenticated
# push remote from the minted App token.
- name: Checkout PR head branch
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
persist-credentials: false
# Python + connector deps via the repo's OWN setup-poetry action (JFrog
# mirror + poetry lock + install) — egress-safe, tolerates a stale lock.
# The agent runs `poetry run python -m pytest tests/unit` to verify fixups.
- name: Setup Poetry + connector deps (for pytest self-verify)
uses: ./.github/actions/setup-poetry
with:
python-version: '3.11'
# Match code-coverage.yml's e2e job: install optional extras (notably
# pyarrow, declared optional=true behind the `pyarrow` extra) so the
# agent's REQUIRED live E2E repro test runs against the same runtime as
# the e2e suite it mirrors. Without this, arrow-touching e2e tests
# ModuleNotFoundError/skip instead of failing red-for-the-right-reason.
install-args: "--all-extras"
# setup-poetry runs `poetry lock` (to reconcile the lock with the internal
# JFrog source it injects), which REWRITES tracked poetry.lock / pyproject.toml
# in the working tree. The venv is already built, so the tree no longer needs
# that churn — revert it, else the followup's fixup commit (or its leftover
# check) would sweep up these bot-untouched dirty files. Keep ONLY the
# agent's edits in the tree.
- name: Revert poetry lock/pyproject churn (keep only the agent's edits)
# No `|| true`: `git checkout -- <tracked-file>` exits 0 for both the
# clean and the reverted-churn cases, so a non-zero exit means the revert
# genuinely failed (e.g. the files went untracked/missing) and the dirty
# tree would otherwise be swept into the followup's fixup commit. Fail
# loudly here instead of masking it.
run: git checkout -- poetry.lock pyproject.toml
# Shared prelude: mint the engineer-bot token (pushes fixup commits, posts
# replies) + the engine-scoped token, set up Node, install the engine.
- name: Bot prelude (tokens + Node + engine install)
id: prelude
uses: ./.github/actions/bot-prelude
with:
app-id: ${{ secrets.ENGINEER_BOT_APP_ID }}
private-key: ${{ secrets.ENGINEER_BOT_APP_PRIVATE_KEY }}
# Engine pin lives in ONE place: bot-prelude's `engine-ref` default.
# Bump it there to move all four bots in lockstep; override here only
# to run this workflow against a different engine commit.
# Run the follow-up. Inlines the engine's bot-run env contract for
# engineer:followup, plus the authenticated push remote bot-run would set
# up (followup pushes fixup commits via a plain `git push`, which doesn't
# consult GH_TOKEN; the checkout is persist-credentials:false).
- name: Run engineer follow-up (catch-up over all unaddressed threads)
env:
GH_TOKEN: ${{ steps.prelude.outputs.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
HEAD_BRANCH: ${{ github.event.pull_request.head.ref }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
TRIGGER_COMMENT_ID: ${{ github.event.comment.id }}
MODEL_ENDPOINT: https://${{ secrets.DATABRICKS_HOST }}/serving-endpoints/databricks-claude-opus-4-8/invocations
DATABRICKS_TOKEN: ${{ secrets.DATABRICKS_TOKEN }}
# Live-warehouse connection env so a followup fixup can also run the
# REQUIRED E2E repro/verification test (see engineer/system.md). Mirrors
# the author run + code-coverage.yml; job is in `environment: azure-prod`.
DATABRICKS_SERVER_HOSTNAME: ${{ secrets.DATABRICKS_HOST }}
DATABRICKS_HTTP_PATH: ${{ secrets.TEST_PECO_WAREHOUSE_HTTP_PATH }}
DATABRICKS_CATALOG: peco
DATABRICKS_USER: ${{ secrets.TEST_PECO_SP_ID }}
RUNNER_TEMP: ${{ runner.temp }}
# The agent's working tree AND the .bot/ lookup root. run.py resolves
# the config at <REPO_ROOT>/.bot/config.yaml. The engine has NO path
# default — it fails with a clear error if this is unset — so it MUST be
# set (we check out at the workspace root, not the driver-repo layout's
# `internal-repo/`). (`TEST_REPO_ROOT` is the deprecated alias.)
REPO_ROOT: ${{ github.workspace }}
run: |
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
python -m databricks_bot_engine.engineer_bot.run --phase followup \
--system-prompt "$GITHUB_WORKSPACE/.bot/prompts/engineer-followup/system.md"