Skip to content

Provider Architecture

Lex edited this page Aug 21, 2026 · 3 revisions

Provider Architecture

SpecGit's evidence model rests on two seams: git facts come exclusively from local git; forge evidence flows exclusively through authenticated CLIs (gh for GitHub, glab for declared GitLab). No direct REST clients, no stored or logged tokens.

The ports

GitPortsrc/gitfacts/port.ts

Local git facts plus the delivery-bootstrap write operations. Production implementation: LocalGitAdapter (src/gitfacts/local.ts).

Member Role
facts Read side: repo, toplevel, branch, HEAD sha, dirty state, worktree layout, origin URL, upstream drift. Feeds the context and drift gates
headContains Ancestor-or-equal containment of a full hex SHA in HEAD history (merged-delivery lineage); non-hex anchors fail closed
checkoutOrCreateBranch Bootstrap: check out the delivery branch, creating it from HEAD when absent
commitFile Bootstrap: pathspec-limited commit of one state file (idempotent)
pushBranch Bootstrap: git push -u of the delivery branch
remoteDefaultBranch origin/HEAD for the PR base (main fallback)
hooksPath The hooks directory git actually uses (worktree and core.hooksPath aware)

ForgeProvidersrc/github/port.ts

Platform-neutral forge evidence and mutations, composed of two surfaces:

  • ForgeReadPort — evidence collection and delivery-lifecycle operations: preflight, getIssue, getOpenIssueNumbers, getOpenIssues, getPr, getCheckRuns, createIssue, createDraftPr, listOpenPrsByHead, addIssueComment
  • ForgeAdminPort — branch-protection and auto-merge administration: getBranchProtection, enableBranchProtection, getRepoAutomerge, enableRepoAutomerge

Every member is required: each feeds a gate or bootstrap decision, and each returns an Evidence envelope so a runtime failure is classified evidence that fails closed — never a silent skip.

The adapters

Adapter File Platform
GhCliGitHubProvider src/providers/github/gh-cli.ts GitHub.com — spawns the authenticated gh CLI
GlabProvider src/providers/gitlab/glab-cli.ts Self-managed GitLab — spawns the authenticated glab CLI; failure taxonomy mirrors gh per platform (glab_missing, glab_unauthenticated, glab_transport, plus gitlab_version_unsupported)

Both adapters share the CLI transport at src/providers/cli-spawn.ts.

Routing — src/providers/routing.ts

The production composition wires one PlatformRoutingProvider that dispatches per call on the ref's platform marker:

  • a GitHub ref's issues, PRs, and check runs flow through gh
  • a GitLab-declared origin's issues, MRs, and pipeline jobs flow through glab

The platform is declared, never guessed: specgit init --gitlab-host <hostname> persists the declaration in spec_git/providers.yaml (committed, shared by the team). A github.com origin defaults to GitHub. An undeclared non-github origin fails closed (gitlab_unsupported / origin_unresolvable with a platform_undecided warning). The invariant: no gh call ever sees a group/subgroup ref.

Compatibility discipline

Member inventories (GIT_PORT_MEMBERS, FORGE_PROVIDER_MEMBERS, plus the surface inventories) are compile-checked against the ports and pinned by the contract test — changing a port forces the same delivery to update every in-tree implementation, the inventories, and the docs. One delivery never spans platforms.


Provider 架构

SpecGit 的证据模型建立在两条边界上:git 事实只来自本地 git;forge 证据只通过已认证的 CLI 获取(GitHub 用 gh,声明的 GitLab 用 glab)。没有直接 REST 客户端,不存储、不记录任何 token。

端口(Ports)

GitPortsrc/gitfacts/port.ts

本地 git 事实与交付引导的写操作。生产实现:LocalGitAdaptersrc/gitfacts/local.ts)。

成员 作用
facts 读侧:仓库、根目录、分支、HEAD sha、脏状态、worktree 布局、origin URL、上游漂移。供上下文与漂移门禁使用
headContains HEAD 历史中完整十六进制 SHA 的祖先包含判断(已合并交付的血缘);非十六进制锚点 fail-closed
checkoutOrCreateBranch 引导:检出交付分支,不存在时从 HEAD 创建
commitFile 引导:按 pathspec 限定提交单个状态文件(幂等)
pushBranch 引导:git push -u 推送交付分支
remoteDefaultBranch PR base 用的 origin/HEAD(回退 main
hooksPath git 实际使用的 hooks 目录(兼容 worktree 和 core.hooksPath

ForgeProvidersrc/github/port.ts

平台中立的 forge 证据与变更,由两个面组合而成:

  • ForgeReadPort —— 证据收集与交付生命周期操作:preflightgetIssuegetOpenIssueNumbersgetOpenIssuesgetPrgetCheckRunscreateIssuecreateDraftPrlistOpenPrsByHeadaddIssueComment
  • ForgeAdminPort —— 分支保护与自动合并管理:getBranchProtectionenableBranchProtectiongetRepoAutomergeenableRepoAutomerge

所有成员都是必需的:每个成员都服务于某个门禁或引导决策,且都返回 Evidence 信封,因此运行时失败会被归类为证据并 fail-closed —— 绝不静默跳过。

适配器

适配器 文件 平台
GhCliGitHubProvider src/providers/github/gh-cli.ts GitHub.com —— 调用已认证的 gh CLI
GlabProvider src/providers/gitlab/glab-cli.ts 自建 GitLab —— 调用已认证的 glab CLI;失败分类按平台镜像 gh(glab_missingglab_unauthenticatedglab_transport,另有 gitlab_version_unsupported

两个适配器共用 src/providers/cli-spawn.ts 的 CLI 传输层。

路由 — src/providers/routing.ts

生产环境组装唯一的 PlatformRoutingProvider,它按调用的 ref 的平台标记分发

  • GitHub ref 的 issues、PR、check runs 走 gh
  • 声明为 GitLab 的 origin,其 issues、MR、pipeline jobs 走 glab

平台是声明出来的,不是猜出来的specgit init --gitlab-host <hostname> 把声明持久化到 spec_git/providers.yaml(提交到仓库,团队共享)。github.com origin 默认 GitHub。未声明的非 github origin 会 fail-closed(gitlab_unsupported / origin_unresolvable,附 platform_undecided 警告)。不变式:gh 永远不会看到 group/subgroup 形式的 ref。

兼容性纪律

成员清单(GIT_PORT_MEMBERSFORGE_PROVIDER_MEMBERS 及各面清单)与端口做编译期校验,并由契约测试钉死 —— 改动端口必须在同一次交付中更新所有树内实现、清单和文档。一次交付永不跨平台。

Clone this wiki locally