Skip to content

Restoring forge operations beyond releases, in lockstep across providers

Authors
Matt Cockayne, Claude Opus 4.8 (AI drafting assistant)
Date
2026-07-19
Status
DRAFT — records a deferred capability set and the constraint on reintroducing it. Not scheduled; no implementation should begin against it as written.

Why this exists

forge and its provider modules currently cover releases only. That is not the whole story of what the code could do.

The GitHub provider was originally written for — and lifted from — a much older variant of go-tool-base, when GitHub was the default forge. It carried a substantially wider API. When the framework was opensourced, most of that functionality stopped being used by the framework itself, but the code was retained deliberately, because it still has value.

At the point of extracting forge-github, only the release surface came across. This spec records what was left behind, why, and the condition on bringing it back.

What was deferred

All of the following remain in go-tool-base/pkg/vcs/github and are not deleted:

Capability Methods
Pull requests CreatePullRequest, GetPullRequestByBranch, UpdatePullRequest, AddLabelsToPullRequest
Repositories CreateRepo
SSH keys UploadKey
File contents GetFileContents
Interactive auth GHLogin — OAuth device flow, via cli/oauth

Only UploadKey has a caller inside go-tool-base today (pkg/setup/github). The rest have none — which measures current framework usage, not value: this is a framework, its packages are exported, and a downstream tool may well be calling them.

The constraint

No capability ships for one provider alone.

This is the whole point. forge's design goal is that consuming code depends on forge.Provider, not on *github.Client — so switching forges is a constructor change, not a rewrite. A method that exists only on GitHub breaks that in the worst way: the code compiles, the tests pass, and the portability claim silently becomes false. A caller who reaches for it has left the contract without being told.

The disparity that produced this spec is exactly that failure, caught before it was published. Reintroducing the capabilities one forge at a time would recreate it.

Shape of the eventual work

Not a design, but the constraints any design must satisfy:

Capability interfaces, not Provider methods. Adding methods to Provider breaks every implementation, including out-of-tree ones nobody can see. The existing precedent is ChecksumProvider and SignatureProvider: optional interfaces discovered by runtime type assertion, with ErrNotSupported as the opt-out. Something like PullRequestProvider, RepositoryProvider, FileProvider follows that grain.

Lockstep means all four, or none. A capability lands when GitHub, GitLab, Gitea and Bitbucket can all satisfy it — or when a platform genuinely cannot, and returns ErrNotSupported by design rather than by omission. The distinction matters: Bitbucket Downloads has no tagged releases, which is a platform fact and documented as such. "Nobody has written it yet" is not.

The conformance harness must enforce it. forgetest.RunProviderConformance already checks capability declarations in both directions. Whatever shape these take, the harness has to be able to tell a deliberate opt-out from an unimplemented one, otherwise lockstep is a convention rather than a guarantee — and conventions erode.

Auth is not a capability. GHLogin is an interactive OAuth device flow. That belongs to whatever is setting up a tool rather than to the release contract, and it should not be smuggled in under this heading.

Open questions

Q1 — Is this forge's remit at all? forge was named over releases precisely to leave room for this (see the VCS stack extraction plan, R6). But pull requests and repository administration are a different problem from publishing and consuming releases, and a separate module family may serve both better than one widening interface.

Q2 — What is the actual demand? Only UploadKey has an in-tree caller. The rest is retained on the judgement that it has value, which is worth confirming against real downstream usage before four implementations are written.

Q3 — What do the non-GitHub platforms actually support? GitLab and Gitea have comparable pull/merge request APIs; Bitbucket Cloud differs more. Some capabilities may be genuinely impossible on one platform, which is fine — but it needs establishing per capability, not assumed.

Q4 — Does the existing code survive the move? It was written against go-github directly, for a single forge, before this contract existed. It is a starting point and a record of intent; it should not be assumed portable.

Status

DRAFT (2026-07-19) — captured at the point of the forge-github extraction so the deferral is deliberate and recorded rather than an omission nobody notices. Requires Q1–Q4 before any implementation.