Skip to content

feat: PR-size stats + size-normalized review depth (implements #26) #52

Description

@gkanitz

Context

Slice C of the v0.2 "Meaning" wave. Shared design context:
docs/superpowers/specs/2026-07-04-impact-roadmap-design.md (PR #47).

Research cited in the spec: ~200–400 changed lines is the recommended PR
size; review quality degrades sharply past ~1,000. Small-PR discipline is a
tellable story for a candidate — and PR size is the missing denominator that
issue #26 needs to make "deep review %" honest. This slice implements and
closes #26
(its charting blocker shipped in #28; its data blocker ships in
#49).

Goal (vertical slice)

Diff-shape data (#49) → PR-size metrics + size-normalized review depth →
schema → rendered sections with band context, end to end.

In scope

  • metrics: PR-size stats over the subject's merged PRs that have diff
    data: median_lines (additions+deletions), files_median, small_share
    (share ≤ 400 lines), count (= PRs measured).
  • Review-depth normalization (Review depth: normalise comment count by PR size (lines changed) #26): for each review the subject gave on a PR
    with diff data, deep review ⇔ comments ≥ clamp(ceil(lines/100), 3, 10)
    where lines = additions + deletions of the reviewed PR. PRs without diff
    data fall back to the legacy absolute ≥3 threshold. Semantics changes to
    this formula require an owner comment on this issue; constant tuning with
    documented rationale is allowed only if goldens make the behavior explicit.
  • Schema (additive): collaboration.pr_size {count, median_lines, files_median, small_share, small_threshold_lines}; reviews_given gains
    depth_basis {measured, fallback} so readers see how many reviews were
    normalized vs legacy-thresholded. deep_review_count now means
    normalized-deep.
  • Shared additive block (also described in the language-fingerprint slice;
    first to merge introduces it, the other rebases):
    collaboration.suppressed [{section, reason}] — machine-readable record
    of sections omitted for sample-size reasons.
  • Suppression: pr_size omitted (with suppressed-entry) when < 5 merged PRs
    have diff data. Section omitted entirely (no suppressed-entry needed) when
    the provider reports diff shape unsupported.
  • Render: PR-size stats in the collaboration section; deep-review KPI and
    the stacked review-depth timeline chart use the normalized threshold
    (per Review depth: normalise comment count by PR size (lines changed) #26's acceptance criteria); band context line for pr_size_lines
    appears automatically once feat: banded context layer — cited typical ranges under every KPI #48 is merged (coupling via band key only).

Out of scope

Design constraints

Acceptance criteria — TDD

Success (write these tests first; all must pass)

  1. Median/files-median/small-share computed correctly over a fixture set
    including odd/even counts and boundary PRs at exactly 400 lines
    (≤ 400 counts as small).
  2. Normalized deep-review table test: (lines, comments) → deep? covering at
    least (10, 3) → deep, (500, 4) → shallow, (500, 5) → deep, (2000, 10) →
    deep, (2000, 9) → shallow, (50, 2) → shallow — i.e. threshold
    clamp(ceil(lines/100), 3, 10).
  3. Reviews on PRs without diff data use the legacy ≥3 threshold and are
    counted in depth_basis.fallback; measured ones in
    depth_basis.measured; the two sum to total classified reviews.
  4. pr_size omitted and suppressed carries
    {section: "pr_size", reason: ...} when only 4 merged PRs have diff
    data; present at 5.
  5. Provider returning ErrDiffShapeUnsupported for everything → pr_size
    absent, review depth entirely on fallback basis, report still validates
    and renders.
  6. Schema round-trip: report with pr_size, depth_basis, suppressed
    marshals → ParseValidate; pre-v0.2 sample JSON still validates.
  7. Rendered goldens: PR-size stats visible; deep-review KPI and stacked
    timeline chart reflect the normalized classification on fixtures where
    normalization flips outcomes vs the old absolute rule.
  8. No per-PR value appears in JSON or HTML (fixtures seed a distinctive
    line count, e.g. 31337 total lines on one PR; the literal may not appear
    in output).
  9. Prohibited-strings golden test passes over new output.
  10. Existing ≥3-absolute tests updated/replaced per Review depth: normalise comment count by PR size (lines changed) #26's acceptance list.

Failure — QA red flags (any one observed → reject the PR)

  • Deep-review formula deviating from the specified threshold without an
    owner comment on this issue approving the change.
  • pr_size rendered from < 5 measured PRs, or fallback reviews silently
    mixed into depth_basis.measured.
  • Per-PR sizes, PR numbers, or titles anywhere in output.
  • REST files/diffs endpoints introduced (must consume feat: GraphQL diff-shape fetch — per-PR file stats without patch content (GitHub + GitLab) #49's GraphQL API).
  • The stacked timeline chart still using the absolute threshold.

Parallel-work contract

Definition of done

TDD skill followed; all success criteria green; go test ./... passes;
PR references this issue and closes #26.

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