fix: pull-requests scope for the door's two PR-API calls; docs catch up

The cast-twin blocker (claude-bot): a declared permissions: block zeroes
unspecified scopes, so the label read and the bump fallback's gh pr
create could only 403 — every genuine ceremony would end red at the
label check. pull-requests: write added, consumers named. CONTRIBUTING
step 3 and the changelog entry now tell the shipped story: push-to-main
door, event.before interlock, self-re-arm, manual-path bump stays the
operator's.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
dan-claude-bot 2026-07-19 16:34:29 +00:00
parent 0dbcad2e3a
commit 700e2185eb
3 changed files with 29 additions and 17 deletions

View file

@ -35,7 +35,11 @@ on:
branches: [main]
permissions:
contents: write
contents: write # tag ref + gh release create + the bump push
# Two consumers (a declared permissions: block zeroes every unspecified
# scope): the decide step's label read (commits/<sha>/pulls) and the bump
# fallback's `gh pr create --label`.
pull-requests: write
jobs:
release:

View file

@ -36,19 +36,24 @@ on the way to cutting its first release, and this file starts there.
### Added
- **Merging a release-labeled PR IS the release** (#47) — the rig twin of
heavy-duty/box#96, born of the ceremony retro: the tag was a separate,
manual, silent-when-forgotten step, and a forgotten tag produces no red
X. `release.yml` now also fires when a PR into main closes, gated on
merged AND the `release` label, and asserts in order — fail-loud,
creating nothing: `VERSION` at the merge commit is non-`-dev`; `VERSION`
*changed in this PR* (the interlock that fails a mislabeled ordinary PR);
the changelog section for that version extracts non-empty via the same
`changelog_section`; no tag or release exists yet. Then, in the same job,
it API-creates the tag at the merge commit and publishes the release with
the extracted notes. A `GITHUB_TOKEN`-created tag never fires the
tag-push trigger, so the paths cannot double-publish — and that tag-push
path survives intact as the documented manual fallback and backfill.
- **Merging a release-labeled PR IS the release — and the release re-arms
main itself** (#47) — the rig twin of heavy-duty/box#96, born of the
ceremony retro: the tag was a separate, manual, silent-when-forgotten
step, and a forgotten tag produces no red X. `release.yml` now fires on
pushes to main (fork-sourced ceremony PRs get a read-only token on
`pull_request` events), reading the transition from the push itself:
`event.before` to the pushed head. A decide step answers four states —
release-flow *work* merged under the `release` label (`-dev` endstates,
the post-release window) no-ops green with a NOTICE; the two genuinely
ambiguous bare states refuse loudly; a true transition then requires a
merged, `release`-labeled PR behind the commit (read via the API — the
label is the operator's declared intent). Then, in the same job, it
API-creates the tag at the merge commit, publishes with the extracted
notes — and bumps main to `X.Y.(Z+1)-dev` itself, direct push with a
loud open-a-PR fallback, so no follow-up bump PR exists on the paved
road. A `GITHUB_TOKEN`-created tag never fires the tag-push trigger, so
the paths cannot double-publish — and that tag-push path survives intact
as the documented manual fallback and backfill.
- **Tagged releases, and an installer that installs them** (#32) — the rig
half of the flow designed in heavy-duty/box#83, near-verbatim. A release

View file

@ -61,9 +61,12 @@ top of #32/box#83's tag flow):
box's tag scheme) and publishes the GitHub release with that section as
the body. No assets — the source tarball for the tag is the package
`install.sh` downloads.
3. A follow-up (or the next feature PR) bumps main's `VERSION` to
`X.Y.(Z+1)-dev`, so a dev install never impersonates the release in the
`versions/<v>` layout.
3. The release re-arms main itself: the same workflow run bumps `VERSION`
to `X.Y.(Z+1)-dev` and pushes the commit straight to main — no
follow-up PR (it opens one only if branch protection refuses the
direct push, loudly). A dev install therefore never impersonates the
release in the `versions/<v>` layout. On the *manual* tag path the
bump stays yours: open the one-line PR after publishing.
Manual fallback (and backfill): if the merge-path run fails, fix what it
named, then tag the merge commit `X.Y.Z` by hand and push the tag — the