Skip to content

feat: GraphQL diff-shape fetch — per-PR file stats without patch content (GitHub + GitLab) #49

Description

@gkanitz

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.GOgo, 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)

  1. Extension reduction table test covering at least: Main.GOgo,
    a/b/service.test.tsts, Makefile"", .gitignore"",
    archive.tar.gzgz, noext."".
  2. GitHub adapter returns the expected []FileStat for a multi-page
    recorded fixture (pagination exercised).
  3. GitLab adapter returns the expected []FileStat for its fixture.
  4. Parity: equivalent GitHub and GitLab fixtures produce identical
    []FileStat slices.
  5. 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(.
  6. 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.
  7. 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).
  8. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    readyLoop-ready: this org's picker reads this labelready-for-agentTriage complete; ready for an agent to pick up

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions