ceremony/changelog.d/191.md

40 lines
1.7 KiB
Markdown
Raw Normal View History

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
### 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).
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
- 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
(#191).
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
### 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 (#191).
- The forgejo backend serves one PR object at `/commits/{sha}/pull` where
GitHub serves an array at `/pulls`; both verbs emit the array shape, so
the call site carries one expression (#191).
- Forgejo creates tags at `POST /tags` — it serves `/git/refs` GET-only,
so GitHub's ref-POST would have 404'd there forever (#191).
fix(forge): an empty REPO cannot become a fact, and the backend verbs are tested Both panel blockers on c63a550. @kimi found the one that mattered: facts.sh got the REPO fix, release.yml's own four call sites did not. A workflow `run:` shell carries no `set -u`, so an unset REPO expands empty and the verb addresses `repos//…` — which 404s, and the 404 is then read as an ANSWER. Reproduced read-only against this instance before fixing: forge_release_exists 0.4.1 -> "no", rc 0 forge_commit_pulls 7fc9afe4 -> "[]", rc 0 (the !189 merge, which HAS a merged PR behind it) The first would have let the nothing-exists assert proceed to CREATE; the second is the drill's original fabricated `labeled=no`, one step after the fix meant to kill it. Fixed once rather than at four call sites, as kimi suggested: forge_select defaults REPO from GITHUB_REPOSITORY, and forgejo_api_base — which every verb reaches the network through — refuses an empty REPO outright. No fifth call site can forget it. @grok and @kimi both blocked on the same AC gap: the backend suite did not cover the five new verbs, so the two measured asymmetries had no offline coverage. test/forge-backends.test.sh now has 15 cases for them — singular /pull wrapped to an array, 404 as an empty array, 500 refusing, release present/absent/unreadable, POST /tags vs /git/refs, the publish body, and the REPO-empty must-fail. Mutation-checked: reading the plural path fails one case, dropping the REPO guard fails the two must-fails. 1029 assertions, 22 suites, shellcheck-all and actionlint clean. Refs #191 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 11:53:28 +00:00
- `forgejo_api_base` refuses when `REPO` is empty. Every verb interpolates
it and every call reaches the network through there, so `repos//…`
whose 404 reads as "no release" and "no PRs" — is now impossible (#191).
- Release asset names are percent-encoded. The hook contract permits any
filename, and the name travels as a query value: a space made curl reject
the URL and `&`/`#`/`+`/`%` silently renamed the asset (#191).
### Changed
- `docs/CONSUMERS.md`'s artifact-hook recovery no longer tells operators to
run `gh release create` by hand — on a Forgejo runner there is no `gh`.
It names the forge-neutral tag-door path first, with both clients shown
(#191).