MF-1: GitHub adapter for ForgePort #292

Closed
opened 2026-04-23 23:33:10 +00:00 by code-lead · 0 comments
Collaborator

As a platform engineer, I want a GitHub implementation of ForgePort so that a GitHub-hosted repo routes through the exact same domain code paths as Forgejo, with no adapter-awareness leaking into the agent runner.

Acceptance criteria

Adapter surface

  • New apps/server/src/infrastructure/forge/github-adapter.ts implementing every method on ForgePort (see forgejo-port.ts).
  • Auth via per-agent GitHub PAT (Personal Access Token), loaded through the same token_file scheme the Forgejo adapter uses today. GitHub App installation tokens are deferred (see Out of scope).
  • All error cases return null / false per the ForgePort contract — no throwing across the hex boundary.

Issues & labels

  • getIssue / listIssues: REST GET /repos/{owner}/{repo}/issues[?...]. Filters: labels, milestone (by title → id lookup), state, limit (per_page).
  • addLabels / removeLabel: REST /issues/{n}/labels. No numeric-id dance — GitHub accepts names.

Pull requests & reviews

  • getPullRequest / listPullRequests: REST /pulls, baseRef filter via base= query.
  • requestReview / removeReviewRequest / listReviews: REST /pulls/{n}/requested_reviewers + /reviews.

Dependencies (divergence)

  • GitHub has no native issue-dependency API. getBlockers / getBlocked parse the issue body for Depends on #n / Blocks #n lines (the existing fallback path in deps.ts).
  • addBlocker appends the line to the body.
  • Document the divergence in the adapter header so operators know dependency ordering is body-text on GitHub, native on Forgejo.

CI / workflow status

  • getAggregateStatus / listWorkflowRuns / getJobLogs: REST /commits/{sha}/check-runs + /actions/runs + /actions/jobs/{jobId}/logs.
  • repoHasWorkflows: HEAD /contents/.github/workflows.

Files & repo scope

  • writeFile: REST PUT /contents/{path} with base64 body + sha.
  • listAccessibleRepos: /user/repos?affiliation=owner,collaborator.

Rate-limit handling

  • On X-RateLimit-Remaining: 0, wait until X-RateLimit-Reset (bounded retry, max 1 attempt) before surfacing the error.

Tests

  • The cross-adapter conformance suite (MF-6) runs against the GitHub adapter using recorded fixtures (not live API).
  • Rate-limit path has a fixture scenario.

Out of scope

  • GitHub App authentication (installation tokens). Defer to a follow-up; only block if PAT hits secondary rate limits in production.
  • GraphQL surface — REST is sufficient for the full ForgePort contract.
  • Mirroring webhooks from GitHub into the claude-hooks webhook endpoint — that is MF-3.

References

  • Spec: specs/multi-forge.md § MF-1.
  • apps/server/src/infrastructure/forge/forgejo-port.ts — port surface to preserve.
  • apps/server/src/infrastructure/forge/forgejo-adapter.ts — reference adapter.
  • apps/server/src/domain/workflow/deps.ts — existing body-text parser used as fallback.

Dependencies

  • Blocks on MF-4 (config schema + adapter factory). The factory decides which adapter class to instantiate per repo, so MF-4 must land before this adapter can be wired into webhook-config / agent-runner.
  • Delivered paired with MF-6 (conformance suite) — the harness emerges from this adapter's test scaffolding, not before.
  • Depends on #295 (MF-4)
As a platform engineer, I want a GitHub implementation of `ForgePort` so that a GitHub-hosted repo routes through the exact same domain code paths as Forgejo, with no adapter-awareness leaking into the agent runner. ## Acceptance criteria ### Adapter surface - [ ] New `apps/server/src/infrastructure/forge/github-adapter.ts` implementing every method on `ForgePort` (see `forgejo-port.ts`). - [ ] Auth via per-agent GitHub PAT (Personal Access Token), loaded through the same `token_file` scheme the Forgejo adapter uses today. GitHub App installation tokens are deferred (see Out of scope). - [ ] All error cases return `null` / `false` per the `ForgePort` contract — no throwing across the hex boundary. ### Issues & labels - [ ] `getIssue` / `listIssues`: REST `GET /repos/{owner}/{repo}/issues[?...]`. Filters: `labels`, `milestone` (by title → id lookup), `state`, `limit` (`per_page`). - [ ] `addLabels` / `removeLabel`: REST `/issues/{n}/labels`. No numeric-id dance — GitHub accepts names. ### Pull requests & reviews - [ ] `getPullRequest` / `listPullRequests`: REST `/pulls`, `baseRef` filter via `base=` query. - [ ] `requestReview` / `removeReviewRequest` / `listReviews`: REST `/pulls/{n}/requested_reviewers` + `/reviews`. ### Dependencies (divergence) - [ ] GitHub has no native issue-dependency API. `getBlockers` / `getBlocked` parse the issue body for `Depends on #n` / `Blocks #n` lines (the existing fallback path in `deps.ts`). - [ ] `addBlocker` appends the line to the body. - [ ] Document the divergence in the adapter header so operators know dependency ordering is body-text on GitHub, native on Forgejo. ### CI / workflow status - [ ] `getAggregateStatus` / `listWorkflowRuns` / `getJobLogs`: REST `/commits/{sha}/check-runs` + `/actions/runs` + `/actions/jobs/{jobId}/logs`. - [ ] `repoHasWorkflows`: HEAD `/contents/.github/workflows`. ### Files & repo scope - [ ] `writeFile`: REST `PUT /contents/{path}` with base64 body + sha. - [ ] `listAccessibleRepos`: `/user/repos?affiliation=owner,collaborator`. ### Rate-limit handling - [ ] On `X-RateLimit-Remaining: 0`, wait until `X-RateLimit-Reset` (bounded retry, max 1 attempt) before surfacing the error. ### Tests - [ ] The cross-adapter conformance suite (MF-6) runs against the GitHub adapter using recorded fixtures (not live API). - [ ] Rate-limit path has a fixture scenario. ## Out of scope - GitHub App authentication (installation tokens). Defer to a follow-up; only block if PAT hits secondary rate limits in production. - GraphQL surface — REST is sufficient for the full `ForgePort` contract. - Mirroring webhooks from GitHub into the claude-hooks webhook endpoint — that is MF-3. ## References - Spec: [`specs/multi-forge.md`](../src/branch/main/specs/multi-forge.md) § MF-1. - `apps/server/src/infrastructure/forge/forgejo-port.ts` — port surface to preserve. - `apps/server/src/infrastructure/forge/forgejo-adapter.ts` — reference adapter. - `apps/server/src/domain/workflow/deps.ts` — existing body-text parser used as fallback. ## Dependencies - **Blocks on MF-4** (config schema + adapter factory). The factory decides which adapter class to instantiate per repo, so MF-4 must land before this adapter can be wired into `webhook-config` / `agent-runner`. - Delivered **paired with MF-6** (conformance suite) — the harness emerges from this adapter's test scaffolding, not before. <!-- machine-parseable deps for the deps.ts body fallback; native POST /dependencies is currently returning HTTP 404 on this Forgejo. --> - Depends on #295 (MF-4)
Sign in to join this conversation.
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
charles/claude-hooks#292
No description provided.