Skip to content

Ask for the token permission this workflow actually needs - #21

Merged
darksidemilk merged 1 commit into
mainfrom
ci-sync-needs-no-token-write
Aug 18, 2026
Merged

Ask for the token permission this workflow actually needs#21
darksidemilk merged 1 commit into
mainfrom
ci-sync-needs-no-token-write

Conversation

@darksidemilk

Copy link
Copy Markdown
Member

Merge this — the merge sync is currently failing on every merge

fogproject's merge stub now fires (that part works), but every run dies at startup:

The workflow is requesting 'contents: write', but is only allowed 'contents: read'.

Both real merges so far — FOGProject/fogproject#1171 and #1172 — ended in startup_failure. Until this lands, every PR merged into working-1.6 or dev-branch shows a red X and no sync happens.

Cause

A reusable workflow's permissions: is a request, not a ceiling. When the caller grants less, GitHub rejects the call outright rather than quietly capping it — no jobs, no logs, just "this run likely failed because of a workflow file issue". That is very hard to read backwards, which is why it took a page fetch of the run to see the actual message.

fogproject's stub grants permissions: contents: read, which is correct — nothing on that path needs a writable GITHUB_TOKEN.

Fix: lower the request, don't widen the callers

The contents: write here has been vestigial since #17. The checkout, the commit, the push and the badge write all run on GitHub App tokens now — grep github.token over the file returns nothing but the comment explaining why the badge stopped using it.

So the request was for a token nothing here holds or uses, and its only real effect was to force every caller to grant write as well. Lowering it to read fixes the caller without widening anyone's permissions. The alternative — making fogproject grant contents: write for a token it never exercises — is the wrong direction.

The comment says not to raise it back, and why: "be safe, ask for write" is the natural instinct, and it is the exact change that breaks every caller at startup. If a step here ever does need the ambient token, it should get its own scoped App token, the way the badge step already does.

CLAUDE.md

Adds this alongside the two trigger rules. Three distinct gotchas have now cost a round trip each on this one integration:

  1. Can the bot's own push raise this trigger? (the 2026-07-28 loop)
  2. Which ref is the trigger read from? (pull_request_target reads the default branch)
  3. Is the caller granting at least what the callee requests? (this one)

Verified

actionlint clean. No github.token / GITHUB_TOKEN usage remains in the workflow, so nothing loses a capability it was using. The scheduled sweep and stable-releases.yml's in-repo call are unaffected — both already grant at least read.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PqufBbuckux8kitJeW3uAK

fogproject's merge stub calls this workflow and grants
`permissions: contents: read`, which is correct -- nothing on that path
needs a writable GITHUB_TOKEN. The call was refused before a single job
started:

  The workflow is requesting 'contents: write', but is only allowed
  'contents: read'.

A reusable workflow's `permissions:` is a REQUEST, not a ceiling, and GitHub
rejects the call outright when the caller grants less rather than quietly
capping it. Both of the stub's first two real merges ended in
startup_failure -- no jobs, no logs, just "this run likely failed because of
a workflow file issue", which is a hard failure to read backwards.

The `contents: write` this asked for has been vestigial since #17. The
checkout, the commit, the push and the badge write all run on GitHub App
tokens now; `grep github.token` over this file returns nothing but the
comment explaining why the badge stopped using it. So the request was for a
token nothing here holds or uses, and its only real effect was to force every
caller to grant write too.

Lowering it to `read` fixes the caller without widening anyone's permissions,
which is the right direction: the alternative is making fogproject grant
write for a token it never exercises.

The comment says not to raise it back, and why -- "be safe, ask for write" is
the natural instinct and it is the exact change that breaks every caller at
startup. If a step here ever does need the ambient token, it should get its
own scoped App token, the way the badge step already does.

Adds the same point to CLAUDE.md, next to the two trigger rules. Three
distinct gotchas have now cost a round trip each on this one integration:
whether the bot can raise its own trigger, which ref a trigger is read from,
and this one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqufBbuckux8kitJeW3uAK
@darksidemilk
darksidemilk merged commit a1bae74 into main Aug 18, 2026
@darksidemilk
darksidemilk deleted the ci-sync-needs-no-token-write branch August 18, 2026 04:52
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