Skip to content

feat(pr-pipeline): fund intake, pr-unblock and pr-verify from their own role secrets - #9552

Merged
kyle-sexton merged 4 commits into
mainfrom
feat/lane-role-secrets
Oct 24, 2026
Merged

kyle-sexton merged 4 commits into
mainfrom
feat/lane-role-secrets

Conversation

@kyle-sexton

Copy link
Copy Markdown
Member

No related issue: lane-funding roles plan (D)5, owner-approved 2026-10-24.

Summary

The intake, pr-unblock and pr-verify lanes stop reading AUTOMATIONS_CLAUDE_API_KEY and AUTOMATIONS_CLAUDE_CODE_OAUTH_TOKEN and run on their own funding roles' org secrets, which now exist: INTAKE_*, PR_UNBLOCK_* and PR_VERIFY_*. Each run's lane-outcome credential= then names its own role, so spend is attributed per role.

Fix

  • intake-triage.yml: role: intake, INTAKE_CLAUDE_API_KEY and INTAKE_CLAUDE_CODE_OAUTH_TOKEN on the reusable call, and INTAKE_CLAUDE_CODE_OAUTH_TOKEN on the refine-issue claude-code-action step (the catalog files refine-issue under intake). The reusable moves from d02b8697 (v0.65.0) to b0ecc27, ci-workflows main one docs-only commit later, with no .github change. A runner-policy contract holds one role, so the intake role needed a new SHA: standards#863 registered it and left the v0.65.0 entry for the old caller. The pin comment takes the pin-comment convention's fallback form, since b0ecc27 is untagged.
  • pr-unblock.yml: every job that passed AUTOMATIONS_* passes PR_UNBLOCK_*, and every role: automations is role: pr-unblock (fix-ci, fix-ci-rung-up, address-feedback, review-dependency, resolve-conflicts, their -subscription legs, and the write-runner jobs that name the role).
  • pr-refine.yml: fix-docs, fix-docs-subscription and apply-docs-patch move the same way, because .github/lane-funding.json lists fix-docs under the pr-unblock role.
  • pr-verify.yml: both legs pass role: pr-verify and PR_VERIFY_*, including the credential-presence checks and the owner warnings.
  • .github/lane-funding.json: each moved lane's currentSecrets names its role's secrets. The role list stays at six (reviews, issue-lane, intake, pr-unblock, pr-verify, automations): each one has lanes in a workflow and both its secrets exist in the org. The plan's separate REVIEWS_SECURITY role does not exist; both review lanes read REVIEWS_*.
  • scripts/check-read-caller-keys.mjs (and its header and doc): the read-run secret allowlist admits pr_unblock_claude_api_key and pr_unblock_claude_code_oauth_token in place of the automations_ pair, since no read caller passes those any more.
  • callers.test.mjs: a new test fails when a catalog lane reads a secret that is not its role's, or passes a role: (job-level or step-level) other than its role's id. run-claude-cli builds credential= from role:, never from the secret passed, so this test is what ties the two together. The fix-ci/fix-docs fallback test and the refine-issue test move to the new names.
  • .github/scripts/pr-verify/workflow.test.mjs: pr-verify's role, secret names and warning text.
  • Docs: the pr-pipeline README Funding section already points at the catalog. It drops the "can still be another role's" caveat and states the new test, and its fix-ci, fix-docs and pr-verify paragraphs name their roles. pr-run-activity.md names PR_UNBLOCK_* and the full read-run allowlist.

Left as they are: the review lanes (REVIEWS_*), work-resolve-issue (ISSUE_LANE_*), eval-browser-dispatch and maintenance-audit-lane-runs (AUTOMATIONS_*). No token-broker-pinned file changed.

Verification

  • nice bash scripts/check-affected.sh --tests: 11 passed, 3 skipped, 0 failed.
  • The 30 Node suites it selects, one node --test at a time: 1348 pass, 0 fail (callers.test.mjs 162 of them, pr-verify/workflow.test.mjs 15).
  • The 51 Python modules it selects, one pytest process each on .github/requirements-ci.txt: 2220 passed.
  • scripts/check-read-caller-keys.test.sh: 48 pass, including a new case that AUTOMATIONS_CLAUDE_API_KEY is now refused for a read caller. scripts/check-read-caller-keys.sh --check passes.
  • Runner policy: runner-policy.mjs --root . passes on the policy from standards#863 and fails on the intake caller under the policy synced before it.
  • Mutation check: putting role: automations back on one pr-verify leg makes callers.test.mjs fail.
  • Before push: the diff has no key, token, email address or machine path.
  • Left out: the moved lanes' live runs, which only main can show; they are checked after merge.

Related

  • standards#863: the intake-role runner-policy contract this caller needs.
  • After merge, the first run of each moved lane should report credential=INTAKE_CLAUDE_API_KEY, PR_VERIFY_CLAUDE_API_KEY or PR_UNBLOCK_CLAUDE_API_KEY.

🤖 Generated with Claude Code

kyle-sexton and others added 4 commits October 23, 2026 22:30
…wn role secrets

