Skip to content

ci(lanes): repin the Claude lane callers to ci-workflows v0.65.13 and pass the fallback token - #10151

Merged
kyle-sexton merged 5 commits into
mainfrom
ci/repin-fallback-v0.65.12
Oct 26, 2026
Merged

kyle-sexton merged 5 commits into
mainfrom
ci/repin-fallback-v0.65.12

Conversation

@kyle-sexton

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

Copy link
Copy Markdown
Member

No related issue: the owner approved the central fallback subscription (decision Q12, 2026-10-25); this repins the lane callers to the ci-workflows release that adds it.

Summary

Repins every Claude lane caller to ci-workflows v0.65.13 (ba7af78d291f10bd75651857a0db2357c23df0fe) and passes the org secret FALLBACK_CLAUDE_CODE_OAUTH_TOKEN to the two reusables that take it. After this merges, the code review, security review and intake triage lanes hand off once more, to the central fallback subscription, when the role key and the role token have both stopped (a limit stop, a credit stop, or a 401 or 402). With the secret empty, the lanes run as before.

Fix

  • Reusable-workflow callers pr-review-hosted.yml, pr-review-security-hosted.yml (pr-review-cli.yml) and intake-triage.yml (intake-triage.yml) move from v0.65.11 and pass FALLBACK_CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.FALLBACK_CLAUDE_CODE_OAUTH_TOKEN }} by name, never secrets: inherit. That is the secret the runner-policy contracts add at v0.65.12 (standards#881) and carry forward unchanged at ba7af78d (standards#882), with no input or permission change.
  • Composite-action callers pr-run-activity-read.yml, pr-run-activity-write.yml, pr-unblock.yml and pr-verify.yml move run-claude-cli, report-lane-outcome and apply-claude-managed-settings to the same SHA. Between e5167fc2 and ba7af78d only ci-workflows#890 and #891 changed them: optional fallback-token and fallback-ready inputs, a handoff-reason output, and a limit-stop fallback reason; v0.65.13 changes none of them (the compare touches only pr-review-cli.yml and its test). With neither input set, a subscription leg never hands off, so these callers behave exactly as today.
  • .github/lane-funding.json lists the fallback secret in the three lanes that now read it. callers.test.mjs admits that secret, the same for every role, only on a call of a ci-workflows intake-triage.yml or pr-review-cli.yml reusable that passes it by name.
  • Pin tests: callers.test.mjs adds ba7af78d to the launchers checked to pass --permission-prompts none: lane.cjs:404-405 at ba7af78d reads "--permission-prompts", "none", and git diff 78ee7a26 ba7af78d -- .github/actions/run-claude-cli/ is empty, so the lane-class keys are unchanged too. The replay.mjs pin, ci-review-lanes.test.sh and ci-review-plugin-source.test.sh move with it. cliArgs and legEnv in lane.cjs are unchanged from e5167fc2 through ba7af78d.

Runners that do not get a fallback leg in this PR, and why:

  • pr-verify.yml (check-intent): a third job would break .github/scripts/pr-verify/workflow.test.mjs, which pins the jobs to [check-intent, check-intent-subscription], asserts the subscription job has no outputs:, and allows no secret but the two PR_VERIFY_* ones; probe/parity.test.mjs:16 pins the same two jobs. Both files are outside this change's scope.
  • pr-run-activity-read.yml (pr-unblock's fix-ci, address-feedback, review-dependency and resolve-conflicts lanes, pr-refine's fix-docs, and the issue lane): the legs are separate calls by each caller, so a fallback leg needs a third call in every caller, including work-resolve-issue.yml and pr-refine.yml. It would also break scripts/check-read-caller-keys.mjs:34-36, the allowlist of secret keys a caller may pass to the read runner, and .github/scripts/pr-unblock/workflow-gates.test.mjs:237, which pins the jobs that follow address-feedback. Adding the leg to the runner alone, with no caller that runs it, would be a path nothing uses.
  • pr-unblock.yml adjudicate-feedback: it runs on the API key only and has no token leg to hand off from.
  • pr-run-activity-write.yml: it runs claude-code-action, not run-claude-cli, so it has no launcher leg.

Verification

  • node --test .github/actions/resolve-config/callers.test.mjs: 182 pass, 0 fail. Before the catalog and test edits, the two catalog tests failed on FALLBACK_CLAUDE_CODE_OAUTH_TOKEN.
  • node --test over .github/scripts/pr-verify/probe/parity.test.mjs, probe/replay.test.mjs, pr-verify/workflow.test.mjs and .github/scripts/pr-unblock/*.test.mjs: 800 pass, 0 fail.
  • bash scripts/ci-review-lanes.test.sh: 88 pass. bash scripts/ci-review-plugin-source.test.sh: 10 pass. bash scripts/check-read-caller-keys.test.sh: 49 pass. The read-runner suites (collect-patch-lane, act-turns-denied, pr-unblock-trusted-base, read-lane-broker-check, read-runner-dispatcher-gate, write-runner-never-rules) pass.
  • node .github/standards/runner-policy/runner-policy.mjs --root .: "Runner policy passed."
  • actionlint and zizmor --offline on the seven workflows: no findings.
  • bash scripts/check-changelog-fragments.sh --check-required origin/main: no fragment-mode plugin changed, so none is required.

Left out: a live fallback run. A real limit stop or credit stop on both role credentials cannot be produced on demand.

Related

🤖 Generated with Claude Code

… pass the fallback token

Repin the reusable-workflow callers (pr-review-hosted.yml,
pr-review-security-hosted.yml, intake-triage.yml) and the composite-action
callers (pr-run-activity-read.yml, pr-run-activity-write.yml, pr-unblock.yml,
pr-verify.yml) to v0.65.12 (78ee7a26). The three reusable callers pass
FALLBACK_CLAUDE_CODE_OAUTH_TOKEN by name, as the runner-policy contracts for
that SHA require, so the reusables' fallback job can run after the
subscription job hands off.

The lane-funding catalog lists the fallback secret for the three lanes that
now read it, and callers.test.mjs admits it only on a call of a ci-workflows
reusable with a fallback job. The pin tests move with the SHA; lane.cjs at
78ee7a26 passes --permission-prompts none at lines 404-405.

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

chatgpt-codex-connector Bot commented Oct 26, 2026 •

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 ✅ Completed 2026-10-26T01:56:52.513325Z 95662f6 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.

@kyle-sexton kyle-sexton added the do-not-merge Hard merge gate: do not merge while applied. label Oct 26, 2026
@kyle-sexton

Copy link
Copy Markdown
Member Author

Held by Lane Funding: on v0.65.12, pr-review-cli's status job fails with failure-class=no-outcome when the key leg is skipped by the credit window and the subscription leg succeeds (runs 40256492380, 40256492385). Merging this would break the review lanes. The fix and v0.65.13 are in progress in ci-workflows; this PR gets repinned to v0.65.13 and the label removed once that lands.

kyle-sexton and others added 4 commits October 25, 2026 22:29
v0.65.13 fixes the pr-review-cli status job's read of the token leg's
job result (ci-workflows#892), which made v0.65.12 report no-outcome
when the key leg was skipped by the credit window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton kyle-sexton changed the title ci(lanes): repin the Claude lane callers to ci-workflows v0.65.12 and pass the fallback token ci(lanes): repin the Claude lane callers to ci-workflows v0.65.13 and pass the fallback token Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

Security review: PR #10151 (head 784414c)

No security findings.

critical-findings: 0

Scope read: the whole diff (12 files) and the PR context. No changed file matches a trusted path (.github/standards/trusted-actors/**, .github/actions/check-trusted-trigger/**, CODEOWNERS, .gitmodules). The diff changes no plugin manifest and removes no standing instruction, so there are no manifest <field> lines. I did not consult the instruction exception register, because nothing in the diff deletes or softens an instruction.

What I checked

  • New secret path. FALLBACK_CLAUDE_CODE_OAUTH_TOKEN is passed by name, never secrets: inherit, to three reusable calls: pr-review-hosted.yml, pr-review-security-hosted.yml and intake-triage.yml. The reusables are pinned by full 40-character SHA, and the job-level permissions are unchanged. The token reaches the same pinned ci-workflows code that already receives the role key and role token. The diff adds no new consumer, no new step that echoes it, and no fork-reachable trigger.

  • Catalog and test loosening. callers.test.mjs now lets a lane read the fallback secret only when both conditions hold:

    • job.uses matches melodic-software/ci-workflows/.github/workflows/(intake-triage|pr-review-cli).yml@<40-hex>, and
    • the job passes secrets.FALLBACK_CLAUDE_CODE_OAUTH_TOKEN by that exact expression.

    The regex is anchored at both ends, so a lookalike repository or a tag ref doesn't match. The other lanes (refine-issue and the activity and unblock runners) don't get the secret, so the exemption is narrow.

  • Composite-action callers. The bumps in pr-run-activity-read.yml, pr-run-activity-write.yml, pr-unblock.yml and pr-verify.yml are pin-only. No fallback input is wired, so no new credential reaches those runners.

Observation, not counted as a finding. The PR description is stale against the head commit.

  • The description says the repin targets v0.65.12 (78ee7a26…). The diff pins ba7af78d291f10bd75651857a0db2357c23df0fe (v0.65.13) everywhere.
  • The description's checks (cliArgs and legEnv unchanged, lane.cjs:404-405 passes --permission-prompts none) are stated for e5167fc2→78ee7a26. The PERMISSION_PROMPTS_NONE_LAUNCHERS allowlist in .github/actions/resolve-config/callers.test.mjs now trusts ba7af78d on that basis.
  • I can't read ci-workflows from here, so I couldn't confirm that ba7af78d still passes --permission-prompts none at the cited lines. The reviewer or the author should confirm that against the v0.65.13 lane.cjs, since the allowlist exists to catch exactly that drift. The pin-hygiene side (SHA pin vs tag) is zizmor's lane, so I did not re-report it.

Spend, not a hold: after merge, the code review, security review and intake triage lanes can fall back to the central org subscription token once the role key and role token have both stopped. That draws on subscription usage only and starts no billed resource, so I set no hold and didn't count it as a finding.

@kyle-sexton kyle-sexton removed the do-not-merge Hard merge gate: do not merge while applied. label Oct 26, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude-review: 1 inline finding(s) on 784414c953c1b1f2c167eed00f2d911dd296c439; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread .github/actions/resolve-config/callers.test.mjs
@github-actions

Copy link
Copy Markdown
Contributor

claude-review reviewed 784414c953c1b1f2c167eed00f2d911dd296c439: 1 finding(s), 1 as inline comments.

The pin and the repo's checks line up, but the PR description describes a different release than the one the diff pins.

Pin consistency (checked by reading, not run). Every uses: change in the 7 workflows moves e5167fc2… to ba7af78d291f10bd75651857a0db2357c23df0fe, annotated # v0.65.13. The test pins and replay.mjs use the same SHA. .github/standards/runner-policy/policy.json already has contracts for ba7af78d (lines 441 and 1268), next to the 78ee7a26 ones. The only remaining mentions of e5167fc2 are the historical launcher list and its comment in callers.test.mjs, which is intended. The fallback secret is passed by name on the three reusable calls (intake-triage.yml, pr-review-hosted.yml, pr-review-security-hosted.yml), and none uses secrets: inherit. The callers.test.mjs exception accepts it only for a 40-hex-pinned intake-triage.yml or pr-review-cli.yml call that passes it by exact name. The check on the reusable and secret is tight. I did not run any tests, actionlint or zizmor. The verification results in the PR body are the author's claims, and I did not confirm them.

Finding (description does not match the diff, at the pin). The PR body says "Repins every Claude lane caller to ci-workflows v0.65.12 (78ee7a26…)". The diff pins v0.65.13 (ba7af78d…), which matches the title and the last commit. The body's evidence is all about the other SHA:

The code now cites lane.cjs:404-405 for ba7af78d and replay.mjs says its constants "were read from that SHA". Nothing in the PR shows the diff from 78ee7a26 to ba7af78d was read. That range could change cliArgs, legEnv, the CLI flags, or the --permission-prompts none behavior, and the callers had pinned that behavior (see the inline comment). Please update the description to v0.65.13 and state the e5167fc2…ba7af78d composite-action delta, or confirm the line citation against ba7af78d.

Minor, not counted. scripts/ci-review-plugin-source.test.sh changes its "a tag ref" fixture to v0.65.12. That is harmless, because any tag is rejected, but it is a stale number next to the v0.65.13 pin.

Closing issues. None. The PR context says there is no related issue.

Trusted paths. No changed file matches the TRUSTED PATHS globs, so no extra scrutiny applied. policy.json is not in the diff.

Spend. More fallback-subscription usage after a limit stop draws on subscription credit only. It is not a hold and not counted.

@kyle-sexton
kyle-sexton enabled auto-merge October 26, 2026 02:41
@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit a90f362 Oct 26, 2026
121 of 124 checks passed
@kyle-sexton
kyle-sexton deleted the ci/repin-fallback-v0.65.12 branch October 26, 2026 02:47
kyle-sexton added a commit to melodic-software/standards that referenced this pull request Oct 26, 2026
…ed (#884)

Refs: melodic-software/claude-code-plugins#10151

## Summary

Owner decision (2026-10-25): a ci-workflows repin moves only the pinned
paths a
release actually changed, not every pin, and the existing repin tool
(`components/claude-lanes/repin-callers.sh`) stays the single source of
truth for
repins.

Evidence:

- The `v0.65.12...v0.65.13` compare changed only
`.github/workflows/pr-review-cli.yml`
  and `.github/scripts/pr-review-cli.test.cjs`.
- Even so, the consumer repin melodic-software/claude-code-plugins#10151
rewrote 21 `uses:`
lines: intake-triage, the `run-claude-cli`,
`apply-claude-managed-settings` and
  `report-lane-outcome` composites, and pr-review-cli.
- Each needless move forces runner-policy contract PRs for job-level
reusable workflows.
- Each needless move also fails lane runs already in flight with a
workflow-modified 403
  (4 intake runs on melodic-software/claude-code-plugins#10147).

## Fix

`repin-callers.sh apply` now decides each
`melodic-software/ci-workflows/<path>@<sha>` ref
separately:

- **One compare per distinct pinned SHA**,
`compare/<pinned>...<release>`, cached for the
run. The same response feeds the downgrade fence and the file list. The
fence is
unchanged in meaning: a fallback-form pin the release does not contain
(`behind` or
  `diverged` in this direction) is left as is.
- **Moved**: the release changed the reusable workflow file, anything
under the composite
directory (tests included), or a path the pin reaches through a
commit-relative
  `uses:`. Those are a step-level `$/<dir>` action or a job-level
`./.github/workflows/<file>` nested workflow, the two forms
runner-policy diffs. Each is
read once from the release, transitively. intake-triage, for example,
runs
`$/.github/actions/run-claude-cli` and `report-lane-outcome`, so a
change to those moves
  intake-triage too.
- **Fail closed**, moving every pin at that SHA, in two cases: the
compare lists 300 or
more files (the documented cap), or it changed a file that is not a test
(`*.test.*`),
not a `README.md`, not under `docs/`, and not reached by any pin (for
example a shared
  `.github/scripts/*.cjs` or `schemas/`). That is today's behavior.
- **Unchanged pins stay byte-identical.** Moved pins get the release SHA
with the
  `# vX.Y.Z` comment. The version note lists what moved and what stayed.
- **`--root <dir>`** runs the same tool against any repository. It
discovers every
git-tracked `.github/` YAML file that pins ci-workflows, so a consumer
checkout such as
claude-code-plugins repins with this tool instead of hand edits. The
rename map is read
from beside the script. The standards lane's invocation (no `--root`,
enumerated
callers) is unchanged. The apply step now gets `GH_TOKEN`, because it
calls `gh` on any
  day a new release resolves.

`repin-policy-lockstep.mjs` plans only the pins apply moved, so an
unchanged reusable pin
keeps its existing contract and needs no contract copy. Its own caller
rewrite
(`rewriteCallerFiles`) is removed: it rewrote every pin in its target
files to the new
SHA, which would have undone the selective repin, and apply already
rewrites those files.
Its two unit tests go with it.

Out of scope and unchanged: no runner-policy contract rule is loosened,
nothing
auto-admits a secret-bearing runner-input contract, and
`components/runner-policy/policy.json`
semantics are untouched.

## Verification

Run from the worktree root:

- `bash harness/shell/run-tests.sh
components/claude-lanes/repin-callers.test.sh`: pass,
  161 assertions (the existing 129 plus 32 new). New cases:
- only `pr-review-cli.yml` changed moves only that ref, with one compare
call;
  - a shared-script change moves all refs;
  - a composite-directory change moves only that composite;
  - a composite reached through a nested workflow moves the reusable;
- an inert-only change moves nothing, while the same files at the
300-file cap move
    everything;
  - two distinct SHAs cost two compares;
- the standards lane's own invocation moves only the changed lane
caller.
- Mutation check: forcing every decision to `move`, dropping
fail-closed, dropping reach,
dropping the cap, dropping the compare cache, counting tests, or
dropping the directory
prefix rule each turns new cases red. Inverting the fence statuses turns
20 assertions
  red.
- `node --test components/claude-lanes/repin-policy-lockstep.test.mjs`:
23 pass. The two
new tests (only moved pins are planned; `settleLockstep` leaves an
unmoved caller pin
  alone) fail against the previous script.
- `bash harness/shell/run-tests.sh
components/claude-lanes/claude-lanes.test.sh` and
`node --test components/runner-policy/runner-policy.test.mjs` (355):
pass.
- `shellcheck --rcfile .shellcheckrc`, biome, actionlint and zizmor on
the changed files:
  clean.
- End to end against the live v0.65.13 compare on a copy of
claude-code-plugins'
  `.github/` from before #10151 (442 files, 47 pins):
- Real snapshot: 46 of 47 pins move, because none of the pinned SHAs is
v0.65.12. The
    lane pins were at v0.65.11, and `v0.65.11...v0.65.13` also changed
`schemas/lane-outcome.v1.schema.json`, so they fail closed exactly as
#10151 moved
them. The v0.55, v0.58 and v0.59 pins fail closed on
`.github/scripts/*.cjs` and
    `.claude/rules/*.md`.
- Synthetic variant with every pin set to v0.65.12: only the two
`pr-review-cli.yml`
    lines move, and the other 45 stay byte-identical.

Consumer command, from any directory with an authenticated `gh`, `jq`,
GNU sed and bash
4.4 or newer:

```bash
GITHUB_OUTPUT=/tmp/repin.out bash <standards-checkout>/components/claude-lanes/repin-callers.sh resolve melodic-software/ci-workflows
GITHUB_OUTPUT=/tmp/repin.out bash <standards-checkout>/components/claude-lanes/repin-callers.sh apply <tag> <sha> <date> --root <consumer-checkout>
```

`resolve` writes `tag`, `sha` and `date` to the output file. `apply`
leaves the moved pins
in the consumer's working tree and writes `changed`, `old-sha`,
`old-tags` and
`version-note`.

## Related

- Refs melodic-software/claude-code-plugins#10147 (in-flight intake runs
failed with a
  workflow-modified 403 by the full repin).
- The inert set stays tests, `README.md` and `docs/` by owner decision:
lanes may load other
`.md` files as instructions, so a change to one can change what a pin
runs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01DpzEJAE4EMndcWQXiKSZYn

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant