Context
Slice B of the v0.2 "Meaning" wave. Shared design context:
docs/superpowers/specs/2026-07-04-impact-roadmap-design.md (PR #47).
The language fingerprint and PR-size slices need per-PR file stats
(extensions + line counts). The REST endpoints that list PR files
(GET /pulls/{n}/files, GitLab /diffs) return patch text — actual code —
in the response body, which would break the "never requests code" claim at
the wire level. The GraphQL file-stats queries on both platforms return paths
and counts with no diff content. This slice adds that minimal GraphQL fetch
path to both providers.
Goal (vertical slice)
Additive provider capability: per-PR diff shape ([]FileStat) on GitHub and
GitLab via GraphQL, with recorded fixtures, cross-provider parity tests, and
honest degradation when GraphQL is unavailable.
In scope
provider interface (additive): FileStat{Ext string; Additions, Deletions int}
and a method to fetch diff shape for a PR/MR, plus a typed sentinel error
ErrDiffShapeUnsupported.
- Ext canonical form (adapter-internal reduction): substring after the final
dot of the basename, lowercased, no leading dot; "" when the basename has
no dot or starts with its only dot (Main.GO → go, Makefile → "",
.gitignore → "").
- GitHub adapter: GraphQL
pullRequest.files { path additions deletions }
with pagination; recorded fixtures.
- GitLab adapter: GraphQL
mergeRequest.diffStats { path additions deletions };
recorded fixtures.
- Capability probe / degradation: auth or availability failure → the typed
error; callers proceed without diff-shape sections.
- Cross-adapter parity fixtures in
provider/testdata/ (existing
parity_* pattern).
Out of scope
- Any metric computation or report/render changes (slices C and D).
- Fetching diff shape for anything beyond the PRs the pipeline already
iterates (subject-authored and subject-reviewed PRs).
Design constraints
Acceptance criteria — TDD
Success (write these tests first; all must pass)
- Extension reduction table test covering at least:
Main.GO→go,
a/b/service.test.ts→ts, Makefile→"", .gitignore→"",
archive.tar.gz→gz, noext.→"".
- GitHub adapter returns the expected
[]FileStat for a multi-page
recorded fixture (pagination exercised).
- GitLab adapter returns the expected
[]FileStat for its fixture.
- Parity: equivalent GitHub and GitLab fixtures produce identical
[]FileStat slices.
- Wire honesty: each adapter's GraphQL query constant exactly matches an
approved golden, and a defense-in-depth test asserts no query contains
any of the substrings patch, hunk, rawTextBlob, content(,
diffs(.
- Path containment: fixtures seed a distinctive path
(internal/secret-project/service.go); the seeded string appears in no
adapter return value when JSON-marshaled, and in no log output captured
during the test run.
- Degradation: a fixture simulating GraphQL 401/404/network failure makes
the method return ErrDiffShapeUnsupported (checked with errors.Is),
and a pipeline-level test with such a provider still produces a valid
report (diff-shape-dependent sections simply absent).
- Existing provider tests pass unchanged (interface change is additive).
Failure — QA red flags (any one observed → reject the PR)
- Any call to
GET .../pulls/{n}/files, .../merge_requests/:iid/diffs,
or .../merge_requests/:iid/changes anywhere in the diff.
- A path string reachable outside the adapter package (struct field, map
key, error message, log line).
- GraphQL query requesting any content-bearing field.
- Hard failure or non-zero exit when GraphQL is unavailable.
- Query constants edited without their goldens (or vice versa).
Parallel-work contract
- Territory:
provider/ (github/, gitlab/, testdata/).
- Shared, additive-only:
provider/provider.go (new type + method +
sentinel only; no changes to existing methods).
- Depends on: nothing — start immediately. Blocks the PR-size and
language-fingerprint slices.
Definition of done
TDD skill followed; all success criteria green; go test ./... passes;
PR references this issue.
Context
Slice B of the v0.2 "Meaning" wave. Shared design context:
docs/superpowers/specs/2026-07-04-impact-roadmap-design.md(PR #47).The language fingerprint and PR-size slices need per-PR file stats
(extensions + line counts). The REST endpoints that list PR files
(
GET /pulls/{n}/files, GitLab/diffs) return patch text — actual code —in the response body, which would break the "never requests code" claim at
the wire level. The GraphQL file-stats queries on both platforms return paths
and counts with no diff content. This slice adds that minimal GraphQL fetch
path to both providers.
Goal (vertical slice)
Additive provider capability: per-PR diff shape (
[]FileStat) on GitHub andGitLab via GraphQL, with recorded fixtures, cross-provider parity tests, and
honest degradation when GraphQL is unavailable.
In scope
providerinterface (additive):FileStat{Ext string; Additions, Deletions int}and a method to fetch diff shape for a PR/MR, plus a typed sentinel error
ErrDiffShapeUnsupported.dot of the basename, lowercased, no leading dot;
""when the basename hasno dot or starts with its only dot (
Main.GO→go,Makefile→"",.gitignore→"").pullRequest.files { path additions deletions }with pagination; recorded fixtures.
mergeRequest.diffStats { path additions deletions };recorded fixtures.
error; callers proceed without diff-shape sections.
provider/testdata/(existingparity_*pattern).Out of scope
iterates (subject-authored and subject-reviewed PRs).
Design constraints
boundary.
FileStatcarries no path field — the reduction happensinside the adapter, giving a type-level guarantee.
Acceptance criteria — TDD
Success (write these tests first; all must pass)
Main.GO→go,a/b/service.test.ts→ts,Makefile→"",.gitignore→"",archive.tar.gz→gz,noext.→"".[]FileStatfor a multi-pagerecorded fixture (pagination exercised).
[]FileStatfor its fixture.[]FileStatslices.approved golden, and a defense-in-depth test asserts no query contains
any of the substrings
patch,hunk,rawTextBlob,content(,diffs(.(
internal/secret-project/service.go); the seeded string appears in noadapter return value when JSON-marshaled, and in no log output captured
during the test run.
the method return
ErrDiffShapeUnsupported(checked witherrors.Is),and a pipeline-level test with such a provider still produces a valid
report (diff-shape-dependent sections simply absent).
Failure — QA red flags (any one observed → reject the PR)
GET .../pulls/{n}/files,.../merge_requests/:iid/diffs,or
.../merge_requests/:iid/changesanywhere in the diff.key, error message, log line).
Parallel-work contract
provider/(github/, gitlab/, testdata/).provider/provider.go(new type + method +sentinel only; no changes to existing methods).
language-fingerprint slices.
Definition of done
TDD skill followed; all success criteria green;
go test ./...passes;PR references this issue.