Adopt official forge SDKs behind Ward adapter seams #1262

Open
opened 2026-07-14 09:59:29 +00:00 by coilyco-ops · 1 comment
Member

Decision

Go, from coilysiren/inbox#211.

Adopt official forge SDKs where they reduce Ward's bespoke REST/API spread, but only behind the tracker and PR/workflow seams already being cut. This is not approval to widen Ward's forge surface.

Scope

  • Audit current Forgejo, GitHub, GitLab, and Shortcut client code for places an official or canonical SDK can replace custom request plumbing.
  • Keep Ward's kernel code talking to typed interfaces, not directly to SDK types.
  • Recommend an order of adoption by provider and by seam.
  • Implement the smallest low-risk SDK swap if the audit finds one that does not cross the seam boundaries.

Non-goals

  • Do not redesign the tracker contract.
  • Do not add new forge capabilities.
  • Do not block or duplicate ward#1251 or ward#1252; this should feed those seam decisions.

Parent: coilysiren/inbox#211.
Related: coilyco-flight-deck/ward#1251, coilyco-flight-deck/ward#1252.

## Decision Go, from coilysiren/inbox#211. Adopt official forge SDKs where they reduce Ward's bespoke REST/API spread, but only behind the tracker and PR/workflow seams already being cut. This is not approval to widen Ward's forge surface. ## Scope - Audit current Forgejo, GitHub, GitLab, and Shortcut client code for places an official or canonical SDK can replace custom request plumbing. - Keep Ward's kernel code talking to typed interfaces, not directly to SDK types. - Recommend an order of adoption by provider and by seam. - Implement the smallest low-risk SDK swap if the audit finds one that does not cross the seam boundaries. ## Non-goals - Do not redesign the tracker contract. - Do not add new forge capabilities. - Do not block or duplicate ward#1251 or ward#1252; this should feed those seam decisions. Parent: coilysiren/inbox#211. Related: coilyco-flight-deck/ward#1251, coilyco-flight-deck/ward#1252.
Author
Member

🔎 ward agent advisor

ward agent advisor --harness codex ran a one-shot standard research pass on this question:

Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue.


Recommendation

Proceed, but make ward#1262 an adapter-internal cleanup after the seam work, not a provider expansion. The safe path is: finish or at least honor the Tracker and PRWorkflowClient seams from ward#1251 and ward#1252, keep SDK types out of Ward kernel contracts, then swap one provider at a time behind those adapters.

Current state

  • Ward currently has no forge SDK dependencies in go.mod.
  • Forgejo uses direct net/http JSON plumbing in cmd/ward/forgejo_ops.go and cmd/ward/forgejo_actions.go.
  • GitHub issue work shells through gh in cmd/ward/github_ops.go, deliberately preserving host-side auth and keeping tokens off argv.
  • GitHub App token minting is custom HTTP/JWT code in cmd/ward/github_app.go.
  • GitLab uses direct net/http in cmd/ward/gitlab_ops.go.
  • Shortcut uses direct net/http in cmd/ward/shortcut_ops.go.
  • The newer internal seam file already defines typed kernel-facing interfaces: Tracker, PullRequestContextTracker, PRRepairClassifier, and PRWorkflowClient. There is still local duplicate seam shape in cmd/ward/forge.go, so the first engineering pass should avoid making SDKs visible there.

