workflow-fix: lint bash function-invocation || rc=$? errexit-suppression in scripts/*.sh
kind: infra#wf-fix#wf-fix-fp:9ee07db6a7b7
track:Highlight text on any card (body, plan, or an event) to anchor a comment, or leave a whole-task comment here. Mention @claude to summon a reply.
No comments yet.
Overview / Motivation
Auto-filed by the workflow-fix-on-bug protocol from a workflow-fix candidate raised on task #1426 (emitting agent: code-reviewer, sampled-rollout round r1, mechanizable: yes).
Goal
Add a workflow_lint check flagging a same-file bash FUNCTION invoked via func || rc=$? (or || true) under set -e in scripts/*.sh — errexit is suppressed inside the function body, so mid-function failures collapse to the last command's rc.
Workflow gap
- Bug observed: the #1426 sampled-rollout dispatch shipped
run_seed "$s" || rc=$?— a Gate-1 terminal failure (driver return 3), the manifest-validation SystemExit, and fit failures all collapsed to rc=0, letting unvalidated fits + partial uploads + the success sentinel[phase=done]proceed. Caught only by the adversarial code-reviewer (round-1 FAIL). - Why it is a workflow gap: the shape is a recurring bash footgun (errexit suppression in
||/&&/ifcontexts) that no mechanical gate catches; workflow_lint already names|| rc=$?-then-ignore as a "residual evasion shape" in another check's docstring (lines 1199/3851) but has no function-invocation detector. - Confidence (emitter): high (reviewer reproduced the failure shape live on this VM)
- verified-at-filing:
grep -rn '|| rc=\$?' scripts/*.sh→ 13 hits in 13 files (2026-07-18), ALL current hits are single-external-command captures (safe — the captured rc IS the command's rc); 0 current function-invocation instances on main (the #1426 instance lives on the unmerged issue-1426-sampled branch, being fixed in its revision round); the check targets the CLASS to prevent recurrence.grep -n 'rc=\$?' scripts/workflow_lint.py→ docstring mentions only (no existing check for this class).
Proposed change (candidate diff sketch — refine in planning)
+ def check_sh_function_rc_capture(...):
+ # per scripts/*.sh: collect function names (^\s*(function\s+)?(\w+)\s*\(\)\s*\{)
+ # flag lines matching ^(\s*)(<fname>)\b.*\|\| (rc=\$\?|true) when set -e is
+ # active in the file; single external commands (non-function LHS) unflagged;
+ # inline waiver # RC_CAPTURE_EXEMPT: <reason ≥10 chars>
Scope / surfaces
- Primary target:
scripts/workflow_lint.py(+ its tests in tests/test_workflow_lint.py) - Decide bundling into the no-flags default run per the sibling checks' policy.
Constraints / invariants
- Workflow-surface only.
scripts/workflow_lint.py --check-asks+ no-flags run stay green; new check must pass on the live tree (13 current single-command uses unflagged). - This session runs under EPM_WORKFLOW_FIX_SESSION=1 and carries a workflow_fix_target: Provenance line — it MUST NOT auto-route its own subagents' workflow-fix candidates (recursion guard).
Provenance
- workflow_fix_target: scripts/workflow_lint.py
- fingerprint: 9ee07db6a7b7
Surfaced prose (code-reviewer #1426 r1): "Mechanizable: yes (grep/lint for multi-step functions invoked via || rc=$? under set -e in scripts/*.sh)."
Activity