feat(tracker): GitHub support — forge flavor on credentials + GitHub REST client (ADR-0015) #9

Merged
dominik.polakovics merged 1 commit from feat/github-tracker into main 2026-07-06 23:48:44 +02:00

Implements issue #1 (ADR-0015): the GitHub REST tracker client plus the forge-flavor mechanism that routes a forge-bound repo to the Forgejo or GitHub REST client by the credential's flavor rather than the detected host.

What

  • GitHub client (internal/tracker/github/) — a structural twin of the Forgejo client, differing only in the pinned deltas: Authorization: Bearer + X-GitHub-Api-Version, the PR-folding filter on /issues, merged derived from merged_at, labels addressed by name with strict pre-resolution (a typo stays a loud ErrUnknownLabel — GitHub auto-creates otherwise), Link: rel="next" pagination, and 403/429 → tracker.ErrRateLimited.
  • Forge flavor on the credentialForgeTokenPayload.forge (forgejo|github); an absent field decodes as forgejo, so every existing credential keeps working with no migration (write-only payload). The registry routes on the credential's flavor — not repos.forge_kind — with a mismatch tripwire (ErrForgeFlavorMismatch) and a flavor-aware BaseURL (GHE-ready, host used verbatim, no derivation). One shared tracker.NormalizeForgeHost validates the host at both credential-create (400) and resolve time, so create and resolve can't diverge.
  • Relaxed reposvc gate — an explicit forge binding now needs only a forge credential (the detected-host requirement is dropped), unlocking arbitrary Forgejo instances (codeberg, a second private instance) and GitHub Enterprise.
  • Rate limits self-heal through the existing scheduler/reaper log-and-skip — no AFK-engine changes needed.
  • UI + docs — flavor selector + per-flavor host prefill + PAT-scope help text; ADR-0015; CONTEXT.md and ops.md updated.

Testing

  • Go: go test ./..., go vet, gofmt -l all clean. New coverage mirrors the Forgejo layers: full github wire suite (queue, PR-folding, merged_at state, Link pagination, name-strict labels, rate-limit classification, 404→ErrNotFound, no-token-leak), registry flavor routing (GHE base URL, mismatch, absent-flavor default, none-kind unlock), credential host-shape validation, and a githublive composition smoke driving the real client through httpapi → registry.
  • Frontend: tsc, eslint, prettier --check, vitest (259) all clean.
  • Adversarial 4-dimension review (twin-fidelity, GitHub-API-correctness, security/secret-hygiene, integration) with per-finding verification → 0 findings.

The manual gate (one real GitHub repo added via the UI with a PAT, driven through the full AFK lifecycle) is left open deliberately, exactly as the issue specifies; GHE ships with unit coverage only.

Closes #1

🤖 Generated with Claude Code

Implements issue #1 (ADR-0015): the GitHub REST tracker client plus the forge-flavor mechanism that routes a forge-bound repo to the Forgejo or GitHub REST client by the credential's flavor rather than the detected host. ## What - **GitHub client** (`internal/tracker/github/`) — a structural twin of the Forgejo client, differing only in the pinned deltas: `Authorization: Bearer` + `X-GitHub-Api-Version`, the PR-folding filter on `/issues`, `merged` derived from `merged_at`, labels addressed by name with strict pre-resolution (a typo stays a loud `ErrUnknownLabel` — GitHub auto-creates otherwise), `Link: rel="next"` pagination, and `403/429 → tracker.ErrRateLimited`. - **Forge flavor on the credential** — `ForgeTokenPayload.forge` (`forgejo`|`github`); an absent field decodes as forgejo, so every existing credential keeps working with no migration (write-only payload). The registry routes on the credential's flavor — not `repos.forge_kind` — with a mismatch tripwire (`ErrForgeFlavorMismatch`) and a flavor-aware BaseURL (GHE-ready, host used verbatim, no derivation). One shared `tracker.NormalizeForgeHost` validates the host at **both** credential-create (400) and resolve time, so create and resolve can't diverge. - **Relaxed reposvc gate** — an explicit forge binding now needs only a forge credential (the detected-host requirement is dropped), unlocking arbitrary Forgejo instances (codeberg, a second private instance) and GitHub Enterprise. - **Rate limits self-heal** through the existing scheduler/reaper log-and-skip — no AFK-engine changes needed. - **UI + docs** — flavor selector + per-flavor host prefill + PAT-scope help text; ADR-0015; CONTEXT.md and ops.md updated. ## Testing - Go: `go test ./...`, `go vet`, `gofmt -l` all clean. New coverage mirrors the Forgejo layers: full github wire suite (queue, PR-folding, `merged_at` state, Link pagination, name-strict labels, rate-limit classification, 404→ErrNotFound, no-token-leak), registry flavor routing (GHE base URL, mismatch, absent-flavor default, none-kind unlock), credential host-shape validation, and a `githublive` composition smoke driving the **real** client through httpapi → registry. - Frontend: `tsc`, `eslint`, `prettier --check`, `vitest` (259) all clean. - Adversarial 4-dimension review (twin-fidelity, GitHub-API-correctness, security/secret-hygiene, integration) with per-finding verification → **0 findings**. The manual gate (one real GitHub repo added via the UI with a PAT, driven through the full AFK lifecycle) is left open deliberately, exactly as the issue specifies; GHE ships with unit coverage only. Closes #1 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(tracker): GitHub REST client + forge flavor on credentials (ADR-0015)
Some checks failed
ci / flake-check (pull_request) Has been cancelled
f0c44d42ab
Adds internal/tracker/github, a structural twin of the Forgejo client, and
the forge-flavor mechanism that routes a forge-bound repo to the Forgejo or
GitHub REST client by the credential's flavor rather than the detected host.

- github client: Bearer auth + X-GitHub-Api-Version, PR-folding filter on the
  /issues list, merged_at-derived pull state, name-addressed labels with
  strict pre-resolution (typo stays a loud ErrUnknownLabel), Link pagination,
  403/429 -> tracker.ErrRateLimited.
- ForgeTokenPayload.forge ("forgejo"|"github"); absent -> forgejo, so every
  existing credential keeps working with no migration (write-only payload).
- registry routes on the credential flavor with a mismatch tripwire
  (ErrForgeFlavorMismatch) and a flavor-aware BaseURL (GHE-ready, verbatim
  host, no derivation).
- one shared tracker.NormalizeForgeHost validates the host at both credential
  create (400) and resolve time -- no create/resolve divergence.
- reposvc explicit-forge gate relaxed: a forge credential alone suffices,
  unlocking arbitrary Forgejo instances and GitHub Enterprise hosts.
- rate limits self-heal through the existing scheduler/reaper log-and-skip;
  no AFK-engine changes.
- UI flavor selector + per-flavor host prefill + PAT-scope help; ADR-0015;
  CONTEXT.md and ops.md updated.

Hermetic coverage mirrors the Forgejo layers: github wire tests, registry
flavor routing (GHE base URL, mismatch, absent-flavor default, none-kind
unlock), credential host-shape validation, and a githublive composition smoke
driving the real client through httpapi -> registry. The manual gate (a real
GitHub repo + PAT through the AFK lifecycle) is left open per the issue.

Closes #1

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dominik.polakovics deleted branch feat/github-tracker 2026-07-06 23:48:44 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab!9
No description provided.