Skip to content

Commit ed07fad

Browse files
committed
ci(bullock): harden feedback and CI-file guardrails
Three independent hardenings found during an audit of the workflow: - Step-level (not just job-level) timeout on the Claude step. The job's 60min timeout CANCELS the job, and the verdict-acting step is guarded by !cancelled(), so a job timeout was a silent red run with no comment. A step timeout FAILS the step instead, which degrades cleanly into the existing no-valid-verdict comment path. - Widen the CI-file backstop from .github/workflows/ to all of .github/. GITHUB_TOKEN can push .github/actions/ composite actions even though it can't push workflows, and those actions execute in future CI runs with secrets present. Bullock has no legitimate reason to touch any CI file, so block it mechanically instead of relying only on the prompt's SECURITY section. - Skip the job when the summoning comment is from a Bot. GITHUB_TOKEN comments never retrigger workflows (GitHub anti-recursion), but GitHub-App comments do — a claude.yml review quoting "@bullock" would otherwise spin a runner and die red at the access gate.
1 parent 0e08fae commit ed07fad

1 file changed

Lines changed: 29 additions & 12 deletions

File tree

.github/workflows/bullock.yml

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ jobs:
7676
# Write-access gating happens twice: the first step fails fast for
7777
# non-write actors, and claude-code-action enforces it again internally —
7878
# this `if` just avoids spinning a runner on unrelated comments.
79-
if: contains(github.event.comment.body, '@bullock')
79+
# The Bot filter matters because GITHUB_TOKEN comments never retrigger
80+
# workflows (GitHub anti-recursion) but GitHub-App comments DO: a claude.yml
81+
# review quoting "@bullock" from the thread would otherwise spin a runner
82+
# and die red at the access gate — noise on the PR, wasted runner.
83+
if: contains(github.event.comment.body, '@bullock') && github.event.comment.user.type != 'Bot'
8084
runs-on: ubuntu-24.04
8185
# analyze_and_test's checks job needs ~30min for setup + `make checks` alone;
8286
# Bullock adds Claude's implement/verify loop on top of the same prefix.
@@ -213,6 +217,13 @@ jobs:
213217
# format, and Write the verdict sentinel.
214218
- name: Bullock (Claude Code)
215219
if: steps.src.outputs.cross != 'true'
220+
# Step-level timeout on purpose: the job-level 60min timeout CANCELS the
221+
# job, and "Act on Bullock's verdict" is guarded by !cancelled() — so a
222+
# job timeout would be a silent red run with no comment. A step timeout
223+
# merely FAILS this step, which degrades to the no-valid-verdict comment.
224+
# Budget: ~6min gate+setup before this step + 45 here + ~2 of git/PR
225+
# plumbing after stays under the job's 60.
226+
timeout-minutes: 45
216227
uses: anthropics/claude-code-action@v1
217228
with:
218229
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -272,6 +283,7 @@ jobs:
272283
273284
## Step 4 — Implement (ONLY if sufficient)
274285
- Change ONLY what was requested. No opportunistic refactors, no unrelated cleanup.
286+
- NEVER modify anything under `.github/` (workflows, actions, CI config) — the workflow refuses to commit such changes. If the request requires a CI change, treat it as insufficient and say so in "question".
275287
- Follow AGENTS.md strictly: use `fvm`; respect the layer/facade/failure/naming rules; NEVER log or expose secrets (mnemonic/seed/xpriv/PIN); no hardcoded user-facing strings (use `context.loc.*`); no raw colors.
276288
- After editing, format your new/changed Dart files so the CI format gate can't fail on untracked files:
277289
run `fvm dart format` on the files you touched, then `git add -A` for the changed source (do NOT add `.bullock/`).
@@ -332,22 +344,27 @@ jobs:
332344
git config user.name "bullock[bot]"
333345
git config user.email "bullock@users.noreply.github.com"
334346
335-
# GITHUB_TOKEN cannot push changes under .github/workflows/ — the push
336-
# (below) would be rejected AFTER commit, going red with no feedback.
337-
# Bail early with an explanation. This is also a deliberate backstop
338-
# against Bullock modifying its own workflow. `git status --porcelain`
339-
# reports both tracked edits and new untracked files, and is checked
340-
# before staging so a workflow-file change never enters the commit.
341-
if [ -n "$(git status --porcelain -- .github/workflows)" ]; then
342-
comment "🐂 Bullock's changes touch \`.github/workflows/\`, which a bot token isn't allowed to push. Please make workflow changes manually."
343-
echo "Workflow files touched — declined (GITHUB_TOKEN cannot push workflows)."
347+
# ALL of .github/ is off-limits to Bullock, for two distinct reasons:
348+
# - .github/workflows/: GITHUB_TOKEN cannot push these — the push
349+
# (below) would be rejected AFTER commit, going red with no feedback.
350+
# - the rest (.github/actions/ composite, CI config): the token CAN
351+
# push them, but they execute in future CI runs with secrets — a
352+
# prompt-injected change here must be blocked mechanically, not just
353+
# by the prompt's SECURITY section. Bullock has no legitimate reason
354+
# to touch CI; a maintainer changes it manually.
355+
# `git status --porcelain` reports both tracked edits and new
356+
# untracked files, and is checked before staging so a CI-file change
357+
# never enters the commit.
358+
if [ -n "$(git status --porcelain -- .github)" ]; then
359+
comment "🐂 Bullock's changes touch \`.github/\` (workflows or CI config), which Bullock isn't allowed to modify. Please make CI changes manually."
360+
echo "CI files touched — declined (.github/ is off-limits to Bullock)."
344361
exit 0
345362
fi
346363
347364
# Stage only source changes; never sweep the whole tree (build_runner
348365
# drift, make side effects, stray artifacts) into the stacked PR, and
349-
# never stage workflow files (guarded above).
350-
git add -A -- ':(exclude).github/workflows'
366+
# never stage CI files (guarded above).
367+
git add -A -- ':(exclude).github'
351368
352369
if git diff --cached --quiet; then
353370
comment "🐂 Bullock judged the request actionable but produced no changes. Please re-summon with a more specific instruction."

0 commit comments

Comments
 (0)