Term 1's foundation. lib/forge.sh gains forge_select, which sources exactly one of lib/forge-github.sh or lib/forge-forgejo.sh; both define the same verbs, so no branching reaches the 61 call sites. The github backend is the current gh invocation extracted 1:1 — term 5 is kept by making that path boring. The page size moves OUT of the call sites and into the backend, because it is not portable and fails silently. Measured 2026-08-02: ?per_page=100 GitHub 100 items Forgejo 30 items (ignored) ?limit=100 GitHub 30 items Forgejo 50 items (capped) Both answer HTTP 200 with valid JSON. Every call site here is GitHub-shaped, so a verbatim port would have swept 30 of rig's 137 issues and printed "reconciled." — criterion 2 failing green, the same failure class as the blind sweep. Both page_url helpers strip a stray page-size parameter in either dialect, so a call site cannot reintroduce it by accident. Forgejo caps a page at 50 whatever is asked, so pagination is mandatory, not an optimisation. The gather is then PROVEN complete against x-total-count rather than assumed complete because a loop ended. @kimi-reviewer-andresmgsl's hardening (#4699): a missing x-total-count is itself a loud refusal. Header exposure is a server setting, and an assert that cannot run must not silently pass — that is the failure class re-entering through the guard built to stop it. Call sites are not ported yet; that is the next commit. Refs #188
1.7 KiB
1.7 KiB
Added
lib/forge.sh— the forge selector:forge_detectnames the forge from the runner's own environment,forge_clientnames the client it needs, andforge_preflightrefuses loudly before any sweep when the two disagree (#188).- The reconcilers and
labels-scoperun that preflight first, so a GitHub-shaped client on a Forgejo instance is a named refusal instead of a sweep that reads nothing and reports success (#188). lib/closes_references.sh— the closing-keyword parser, sibling ofrefs_references, so "which issues does this PR close" is answered from a PR body rather than from GitHub's GraphQL API (#188).lib/forge-github.shandlib/forge-forgejo.sh— one call surface, two backends, selected byforge_select; no forge branching at the call sites (#188).- The forgejo backend proves each paginated gather complete against the
server's
x-total-countand refuses loudly when it cannot — a missing header is a refusal, not a pass (#188).
Changed
-
issueflow-reconcilegathers open and merged PRs over REST instead ofgh api graphql. Forgejo serves no GraphQL at all, so the two queries were replaced rather than translated; both forges returnnumberandbodyfrom/pullsin the same shape (#188). -
forge_apiowns the page size, because each forge silently ignores the other's parameter:per_page=100reads 30 items on Forgejo andlimit=100reads 30 on GitHub, both HTTP 200. No call site names one (#188).
Fixed
labels-reconcileandlabels-scopeno longer exit 0 on a Forgejo consumer having read zero facts — measured onheavy-duty/rig, where the sweep printedreconciled.over an empty PR list and scope reported "no labeler.yml" for a file that exists (#188).