Provider read

  • GitLab is the best first SDK candidate. GitLab has an official Go SDK at gitlab.com/gitlab-org/api/client-go, described by GitLab as the GitLab Go SDK and migrated from xanzy/go-gitlab: https://gitlab.com/gitlab-org/api/client-go. Ward's GitLab adapter is narrow, already REST-shaped, and not the canonical Ward forge. That makes it the smallest blast radius for proving the pattern. Start with GetIssue, ListIssueComments, GetPullRequestContext, and create/comment/close/reopen only if the first read-only swap stays clean.
  • GitHub should stay gh-backed unless Ward intentionally changes its auth contract. go-github is the canonical Go client for GitHub REST v3 and currently documents github.com/google/go-github/v89: https://github.com/google/go-github. It is a good library, but moving Ward's GitHub tracker off gh would also move token sourcing, CLI availability assumptions, and possibly App auth behavior. That is more than a mechanical SDK swap. If Ward later wants to remove the gh dependency, use go-github behind Tracker, and consider ghinstallation or go-githubauth for the GitHub App path. Do not do that as the first slice.
  • Forgejo should not be first. Ward's Forgejo client is the canonical path for dispatch, PR merge, Actions status, reaper notices, startup triage, issue viewing, and owner repo scans. The available Go options are not an obvious low-risk win: Gitea has an official Go SDK at code.gitea.io/sdk/gitea (https://code.gitea.io/sdk/), and there is a Forgejo-oriented SDK package at codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2, with pkg.go.dev showing v3 as the highest tagged major version (https://pkg.go.dev/codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2). The risk is Forgejo/Gitea API drift, missing Forgejo Actions support, and losing Ward's custom error, retry, and merge-head pin behavior. If Ward adopts one here, start with read-only issue methods behind Tracker, not PR merge or Actions.
  • Shortcut should remain direct HTTP for now. Shortcut documents REST v3, token auth, rate limits, and machine-readable Swagger/OpenAPI files: https://developer.shortcut.com/api/rest/v3. The official client surfaced in research is JavaScript/TypeScript, not Go: https://github.com/useshortcut/shortcut-client-js. Generating a Go client from OpenAPI would likely add more code than Ward's current small adapter removes.

Recommended adoption order

  1. GitLab Tracker read path: use gitlab.com/gitlab-org/api/client-go inside gitlabClient, map SDK structs into Ward's Issue, IssueComment, and PR context projections, and keep Ward's public behavior unchanged.
  2. GitLab Tracker write path: only after read behavior is pinned, replace issue create, comment, close, reopen, and MR creation. Preserve body signing and existing error wording at the adapter boundary.
  3. GitHub Tracker: evaluate only if the team is willing to replace the gh CLI contract with explicit token resolution. If not, leave it.
  4. Forgejo Tracker read path: after ward#1252 settles the PR workflow seam, choose either the Gitea SDK or the Forgejo SDK explicitly. Do not start with merge, branch protection, combined status, or Actions runs.
  5. Forgejo PR/CI path: last, and only for SDK calls that directly preserve head-pinned merge, repository merge-style validation, required-status gating, and the current loud rerun fallback.
  6. Shortcut: no SDK adoption unless Shortcut publishes an official Go client or Ward's Shortcut surface grows enough to justify an OpenAPI-generated adapter.

Options

  • Preferred option: seam-first, provider-by-provider adoption. This fits the issue decision and protects the kernel.
  • Acceptable option: no SDK swap yet, but commit an audit note and test fixtures that describe which methods are SDK-ready. This is better than a rushed dependency change on Forgejo.
  • Avoid: importing SDK types into internal/contracts, agent.go, director merge logic, reservation logic, or reaper logic. That would make the SDK the kernel contract.

Open questions

  • Which Forgejo SDK should Ward treat as authoritative: the official Gitea SDK because Forgejo keeps Gitea-compatible REST for these endpoints, or the Forgejo-specific SDK because the runtime target is Forgejo?
  • Does Ward want GitHub issue operations to keep depending on gh, or should Ward own GitHub token resolution directly?
  • Should the local cmd/ward tracker interface be retired in favor of internal/contracts.Tracker before SDK adoption?
  • Should DeleteIssueComment grow the parent issue/story number later? Today GitLab and Shortcut cannot implement deletion through the shared method shape without extra context, but changing that is outside this issue's non-goals.
  • Which error strings and retry behaviors are contractually important? Forgejo PR reads currently retry parse failures and preserve bounded response snippets, which SDKs may hide or reformat.

