Skip to content

fix(pr-pipeline): start the address-feedback and review-dependency fallback calls from trusted values - #9368

Merged
kyle-sexton merged 4 commits into
mainfrom
fix/9365-subscription-legs-trusted-start
Oct 23, 2026
Merged

kyle-sexton merged 4 commits into
mainfrom
fix/9365-subscription-legs-trusted-start

Conversation

@kyle-sexton

Copy link
Copy Markdown
Member

Closes #9365

Summary

address-feedback-subscription and review-dependency-subscription started on lane-class and credential-present. The read runner's run job sets those outputs after head code runs, so a forged run job could start or suppress the OAuth fallback leg. Both legs now start the way fix-ci's do since #9363: on a base job's single Actions API read of whether the first call's report job posted its check.

Fix

  • pr-unblock.yml: new jobs address-feedback-handoff and review-dependency-handoff. Each has actions: read only, no checkout and no secret, and runs fix(pr-pipeline): derive fix-ci's fallback, restart count and head binding from trusted jobs #9363's fix-ci-handoff step verbatim with CALLS set to its own call. Each subscription job now needs its handoff job and runs on needs.<leg>-handoff.outputs.posted != 'true'.
  • The fallback: label: credit-stop vs no-key had no trusted source. Only the run job knows the cause, and nothing may test the key. That distinction only picks the lane record's text. The input itself still gates which credential step runs, so it stays. Every leg with a handoff job, fix-ci and fix-ci-rung-up included, now passes the literal fallback: handoff. That value is the one fact the handoff read attests.
  • pr-run-activity-read.yml accepts handoff and labels the lane record (key <name> posted no check). credit-stop and no-key stay accepted, because fix-docs-subscription still passes them until feat(pr-pipeline): restore pr-refine as the refine stage for fix-docs #9332.
  • pr-run-activity.md "Two calls on one credential each" now describes the handoff jobs and the handoff label.

Verification

  • Tests written first. On the unfixed workflow, callers.test.mjs had 9 of 140 failing and workflow-gates.test.mjs 3 of 14. With the fix, after merging main: 143/143 and 14/14 (nice node --test, one suite at a time).
  • New pins:
    • No read-runner caller with a fallback input reads a run-job output (jobs.run.outputs.*) in its if:. That covers all 5 legs; fix-docs-subscription is exempt until feat(pr-pipeline): restore pr-refine as the refine stage for fix-docs #9332 and asserted to still read one.
    • The handoff jobs' shape, and their read against stubbed jobs listings.
    • Forged lane-class / credential-present / handed-off values don't change either subscription gate.
    • The read runner's fallback input check accepts handoff.
  • runner-policy.mjs --root . passed, and actionlint and zizmor --offline report nothing. scripts/pr-unblock-trusted-base.test.sh 24/24 and scripts/read-runner-dispatcher-gate.test.sh 9/9.
  • Real run 39894065797 shows the step names the read keys on: address-feedback / report has Post the check run, and address-feedback / broker-check has the broker step.
  • Left out: a live pr-unblock run of either leg, which needs a credit stop or an empty key.

Related

🤖 Generated with Claude Code

kyle-sexton and others added 3 commits October 23, 2026 03:44
…llback calls from trusted values

address-feedback-subscription and review-dependency-subscription started
on the first call's lane-class and credential-present, which the read
runner's run job sets after head code runs. Give each leg a handoff job,
as fix-ci has, that reads this run's jobs through the Actions API once,
and start the fallback call on its posted output.

Every fallback call with a handoff job, fix-ci's included, now passes
fallback: handoff instead of a label from the first call's lane-class;
the read runner accepts it and labels the lane record "posted no check".

Closes #9365

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…step names

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 23, 2026 07:51
@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 🔄 Running since 2026-10-23T07:51:20.425421Z 88fc5fc 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.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

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

No blocking findings on head ca7fcc0. I read the diff and PR context and checked the changed workflows against the base files. I did not run the tests or workflows, and the PR's numbers (143/143, 14/14, the real run) are the author's and unverified.

Closing issue #9365. It asks for three things, and the change covers each.

  • It asks for a handoff job for address-feedback and review-dependency, with the subscription started on its output. address-feedback-handoff and review-dependency-handoff do this. Each has actions: read only, no checkout and no secret, and repeats fix-ci-handoff's step with its own CALLS. Each subscription's if: reads only the select job's proceed and the handoff job's posted.
  • It asks that the fallback: label come from a trusted source or be dropped. The literal fallback: handoff replaces the lane-class ternary in all four legs that have a handoff job (address-feedback, review-dependency, fix-ci, fix-ci-rung-up). The read runner's input check (pr-run-activity-read.yml:502-506) and lane-record label (:1589-1593) accept it. The fallback input is only read in those two places and in the credential-step gates at :1125 and :1142, which test for empty or non-empty. So the new value changes nothing else.
  • It asks that the callers.test.mjs pin cover every subscription leg. The new test "no subscription leg's if: reads a read runner run-job output" does this. It checks every read-runner caller that has a fallback input and exempts fix-docs-subscription explicitly. That exemption is tracked, because the test fails once fix-docs stops reading a run-job output and the entry must then be dropped. The test also pins the list of legs, so a new leg has to be added deliberately.

