ceremony/changelog.d/198.md

59 lines
2.8 KiB
Markdown
Raw Permalink Normal View History

merge upstream 0.6.0 onto the forge tree, and port every gh call site it brought (#198) `git merge` of upstream `8c3a4d1` onto `dad99dd`, common ancestor `84bb1a4`. 18 hunks in 10 files; `lib/forge.sh`, `lib/forge-github.sh` and `lib/forge-forgejo.sh` conflict in none and come out byte-identical. The resolutions the issue decided: VERSION and both CEREMONY_SELF_REF carriers take upstream's numbers; `.github/labels.conf` and `drills/0.4.1.md` keep this forge's; CHANGELOG keeps both sides and names the upstream commit this tree carries. The part the hunks did not contain. Upstream's 0.5.0/0.6.0 work added whole functions to files this tree already owned, so `git merge` took its side without raising a conflict — and with them, EIGHT runtime `gh` call sites that #188 had removed. Seven are ported onto the shim: two reads and four comment writes in issueflow-reconcile, and labels-reconcile's HEAD_COMMIT_AT read. The eighth is `gh workflow run` in labels.yml, which a workflow cannot declare a client for and whose Forgejo equivalent this instance answers with 500 rather than a 4xx — named with its reason rather than ported on a guess. test/no-runtime-gh.test.sh makes the rule mechanical, because reviewing the diff could not: four reviewers reading it each found a different subset, and the contract suite stubs `gh`, so a reintroduced call site passes it. Three seams the resolution decides are silent when resolved wrongly, and each now has a case that fails on the wrong one: the merged record's `merged_at` third column (without it every sort key ties and the highest PR number comes back), the open gather's one-BODY-row-per-line feed (a whole decoded body as one record loses every declaration including the first), and the whole-board read whose COLLISION_FLAGS/WINDOW_FLAGS consumers auto-merged. The open gather carries CLOSING rows as well as BODY rows. `Refs` alone would drop every `Closes #N` link on the open side and reclaim a claim the PR was holding — the existing base64 round-trip case is red without it. actions/refs-not-closing declares CEREMONY_FORGE_CLIENT=gh: its only gather is GraphQL, which Forgejo does not serve at all. #199 ports it. test/run.sh: 28 test files, 0 failed. shellcheck and actionlint clean. Refs #198
2026-08-05 11:56:23 +00:00
### Added
- This tree carries upstream ceremony through `8c3a4d1` (upstream `0.6.0`):
`lib/attention.sh`, `lib/read.sh`, `actions/refs-not-closing`, the guarded
reads, and the ruling and window rules (#198).
- `test/no-runtime-gh.test.sh` — the forge-portability guard: no runtime `gh`
outside `lib/forge-github.sh` unless the file declares
`CEREMONY_FORGE_CLIENT=gh` (#198).
- `CHANGELOG.md` names the upstream commit this tree carries, so a drill
record can say which `0.6.0` it exercised (#197, #198).
### Fixed
- Eight runtime `gh` call sites arrived with the merge outside every conflict
hunk, in functions upstream added to files this tree already owned. Seven
are ported onto the shim; the eighth is named with its reason (#198).
- The open-PR gather reads `Refs`, not only closing keywords. Reading one side
for closing links and the other for `Refs` is what released a live claim in
crew#321, and this tree carried that shape (#198).
- The merged record gains `merged_at`, so `post_merge_pr_for_issue` answers
the PR that merged last rather than the highest-numbered one. Without the
column every sort key ties and the old order returns silently (#198).
- The open gather feeds `open_pr_issues` one record per physical body line. A
whole decoded body as one record loses every declaration including the
first, and reclaims a claim a live PR was holding (#198).
- The post-merge nudge links the issue on the forge in play rather than a
hard-coded `github.com` (#198).
- `actions/refs-not-closing` reports and skips on a forge it cannot speak,
naming the client and #199, instead of standing red on every PR. It reaches
the forge zero times, so no verdict is produced either way (#198).
fix(198): the workflow declares and refuses instead of being exempted by name (#198) @codex-reviewer-andresmgsl's blocker 1 is right and the filename exemption was the wrong shape. It exempted the whole FILE — any later `gh` call anywhere in labels.yml would have ridden in free — and it let the merge ship a step that dies with `command not found` on every sweep on this forge, which #197's bar does not permit. The declaration mechanism already existed; a workflow simply could not reach it. It can: `CEREMONY_FORGE_CLIENT: gh` in the step's env is the same declaration actions/refs-not-closing carries, and the refusal that a script gets from forge_preflight is inline here because a workflow has no shell to call it from. The dispatch now warns by name, cites #205, and exits 0 rather than reddening every sweep for a known gap. So the guard needs no exemption list at all. It now requires the pair — declared AND refusing — and reports a declaration that carries no refusal, which is a permission slip for `command not found`. That predicate was wrong on its first write, and its mutation test caught it: `refuses_when_unavailable` matched the word `forge_preflight` inside labels.yml's own comment explaining that it has NO forge_preflight to call. A guard reading prose as evidence is the blind sweep again, in the guard written to forbid it. Comments are stripped now, as gh_calls already stripped them. Blocker 4: the nudge strips a trailing slash from the server URL. Reverting the strip reds two cases. Blockers 2 and 3 were already fixed in 97e63ac, before either review landed. test/run.sh 28 files 0 failed under CI's env; shellcheck 0.10.0 (CI's pin), actionlint, self-ref, marker, vendored and changelog-armed all clean, with every file tracked this time. Refs #198
2026-08-05 12:16:53 +00:00
- `.github/workflows/labels.yml`'s sweep dispatch declares the client it
fix(198): the action fails closed, the caller decides scheduling, the guard decides the forge (#198) @codex-reviewer-andresmgsl's second review, both points taken. The refs action goes back to `forge_preflight || exit 1`. 97e63ac had it exit 0 with a notice so the PR check would not be red, and that conflated two different questions: "this action cannot produce a verdict" is the ACTION's contract and must stay a refusal, while "this check should not block the board" is the CALLER's decision. The caller now carries it — refs-guard.yml skips unless github.server_url is github.com, mirroring forge_detect positively. A skipped check is a green head; an action that reports success it did not earn is not. The leaked preflight_err temp file goes with the revert. The workflow guard asked the wrong question. `command -v gh` alone passes the moment a Forgejo runner image happens to ship gh, and then dispatches against a forge that cannot serve it — the client/forge mismatch forge_preflight exists to prevent. It decides the FORGE first now, mirroring forge_detect positively, and the binary second. The source guard splits to match: a declaration guarded only by binary presence is reported, with a fixture that fails on exactly that shape. The warning text was also wrong on the facts, as noted: issue-event sweeps ARE this caller's event-driven wakes, so they are precisely what is lost. It now says the hourly scheduled sweep survives and every event-driven wake through this caller does not, until #205. Point 1 of that review — jq 1.6 accepting an empty payload — was already fixed in 728102a, pushed before the review landed. Verified under the runner's jq 1.6 as well as 1.7: 28 test files, 0 failed both ways. shellcheck 0.10.0 (CI's pin), actionlint, self-ref, marker, vendored, changelog-armed all clean with every file tracked. Refs #198
2026-08-05 12:30:28 +00:00
speaks and decides the FORGE before the binary, so a Forgejo runner that
happens to ship `gh` cannot dispatch against a forge that cannot serve it.
#205 ports it to REST (#198).
- `actions/refs-not-closing` fails closed on a forge it cannot speak, and
`.github/workflows/refs-guard.yml` carries the scheduling decision — the
action never reports a success it did not earn (#198).
fix(198): the workflow declares and refuses instead of being exempted by name (#198) @codex-reviewer-andresmgsl's blocker 1 is right and the filename exemption was the wrong shape. It exempted the whole FILE — any later `gh` call anywhere in labels.yml would have ridden in free — and it let the merge ship a step that dies with `command not found` on every sweep on this forge, which #197's bar does not permit. The declaration mechanism already existed; a workflow simply could not reach it. It can: `CEREMONY_FORGE_CLIENT: gh` in the step's env is the same declaration actions/refs-not-closing carries, and the refusal that a script gets from forge_preflight is inline here because a workflow has no shell to call it from. The dispatch now warns by name, cites #205, and exits 0 rather than reddening every sweep for a known gap. So the guard needs no exemption list at all. It now requires the pair — declared AND refusing — and reports a declaration that carries no refusal, which is a permission slip for `command not found`. That predicate was wrong on its first write, and its mutation test caught it: `refuses_when_unavailable` matched the word `forge_preflight` inside labels.yml's own comment explaining that it has NO forge_preflight to call. A guard reading prose as evidence is the blind sweep again, in the guard written to forbid it. Comments are stripped now, as gh_calls already stripped them. Blocker 4: the nudge strips a trailing slash from the server URL. Reverting the strip reds two cases. Blockers 2 and 3 were already fixed in 97e63ac, before either review landed. test/run.sh 28 files 0 failed under CI's env; shellcheck 0.10.0 (CI's pin), actionlint, self-ref, marker, vendored and changelog-armed all clean, with every file tracked this time. Refs #198
2026-08-05 12:16:53 +00:00
- `issue_payload_valid` refuses an empty payload on jq 1.6 as well as 1.7.
`jq -e` exits 4 on empty input under 1.7 and **0** under 1.6, and this
instance's runner carries 1.6 — so the guard #247 D3 added to refuse an
unreadable read was accepting one here (#198).
fix(198): the workflow declares and refuses instead of being exempted by name (#198) @codex-reviewer-andresmgsl's blocker 1 is right and the filename exemption was the wrong shape. It exempted the whole FILE — any later `gh` call anywhere in labels.yml would have ridden in free — and it let the merge ship a step that dies with `command not found` on every sweep on this forge, which #197's bar does not permit. The declaration mechanism already existed; a workflow simply could not reach it. It can: `CEREMONY_FORGE_CLIENT: gh` in the step's env is the same declaration actions/refs-not-closing carries, and the refusal that a script gets from forge_preflight is inline here because a workflow has no shell to call it from. The dispatch now warns by name, cites #205, and exits 0 rather than reddening every sweep for a known gap. So the guard needs no exemption list at all. It now requires the pair — declared AND refusing — and reports a declaration that carries no refusal, which is a permission slip for `command not found`. That predicate was wrong on its first write, and its mutation test caught it: `refuses_when_unavailable` matched the word `forge_preflight` inside labels.yml's own comment explaining that it has NO forge_preflight to call. A guard reading prose as evidence is the blind sweep again, in the guard written to forbid it. Comments are stripped now, as gh_calls already stripped them. Blocker 4: the nudge strips a trailing slash from the server URL. Reverting the strip reds two cases. Blockers 2 and 3 were already fixed in 97e63ac, before either review landed. test/run.sh 28 files 0 failed under CI's env; shellcheck 0.10.0 (CI's pin), actionlint, self-ref, marker, vendored and changelog-armed all clean, with every file tracked this time. Refs #198
2026-08-05 12:16:53 +00:00
- The post-merge nudge strips a trailing slash from the server URL, so a forge
URL carrying one does not render `//owner/repo` (#198).
merge upstream 0.6.0 onto the forge tree, and port every gh call site it brought (#198) `git merge` of upstream `8c3a4d1` onto `dad99dd`, common ancestor `84bb1a4`. 18 hunks in 10 files; `lib/forge.sh`, `lib/forge-github.sh` and `lib/forge-forgejo.sh` conflict in none and come out byte-identical. The resolutions the issue decided: VERSION and both CEREMONY_SELF_REF carriers take upstream's numbers; `.github/labels.conf` and `drills/0.4.1.md` keep this forge's; CHANGELOG keeps both sides and names the upstream commit this tree carries. The part the hunks did not contain. Upstream's 0.5.0/0.6.0 work added whole functions to files this tree already owned, so `git merge` took its side without raising a conflict — and with them, EIGHT runtime `gh` call sites that #188 had removed. Seven are ported onto the shim: two reads and four comment writes in issueflow-reconcile, and labels-reconcile's HEAD_COMMIT_AT read. The eighth is `gh workflow run` in labels.yml, which a workflow cannot declare a client for and whose Forgejo equivalent this instance answers with 500 rather than a 4xx — named with its reason rather than ported on a guess. test/no-runtime-gh.test.sh makes the rule mechanical, because reviewing the diff could not: four reviewers reading it each found a different subset, and the contract suite stubs `gh`, so a reintroduced call site passes it. Three seams the resolution decides are silent when resolved wrongly, and each now has a case that fails on the wrong one: the merged record's `merged_at` third column (without it every sort key ties and the highest PR number comes back), the open gather's one-BODY-row-per-line feed (a whole decoded body as one record loses every declaration including the first), and the whole-board read whose COLLISION_FLAGS/WINDOW_FLAGS consumers auto-merged. The open gather carries CLOSING rows as well as BODY rows. `Refs` alone would drop every `Closes #N` link on the open side and reclaim a claim the PR was holding — the existing base64 round-trip case is red without it. actions/refs-not-closing declares CEREMONY_FORGE_CLIENT=gh: its only gather is GraphQL, which Forgejo does not serve at all. #199 ports it. test/run.sh: 28 test files, 0 failed. shellcheck and actionlint clean. Refs #198
2026-08-05 11:56:23 +00:00
- `.github/scripts/release-path.sh` names `lib/forge.sh`: #191 put the shim on
the release doors' executable path here, so a doors-unchanged record that
omitted it was measuring the wrong set (#198).