Risks

  • SDK type leakage would couple the Ward kernel to provider packages and make future provider work harder.
  • A Forgejo SDK swap could regress the most critical path, especially PR merge, Actions status, rerun fallback, and salvage notices.
  • A GitHub SDK swap could silently change auth behavior from host gh state to explicit token state.
  • SDKs may normalize pagination, errors, rate limits, timestamps, and zero values differently than Ward's adapters do today.
  • Adding SDKs before ward#1251 and ward#1252 settle could duplicate or fight the adapter registry and PR workflow seam decisions.

Advisory close

The next engineer should make the first implementation, if any, a GitLab adapter-internal read-path swap using gitlab.com/gitlab-org/api/client-go. That proves the SDK pattern behind Tracker without touching Ward's canonical Forgejo path or GitHub's deliberate gh auth model. Forgejo and GitHub should remain audit-only until the seam issues answer the registry and PR workflow boundary questions.


Researched and posted automatically by ward agent advisor --harness codex (ward#179). This is one-shot research, not a carried change - verify before acting on it.

— Codex, via ward agent

### 🔎 ward agent advisor `ward agent advisor --harness codex` ran a one-shot **standard** research pass on this question: > Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue. --- **Recommendation** Proceed, but make ward#1262 an adapter-internal cleanup after the seam work, not a provider expansion. The safe path is: finish or at least honor the `Tracker` and `PRWorkflowClient` seams from ward#1251 and ward#1252, keep SDK types out of Ward kernel contracts, then swap one provider at a time behind those adapters. **Current state** * Ward currently has no forge SDK dependencies in `go.mod`. * Forgejo uses direct `net/http` JSON plumbing in `cmd/ward/forgejo_ops.go` and `cmd/ward/forgejo_actions.go`. * GitHub issue work shells through `gh` in `cmd/ward/github_ops.go`, deliberately preserving host-side auth and keeping tokens off argv. * GitHub App token minting is custom HTTP/JWT code in `cmd/ward/github_app.go`. * GitLab uses direct `net/http` in `cmd/ward/gitlab_ops.go`. * Shortcut uses direct `net/http` in `cmd/ward/shortcut_ops.go`. * The newer internal seam file already defines typed kernel-facing interfaces: `Tracker`, `PullRequestContextTracker`, `PRRepairClassifier`, and `PRWorkflowClient`. There is still local duplicate seam shape in `cmd/ward/forge.go`, so the first engineering pass should avoid making SDKs visible there. **Provider read** * **GitLab is the best first SDK candidate.** GitLab has an official Go SDK at `gitlab.com/gitlab-org/api/client-go`, described by GitLab as the GitLab Go SDK and migrated from `xanzy/go-gitlab`: https://gitlab.com/gitlab-org/api/client-go. Ward's GitLab adapter is narrow, already REST-shaped, and not the canonical Ward forge. That makes it the smallest blast radius for proving the pattern. Start with `GetIssue`, `ListIssueComments`, `GetPullRequestContext`, and create/comment/close/reopen only if the first read-only swap stays clean. * **GitHub should stay `gh`-backed unless Ward intentionally changes its auth contract.** `go-github` is the canonical Go client for GitHub REST v3 and currently documents `github.com/google/go-github/v89`: https://github.com/google/go-github. It is a good library, but moving Ward's GitHub tracker off `gh` would also move token sourcing, CLI availability assumptions, and possibly App auth behavior. That is more than a mechanical SDK swap. If Ward later wants to remove the `gh` dependency, use `go-github` behind `Tracker`, and consider `ghinstallation` or `go-githubauth` for the GitHub App path. Do not do that as the first slice. * **Forgejo should not be first.** Ward's Forgejo client is the canonical path for dispatch, PR merge, Actions status, reaper notices, startup triage, issue viewing, and owner repo scans. The available Go options are not an obvious low-risk win: Gitea has an official Go SDK at `code.gitea.io/sdk/gitea` (https://code.gitea.io/sdk/), and there is a Forgejo-oriented SDK package at `codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2`, with pkg.go.dev showing v3 as the highest tagged major version (https://pkg.go.dev/codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2). The risk is Forgejo/Gitea API drift, missing Forgejo Actions support, and losing Ward's custom error, retry, and merge-head pin behavior. If Ward adopts one here, start with read-only issue methods behind `Tracker`, not PR merge or Actions. * **Shortcut should remain direct HTTP for now.** Shortcut documents REST v3, token auth, rate limits, and machine-readable Swagger/OpenAPI files: https://developer.shortcut.com/api/rest/v3. The official client surfaced in research is JavaScript/TypeScript, not Go: https://github.com/useshortcut/shortcut-client-js. Generating a Go client from OpenAPI would likely add more code than Ward's current small adapter removes. **Recommended adoption order** 1. **GitLab Tracker read path**: use `gitlab.com/gitlab-org/api/client-go` inside `gitlabClient`, map SDK structs into Ward's `Issue`, `IssueComment`, and PR context projections, and keep Ward's public behavior unchanged. 2. **GitLab Tracker write path**: only after read behavior is pinned, replace issue create, comment, close, reopen, and MR creation. Preserve body signing and existing error wording at the adapter boundary. 3. **GitHub Tracker**: evaluate only if the team is willing to replace the `gh` CLI contract with explicit token resolution. If not, leave it. 4. **Forgejo Tracker read path**: after ward#1252 settles the PR workflow seam, choose either the Gitea SDK or the Forgejo SDK explicitly. Do not start with merge, branch protection, combined status, or Actions runs. 5. **Forgejo PR/CI path**: last, and only for SDK calls that directly preserve head-pinned merge, repository merge-style validation, required-status gating, and the current loud rerun fallback. 6. **Shortcut**: no SDK adoption unless Shortcut publishes an official Go client or Ward's Shortcut surface grows enough to justify an OpenAPI-generated adapter. **Options** * **Preferred option**: seam-first, provider-by-provider adoption. This fits the issue decision and protects the kernel. * **Acceptable option**: no SDK swap yet, but commit an audit note and test fixtures that describe which methods are SDK-ready. This is better than a rushed dependency change on Forgejo. * **Avoid**: importing SDK types into `internal/contracts`, `agent.go`, director merge logic, reservation logic, or reaper logic. That would make the SDK the kernel contract. **Open questions** * Which Forgejo SDK should Ward treat as authoritative: the official Gitea SDK because Forgejo keeps Gitea-compatible REST for these endpoints, or the Forgejo-specific SDK because the runtime target is Forgejo? * Does Ward want GitHub issue operations to keep depending on `gh`, or should Ward own GitHub token resolution directly? * Should the local `cmd/ward` tracker interface be retired in favor of `internal/contracts.Tracker` before SDK adoption? * Should `DeleteIssueComment` grow the parent issue/story number later? Today GitLab and Shortcut cannot implement deletion through the shared method shape without extra context, but changing that is outside this issue's non-goals. * Which error strings and retry behaviors are contractually important? Forgejo PR reads currently retry parse failures and preserve bounded response snippets, which SDKs may hide or reformat. **Risks** * SDK type leakage would couple the Ward kernel to provider packages and make future provider work harder. * A Forgejo SDK swap could regress the most critical path, especially PR merge, Actions status, rerun fallback, and salvage notices. * A GitHub SDK swap could silently change auth behavior from host `gh` state to explicit token state. * SDKs may normalize pagination, errors, rate limits, timestamps, and zero values differently than Ward's adapters do today. * Adding SDKs before ward#1251 and ward#1252 settle could duplicate or fight the adapter registry and PR workflow seam decisions. **Advisory close** The next engineer should make the first implementation, if any, a GitLab adapter-internal read-path swap using `gitlab.com/gitlab-org/api/client-go`. That proves the SDK pattern behind `Tracker` without touching Ward's canonical Forgejo path or GitHub's deliberate `gh` auth model. Forgejo and GitHub should remain audit-only until the seam issues answer the registry and PR workflow boundary questions. --- Researched and posted automatically by `ward agent advisor --harness codex` (ward#179). This is one-shot research, not a carried change - verify before acting on it. <!-- ward-agent-reply --> <!-- ward-agent-signature --> — Codex, via `ward agent`
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
coilyco-flight-deck/ward#1262
No description provided.