diff --git a/claude/action.yaml b/claude/action.yaml index 6ed13d15..747d733d 100644 --- a/claude/action.yaml +++ b/claude/action.yaml @@ -115,6 +115,7 @@ runs: # and runs the script (reached via ../shared/steps, since this action is # one level deep). - name: Security preflight + id: security shell: bash run: bash "${{ github.action_path }}/../shared/steps/security-preflight.sh" env: @@ -607,8 +608,27 @@ runs: "*Cost at API list prices — a large multiple of the effective rate on Claude Code subscriptions.*" ' <<< "$USAGE" >> "$GITHUB_STEP_SUMMARY" + # Gate on the job being red, not on the agent step specifically. A dozen + # steps run before `claude` — the rate-limit preflight, auth validation, + # the proxy and sandbox build, the adopter's `sandbox_setup:` — and any of + # them failing strands the same work, so each needs to reach the outage + # tracker. Keying on `steps.claude.outcome` meant a pre-agent failure went + # out as a red run and nothing else; a rate-limit preflight abort held + # every workflow on this repo for a full UTC day without filing a thing. + # + # The security preflight is the one exclusion, and what carves it out is + # that reporting would have the bot write to the repo (`gh issue create`, + # and `gh issue close` on the reconcile path) with its PAT immediately + # after the security gate refused to let it operate there. Its failure is + # also persistent — red until a human fixes branch protection, so an issue + # titled "Bot temporarily unavailable" would collect a row per trigger — + # but that argument doesn't single it out: `Validate auth configured` and + # the adopter's `sandbox_setup:` fail the same way and stay in. Bounding + # that repeated append belongs in `report-failure.sh`, where it covers + # every such step at once (#859); enumerating more step ids here would go + # stale as steps move. - name: Report failure - if: failure() && steps.claude.outcome == 'failure' + if: failure() && steps.security.outcome != 'failure' shell: bash run: bash "${{ github.action_path }}/../shared/steps/report-failure.sh" env: diff --git a/codex/action.yaml b/codex/action.yaml index c42895f0..c3c1d971 100644 --- a/codex/action.yaml +++ b/codex/action.yaml @@ -83,6 +83,7 @@ runs: # Preflight/teardown step bodies are shared with the Claude actions via # ../shared/steps; codex's auth, prompt staging, and run path are its own. - name: Security preflight + id: security shell: bash run: bash "${{ github.action_path }}/../shared/steps/security-preflight.sh" env: @@ -329,8 +330,15 @@ runs: env: GITHUB_TOKEN: ${{ inputs.github_token }} + # Gate on the job being red, not on the agent step specifically — see the + # matching note in claude/action.yaml, including why the security preflight + # is excluded. Every step ahead of `codex` (the rate-limit preflight, bot-ID + # resolution, auth validation, the CLI and plugin installs, the AGENTS.md + # staging, the fork-PR pinning) strands the same work when it fails, and + # keying on `steps.codex.outcome` kept all of them out of the outage + # tracker. - name: Report failure - if: failure() && steps.codex.outcome == 'failure' + if: failure() && steps.security.outcome != 'failure' shell: bash run: bash "${{ github.action_path }}/../shared/steps/report-failure.sh" env: diff --git a/shared/steps/report-failure.sh b/shared/steps/report-failure.sh index e8b1c6ae..635ff9bc 100755 --- a/shared/steps/report-failure.sh +++ b/shared/steps/report-failure.sh @@ -1,7 +1,12 @@ #!/usr/bin/env bash # File or append to a `tend-outage` issue when a run fails, so outages are -# tracked until resolved. Shared verbatim by both harness actions; the -# caller gates it on the agent step having failed. +# tracked until resolved. Shared verbatim by both harness actions; the caller +# gates it on the job being red, so a failure anywhere in the action — the +# rate-limit preflight and sandbox build ahead of the agent as much as the +# agent itself — lands in the tracker. The one exclusion is the security +# preflight: that failure is a persistent config refusal rather than an +# outage, and the issue this files ("temporarily unavailable", closed once +# resolved) would never resolve. # # Just records the run link. Error annotations and logs are not reliably # available while the job is in_progress, so the nightly skill enriches these