Skip to content

feat(review): navigation intents, command catalog, and the address grammar (Phase 1 PR 3) - #720

Open
benvinegar wants to merge 3 commits into
claude/review-rebuild-phase-1-pr2from
claude/review-rebuild-phase-1-pr3
Open

feat(review): navigation intents, command catalog, and the address grammar (Phase 1 PR 3)#720
benvinegar wants to merge 3 commits into
claude/review-rebuild-phase-1-pr2from
claude/review-rebuild-phase-1-pr3

Conversation

@benvinegar

Copy link
Copy Markdown
Member

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:

  1. One shared navigation walksrc/core/review/navigation.ts plans selection/move (hunk | file | annotated-hunk | annotated-file scopes, explicit per-scope wrap policy) and selection/select-file; both the terminal keyboard path and the agent-session comment navigation now plan through it. src/ui/lib/hunks.ts deleted and tombstoned in the boundary suite.
  2. Command identity split out of the terminal tablesrc/core/commandCatalog.ts holds id/title/category/default chords/resolution locus as data; semantic commands declare their review effect as data with one lowerAppCommandToReviewIntent. The terminal's dispatch table is built from the catalog; menus and help read through it. Keyboard behavior byte-identical.
  3. Semantic address grammarsrc/core/review/address.ts, formatReviewAddress/parseReviewAddress over 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
    end
Loading

The 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_POLICY states 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
    end
Loading

Catalog↔handler parity is a compile error, not just a test: AppCommandId is 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"]
Loading

Residuals recorded in the audit (deliberate)

  • B4 correction: the audit assumed the terminal rendered "no file" for a filtered-out selection — in fact it kept rendering the selected file. Terminal behavior won; selectNormalizedSelection yields fileKey: null only when nothing is visible.
  • B8: 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.
  • B5: committed-vs-live filter timing still undecided (planning reads immediate, rendering reads one-render-deferred).
  • Two long-standing navigation quirks are now named rather than incidental (edge-hunk re-reveal; annotated-file ring entry at index 0).
  • expansion/toggle and 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 lets z and c reach a remote client.

Design notes (fresh implementation, not a port)

  • Navigation plans over an explicit ReviewNavigationModel + caller-supplied ReviewAnnotationIndex — core never reaches into a renderer's diff model for facts it can't own.
  • Catalog placement: src/core/commandCatalog.ts — UI vocabulary doesn't belong in core/review (pure review semantics), and src/ui is 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.
  • Address grammar: slash path with percent-encoded identifiers, fragment-safe, strict parsing.

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.
  • Boundary suite green with the first tombstone entry (src/ui/lib/hunks.ts — B1).
  • Real source invocation smoke (bun run src/main.tsx -- diff) renders correctly.

Generated by Claude Code

@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hunk-web Ignored Ignored Preview Aug 13, 2026 12:49am

Request Review

@greptile-apps

greptile-apps Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR centralizes review navigation, command identity, filtering/selection derivations, and semantic address parsing for future terminal and browser consumers.

  • Adds shared selection movement and file-jump planning with explicit reveal and wrap policies.
  • Extracts built-in command metadata into a renderer-neutral catalog while retaining client-specific handlers.
  • Adds strict semantic address formatting/parsing and expands conformance coverage.

Confidence Score: 4/5

The 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

Filename Overview
src/core/review/navigation.ts Introduces the shared cursor walk, per-scope wrap policy, and reveal behavior for review navigation.
src/ui/hooks/useReviewController.ts Routes terminal and session navigation through shared intents while retaining terminal-specific projection and lifecycle behavior.
src/core/commandCatalog.ts Defines shared built-in command identity, resolution locus, default chords, and semantic review effects.
src/core/review/address.ts Adds strict percent-encoded semantic review-address formatting and parsing with no consumers yet.
src/core/review/navigation.test.ts Provides broad navigation-policy coverage, but two new helper functions omit required TSDoc comments.

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]
Loading
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

Comment on lines +35 to +41
function model(annotations = EMPTY_REVIEW_ANNOTATION_INDEX): ReviewNavigationModel {
return { files: FILES, annotations };
}

function at(fileKey: string | null, hunkIndex: number): ReviewSemanticSelection {
return { fileKey, hunkIndex };
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Suggested change
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!

claude added 3 commits August 13, 2026 00:48
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
@benvinegar
benvinegar force-pushed the claude/review-rebuild-phase-1-pr2 branch from faa7fcf to fad85b8 Compare August 13, 2026 00:49
@benvinegar
benvinegar force-pushed the claude/review-rebuild-phase-1-pr3 branch from f9d9245 to 9ebd8ed Compare August 13, 2026 00:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants