Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ea2f58f
refactor(review): share the stat formatter, view-option locus, file i…
claude Aug 14, 2026
29f5b57
feat(web): read one live review over HTTP — capability, verified reso…
claude Aug 14, 2026
ae9dff9
feat(web): render one publication as a read-only review stream with P…
claude Aug 14, 2026
db2bf41
docs(review): record what Phase 5 PR 1 repaid, and the two questions …
claude Aug 14, 2026
e60c80e
test(web): cover the review page shell, so the file list's order is p…
claude Aug 15, 2026
0ff8db3
build(nix): record @types/react-dom in the generated bun lockfile
claude Aug 15, 2026
253e28d
fix(web): answer undefined for a malformed review path instead of thr…
claude Aug 15, 2026
32829ff
docs(review): record the E2 and G2 decisions the plan requires before…
claude Aug 15, 2026
4adac3a
fix(web): recover the review mirror from a failed load or a dropped s…
claude Aug 15, 2026
e1348e6
fix(web): read each file's source once, for the generation that asked
claude Aug 15, 2026
26029fb
fix(web): number expanded gap lines by the line-number option, not th…
claude Aug 15, 2026
2f947d7
test(review): say why the mirror's ordering adapter delivers without …
claude Aug 15, 2026
ebc35d9
fix(web): draw gap lines only from source read for the generation on …
claude Aug 15, 2026
04e58bf
Merge remote-tracking branch 'origin/main' into claude/review-rebuild…
claude Aug 15, 2026
abd2ba5
fix(review): state one file's churn the same way in every surface
claude Aug 15, 2026
d7cd641
refactor(review): read the event stream with the grammar that writes it
claude Aug 15, 2026
20899bf
refactor(review): bound concurrent work with one shared helper
claude Aug 15, 2026
214fc2a
refactor(review): let one module own what a refused review request lo…
claude Aug 15, 2026
5cd45a3
fix(web): explain a failure the way the catalog explains it
claude Aug 15, 2026
642d16b
refactor(review): derive an expanded gap's lines once, next to their …
claude Aug 15, 2026
c4ad8cd
refactor(review): name one layout union and two content types once
claude Aug 15, 2026
0702317
refactor(review): let a symbol's name say which runtime it belongs to
claude Aug 15, 2026
f5435e8
refactor(web): name browser modules' files after the exports they carry
claude Aug 15, 2026
76dcdec
Merge remote-tracking branch 'origin/main' into claude/review-rebuild…
claude Aug 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/consistent-file-stat-badges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hunkdiff": patch
---

