ci: dispatch agent-pane as the release App over workflow_dispatch - #252
Merged
Merged
Conversation
Retires the AGENT_PANE_DISPATCH_TOKEN PAT. Both notify paths — the release: published workflow and the explicit step in release.yml — now authenticate as copse-release-bot, the App this repo already uses to push version bumps past the main ruleset. The dispatch also moves from repository_dispatch to workflow_dispatch. repository_dispatch sits under the Contents permission, so the credential needed Contents: write on agent-pane — the ability to push code there — purely to start a workflow. workflow_dispatch needs only Actions: write, which cannot. The consumer side is copse-dev/agent-pane#1816. Each call mints its own token scoped to agent-pane alone: left empty, create-github-app-token scopes to the current repository, which is why the existing release token could not be reused. release.yml mints a second one rather than widening the first, because actions/checkout persists that one as the job's git push credential for the whole release. That existing mint now pins permission-contents: write instead of inheriting. The App is installed org-wide and agent-pane's workflows need Pull requests: write and Actions: write from it, so an inherited token would have silently widened the moment those were added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jonathanKingston
added a commit
to copse-dev/agent-pane
that referenced
this pull request
Aug 20, 2026
…1816) Replaces `SYNC_PR_TOKEN` and `SCREENSHOTS_PAT` with short-lived installation tokens minted per run from the **copse-release-bot** GitHub App — the same App `streaming-markdown`'s `release.yml` already uses. Why the App beats a PAT here: - Tokens are minted per run and expire in ~1h. No annual rotation deadline, no "the PAT expired and every sync workflow went quietly red". - Not tied to a human, and consumes no seat (the org is 2/2). - Permissions live on the App and are pinned per call site, so each job gets only what it needs. - Only the App id and private key are stored, once, as org secrets. - It is a distinct actor from `GITHUB_TOKEN`, so both properties the PATs existed for still hold: org policy lets it open PRs, and its pushes and PRs trigger downstream CI instead of being recursion-guarded. ## What changed | Workflow | Was | Now | | --- | --- | --- | | `sync-model-catalog`, `sync-model-cards`, `sync-intellect`, `sync-streaming-markdown` | `SYNC_PR_TOKEN` | App, `contents` + `pull-requests: write` | | `promote-develop` | `SYNC_PR_TOKEN` | App, `contents` + `pull-requests: write` | | `ci.yml` `autoformat` / `commit-screenshots`, `reconcile-screenshots` | `SCREENSHOTS_PAT \|\| github.token` | App (`contents: write`) `\|\| github.token` | `sync-streaming-markdown` also drops its `repository_dispatch` trigger for `workflow_dispatch`. That endpoint sits under **Contents**, so the cross-repo credential `streaming-markdown` holds needed `Contents: write` here — the ability to push code — purely to start a workflow. `workflow_dispatch` needs only `Actions: write`, which cannot. The producer side is copse-dev/streaming-markdown#252; **merge that one first or alongside this**, since the two triggers are swapped in lockstep. ## Deliberate non-changes **The App is not granted `workflows: write`.** `SCREENSHOTS_PAT` deliberately lacked the equivalent `workflow` scope, and both screenshot jobs have an escape hatch for merges that touch `.github/workflows/**`. An App token hits the same wall for the same reason, so the guardrail stays — an actor that can rewrite CI definitions org-wide is a bigger grant than auto-resolving those merges is worth. Only the comments and PR-comment text change, to name the App permission rather than the PAT scope. **Permissions are pinned at every mint rather than inherited.** The App is installed org-wide, so an inherited token would silently widen the moment the installation gains a permission. (`streaming-markdown`'s existing release mint is pinned for the same reason in the companion PR — it was inheriting, and it is persisted to disk by `actions/checkout`.) **The `github.token` fallback survives.** `autoformat`, `commit-screenshots` and `reconcile-screenshots` mint with `continue-on-error`, so a missing or broken credential degrades exactly as before — the commit lands, just without the CI re-trigger — instead of reddening a PR. The sync workflows and `promote-develop` fail loudly instead, matching their existing "require the token" preflight. ## Verification - All workflows parse; every embedded `github-script` block passes `node --check`. - `node scripts/run-tests.mts ci-workflow-invariants sync-intellect` — 58/58 pass. Two invariants asserted the old secret names and were updated. - All nine changed files are Prettier-clean. ## Prerequisites (already done) - `copse-release-bot` now holds `actions: write`, `contents: write`, `pull_requests: write`, `metadata: read` — and no `workflows`. - `RELEASE_APP_ID` / `RELEASE_APP_PRIVATE_KEY` are org secrets, confirmed visible to this repo. Once both PRs are green, `SYNC_PR_TOKEN`, `SCREENSHOTS_PAT` and `AGENT_PANE_DISPATCH_TOKEN` can be deleted. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jonathan Kingston <KingstonMailBox@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
jonathanKingston
added a commit
to copse-dev/agent-pane
that referenced
this pull request
Aug 31, 2026
…1816) Replaces `SYNC_PR_TOKEN` and `SCREENSHOTS_PAT` with short-lived installation tokens minted per run from the **copse-release-bot** GitHub App — the same App `streaming-markdown`'s `release.yml` already uses. Why the App beats a PAT here: - Tokens are minted per run and expire in ~1h. No annual rotation deadline, no "the PAT expired and every sync workflow went quietly red". - Not tied to a human, and consumes no seat (the org is 2/2). - Permissions live on the App and are pinned per call site, so each job gets only what it needs. - Only the App id and private key are stored, once, as org secrets. - It is a distinct actor from `GITHUB_TOKEN`, so both properties the PATs existed for still hold: org policy lets it open PRs, and its pushes and PRs trigger downstream CI instead of being recursion-guarded. ## What changed | Workflow | Was | Now | | --- | --- | --- | | `sync-model-catalog`, `sync-model-cards`, `sync-intellect`, `sync-streaming-markdown` | `SYNC_PR_TOKEN` | App, `contents` + `pull-requests: write` | | `promote-develop` | `SYNC_PR_TOKEN` | App, `contents` + `pull-requests: write` | | `ci.yml` `autoformat` / `commit-screenshots`, `reconcile-screenshots` | `SCREENSHOTS_PAT \|\| github.token` | App (`contents: write`) `\|\| github.token` | `sync-streaming-markdown` also drops its `repository_dispatch` trigger for `workflow_dispatch`. That endpoint sits under **Contents**, so the cross-repo credential `streaming-markdown` holds needed `Contents: write` here — the ability to push code — purely to start a workflow. `workflow_dispatch` needs only `Actions: write`, which cannot. The producer side is copse-dev/streaming-markdown#252; **merge that one first or alongside this**, since the two triggers are swapped in lockstep. ## Deliberate non-changes **The App is not granted `workflows: write`.** `SCREENSHOTS_PAT` deliberately lacked the equivalent `workflow` scope, and both screenshot jobs have an escape hatch for merges that touch `.github/workflows/**`. An App token hits the same wall for the same reason, so the guardrail stays — an actor that can rewrite CI definitions org-wide is a bigger grant than auto-resolving those merges is worth. Only the comments and PR-comment text change, to name the App permission rather than the PAT scope. **Permissions are pinned at every mint rather than inherited.** The App is installed org-wide, so an inherited token would silently widen the moment the installation gains a permission. (`streaming-markdown`'s existing release mint is pinned for the same reason in the companion PR — it was inheriting, and it is persisted to disk by `actions/checkout`.) **The `github.token` fallback survives.** `autoformat`, `commit-screenshots` and `reconcile-screenshots` mint with `continue-on-error`, so a missing or broken credential degrades exactly as before — the commit lands, just without the CI re-trigger — instead of reddening a PR. The sync workflows and `promote-develop` fail loudly instead, matching their existing "require the token" preflight. ## Verification - All workflows parse; every embedded `github-script` block passes `node --check`. - `node scripts/run-tests.mts ci-workflow-invariants sync-intellect` — 58/58 pass. Two invariants asserted the old secret names and were updated. - All nine changed files are Prettier-clean. ## Prerequisites (already done) - `copse-release-bot` now holds `actions: write`, `contents: write`, `pull_requests: write`, `metadata: read` — and no `workflows`. - `RELEASE_APP_ID` / `RELEASE_APP_PRIVATE_KEY` are org secrets, confirmed visible to this repo. Once both PRs are green, `SYNC_PR_TOKEN`, `SCREENSHOTS_PAT` and `AGENT_PANE_DISPATCH_TOKEN` can be deleted. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Jonathan Kingston <jonathan@copse.dev> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Retires the
AGENT_PANE_DISPATCH_TOKENPAT. Both notify paths — therelease: publishedworkflow and the explicit step inrelease.yml— now authenticate as copse-release-bot, the App this repo already uses to push version bumps past themainruleset.Consumer side: copse-dev/agent-pane#1816. The two triggers are swapped in lockstep, so merge these together (or agent-pane's first).
Why
workflow_dispatchinstead ofrepository_dispatchrepository_dispatchsits under the Contents permission. That meant the cross-repo credential neededContents: writeonagent-pane— the ability to push code there — purely to start a workflow.workflow_dispatchneeds onlyActions: write, which cannot push anything.agent-pane'ssync-streaming-markdown.ymlalready had aworkflow_dispatchtrigger with aversioninput as its manual backfill path, and already fell back toformat('v{0}', version)when no tag was supplied — and this repo's tags are exactlyv<version>— so the PR-body link it generates is unchanged.Why two token mints
Left empty,
create-github-app-tokenscopes the token to the current repository. That is why the existing release-token at the top of the job cannot be reused for a call againstagent-pane— it genuinely cannot reach it.release.ymlmints a second token rather than addingagent-paneto the first, becauseactions/checkoutpersists the first one as the job's git push credential: widening it would leave a cross-repo credential on disk for the whole release.One thing worth a look
The existing release mint now pins
permission-contents: writeinstead of inheriting the installation's permissions. The App is installed org-wide, andagent-paneneedsPull requests: writeandActions: writefrom it — so without this pin, this token would have silently widened the moment those were granted. It is the one that gets written to disk.Verification
Both workflows parse. Prerequisites confirmed live: the App holds
actions: write, andRELEASE_APP_ID/RELEASE_APP_PRIVATE_KEYresolve here as before.AGENT_PANE_DISPATCH_TOKENcan be deleted once both PRs land.🤖 Generated with Claude Code