Skip to content

ci: end a work-resolve-issue kill-switch deny neutral, keeping work-unblock's re-fire - #11236

Merged
kyle-sexton merged 4 commits into
mainfrom
fix/resolve-issue-killswitch-neutral
Oct 30, 2026
Merged

kyle-sexton merged 4 commits into
mainfrom
fix/resolve-issue-killswitch-neutral

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Closes #10753

Summary

A work-resolve-issue run that the token broker refuses with reason kill-switch at a write job's mint now ends green, and work-unblock still re-fires the issue as a breaker trip. A kill switch still stops the run; only the run's colour changes.

Fix

  • .github/workflows/pr-run-activity-write.yml: kill-switch-ok is now ${{ inputs.activity != 'report-escalation' }}, which drops the inputs.issue-number == '' clause. Issue runs (apply-issue-patch, report-outcome, report-skip) end a kill-switch refusal green, with no token and skip-reason kill-switch. report-escalation stays red. Every later step already keys on the token, and the existing callers.test.mjs check ("every kill-switch-ok caller runs no token step and no dependent job on a kill-switch stop") already covered the work-resolve-issue callers and still passes.
  • .github/scripts/work-unblock/label-unblocked.mjs: didNoWork no longer treats every successful Request the lane token step as work done. A mint success whose Stage the activity from the base step was skipped is a stopped mint (STAGE_STEP), read straight from the job listing. Only that shape gets one job-log read, and a timestamped ##[notice]lane-token-stopped: the broker refused the token because a lanes kill switch is on line (STOPPED) makes it a breaker trip at stage token. A stopped mint with no such line is a failed mint (mint-failed), so it is still re-fired. The failed-step DENIAL.token path is unchanged for runs from before this change.
  • Jobs downstream of a green stop, raised by the independent security review: work-resolve-issue.yml report-outcome skips when apply-issue-patch's skip-reason is kill-switch. That run's patch was dropped, and otherwise report-outcome would report it with no escalation reason. work-unblock then re-fires the issue as a breaker trip. report-statuses passes a report-outcome that the switch stopped as stopped, which post-statuses.mjs jobState posts as error, never success. In label-unblocked.mjs, a green run whose report mint was stopped after a patch mint is re-fired at once like a failed run (stoppedMint), with no log read, instead of waiting the stranded hours.

Verification

Tests were written first and failed on the old code, then passed after the fix (node --test):

  • Criterion 1, a kill-switch refusal in a work-resolve-issue run ends green: callers.test.mjs › "pr-run-activity-write.yml takes kill-switch-ok on every run but report-escalation". It was red before the fix, at at("7","apply-issue-patch"), and is green after (206/206).
  • Criterion 2, work-unblock still re-fires that issue as a breaker trip: these label-unblocked.test.mjs tests were red before the fix (3 failing) and are green after (233/233):
    • "a green run whose mints the kill switch stopped is a breaker trip, read from one log"
    • "a stopped patch mint before a report the switch also stopped, or one alone, is a breaker trip"
    • "a stopped mint whose log does not carry the kill-switch notice is a failed mint, never a breaker trip"
  • Downstream jobs, red before the fix and green after: callers.test.mjs › "work-resolve-issue.yml: a kill-switch stop skips report-outcome and never reads as a passed report", and label-unblocked.test.mjs › "a green run whose report mint the kill switch stopped after a patch mint is re-fired like a failed run". Totals: callers 208/208, label-unblocked 234/234, post-statuses 23/23, report-outcome 326/326.
  • Guards on the new code: "a run whose stopped mint is followed by a mint that took its token did work, read with no log". The job/step-name shape test now pins the stage step's if: steps.lane-token.outputs.token != ''.
  • Criterion 3, a pre-change run that failed with lane-token-denied ... reason kill-switch is still a breaker trip: "a run the broker refused at its mints for the kill switch, after the model ran, is a breaker trip read from one log" and "a refusal whose log does not name the kill switch on the step's last line is a failed mint" pass unchanged.
  • Also green: decide.test.mjs (60), apply-patch.test.mjs (174), refire-refused.test.mjs (26), gate-source.test.mjs (31), check-signed-commits.test.mjs (39), and actionlint on the workflow.

Metrics

  • Per-run GitHub API call delta: 0 for every run whose mint took a token, because the stopped-mint test reads the job listing work-unblock already fetches. A stopped run costs 1 job-log read, the same single read the old failed-mint path made. The workflow change adds no calls, and it saves the issue lane's red-run noise.
  • What it makes measurable: a kill-switch stop in an issue run now ends as a green work-resolve-issue run, not a failed one, so the lane's failed-run counts stop including designed stops. The stop is still counted: work-unblock's breaker record comment names the run.

Related

🤖 Generated with Claude Code

kyle-sexton and others added 2 commits October 30, 2026 07:06
…nblock's re-fire

An issue run of pr-run-activity-write.yml now takes request-lane-token's
kill-switch-ok, so a broker kill-switch refusal at apply-issue-patch,
report-outcome or report-skip ends green with no token. report-escalation
stays red.

work-unblock's label-unblocked.mjs reads that stop as a breaker trip: a
`Request the lane token` success whose `Stage the activity from the base`
step skipped is a stopped mint, and its job log's timestamped
`##[notice]lane-token-stopped: ... kill switch is on` line makes it a trip.
A stopped mint with no such line is a failed mint. The failed-step
DENIAL.token path stays for runs from before this change.

Closes #10753

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 30, 2026 11:12
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-30T11:12:06.227500Z
We couldn’t complete this review. Comment @codex review to try again.
06069bd Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

