### Added - `lib/forge.sh` — the forge selector: `forge_detect` names the forge from the runner's own environment, `forge_client` names the client it needs, and `forge_preflight` refuses loudly before any sweep when the two disagree (#188). - The reconcilers and `labels-scope` run 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 of `refs_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.sh` and `lib/forge-forgejo.sh` — one call surface, two backends, selected by `forge_select`; no forge branching at the call sites (#188). - The forgejo backend proves each paginated gather complete against the server's `x-total-count` and refuses loudly when it cannot — a missing header is a refusal, not a pass (#188). ### Changed - `issueflow-reconcile` gathers open and merged PRs over REST instead of `gh api graphql`. Forgejo serves no GraphQL at all, so the two queries were replaced rather than translated; both forges return `number` and `body` from `/pulls` in the same shape (#188). - `forge_api` owns the page size, because each forge silently ignores the other's parameter: `per_page=100` reads 30 items on Forgejo and `limit=100` reads 30 on GitHub, both HTTP 200. No call site names one (#188). - Outstanding review requests are derived from the reviews on the current head rather than from `requested_reviewers`, which Forgejo never clears — read raw there, a PR would sit at `state:bots-reviewing` forever (#188). ### Fixed - `labels-scope` no longer fails to compile its jq program on jq 1.6, which the Forgejo runner image ships: `label` is a reserved word in jq's grammar, so `$label` is a syntax error there and every scope derivation died before reading the config (#188). - `labels-reconcile` and `labels-scope` no longer exit 0 on a Forgejo consumer having read zero facts — measured on `heavy-duty/rig`, where the sweep printed `reconciled.` over an empty PR list and scope reported "no labeler.yml" for a file that exists (#188). - `forge_timeline` projects Forgejo's label events (`.type` / `.body` / `.user.login`) into the GitHub shape (`.event` / `.actor.login`) so the ruling ladder reads the same board on both forges (#188). - `forge_pr_activity` no longer calls `/pulls/{n}/comments` on Forgejo (HTTP 404); inline review comments come from reviews with `comments_count > 0` (#188). - CI installs shellcheck before linting, matching actionlint — the Forgejo runner image does not ship it (#188).