feat(review): document projection, diff geometry primitives, and the conformance harness (Phase 1 PR 2) - #719
Open
benvinegar wants to merge 3 commits into
Open
feat(review): document projection, diff geometry primitives, and the conformance harness (Phase 1 PR 2)#719benvinegar wants to merge 3 commits into
benvinegar wants to merge 3 commits into
Conversation
Contributor
|
Too many files changed for review (53 files, 50 file limit). Bypass the limit by tagging |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
benvinegar
force-pushed
the
claude/review-rebuild-phase-1-pr2
branch
from
August 12, 2026 23:58
3e0e612 to
faa7fcf
Compare
… core Every review consumer asks the same questions about a diff — which lines a hunk occupies, which lines a collapsed gap hides, where a whole-hunk note hangs, whether two projections describe the same content — and the prototype answered each of them several times, differently. This lands the one answer for each, renderer-free and platform-neutral, so the terminal, the producer, and a browser client cannot drift. - `geometry.ts`: per-side hunk extents, default hunk note target, hunk re-basing, and normalized source-line splitting (audit A3, A4, A6, A10). - `expansion.ts`: gap ids and per-side gap addresses, trailing-gap existence, and the expansion side policy (A1, A2, A5). - `anchors.ts`: one note-owner/intersection resolver, with explicit fallback ownership so a note core placed is not dropped by a consumer that re-derives. - `identity.ts`: content identity, file keys, and source identity, hashed with plain arithmetic rather than a platform runtime. - `document.ts`: the `DiffFile` -> `ReviewDocumentV1` projection, per-file split/unified row totals (A7), and the shared empty-diff reason (A8). - `stml.ts`: the STML tag-to-role vocabulary both renderers switch on (A9). - `contentManifest.ts`: a deterministic semantic snapshot that records derived geometry, so parity tests fail when a consumer re-derives it. The primitives take the minimal structural shape they need rather than a whole `ReviewFileV1`, which is what lets a renderer consume them from its row path without projecting a document first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
…rimitives The terminal kept its own copies of derivations the shared model now owns, and one of them was wrong. Each copy is deleted here rather than left beside the primitive that replaces it (audit A1-A6, A8-A10). - `pierre.ts` builds collapsed rows from `reviewLeadingGap`/`reviewTrailingGap`; `leadingCollapsedRanges`, `trailingCollapsedRanges` and `trailingCollapsedLines` are gone, and `CollapsedGapPosition` is now the core gap-position vocabulary. - `expandCollapsedRows.ts` splits source with `normalizedReviewSourceLines` and addresses gaps with `reviewGapId`; `sourceBackedHighlight.ts` re-bases hunks with `rebaseReviewHunk` and says why its own line split is different. - `diffSectionRowPlan.ts` and `useReviewController.ts` read `reviewExpansionSide` instead of recomputing it; `liveComments.ts` loses `hunkLineRange` and `firstCommentTargetForHunk` to `reviewHunkRange(s)` and `reviewDefaultHunkLineTarget`. - `renderRows.tsx` and `staticDiffPager.ts` explain an empty diff from one shared reason, each keeping its own wording. - `ui/lib/stml` switches on tag roles from core, including the parser's void and raw-text tag sets. - `reviewProjection.ts` hands the document projection to core and drops its process-local source-identity token. Two deliberate behavior changes, both fixes: 1. A1: expanding the collapsed gap that leads into a hunk with a zero-count side labeled that side one line low, and read its text from one line early. A pure insertion parsed at zero context (`git diff -U0`) showed old line numbers starting at 0; a pure deletion showed the wrong new-side line. The gap now ends at the last line before the hunk on each side. Row counts are unchanged. 2. Expansion state and loaded source now survive a reload that changed nothing. Source identity is derived from content instead of from the fetcher object the reload happened to allocate, so state derived from unchanged content stays valid. A file whose content did change still retires both, and file keys stay stable across content changes so notes keep their file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
Import gates prove a consumer may use a shared primitive; they cannot prove it does. `test/review-conformance/` closes that gap: one golden corpus, and every consumer registers an adapter that projects the same fixtures into the same normalized shape. Core and terminal render planning are the first two, and each later phase joins the same corpus (rung 2 of the plan's verification ladder). Expectations are written by hand from the unified-diff semantics, never captured from a primitive — a captured expectation would follow a bug instead of catching it. The adversarial fixtures are the inputs the deleted copies got wrong: a pure-insertion hunk and a pure-deletion hunk (A1/A2), a hunk with leading context (A3/A10), CRLF and unterminated sources (A4), and a renamed binary with no rows (A8). A separate note-body corpus pins the one empty-body policy (D2). A seam probe confirms the harness bites: reverting the A1 fix in core fails the two zero-count fixtures for the terminal adapter as well as for core. Also shrinks the review model's node-debt map: `document.ts` and `identity.ts` landed platform-neutral, and source identity lives in `identity.ts` with no path handling, so those three entries are repaid rather than carried. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
benvinegar
force-pushed
the
claude/review-rebuild-phase-1-pr2
branch
from
August 13, 2026 00:49
faa7fcf to
fad85b8
Compare
benvinegar
changed the base branch from
claude/review-rebuild-phase-1-pr1
to
main
August 13, 2026 00:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1 PR 2 of the browser-review rebuild (
docs/browser-review-rebuild.md). Stacked on PR 1 (#716).What
Lands the shared diff-geometry and document-projection primitives in
src/core/review/(geometry / expansion / anchors / identity / document / stml / contentManifest), moves every terminal derivation the audit flagged onto them (deleting the local copies), and builds the conformance harness (test/review-conformance/) with hand-written adversarial fixtures. Repays audit findings A1–A10 and D2 (terminal/model sites; browser halves close in Phase 5). Three commits: core primitives → terminal adoption → harness.What changed, in diagrams
Derivation ownership: before
Each geometric fact about a diff was computed independently wherever it was needed — the audit found up to four copies per derivation, at least one of them wrong:
flowchart TB subgraph before["One derivation, many owners (copy count from the audit)"] HR["per-side hunk range — 4 copies, 1 wrong (A3)"] GAP["collapsed-gap line ranges — 3 copies, terminal off by one (A1)"] TRAIL["trailing-gap existence — 3 formulations (A2)"] SPLIT["source-line splitting — 3 copies, 1 unnormalized (A4)"] SIDE["expansion side — 3 copies (A5)"] MSG["empty-diff reason — 3 precedence orders (A8)"] TGT["default note target — 2 divergent rules (A10)"] REB["hunk rebasing — 2 copies, opposite isPartial (A6)"] end P["ui/diff/pierre.ts"] --- GAP & TRAIL E["ui/diff/expandCollapsedRows.ts"] --- SPLIT DSR["ui/diff/diffSectionRowPlan.ts"] --- SIDE SBH["ui/diff/sourceBackedHighlight.ts"] --- REB RR["ui/diff/renderRows.tsx"] --- MSG SDP["ui/staticDiffPager.ts"] --- MSG LC["core/liveComments.ts"] --- HR & TGT REG["session/app/registration.ts"] --- TRAILDerivation ownership: after
One primitive per derivation, in the boundary-gated core; consumers import, never re-derive. The deleted copies are gone (
leadingCollapsedRanges,trailingCollapsedRanges,trailingCollapsedLines,sliceLines,hunkLineRange,firstCommentTargetForHunk, the expansion-side recomputations, the per-surface empty-diff precedence):flowchart LR subgraph core["src/core/review — one owner per derivation"] GEO["geometry.ts<br/>reviewHunkRange · rebaseReviewHunk<br/>normalizedReviewSourceLines · reviewDefaultHunkLineTarget"] EXP["expansion.ts<br/>reviewLeadingGap · reviewTrailingGap<br/>reviewGapAddress · reviewExpansionSide"] DOC["document.ts<br/>projectReviewDocument · reviewEmptyDiffReason<br/>split/unified line totals (A7)"] ANC["anchors.ts<br/>resolveReviewNoteAnchor"] STML["stml.ts — stmlTagRole (A9)"] IDN["identity.ts — content identity, no node:crypto"] end P2["pierre.ts"] --> EXP E2["expandCollapsedRows.ts"] --> GEO DSR2["diffSectionRowPlan.ts"] --> EXP SBH2["sourceBackedHighlight.ts"] --> GEO RR2["renderRows.tsx"] --> DOC SDP2["staticDiffPager.ts"] --> DOC LC2["liveComments.ts"] --> GEO REG2["registration.ts"] --> EXP LAY["ui/lib/stml/layout.ts"] --> STML RP2["ui/lib/reviewProjection.ts"] --> IDN & ANC & DOCThe A1 bug fix, concretely
For a hunk with a zero-count side (reachable via
git diff -U0 | hunk pager), the terminal's local gap math labeled expanded context off by one. Fixed once inreviewLeadingGap, adopted by the terminal:flowchart TB H["pure-insertion hunk @@ -6,0 +7,1 @@<br/>old side contributes zero lines"] B["before — pierre.ts local math<br/>leading gap labeled old 0–5<br/>(line 0 does not exist)"] A["after — core reviewLeadingGap<br/>leading gap labeled old 1–6<br/>labels match the source file"] H --> B H --> AThe symmetric pure-deletion case had the new-side range one low, revealing text one line early. Row counts are unchanged in both cases — only labels/content alignment corrected.
The conformance harness
Every fixture's expectations are hand-written from unified-diff semantics (never generated by calling the primitive), and each adversarial case is precisely an input a deleted copy got wrong. Consumers register as phases land:
flowchart LR subgraph fx["test/review-conformance — golden corpus"] F1["pure-insertion-hunk · pure-deletion-hunk (A1/A2)"] F2["hunk-with-leading-context (A3)"] F3["crlf-source · source-without-trailing-newline (A4)"] F4["binary-rename-with-no-rows (A8)"] F5["note-body cases ×8 (D2)"] end subgraph now["Registered now"] C1["core model"] C2["terminal render planning"] end fx --> C1 fx --> C2 fx -.-> P2ph["producer (Phase 2)"] fx -.-> P3ph["broker + wire (Phase 3)"] fx -.-> P5ph["browser projection (Phase 5)"]Seam probe run: reverting the A1 formula inside core made the terminal's conformance suite fail alongside core's — mechanical proof the terminal consumes the primitive rather than keeping a shadow copy.
Disclosed behavior changes (2)
useReviewControllerunit tests were rewritten to reload with changed content and one new test pins the preservation contract — this is the rung-4 "test edit = behavior change" red flag, raised deliberately: the change is the fix the identity primitive exists for.Deliberate residuals (recorded in the audit doc)
reviewTrailingGapkeeps the terminal's existing existence rule rather than the "correct" math — the correction would add trailing gaps to files whose last hunk has a zero-count side, an unsanctioned behavior change staged separately. Every consumer now agrees on one rule, which is the finding's point.ui/lib/stml(it sanitizes vialib/terminalText, which core may not import); core owns the tag→role table and the parser derives its void/raw-text sets from it. Parser relocation is Phase 5 work.rebaseReviewHunkreturns per-side end indices so both use cases fit without either copy'sisPartialopinion).Design notes (fresh implementation, not a port)
ReviewHunkSpan,ReviewGapSource) that Pierre metadata satisfies directly — the prototype's primitives demanded a fully projected file, which is why its terminal kept local copies.identity.tsis platform-neutral (nonode:crypto) — the node-debt map shrank from 4 entries to 1 (jsonStream.tsonly).contentIdentityseparately answers "did the content move".Gates
bun run typecheck,bun run lint(deny-warnings),bun run format— clean.bun test: 2414 pass / 5 fail — every failure pre-existing and reproduced on the base branch (dev-dep imports ×2, one baselineui-componentsfailure, PTY load-flakes).bun run test:integration(PTY): 106/106, untouched.test/pty/test/cli/test/sessionedits.AGENTS.mdgains thetest/review-conformance/directory entry; per-finding repayment recorded inline indocs/browser-review-seam-audit.md.Generated by Claude Code