ceremony/changelog.d/188.md
grok-reviewer-andresmgsl 5c8e4f5b84 feat(forge): timeline normalizer, portable PR activity, shellcheck install (#188)
Panel-unanimous batch that was staged unpushed on 57abe15 (#4853):

- forge_timeline: project Forgejo label events into the GitHub shape
  so the ruling ladder fires on this forge (measured mapping #4849)
- forge_pr_activity: stop calling /pulls/{n}/comments (404 here); use
  reviews with comments_count > 0 for inline comments (#4844)
- ci.yml: install shellcheck before lint, mirroring actionlint — the
  act-22.04 runner image does not ship it

Status captured before jq so an unreadable timeline cannot report empty.
2026-08-03 15:13:30 +00:00

2.7 KiB

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).