File change stats now render consistently across the sidebar, diff header, and pager, hiding zero counts everywhere.
14 changes: 9 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,19 @@ ReviewIntent + caller facts -> planReviewIntent -> ReviewAction[] -> reducer ->
`selectors.ts` shared policies; `store.ts` synchronous observable storage. New cross-surface
operations start as intents. Callers supply mutable-note IDs/timestamps; core derives identities.
- **Surfaces/publishers:** `useTerminalReview.ts` is the TUI adapter and
`reviewNoteMapping.ts` is terminal-only. Rows, measurement, scrolling, layout, themes, DOM
mechanics, and source I/O stay local. `useHunkSessionBridge.ts` publishes the current terminal
`reviewNoteMapping.ts` is terminal-only; `src/web/` is the browser client — read-only today
— where `browserReviewApiClient`/`browserReviewMirror` speak the Phase 4 HTTP contracts and
`browserPierreDocument`/`BrowserReviewStream` render with Pierre. Rows, measurement, scrolling, layout,
themes, DOM mechanics, and source I/O stay local to each surface. `useHunkSessionBridge.ts` publishes the current terminal
session export; `registration.ts` builds its metadata/initial snapshot and `bridge.ts` receives
agent commands. This broker export is not a full `ReviewState` mirror.
- **Future consumers:** Web/API consumers reuse the model, derivations, state, intents, and then
the producer/protocol tier (see Phases 2–3 in `docs/browser-review-rebuild.md` for modules and
status). Never build a parallel protocol. Keep presentation/client-local state local;
host/extension commands need explicit remote capabilities.
- **Conformance:** `test/review-conformance/` has hand-authored semantic fixtures and currently
covers core plus terminal render planning. Every new semantic consumer registers its real
- **Conformance:** `test/review-conformance/` has hand-authored semantic fixtures and covers
core, terminal render planning, the producer, the broker mirror, the wire, the HTTP
surface, and the browser client's projection, mirror, and event reader. Every new semantic consumer registers its real
projection and runs the whole corpus. `scripts/source-boundaries.test.ts` keeps the seam
renderer/platform-free; its Node-debt list is shrink-only and tombstone lists append-only. A
repaid seam finding deletes copies, adds a file or banned-symbol tombstone and adversarial
Expand All @@ -65,7 +68,7 @@ ReviewIntent + caller facts -> planReviewIntent -> ReviewAction[] -> reducer ->
core VCS catalog. Do not add provider commands, spawning, or source readers under `src/core`.
- Pager mode has two paths: full diff UI for patch-like stdin, plain-text fallback for non-diff pager content.
- View defaults are layered through built-ins, user config, repo `.hunk/config.toml`, command sections, pager sections, and CLI flags.
- `hunk daemon serve` runs one loopback daemon that brokers agent commands to many live Hunk sessions. Normal Hunk sessions should auto-start and register with that daemon when session brokering is enabled. Keep it local-only and session-brokered rather than opening per-TUI ports. The daemon also mirrors each session's current review publication (generation plus resource catalog) and reads bulky content — patch text, canonical files, source — back as bounded, digest-verified resource chunks instead of holding it in the registration. Order publications with `classifyReviewPublication` and assemble chunks with `ReviewChunkAssembler`; do not add a second acceptance rule or a second assembly loop. The same daemon serves each session's review over HTTP (`src/session/broker/browserReviewServer.ts`): loopback and same-origin only, no CORS, and every route authorized by a per-session capability the session mints and publishes only the digest of. That surface is transport and authorization — its routes, capability grammar, SSE event contract, and error messages are the browser-safe modules `src/session/review{HttpProtocol,EventProtocol,ErrorCatalog}.ts`, and its semantic answers come from the producer through the existing intent path.
- `hunk daemon serve` runs one loopback daemon that brokers agent commands to many live Hunk sessions. Normal Hunk sessions should auto-start and register with that daemon when session brokering is enabled. Keep it local-only and session-brokered rather than opening per-TUI ports. The daemon also mirrors each session's current review publication (generation plus resource catalog) and reads bulky content — patch text, canonical files, source — back as bounded, digest-verified resource chunks instead of holding it in the registration. Order publications with `classifyReviewPublication` and assemble chunks with `ReviewChunkAssembler`; do not add a second acceptance rule or a second assembly loop. The same daemon serves each session's review over HTTP (`src/session/broker/webReviewServer.ts`): loopback and same-origin only, no CORS, and every route authorized by a per-session capability the session mints and publishes only the digest of. That surface is transport and authorization — its routes, capability grammar, SSE event contract, and error messages are the browser-safe modules `src/session/review{HttpProtocol,EventProtocol,ErrorCatalog}.ts`, and its semantic answers come from the producer through the existing intent path.
- Extensions come in two tiers — user TypeScript extensions and the bundled tier in `src/extensions/default/` — running through one per-extension API object and registry (`src/extensions/runExtension.ts`, resolved via `src/extensions/apply.ts`). Every shipped VCS backend and the built-in sidebar are bundled extensions registering through the public API; that dogfooding keeps `hunkdiff/extension` honest. Hard rules: `src/extension-api/types.ts` stays import-free (declaration emission publishes whatever it reaches; `scripts/check-pack.ts` gates it); `src/extensions/default/vcs/` loads from VCS adapter resolution and must stay renderer-free (the sidebar loads separately via `getBundledSidebarView`); repo-local `.hunk/extensions/` never executes without the trust prompt; bundled extensions stay loaded under `--no-extensions`. The full architecture — host-served runtime modules, sidebar pane model, command dispatch, VCS detection ordering, conversion boundaries — is mapped in `docs/extension-architecture.md` and documented in depth by the module headers it names; the authoring guide is `docs/extensions.md`, and `skills/hunk-extensions/SKILL.md` is the agent-facing map of those touchpoints.
- Agent rationale is optional sidecar JSON matched onto files/hunks.
- The order of `files` in the sidecar is intentional. Hunk uses that order for the sidebar and main review stream.
Expand Down Expand Up @@ -108,6 +111,7 @@ ReviewIntent + caller facts -> planReviewIntent -> ReviewAction[] -> reducer ->
- `test/session/` for daemon/session integration and end-to-end flows.
- `test/pty/` for PTY-backed live UI integration tests.
- `test/review-conformance/` for the shared review model's golden fixtures and per-consumer conformance suites.
- Browser client code lives in `src/web/` with colocated unit tests; its end-to-end flow against a real session is `test/session/browserReviewClient.integration.test.ts`.
- `test/smoke/` for opt-in terminal transcript smoke coverage.

