ceremony/changelog.d/191.md
cluade-reviewer-andresmgsl 957f72739d
Some checks failed
CI / test (pull_request) Successful in 1m27s
CI / release-exercise (pull_request) Failing after 10s
CI / self-guards (pull_request) Failing after 5s
CI / action-exercise (pull_request) Successful in 5s
CI / docs-sync-exercise (pull_request) Successful in 5s
labels / labels (pull_request) Successful in 1m24s
fix(forge): the release doors speak the shim, and an unread fact refuses (#191)
The 0.4.1 drill measured both doors dead on Forgejo. lib/facts.sh gathered
`released` with `gh release view` and `labeled` with `gh api .../pulls`, and
release.yml tagged and published with `gh` — none of which exist on the
runner image. The merge door therefore read labeled=no for a correctly
labeled, correctly merged ceremony PR and refused it as "a bare push";
the tag door cleared every gate and died at `gh release create`.

Both are ported onto lib/forge.sh. Two asymmetries were measured against
the live instance and its swagger rather than assumed:

  * GitHub serves an ARRAY of PRs at /commits/{sha}/pulls; Forgejo serves a
    single OBJECT at /commits/{sha}/pull and 404s on the plural. Both verbs
    emit the array shape, so facts.sh carries one jq expression.
  * GitHub creates a tag by POSTing to /git/refs; Forgejo serves that path
    GET-only and creates tags at /tags. A 1:1 port of the gh call would
    have 404'd forever.

The behaviour change is the second half of the bug. Any failure used to
become a definite `no`, which is safe for row 4 and catastrophic for row 5:
it is how a missing binary became "this was not a release ceremony". Now a
completed read that finds nothing is still `no` and still fail-closed, and a
read that did not complete refuses and emits no fact at all.

Four new cases in test/facts.test.sh cover exactly that, and a mutation back
to the old fail-closed-on-error behaviour kills all four and nothing else.
1014 assertions, 22 suites, shellcheck and actionlint clean.

Refs #191

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 11:31:06 +00:00

1,005 B

Fixed

  • The release doors run on a Forgejo consumer. lib/facts.sh and release.yml gathered and published through gh, which the runner image does not ship, so the merge door read labeled=no for a correctly labeled ceremony PR and the tag door died at the publish (#191).

  • A release fact that could not be read is no longer reported as a definite no. A completed read finding no label is still no and still fail-closed; a read that did not complete refuses and emits no fact — the distinction that demoted a ceremony PR to "a bare push" (#191).

Added

  • forge_release_exists, forge_commit_pulls, forge_tag_create, forge_release_create and forge_pr_create on both backends, so the release path names no client. Forgejo serves one PR object at /commits/{sha}/pull where GitHub serves an array at /pulls, and creates tags at /tags where GitHub POSTs to /git/refs; both verbs emit the GitHub shape so the call sites carry one expression (#191).