intake-triage passes role intake with INTAKE_*, on the intake reusable
at ci-workflows b0ecc27 (registered on that role by standards#863).
pr-unblock and pr-refine's fix-docs pass role pr-unblock with
PR_UNBLOCK_*; pr-verify passes role pr-verify with PR_VERIFY_*. The
catalog, the read-run secret allowlist and the docs follow, and
callers.test.mjs checks each catalog lane's secrets and role: together.

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

Every pr-run-activity read or write caller in a single-role catalog
workflow must pass that role, so a write caller that reads no Claude
secret cannot keep a stale role label. pr-run-activity.md credits
fix-docs to pr-refine.yml, and ADR 0074 names the pr-unblock role.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 24, 2026 03:05
@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-24T03:05:22.775601Z bb74d98 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

Copy link
Copy Markdown
Contributor

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

No security issues found in this pull request.

critical-findings: 0

Scope: I read the full diff (.claude-lane/pr.diff) and the PR context. The PR context contained no embedded instructions. No plugin manifest (plugin.json or marketplace.json) is touched, so there are no manifest <field> lines. The change touches no trust-gate path, so there is no hold.

What I checked:

  • Secret scoping: Every AUTOMATIONS_* reference in intake-triage, pr-unblock, pr-refine and pr-verify moves to the matching role's secret (INTAKE_*, PR_UNBLOCK_*, PR_VERIFY_*). The role: inputs move with them. The diff never sets secrets: inherit and never widens a permissions: block.
  • Read-run allowlist: scripts/check-read-caller-keys.mjs swaps automations_* for pr_unblock_*. It still rejects every other role's secret, and the new test case confirms AUTOMATIONS_CLAUDE_API_KEY is now refused. The allowlist narrows rather than widens.
  • Remaining consumers: A grep of .github found AUTOMATIONS_* only in eval-browser-dispatch.yml, maintenance-audit-lane-runs.yml, the role catalog, and the synced runner-policy file. The PR says it leaves the first two unchanged, so none of them is a pr-unblock, pr-refine, pr-verify or intake caller that this change should have moved.
  • New tests: The callers.test.mjs test ties each catalog lane's secrets and role: values to its role. It adds a guard and removes none.
  • Reusable pin: The intake reusable moves from the tagged v0.65.0 SHA to an untagged SHA, b0ecc27. It is still pinned by full SHA, so it is not a loosened pin and is zizmor's lane. The "docs-only commit, no .github change" claim comes from the PR description and I could not verify it from the diff. The reviewer may want to confirm the upstream commit before merge.
  • Instruction surface: No standing instruction, guardrail, hook or allowlist was deleted or softened. The doc edits only rename roles and describe the new test.

I did not consult docs/conventions/instruction-exception-register/README.md, because no instruction-surface deletion was in the diff.

@github-actions

Copy link
Copy Markdown
Contributor

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

No findings. I read the full diff (14 files) and the PR context, and checked the secret and role changes against the rest of the repository by search. I ran no tests, so the PR's verification section is author-claimed and unverified by me.

What I checked

  • No AUTOMATIONS_* left on the moved lanes. A search of the working tree finds no remaining role: automations and no AUTOMATIONS_CLAUDE_* secret in intake-triage.yml, pr-unblock.yml, pr-refine.yml or pr-verify.yml. The remaining hits are the lanes the PR leaves alone (eval-browser-dispatch.yml, maintenance-audit-lane-runs.yml), the policy file, historical changelog lines, and test fixtures.
  • Role and secret agree on every job. Each job's role: is paired with its own role's secrets, including the pr-verify credential-presence checks and the warning text. The lane-funding.json currentSecrets match the workflow edits.
  • Intake pin has a runner-policy entry. .github/standards/runner-policy/policy.json already has an intake-triage.yml@b0ecc273… entry with role: intake and INTAKE_CLAUDE_* as the allowed secrets. The caller's inputs and secrets match that entry.
  • Read-run allowlist matches its callers. Dropping automations_* from check-read-caller-keys.mjs is safe. The only read callers are the pr-unblock and pr-refine jobs, and they now pass PR_UNBLOCK_*. The shell test adds a case that AUTOMATIONS_CLAUDE_API_KEY is now refused.
  • New role test is sound. The callers.test.mjs test checks job-level and step-level role: and the runner jobs that read no secret. Its second loop only constrains a workflow that has a single role (pr-unblock.yml, pr-refine.yml), so it does not misfire on intake-triage.yml or pr-verify.yml.
  • Changelog fragment. bump: minor is reasonable for a funding change that adds new required org secrets, which the PR states exist.

Manifests
No plugin.json or marketplace.json is touched, so no manifest <field> lines apply.

Closing issues
The PR context lists none ("No related issue"), so there are no acceptance criteria to check.

Hold
None. The change moves credentials between existing runs and adds no model call, schedule or runner. It touches no trusted-actors, CODEOWNERS or .gitmodules files.

Not verifiable from the diff
The new INTAKE_*, PR_UNBLOCK_* and PR_VERIFY_* org secrets are claimed to exist. I can't confirm that here. The PR says live runs are checked after merge, and until then an unset secret would make the moved lanes fail.

@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 24, 2026
Merged via the queue into main with commit 35e07f0 Oct 24, 2026
57 of 68 checks passed
@kyle-sexton
kyle-sexton deleted the feat/lane-role-secrets branch October 24, 2026 03:46
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