Skip to content

fix(ci): resolve the release lane's model through a lane slot and queue every run - #12736

Merged
kyle-sexton merged 5 commits into
mainfrom
fix/upstream-apply-changelog-route
Nov 3, 2026
Merged

kyle-sexton merged 5 commits into
mainfrom
fix/upstream-apply-changelog-route

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Refs: #7407

Summary

The release intake lane (upstream-apply-changelog.yml, from #12508) cannot process any release. Its model step calls resolve-config with model-route: resolve-issue, and resolve-config rejects that because resolve-issue is not one of its run routes (.github/actions/resolve-config/resolve.mjs:20). Every run that reached the model leg failed with "resolve-issue is not a run route": 41565429093, 41566462920, 41567290470.

The workflow's concurrency also kept GitHub's default queue: single, which cancels a pending run when a newer one queues. That breaks #7407's acceptance item "two releases in quick succession produce two queued runs, and neither run is cancelled" (workflow syntax, concurrency.queue).

Fix

  • docs/conventions/pr-pipeline.yaml gets a new skill activity, apply-changelog (harness-ops:changelog, apply --ci, effect: read). It becomes the lane's first slot, routed to the code-fix profile that resolve-issue pointed at. The model step resolves it in lane + activity mode, as push-branches and publish already do. No RUN_ROUTES or broker change. The broker loads resolve.mjs from this repository's main (azure-iac ADR 0002), so the old "pinned resolver" comment was stale and is replaced.
  • lane-grants.json adds "apply-changelog": "read". lane-grants.test.mjs requires every resolved slot to be listed. It is a read grant and no mint names it.
  • Every later model-job step requires steps.models.outputs.applies == 'true', so enabled: false on the slot stops the model.
  • Top-level concurrency adds queue: max, as pr-feedback-dispatch.yml does. Queued schedule ticks stay cheap: each is a gate-only run of at most 11 REST calls that skips a done release, and the 24 h start cap (3) stops a retaken failed release before the model.

Verification

  • New callers.test.mjs test: it runs resolve-config's own main() over each resolve-config step's inputs on schedule and workflow_dispatch (any GitHub call throws), and asserts that the model step yields a model, effort and max-turns. It fails with the live error on the old step and passes on the new one.
  • node --test over .github/actions/resolve-config/*.test.mjs and upstream-apply-changelog.test.mjs: 918/918 pass. scripts/check-affected.sh --base origin/main --no-fetch: 11 passed, 4 skipped. actionlint runs in CI only; queue is already used in this repository's workflows.
  • Resolved locally: {"profile":"code-fix","model":"opus","effort":"high","max-turns":75}.

Related

  • feat(lanes): Claude Code release intake lane #7407 done-when amendment (read marker, CLI version) is posted on the issue. The owed acceptance run follows this merge.
  • Touches .github/actions/resolve-config/, which may sit on the merge lane's risk floor.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VcupojVgxFqBnrah9DwFLE

kyle-sexton and others added 5 commits November 3, 2026 00:10
…ue every run

Refs: #7407

The upstream-apply-changelog model job called resolve-config with
`model-route: resolve-issue`, which resolve-config refuses: resolve-issue
is a skill activity's route, not a run route, so every release run failed
before the model. The lane now has an `apply-changelog` skill slot
(harness-ops:changelog, effect read, route code-fix), and the job resolves
it with `lane` + `activity`, as push-branches and publish do. Later steps
also wait on its `applies` output, so disabling the slot stops the model.

The workflow's concurrency group now sets `queue: max`, so a newer run no
longer cancels a pending one and two dispatches in quick succession both
run.

callers.test.mjs runs resolve-config's own entry point over every
resolve-config step of the workflow on schedule and workflow_dispatch;
it fails on the old step with the live error.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VcupojVgxFqBnrah9DwFLE
…angelog-route

# Conflicts:
#	.github/actions/resolve-config/lane-grants.json
@kyle-sexton
kyle-sexton marked this pull request as ready for review November 3, 2026 07:08
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 3, 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-11-03T07:09:38.215614Z 4ffecc3 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 Nov 3, 2026

Copy link
Copy Markdown
Contributor

claude-review reviewed 4ffecc33d0a8243706dc41cfcefd74d7f087135e: read 5 of 5 changed file(s); 0 finding(s), 0 as inline comments.

Code review: no findings

I read every hunk of all 5 changed files. I also checked the resolver code the change depends on and the rest of upstream-apply-changelog.yml.

What the change does

  • The model job's step Resolve the model (id models) now resolves through lane + activity mode: lane: upstream-apply-changelog, activity: apply-changelog. It previously used model-route: resolve-issue, which resolve-config refuses because it only accepts run routes.
  • docs/conventions/pr-pipeline.yaml adds a new apply-changelog skill activity with effect: read, gating: advisory and reads-untrusted: true.
    • It is the lane's first slot.
    • apply-changelog-branches now needs it.
    • Its route goes to the code-fix profile, the same profile resolve-issue used.
  • lane-grants.json lists the new slot as read.
  • Every later step in the model job also requires steps.models.outputs.applies == 'true', so enabled: false on the slot stops the model. This matches how the push-branches and publish jobs already check steps.config.outputs.applies.
  • Workflow-level concurrency adds queue: max, which other lanes in this repository already use widely.

What I checked

  • Disabled slot. If the slot is disabled, resolveSlot returns applies: false (resolve.mjs#L831-L832). Every later step in the model job then skips, including Run the changelog apply and the steps gated on steps.run.outcome != 'skipped'. sealed-length stays empty, so the scan job and everything downstream skip cleanly (upstream-apply-changelog.yml#L417-L419).
  • Test coverage. The new test in callers.test.mjs collects all three ./.base/.github/actions/resolve-config steps in the workflow (lines 229, 663 and 800). None of their with values is an expression the test cannot evaluate, and its imports (fileURLToPath, join, readFileSync, writeFileSync, RESOLVE, workflow) are all present. The test would have caught the original misroute.
  • Comment accuracy. The updated lane comment ("Started every ten minutes") matches the cron 5-59/10 * * * *.
  • Changelog fragment. bump: patch fits a fix.

Verification. The PR's test results (918/918 pass, and the resolved code-fix → opus/high/75) are claimed by the author. I did not run them: this lane has no shell.

Closing issue. The PR only references #7407 (Refs:) and closes no issue, so there are no acceptance criteria to check.

Spend. With queue: max, schedule ticks that arrive while a run is in progress now wait instead of being cancelled. Each one is a gate-only run that uses GitHub Actions runner time. This draws no new billed resource, so it is not a hold.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

claude-security-review reviewed 4ffecc33d0a8243706dc41cfcefd74d7f087135e: read 5 of 5 changed file(s); 0 finding(s), 0 as inline comments.

Security review: no findings

I read all 5 changed files in full. Nothing in this pull request widens a trust control or reaches a leak, a compromise or a charge.

Controls checked

  • Lane grant (lane-grants.json, broker grant file). The PR adds "apply-changelog": "read" to the upstream-apply-changelog lane (lane-grants.json#L11). On paper this lets the broker mint a token for one more activity, but that token is read-only. No job in this workflow can obtain a token through it:
    • The model job, which reads the untrusted changelog, holds only contents: read and no id-token: write (upstream-apply-changelog.yml#L182-L183). It cannot prove its identity to the broker, so it cannot mint.
    • Only push-branches and publish hold id-token: write (L629, L770). Each requests a fixed activity: apply-changelog-branches and publish-changelog-intake (L711, L853). The model's untrusted input cannot change those names. Workflow files cannot be edited by any lane patch, and lane-grants.json is in every patch-rules row's refuse list.
    • A read grant is also narrower than the write grants those two jobs already hold.
    • The resolve-config README says the broker reads this file only after its one-time switch-over (ADR 0085 Decision 7).
  • Grant config (docs/conventions/pr-pipeline.yaml, decides what the broker mints).
    • The new apply-changelog activity has effect: read, gating: advisory and reads-untrusted: true. It is added as the lane's first slot, and apply-changelog-branches now waits on it (needs: [apply-changelog]).
    • The code-fix model profile it routes to is the one resolve-issue already used.
    • No effect, permission or allowlist grows.
  • Kill switch. The model job still checks the kill switch before resolving the model (L215-L226). Every later model-job step now also requires steps.models.outputs.applies == 'true', so this change can only stop runs, never let more through.
  • Model job isolation. Credentials are still not persisted, git config is still isolated and the symlink check on staged plugins is unchanged. Only the if: conditions and one error message changed.
  • Test (callers.test.mjs). It runs resolve-config locally, and any GitHub API call throws. It is test code with no security surface.
  • Release note (.changes/ file). Prose only.

Not checked: the PR deletes no instruction text and changes no plugin manifest, so the instruction-surface and manifest checks did not apply.

Usage, not a finding: concurrency.queue: max keeps every waiting run (GitHub caps the queue at 100) instead of cancelling a pending one. This spends more Actions minutes on gate-only runs of the 10-minute schedule; the model leg is still capped by the gate's 24-hour start limit.

PR description: no instructions aimed at this review.

critical-findings: 0

@kyle-sexton
kyle-sexton added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 8a40f13 Nov 3, 2026
77 checks passed
@kyle-sexton
kyle-sexton deleted the fix/upstream-apply-changelog-route branch November 3, 2026 07:25
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