## code comments
Expand Down
4 changes: 4 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 27 additions & 6 deletions docs/browser-review-rebuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ parallel-load test; vocabulary derivation checks active (rung 5).

## Phase 4 — HTTP surface, no client (landed)

`browserReviewServer` + capability auth + SSE, loopback-only, tested with plain `fetch`.
`webReviewServer` + capability auth + SSE, loopback-only, tested with plain `fetch`.
Four routes per live session, mounted inside the existing daemon rather than on a port per
terminal: the current publication (position plus resource catalog), bounded digest-verified
resource reads through the existing mirror and cache, an SSE stream, and action submission
Expand All @@ -97,21 +97,42 @@ this PR alone.

## Phase 5 — browser client (two PRs)

1. **Read-only mirror**: `apiClient` / `mirror` / `pierreDocument` / review stream rendering a
snapshot with Pierre — built on the shared geometry/selector/ordering primitives from day
one (no `sideRange`, no local acceptance rules, no bare `split("\n")`). No actions, no note
editing.
1. **Read-only mirror (landed)**: `src/web/` — `browserReviewApiClient` / `browserReviewMirror` /
`browserPierreDocument` / `BrowserReviewStream` rendering a publication with Pierre, built on the
shared geometry/selector/ordering primitives from day one (no `sideRange`, no local
acceptance rules, no bare `split("\n")`). No actions, no note editing.

Two findings the PR settled rather than inherited. First, the question the Phase 4 run
boundary left open: a publication is a position plus a resource catalog, and the
catalog's content resources carry no selection, filter, expansion, or notes — so a
read-only client mirrors a review's _content_ and nothing about its semantic position,
and every note-shaped browser finding waits on PR 2 putting review state on the wire.
Second, a gap in the Phase 4 surface only a client could find: catalog descriptors are
unmeasured until the producer materializes them, so a resource response now states the
whole resource's size and digest in headers declared beside the routes, and the daemon
keeps the digest its own assembly verified against.

2. **Interactivity**: action dispatch through the broker, selection sync (G2 policy decided
before this PR), note editing, watch/reload generation swaps, browser key bindings and the
command palette rendered from the shared catalog.

Repays: A11, C3, C5, E1, G1; the browser sites of A/B/C/D findings left open in earlier
phases; F browser bindings; G3/G4 browser adoption. E2 and the G2 selection policy must be
decided (not necessarily built) before PR 2.
PR 1 closed: A6, A7, A11, C1's browser site, C2's browser site, C3, C4's client half, C5,
D4's browser site, E1, G4's browser adoption, and G1 part (a); A3/A4/A5/A8/A10 and B6's
geometry half are answered by the browser projection beside the terminal. Left for PR 2:
B3–B6's state halves, B7/B8, B10's client, D1's composers, D3, A9's parser relocation, E2,
F1–F3's browser halves, G1's persistence half, G2's policy, and G3's deep-link navigation.
Gate: browser projection joins the conformance harness — the same fixtures every other
consumer runs, closing the renderer-parity loop (note placement, gap addressing, reveal
targets, default note targets); web boundary gates and the browser-closure node-free gate
active; command-parity check (both clients render command surfaces from the shared catalog).
PR 1's half of that gate is met: `browser review projection` (geometry), `browser review
mirror` (ordering), and `browser review client reader` (events) are registered and run the
whole corpus, and `scripts/review-vocabulary.test.ts` now scans `src/web` for re-declared
constants. Navigation and wire consumers stay unregistered rather than hollow — a read-only
client plans no moves and sends no actions — and join with PR 2.

## Phase 6 — entry points and packaging

Expand Down Expand Up @@ -218,7 +239,7 @@ re-derive. Every phase therefore passes the same five-rung ladder, and each rung
one golden fixture corpus under `test/review-conformance/`, with every consumer registering
a suite against the _same_ fixtures as it lands — terminal render planning (Phase 1),
producer projection (Phase 2), broker mirror and wire round-trip (Phase 3), HTTP surface
(Phase 4), browser projection (Phase 5). A phase's gate is that all previously registered
(Phase 4), browser projection, mirror, and event reader (Phase 5 PR 1). A phase's gate is that all previously registered
suites still pass plus its own joins.
3. **Adversarial fixtures from the audit**: every audit finding that documented a divergence
contributes the fixture its old copy got wrong — pure-insertion hunks and zero-count sides
Expand Down
Loading
Loading