claude-security-review reviewed 4ff6c7de25d555e5df9252d6e780d3bd388bb0f2: 0 finding(s), 0 as inline comments.

No security findings in the incremental change since 06069bd. critical-findings: 0

This is an incremental review of the four changed files. I read the incremental diff and the surrounding workflow, and did not dispatch subagents.

  • .github/workflows/work-resolve-issue.yml
    • report-outcome now also skips when needs.apply-issue-patch.outputs.skip-reason == 'kill-switch'. This makes the kill switch stop more of the run, not less.
    • The if: precedence parses as !cancelled() && (started || reuse) && skip-reason != 'kill-switch'. The original grouping is kept.
    • apply-issue-patch is already in needs, and pr-run-activity-write.yml exposes skip-reason as a workflow_call output (lines 486–492). If apply-issue-patch is skipped, the output is empty, so the job still runs and nothing is silently dropped.
    • report-statuses maps a kill-switch-stopped report-outcome to stopped. In post-statuses.mjs, jobState turns any value other than success or failure into error, so a stopped report cannot post a success status.
    • This is not a trust-control widening. Token scopes, the trusted-actor list and the kill-switch action are unchanged. The kill switch can still stop the lane.
  • .github/scripts/work-unblock/label-unblocked.mjs
    • The new stoppedMint check reads only the job listing that work-unblock already fetches. It adds no log read and no new input from untrusted text.
    • A green run whose report mint the kill switch stopped is re-fired like a failed run. The once-per-version cap still bounds the re-fire, so this does not open an unbounded dispatch loop or a charge path.
    • Moving endedAt and stoppedAt to module scope changes no behavior.
  • callers.test.mjs and label-unblocked.test.mjs
    • These are test-only changes.
    • callers.test.mjs is under the resolve-config trusted path. The change is only test code and widens no control.

Spend: the extra re-fire is one more work-unblock dispatch per version. That draws on subscription or prepaid usage, so it is not a hold.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

claude-review reviewed 4ff6c7de25d555e5df9252d6e780d3bd388bb0f2: 0 finding(s), 0 as inline comments.

Incremental review of the four files changed since 06069bd, with no findings.

I read the incremental diff, the PR context, and the surrounding code in work-resolve-issue.yml and label-unblocked.mjs. I did not run any tests. The test results in the PR description are the author's claims and I did not confirm them.

  • report-outcome if: (.github/workflows/work-resolve-issue.yml, around line 818): the new && needs.apply-issue-patch.outputs.skip-reason != 'kill-switch' binds to the whole (started || reuse-branch) group, because the parentheses close before the &&. apply-issue-patch is already in the job's needs. When it is skipped, its output is empty, which is not 'kill-switch', so the job still runs as before. The skip-reason output is exposed by pr-run-activity-write.yml (workflow_call output at line 486).
  • report-statuses REPORT_RESULT: it now maps a kill-switch-stopped report-outcome to stopped. jobState in post-statuses.mjs turns anything other than success or failure into error, so the status is never posted as passed. A report-outcome skipped by the new if: has result skipped, which also maps to error. This matches the PR description.
  • label-unblocked.mjs:
    • Moving endedAt and stoppedAt to module scope is safe. didNoWork still reaches both.
    • stoppedMint reuses the already-cached jobsOf listing, so it adds no API calls.
    • It only widens the "did the run fail" gate in refireRefusal, after didNoWork. So a green run whose patch mint took a token and whose report mint was stopped (didNoWork returns null) is re-fired like a failed run. The new test covers that case and asserts no log read.
    • A run where every mint stopped still goes through the breaker path first.
  • Tests: the new callers.test.mjs case checks the report-outcome if: and the REPORT_RESULT expression. The ??= "" default added to the shared context helper is harmless. The label-unblocked test covers the reverse-order case.
  • Closing issue ci: end a work-resolve-issue kill-switch deny neutral, keeping work-unblock's re-fire #10753 criteria: the three acceptance criteria are covered by the earlier-reviewed hunks and their tests. This increment only closes the downstream-job gaps.

No cost or billing change. No critical security finding.

kyle-sexton and others added 2 commits October 30, 2026 07:16
A green kill-switch stop now reaches the jobs after the stopped mint, so:

- report-outcome skips when apply-issue-patch stopped on the kill switch,
  instead of reporting a run whose patch was dropped with no escalation
  reason; work-unblock re-fires the issue as a breaker trip.
- report-statuses posts a report-outcome the switch stopped as `error`
  ("did not finish"), never `success`.
- work-unblock re-fires a green run whose report mint stopped after a patch
  mint like a failed run, at once, instead of waiting the stranded hours.

Refs: #10753

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit bebc7bc Oct 30, 2026
94 of 96 checks passed
@kyle-sexton
kyle-sexton deleted the fix/resolve-issue-killswitch-neutral branch October 30, 2026 11:28
@github-actions

Copy link
Copy Markdown
Contributor

Post-merge mutation testing

Killed 6, survived 1, timeout 0, no coverage 0, invalid 0, not run 0.
Covered-code score 86%; mutation score 86%.

File Killed Survived Timeout No coverage Invalid Not run
.github/scripts/work-unblock/label-unblocked.mjs 6 1 0 0 0 0

Surviving mutants

Location Operator Disposition Cause
.github/scripts/work-unblock/label-unblocked.mjs:1090 LCR productive input-gap

The run's summary has the full report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: end a work-resolve-issue kill-switch deny neutral, keeping work-unblock's re-fire

1 participant