Repository navigation
fix(sweep): stop repo_settings.yml aborting silently under bash -e - #294
Merged
Merged
Conversation
GitHub runs a `run:` block as `bash -e {0}`. The block set `-uo pipefail` and
never disabled `-e`, so the per-repo read
before=$(gh api "repos/$slug" --jq '.delete_branch_on_merge' 2>/dev/null)
aborted the entire script the moment one repo was unreadable — and silently,
because that stderr is discarded. The `case "$before" in ... *)` unreadable
branch, and with it the in-org/out-of-org failure-policy split #292 added, could
never execute. The first real dispatch failed in 20s with no output at all.
A second instance was latent: `[ "$hard" -eq 1 ] && failed=1` ends `visit()` in
two branches, and with hard=0 that compound returns 1, so the function returned
1 and `-e` aborted on the first out-of-org repo. Both are now `if` blocks.
`-e` is disabled explicitly rather than with `|| true` on the read, because the
hazard applies to every guarded command in the block.
The tests are the real fix. #292 was control-tested across seven scenarios and
still shipped this, because the harness ran `bash <script>` while GitHub runs
`bash -e <script>` — it validated a shell mode that never executes. The new
tests run the extracted block exactly as the runner does, and were verified to
FAIL against the pre-fix workflow and pass against this one.
Sibling sweep: branch_archive.yml and branch_sweep_all.yml explicitly `set -e`
and discard no stderr, so abort-on-error is intended there; no other run: block
in this repo combines a swallowed failure with an inherited -e.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqUT9Wc6mVLiJmbcstT7Z3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #293. Fixes a regression in #292, merged an hour earlier.
What broke
GitHub runs a
run:block as/usr/bin/bash -e {0}.repo_settings.ymlset-uo pipefailand never disabled-e, so the per-repo readbefore=$(gh api "repos/$slug" --jq '.delete_branch_on_merge' 2>/dev/null)aborted the whole script the moment one repo was unreadable — silently, because that stderr is discarded. The
case "$before" in ... *)unreadable branch, and with it the entire in-org (error) vs out-of-org (warning) failure-policy split #292 was partly written to add, was dead code in production.The first real dispatch (run 32992618192,
mode=audit) failed in 20s with no stdout at all between##[endgroup]and##[error]Process completed with exit code 1. That silence is the signature.A second instance was latent:
[ "$hard" -eq 1 ] && failed=1endsvisit()in two branches, and withhard=0the compound returns 1, so the function returned 1 and-eaborted on the first out-of-org repo. Confirmed independently — pre-fix, the out-of-org PATCH-refused test exits 1 after printing its warning.The PATCH path itself was always fine:
if gh api ...; thenis-e-safe.Why it shipped — the part worth keeping
#292 was control-tested, across seven scenarios, before merge. The harness ran
bash <script>. GitHub runsbash -e <script>. The harness did not reproduce the caller's invocation, so it validated a shell mode that never executes, and seven green scenarios said nothing about the one that mattered.So the tests are the real fix here, not the two-line shell change.
tests/test_repo_settings_workflow.pyparses the workflow, extracts the block, stubsghonPATH, and runs it as the runner does — and pins that withtest_the_step_does_not_override_the_shell, so if anyone adds ashell:key the assumption fails loudly instead of rotting.Verified non-vacuous: checked out the pre-fix workflow and ran the new tests against it — 3 failed, 3 passed; restored, 6 pass. A regression test that passes on the broken code would have been worse than none.
The fix
set +eimmediately afterset -uo pipefail, commented with why. Explicit rather than|| trueon the read, because the hazard applies to every guarded command in the block, not just that one.[ ... ] && failed=1compounds becomeifblocks.Sibling sweep
Audited every
run:block in the repo.branch_archive.ymlandbranch_sweep_all.ymlexplicitlyset -eand discard no stderr — abort-on-error is intended there, so they are consistent, not defective, and are left alone.branch_sweep.yml,brain_board.yml,docs.yml,nightly-release.ymlshow no matching pattern.repo_settings.ymlwas the only block combining an inherited-ewith a swallowed failure.Verification
pytest tests/ -q→ 554 passed (548 before, +6 new)..pyunder a framework organ.check_skill_line_counts.sh47/47; YAML parses.Still open after this
We do not yet know which repo failed to read. The aborted run proves at least one did, and the silence is exactly what hid it. The acceptance test for this PR is a fresh
auditdispatch that goes green and names any repo whose read fails — a green run naming nothing is only acceptable if all 46 reads genuinely succeed.Heart remains RED on
release validation FAILED(integrate:fail, since 2026-08-23), unrelated to this diff; shipped under the same standing human override as #292.Generated by Claude Code