feat(review): navigation intents, command catalog, and the address grammar (Phase 1 PR 3) - #720
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Greptile SummaryThe PR centralizes review navigation, command identity, filtering/selection derivations, and semantic address parsing for future terminal and browser consumers.
Confidence Score: 4/5The PR appears safe to merge after addressing the non-blocking documentation-rule violation in the navigation tests. The shared navigation, command-catalog, and address paths have focused coverage, and the only accepted issue is missing required comments on two test helpers. Files Needing Attention: src/core/review/navigation.test.ts Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
Input[Keyboard, menu, or session request] --> Catalog[Shared command catalog]
Catalog --> Intent[Review intent]
Intent --> Planner[Shared navigation planner]
Planner --> Store[Review store]
Store --> Terminal[Terminal projection]
Store -. future .-> Browser[Browser projection]
Prompt To Fix All With AI### Issue 1
src/core/review/navigation.test.ts:35-41
**Undocumented navigation test helpers**
The new `model` and `at` helpers lack the required TSDoc-style explanations, leaving their fixture roles undocumented and violating the repository's function-comment standard.
```suggestion
/** Build a navigation model over the shared test files and supplied annotations. */
function model(annotations = EMPTY_REVIEW_ANNOTATION_INDEX): ReviewNavigationModel {
return { files: FILES, annotations };
}
/** Build a semantic selection at one file and hunk position. */
function at(fileKey: string | null, hunkIndex: number): ReviewSemanticSelection {
return { fileKey, hunkIndex };
}
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(review): add the semantic address g..." | Re-trigger Greptile |
| function model(annotations = EMPTY_REVIEW_ANNOTATION_INDEX): ReviewNavigationModel { | ||
| return { files: FILES, annotations }; | ||
| } | ||
|
|
||
| function at(fileKey: string | null, hunkIndex: number): ReviewSemanticSelection { | ||
| return { fileKey, hunkIndex }; | ||
| } |
There was a problem hiding this comment.
Undocumented navigation test helpers
The new model and at helpers lack the required TSDoc-style explanations, leaving their fixture roles undocumented and violating the repository's function-comment standard.
| function model(annotations = EMPTY_REVIEW_ANNOTATION_INDEX): ReviewNavigationModel { | |
| return { files: FILES, annotations }; | |
| } | |
| function at(fileKey: string | null, hunkIndex: number): ReviewSemanticSelection { | |
| return { fileKey, hunkIndex }; | |
| } | |
| /** Build a navigation model over the shared test files and supplied annotations. */ | |
| function model(annotations = EMPTY_REVIEW_ANNOTATION_INDEX): ReviewNavigationModel { | |
| return { files: FILES, annotations }; | |
| } | |
| /** Build a semantic selection at one file and hunk position. */ | |
| function at(fileKey: string | null, hunkIndex: number): ReviewSemanticSelection { | |
| return { fileKey, hunkIndex }; | |
| } |
Context Used: guidelines.mdc Cursor rule (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/core/review/navigation.test.ts
Line: 35-41
Comment:
**Undocumented navigation test helpers**
The new `model` and `at` helpers lack the required TSDoc-style explanations, leaving their fixture roles undocumented and violating the repository's function-comment standard.
```suggestion
/** Build a navigation model over the shared test files and supplied annotations. */
function model(annotations = EMPTY_REVIEW_ANNOTATION_INDEX): ReviewNavigationModel {
return { files: FILES, annotations };
}
/** Build a semantic selection at one file and hunk position. */
function at(fileKey: string | null, hunkIndex: number): ReviewSemanticSelection {
return { fileKey, hunkIndex };
}
```
**Context Used:** guidelines.mdc Cursor rule ([source](https://github.com/modem-dev/modem/blob/main/.cursor/rules/guidelines.mdc))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
3e0e612 to
faa7fcf
Compare
4165b52 to
f9d9245
Compare
Hunk navigation, file navigation, and annotated navigation were implemented twice — once for the keyboard over the terminal's diff-file model, once for the session daemon's comment navigation — so the two could disagree about where a repeated step lands, and a browser client would have made three (docs/browser-review-seam-audit.md, B1-B9/B11). Move the walk into core/review/navigation.ts behind a `selection/move` intent, with the two rules the copies disagreed about stated by name: the wrap policy is per scope (annotated-file cycles, everything else clamps), and a move carries the reveal its scope earns. File jumps get `selection/select-file`, and a viewport that reports where it settled publishes through `selection/anchor`, which moves the selection without asking any viewport to scroll. The selectors those plans read are shared too: one filter matcher, one selection normalization and fallback rule, one reveal target that only picks a side with rows, one notes-by-hunk grouping over recorded ownership, and one note-visibility predicate. Behavior is unchanged, including the quirks that behavior includes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
The command table fused three separable things: identity (id, title, chords), binding (terminal KeyEvent matching), and effect (closures over live App state). A browser command palette or help screen would have had to restate the list, and the two clients would then drift on what a command is called, what it is bound to, and what it does (docs/browser-review-seam-audit.md, F1-F3). Move identity into src/core/commandCatalog.ts as data, with a declared resolution locus per command — semantic, client-local, or host-only — so a remote client can tell which commands it may invoke at all. Semantic commands declare their review effect as data too, and one lowering turns a command plus a repeat count into a ReviewIntent, which is what keeps the keyboard, an agent command, and a future palette meaning the same thing by "next hunk". The terminal keeps its matchers and handlers; its handler map is keyed by the catalog's id union, so a catalogued command with no handler fails to typecheck. Menus, help, and keybinding resolution are unchanged in behavior and now derive from the catalog, with a parity test asserting no surface names a command the catalog does not declare. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
Three consumers will need to name a place in a review across a boundary: browser deep links and history, a terminal "copy link", and agent surfaces that already address targets by file and hunk (docs/browser-review-seam-audit.md, G3). Add one serialize/parse pair over semantic keys only — never an index into rendered rows, which mean something different in the next client — with strict parsing, since an address arriving from a link is untrusted input. Land the navigation half of the conformance corpus alongside it, with the intent planner registered as its first consumer: the multi-step carry the session's walk lacked, the wrap-versus-clamp difference between scopes, a selection that outlives its file, and the pure-deletion reveal target the prototype browser resolved to a line that does not exist. Expectations are written by hand from the semantics, so they catch the bug rather than following it, and the same fixtures drive the wire in Phase 3 and the browser in Phase 5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
faa7fcf to
fad85b8
Compare
f9d9245 to
9ebd8ed
Compare
Phase 1 PR 3 — the final Phase 1 PR of the browser-review rebuild (
docs/browser-review-rebuild.md). Stacked on PR 2 (#719).What
Three commits, each green standalone:
src/core/review/navigation.tsplansselection/move(hunk | file | annotated-hunk | annotated-filescopes, explicit per-scope wrap policy) andselection/select-file; both the terminal keyboard path and the agent-session comment navigation now plan through it.src/ui/lib/hunks.tsdeleted and tombstoned in the boundary suite.src/core/commandCatalog.tsholds id/title/category/default chords/resolution locus as data; semantic commands declare their review effect as data with onelowerAppCommandToReviewIntent. The terminal's dispatch table is built from the catalog; menus and help read through it. Keyboard behavior byte-identical.src/core/review/address.ts,formatReviewAddress/parseReviewAddressover semantic keys (never array indices), round-trip + strict-rejection tests. No consumers yet by design (browser Phase 5, URL fragments Phase 6).Repays (four-part definition, recorded inline in the audit doc): B1, B2, B4, B5, B9, B11 fully for existing sites; B3, B6, B7 core side (browser halves Phase 5); B8 primitive landed with the terminal site deliberately open (see residuals); F1–F3 catalog side; G3 grammar. B10/B12 untouched (Phase 3, wire).
What changed, in diagrams
Navigation: three walks → one
flowchart TB subgraph before["Before — same question, three answers"] K1["terminal keyboard<br/>ui/lib/hunks.ts cursors + carry rule"] S1["agent session navigation<br/>separate walk, parity by comment only"] W1["browser (prototype)<br/>would have been the third"] end subgraph after["After — one planner, callers supply facts"] NAV["core/review/navigation.ts<br/>selection/move · selection/select-file<br/>REVIEW_SELECTION_WRAP_POLICY"] K2["terminal keyboard"] -->|"ReviewIntent"| NAV S2["hunk session comment nav"] -->|"same intent"| NAV B2x["browser — Phase 5"] -.->|"same intent, over the wire"| NAV endThe wrap/clamp asymmetry that was previously incidental is now a named policy: plain hunk/file navigation clamps at edges, annotated-file navigation wraps —
REVIEW_SELECTION_WRAP_POLICYstates it once, and the multi-step carry rule for annotated jumps is in the planner with a conformance fixture pinning it.Commands: one fused table → catalog + per-client bindings
flowchart LR subgraph before2["Before — appCommands.ts fused three things"] FUSED["id + title + chords<br/>+ KeyEvent matchers<br/>+ App-state closures"] MENUS1["menus/help read the table"] end subgraph after2["After"] CAT["core/commandCatalog.ts<br/>id · title · category · chords · locus<br/>semantic entries: review effect as data"] TERM["ui/lib/appCommands.ts<br/>matchers + handlers only,<br/>built FROM the catalog"] LOWER["lowerAppCommandToReviewIntent<br/>(one statement of 'which way does ] move')"] MENUS2["menus · help · future palette"] CAT --> TERM CAT --> LOWER CAT --> MENUS2 WEB["browser — Phase 5"] -.->|"renders palette + bindings<br/>from the same catalog"| CAT endCatalog↔handler parity is a compile error, not just a test:
AppCommandIdis derived from the catalog literal and the terminal handler map is keyed by it. A parity test additionally pins that menus and the dispatch table contain exactly the catalog's built-ins.Where each command resolves
flowchart TB KEY["key press / menu click"] --> MATCH["terminal matcher (unchanged chords)"] MATCH --> LOCUS{"catalog locus"} LOCUS -->|semantic| INT["lower to ReviewIntent → planner → store<br/>(navigation, filter, toggle notes)"] LOCUS -->|client-local| LOCAL["terminal handler<br/>(scroll, layout, theme, help)"] LOCUS -->|host-only| HOST["host effect<br/>(quit, refresh, $EDITOR)"] INT --> ALL["every attached client re-renders"]Residuals recorded in the audit (deliberate)
selectNormalizedSelectionyieldsfileKey: nullonly when nothing is visible.selectNotesByHunk(owner-index grouping) landed, but DiffPane still groups by range overlap — converting is user-visible and belongs in a behavior-changing PR, not this one.expansion/toggleand the note-draft intent are the two named gaps in the catalog's semantic set (SEMANTIC_COMMANDS_WITHOUT_REVIEW_EFFECT) — Phase 2 fills them, which is what letszandcreach a remote client.Design notes (fresh implementation, not a port)
ReviewNavigationModel+ caller-suppliedReviewAnnotationIndex— core never reaches into a renderer's diff model for facts it can't own.src/core/commandCatalog.ts— UI vocabulary doesn't belong incore/review(pure review semantics), andsrc/uiis unreachable from a browser bundle. No react/opentui/node imports; string chords only. Phase 5 note: the web boundary gate's allowed-targets list must add this path when the browser adopts it.Gates
bun run typecheck,bun run lint(0/0),bun run format— clean; empty changeset.bun test: 2457 pass / 5 fail — failures identical to the base commit's (dev-dep ×2 + three known flakes, each passing in isolation); strictly fewer failures than baseline's run.bun run test:integration(PTY): 105/106; the one failure reproduces identically on the base commit. PTY files untouched.src/ui/lib/hunks.ts— B1).bun run src/main.tsx -- diff) renders correctly.Generated by Claude Code