Skip to content

feat(session): review wire protocol and daemon mirror derived from the intent vocabulary (Phase 3) - #722

Open
benvinegar wants to merge 5 commits into
claude/review-rebuild-phase-2from
claude/review-rebuild-phase-3
Open

feat(session): review wire protocol and daemon mirror derived from the intent vocabulary (Phase 3)#722
benvinegar wants to merge 5 commits into
claude/review-rebuild-phase-2from
claude/review-rebuild-phase-3

Conversation

@benvinegar

Copy link
Copy Markdown
Member

Phase 3 of the browser-review rebuild (docs/browser-review-rebuild.md) — the final phase of this run. Stacked on Phase 2 (#721). Phases 4–6 (HTTP surface, browser client, entry points) are deferred; the audit doc now carries a run-boundary note listing exactly what remains open.

What

The wire protocol and the daemon's review mirror — the layer that makes the producer's publications consumable beyond the terminal process, serving the existing hunk session agent surface. Five commits: wire schema → mirror + resources → agent errors → conformance → naming cleanup.

  • src/session/reviewProtocol.ts — the wire schema derived from the intent vocabulary (B12): HUNK_REVIEW_ACTION_TYPES = deriveReviewActionTypes(REVIEW_INTENT_TYPES, exclusions) with an empty, documented exclusion list — all twelve intents are wire-reachable, and a wire-reachable intent without a parser fails to compile. Carries expandedLineProof (B10) and a required actor tag (clientId + terminal|browser|agent, G2 wire fields — policy deliberately not applied) from version one, so the browser never needs a schema break. Passes the Phase 0 browser-safe boundary gate.
  • src/session/broker/reviewMirror.ts — the daemon mirrors each session's publication with exactly one classifyReviewPublication call (C1 broker site): a publication address plus a resource catalog — no notes, no manifest. The prototype mirrored notes, which is what forced its broker to re-derive anchors (D3, the dropped-fallback-branch bug); not mirroring them makes that duplication impossible rather than merely fixed.
  • core/review/resourceAssembly.ts — the single chunk-assembly/verification path (C2 broker site) with an injected hasher; the broker's loop only fetches and decodes. reviewResourceCache.ts bounds daemon-wide bytes with reservations taken before bytes are requested (genuinely bounded in-flight — the prototype bounded only completed bytes), and reservation resize so unmeasured resources don't serialize loads meant to run in parallel.
  • Agents get the payoff: hunk session review --include-patch reconstructs patches through bounded resource reads; registrations no longer embed patch text at all (the memory win the resource path exists for), with an older in-process session's inline patches still served unchanged.
  • Rung 5 activates: scripts/review-vocabulary.test.ts asserts wire vocabulary ≡ intent vocabulary minus named exclusions, every wire type actually reaches a parser (probed to distinguish "rejected" from "unrouted"), no session module re-declares a review-model constant or hand-writes a digest regex, and MAX_WS_MESSAGE_BYTES ≥ MAX_HUNK_REVIEW_ENVELOPE_BYTES — the one coupling the browser-safe protocol can't express as an import.

The shape, in one diagram

flowchart LR
    subgraph term["Terminal session (producer)"]
        STORE["review store"] --> PROD["ReviewProducer"]
    end
    subgraph daemon["Daemon (loopback only)"]
        MIR["reviewMirror<br/>publication address + resource catalog<br/>ONE classify() call — no notes, no anchors"]
        CACHE["reviewResourceCache<br/>reserved-before-read byte budget"]
        ASM["core resourceAssembly<br/>single verification path, injected hasher"]
        MIR --- CACHE --- ASM
    end
    PROD -- "publishes generation:producer:seq" --> MIR
    AGENT["hunk session CLI<br/>review --include-patch"] -- "bounded chunk reads" --> MIR
    AGENT -- "apply_review_action<br/>{actor, expandedLineProof, …}" --> MIR
    MIR -- "toReviewIntent → producer.applyIntent<br/>(same planner as the keyboard)" --> PROD
    WEB["browser — Phase 5"] -.-> MIR
    subgraph derive["Wire schema is derived, not restated"]
        IV["REVIEW_INTENT_TYPES (core)"] -->|"deriveReviewActionTypes<br/>exclusions: [] (documented)"| WT["HUNK_REVIEW_ACTION_TYPES"]
        WT -->|"parser table keyed by vocabulary<br/>missing parser = compile error"| P["strict parsers on shared validation"]
    end
Loading

Findings repaid (recorded inline in the audit doc)

B10 (wire+producer: ReviewExpandedLineClaim, resolved only by resolveReviewExpandedLine), B12 (derived vocabulary), C1 broker site (one classify), C2 broker site (one assembler), D1 wire site (isTransportableReviewNote is reviewNoteWithinBounds — the every-field-fits/whole-note-fails case now rejected at the wire, pinned by the harness), D3 broker site (repaid by absence; the prototype's rejected-registration case pinned from the wire end), D5 broker/wire sites, G2 wire fields. Wire round-trip and broker mirror registered as conformance consumers over the Phase 2 ordering fixtures.

Behavior notes

  • Existing hunk session command output verified byte-identical against a Phase 2 worktree (review --json, --include-patch --json, text mode), via a real TUI session + daemon exercise.
  • --include-patch now round-trips to the session; a session that closes mid-read fails with a new, specific agent-facing message (generated skill regenerated via bun run generate:skill).
  • Oversized-patch sessions now appear in hunk session list (previously silently rejected at registration) — net improvement, recorded.
  • Daemon version 7 → 8; stale daemons restart through the existing capability flow.
  • One honest gap recorded for Phase 5: the vocabulary has no draft-body intent, so a remote client can open and save a draft but not type into it — remote note composition is Phase 5's note-editing work.
  • apply_review_action has no production caller yet by design (Phase 4/5); it is implemented and tested end-to-end (broker → socket → bridge → producer → intent), not stubbed.

Gates

  • bun run typecheck, bun run lint (0/0), bun run format — clean; empty changeset.
  • bun test: 2695 pass / 5 fail — strict subset of the Phase 2 baseline's failures (dev-dep ×2 + three known flakes).
  • bun run test:integration (PTY): 106/106 (baseline had 1 flake).
  • bun test test/session/: 11/11, files untouched (new integration coverage colocated at src/session/broker/reviewResources.integration.test.ts to honor that literally).
  • Boundary suite green — reviewProtocol.ts passes the browser-safe gate written for it in Phase 0; vocabulary-derivation suite green.

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 introduces the review wire protocol and daemon-side mirror that expose producer publications and digest-verified resources to session clients.

  • Derives the complete wire action vocabulary from core review intents and adds strict action parsing.
  • Mirrors publication addresses and resource catalogs while applying shared generation-order semantics.
  • Adds bounded, single-flight resource reconstruction with digest and chunk verification.
  • Routes remote review actions through the existing producer intent planner.
  • Moves optional patch delivery from session registration into bounded resource reads while preserving compatibility with older inline registrations.

Confidence Score: 5/5

The PR appears safe to merge; no concrete changed-code failure remained after checking action concurrency, resource bounds, and mirror lifecycle behavior.

Remote stale-action checks use the publication classifier correctly, resource sizes remain below the production cache ceiling with eviction preserving the completed-byte budget, and failure or generation-change paths clean up reservations and prevent obsolete resources from being served.

Important Files Changed

Filename Overview
src/session/reviewProtocol.ts Defines the derived wire action vocabulary, strict parsers, actor metadata, expanded-line proofs, and resource catalog envelopes.
src/session/broker/reviewMirror.ts Maintains one generation-ordered publication and resource catalog per live session.
src/session/broker/reviewResourceCache.ts Adds generation-qualified LRU storage and pre-read reservation accounting under separate completed and in-flight budgets.
src/session/broker/state.ts Integrates mirror lifecycle, single-flight chunk loading, digest-verified assembly, cache invalidation, and legacy inline-patch compatibility.
src/core/review/resourceAssembly.ts Centralizes ordered chunk assembly, progress validation, size enforcement, and final digest verification.
src/session/app/reviewCommands.ts Validates remote action position and expanded-line evidence before lowering the action into the producer’s existing intent path.
src/session/app/registration.ts Publishes review resources separately from registration and retains compatibility with older inline-patch sessions.
src/session/broker/brokerServer.ts Exposes the new review operations through the existing validated loopback daemon surface.
src/ui/hooks/useHunkSessionBridge.ts Connects current UI review state and producer operations to the expanded session bridge.

Sequence Diagram

sequenceDiagram
    participant Producer as ReviewProducer
    participant Mirror as Daemon ReviewMirror
    participant Cache as Resource Cache
    participant Client as Session Client
    Producer->>Mirror: Publish generation, revision, catalog
    Client->>Mirror: Request review with patch
    Mirror->>Cache: Load generation-qualified resource
    alt cache miss
        Cache->>Producer: Request bounded chunks
        Producer-->>Cache: Chunk envelopes
        Cache->>Cache: Assemble and verify digest
    end
    Cache-->>Client: Reconstructed patch
    Client->>Mirror: Apply review action
    Mirror->>Producer: Validated ReviewIntent
    Producer-->>Mirror: Updated publication
Loading

Reviews (1): Last reviewed commit: "refactor(session): name the action resul..." | Re-trigger Greptile

claude added 5 commits August 13, 2026 00:48
A browser client needs a wire schema before it needs a transport, and the
prototype's schema was a hand-copy of the intent union restated in three more
places — so an intent added to one was silently unreachable from the others
(browser-review-seam-audit.md, B12). This lands the schema by derivation
instead: `REVIEW_INTENT_TYPES` is the vocabulary, made total in both directions
by type assertions, and the wire is that list minus a named exclusion list that
is empty and says why. The wire type is derived the same way, so an action is
validated and narrowed rather than restated, and `toReviewIntent` lowers it back.

Two fields the browser cannot work without arrive from version one.
`expandedLineProof` (B10) makes a line inside an expanded gap addressable at
all — the prototype's browser had no field for it and its clicks in expanded
regions were rejected or mis-sided — and it is exactly the claim core resolves
rather than a wire-local twin. An actor tag (G2) records who acted, with no
policy attached, so deciding one later changes behavior rather than the schema.

Also lands the reader half of the resource model: one `ReviewChunkAssembler` in
core that holds a stream to what it declares, requires bounded progress, and
verifies the assembled bytes with an injected digest function (C2). The
resource-read request moves into core so the producer and the wire parse one
shape, and the failure vocabulary is composed from the codes that already exist
rather than restated as a third list (D5).

`scripts/review-vocabulary.test.ts` is the plan's rung 5: the wire vocabulary
equals the intent vocabulary minus its exclusions, every type in it reaches a
parser, no session module re-declares a name the review model exports, no module
writes its own digest pattern, and the transport frame the browser-safe protocol
deliberately does not import still fits a complete envelope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
…as resources

The daemon knew a session's files but not which review it was serving, and it
learned a file's patch text by being handed a copy of it in the registration —
so every patch of every live session sat in daemon memory for as long as those
sessions were open, and a review whose patches exceeded the registration budget
failed to register at all rather than appearing without them.

The daemon now mirrors what a session publishes: a position and a resource
catalog. Ordering that mirror is one `classifyReviewPublication` call and
nothing else (C1) — the prototype's mirror had its own comparison, which is one
of the five disagreeing acceptance rules the audit found. A generation change
retires the old generation's cached bytes and in-flight reads with it.

Bulky content is read back on demand instead: bounded chunks, assembled and
verified by the shared assembler, single-flight per resource, and reserved
against a daemon-wide in-flight budget before any bytes are requested (C2).
There is one load loop where the prototype had two near-verbatim copies inside
one file that already disagreed about progress and end-of-stream rules. An
unmeasured resource reserves one chunk and grows to what the writer declares
rather than reserving its kind's ceiling, so ordinary patches still load in
parallel.

Review actions are forwarded to the producer that owns the review state and
planned through the same intent path the keyboard uses. The daemon decides
nothing semantic — in particular it never re-derives where a note hangs, which
is what the prototype's broker copy got wrong by dropping the fallback branch
and rejecting legal expanded-gap notes (D3). The producer attaches to the
terminal's own store at mount, so a remote action and a key press move the same
review, and the controller now projects its document with the source label the
producer publishes under so both address files by the same keys.

`hunk session review --include-patch` produces exactly the same text as before,
now reconstructed from the generation that published it under the shared load
concurrency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
`review --include-patch` now reads patch bodies from the live session rather
than from a copy the daemon was handed, so it has a failure mode it did not have
before: the session can reload or close mid-read. That message goes through the
same catalog every other agent-facing message uses, so the generated skill
quotes wording the CLI really throws, and it carries the remedy — re-run, or
drop `--include-patch` when only file and hunk structure is needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
…nce harness

Import gates prove a tier may use a shared primitive; only the harness proves it
does. The mirror answers the Phase 2 ordering corpus by being driven through it —
seed a position, hand it an arriving publication, read the verdict out of what it
did — which is what shows it has no comparison of its own. The wire answers a new
corpus of every action in the vocabulary paired with the intent it lowers to,
written by hand, so a wire type drifting from the semantics it carries fails here
rather than at a client.

The adversarial cases are the two this phase owes. B10: a draft and a note on a
line inside an expanded gap, plus evidence that accompanies no line, which is
malformed rather than tolerated. D1: the wire runs the note-bounds corpus as a
consumer, so the note whose summary, rationale, and markup each pass a per-field
check while the whole note is triple the bound is now refused at the wire instead
of being admitted and then failing at the publisher.

The audit records the repayments and opens with a run boundary: this run stops
after Phase 3, so the findings left for Phases 4-6 are listed where the next run
will look for them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018L6h5GBz6RAxRXbgUS4mx4
The action result was spelled out twice — as an alias in the session tier and
as a union at the broker's dispatch site — while the resource result already had
a name in the protocol. Giving it the same treatment leaves one spelling of what
a producer answers an action with, which is the point of the module.

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-3 branch from 546e4f4 to 89826ed Compare August 13, 2026 00:49
@benvinegar
benvinegar force-pushed the claude/review-rebuild-phase-2 branch from cf89bb7 to aa2bfa0 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