Behaviour checked.

  • The first-call jobs address-feedback and review-dependency run on the same proceed condition as their handoff jobs. A skipped first call therefore can't turn into a spurious fallback, which is why fix-ci-rung-up needs its extra result != 'skipped' guard and these legs don't.
  • A failed jobs read leaves posted empty, and != 'true' then starts the fallback. This matches fix-ci's documented "a second leg, never a lost check" behaviour.
  • A forged lane-class, credential-present or handed-off no longer reaches any gate. The new tests in workflow-gates.test.mjs and callers.test.mjs cover this.
  • The docs update in pr-run-activity.md matches the workflow, and the .changes fragment is a patch bump, which fits a CI-workflow fix with no interface break.

Nit, below the review bar (not counted). The comment above review-dependency-handoff (pr-unblock.yml:674) reads "never from an review-dependency output". It should be "a".

Not verified. The PR says no live pr-unblock run of either fallback leg was made, since that needs a credit stop or an empty key. The handoff read's step names are only evidenced by the author's cited run.

No hold. The change adds no model call or spend, since the subscription legs already existed. It touches no trusted-actor, CODEOWNERS or .gitmodules files.

No plugin manifests changed.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

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

No security issues found in this pull request.

critical-findings: 0

What I checked

  • The diff changes the start condition of address-feedback-subscription and review-dependency-subscription. They no longer read lane-class or credential-present, which the run job can forge because it runs head code. They now start on new address-feedback-handoff and review-dependency-handoff base jobs.
  • Each handoff job has actions: read only. It has no checkout and no secrets, and it reads the run's jobs through the Actions API. Job and step names come from base workflow code, so head code cannot forge them. The jobs match the existing fix-ci-handoff pattern. needs.<first>.outputs.* is no longer used anywhere in the gates.
  • The failure mode is fail-open to a second leg. If the read fails, posted is empty, so != 'true' starts the fallback call. That call holds only the OAuth token and goes through its own broker check, as fix-ci-subscription already does. A forged run-job output can no longer start or suppress the leg. The PR's stated goal holds.
  • fallback: handoff is a new accepted value in the read runner. The input check is an exact case match. The value only changes the lane-record label, and other inputs.fallback uses test == '' or != '', so credential selection is unchanged. handoff is a literal in every caller, with no interpolated input.
  • fix-docs-subscription still starts on forgeable run-job outputs. The PR documents this as pending feat(pr-pipeline): restore pr-refine as the refine stage for fix-docs #9332 and pins it in a test with SUBSCRIPTION_START_PENDING. It is pre-existing and out of scope.
  • The allowed output in the new handoff steps is computed but unused. That is cosmetic and copied from fix-ci-handoff.
  • PR context: the description contains no embedded directives aimed at the reviewer.

Not applicable

  • No plugin manifests (plugin.json or marketplace.json) are touched, so there are no manifest <field> lines.
  • No trust-gate paths are touched (trusted-actors, check-trusted-trigger, CODEOWNERS, .gitmodules).
  • No instruction-surface rules are removed. The changed comments and docs describe the new mechanism, and a test still enforces the property they described.
  • I did not read the instruction exception register, because no standing instruction was deleted or softened.

@melodic-automation-lanes melodic-automation-lanes Bot added the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 23, 2026
@melodic-automation-lanes

Copy link
Copy Markdown
Contributor

The pr-unblock lane stopped on this pull request and needs a person: the merge stages a change under .github/workflows/, which the lane does not push.

  • Reason: workflow-staged
  • Resume key: ca7fcc090194ac21f0a542c346f38e897b7fc14b (the head the lane stopped on)

To resume, deal with the reason above, remove the needs-human label (and do-not-merge, if set), then dispatch the pr-unblock workflow with pr-number=9368 and activity=resolve-conflicts:

gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=9368 -f activity=resolve-conflicts

@kyle-sexton kyle-sexton removed the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 23, 2026
@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 23, 2026
Merged via the queue into main with commit b5fcdf8 Oct 23, 2026
69 of 72 checks passed
@kyle-sexton
kyle-sexton deleted the fix/9365-subscription-legs-trusted-start branch October 23, 2026 08:28
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.

fix(pr-pipeline): start the address-feedback and review-dependency subscription legs from trusted values

1 participant