Render F10 review proposals inline at their resolved anchor (not as trailing blocks) #31

Closed
opened 2026-06-12 08:09:11 +00:00 by benstull · 0 comments
Owner

Render F10 review proposals inline at their resolved anchor (not as trailing blocks)

Summary

In the F10 review preview, render each pending Claude proposal at its resolved
anchor position within the rendered document
— inline where the change would
land — instead of appending all proposals as trailing cw-proposal blocks after
the diff body.

Problem / pain

F10 shipped (#29) with renderReview appending every proposal as a trailing
block at the bottom of the preview (anchored ones first, then unanchored). The
graduated spec (coauthoring-interactive-review.md §2 and §6.2) actually calls
for a pending proposal to be "rendered inline at its resolved anchor" — a blue
block in context, where the replaced text sits. The trailing-block behavior is a
deliberate v1 simplification (chosen for determinism + because inline injection
into the block-level diff body is materially more complex), recorded as a deferred
decision in session 0030. It is functional but lower-fidelity: when several
proposals are pending, the reviewer has to mentally map each trailing block back to
the place in the document it affects, which is exactly the "where does this change
go?" friction the inline-at-anchor design was meant to remove.

Who feels it

The human coauthor (PP-1) reviewing a session with multiple pending Claude
proposals — the more proposals, the more the trailing list loses the spatial
"this change happens here" cue.

Desired outcome / value

Resolved proposals appear in place in the rendered preview — the blue ✓/✗
block sits at the document location it would change, so "what is Claude proposing,
and where?" is answered by position, not by cross-referencing a trailing list.
Unanchored (unresolvable) proposals keep the trailing-block fallback (never
dropped — INV-34).

Acceptance — "done right"

  • A pending proposal whose anchor resolves renders as a cw-proposal block
    at its resolved offset within the rendered body (in the block/paragraph that
    contains the anchor), carrying its data-proposal-id + ✓/✗, the struck replaced
    text, and the proposed text.
  • A proposal whose anchor does not resolve still renders as a trailing
    cw-proposal-unanchored block (the current fallback — never silently dropped,
    INV-34 preserved).
  • renderReview stays pure, vscode-free, and deterministic (INV-33): same
    inputs → identical HTML.
  • ✓/✗ still route only through the F4 seam; the webview never mutates the document
    (INV-34/INV-21 unchanged).
  • Unit coverage: a proposal injected mid-document renders at the right block; two
    proposals in different blocks each render in place; an unanchored proposal still
    trails; determinism holds.

Scope / non-goals

In scope: the positional placement of resolved proposal blocks inside
renderReview's output; the unit tests for it.

Non-goals: any change to the F4 propose/accept model, the accept/reject seam,
the sidecar, or SCHEMA_VERSION; changing the visual styling of a proposal block;
the unanchored-proposal fallback (keep it).

Assumptions · constraints · dependencies

  • Builds directly on F10 (#29, shipped). renderReview already receives
    ProposalView[] with resolved anchorStart/anchorEnd offsets — the data
    needed for placement is already in hand; the work is in the render composition.
  • Constraint: the block-level diff (diffBlocks) emits ops in document order;
    injecting a proposal "at its block" means associating each resolved offset with
    the current-side block range it falls in (the same positional mapping F10 uses
    for author-coloring) and emitting the proposal block adjacent to that block.
  • Determinism must be preserved — stable ordering when multiple proposals fall in
    the same block.

Source / signal

Self-originated — capture session vscode-cowriting-plugin-0031 (2026-06-12),
from the deferred decision recorded in planning-and-executing session 0030
(F10 ship). Spec: specs/coauthoring-interactive-review.md §2 / §6.2
("rendered inline at its resolved anchor") and §9 (open items). Builds on F10 #29.

Priority (WSJF)

Provisional: Value medium (sharpens the just-shipped review UX; the
trailing-block v1 is functional, so this is fidelity, not a fix) ·
Time-criticality low · Opportunity medium ÷ Size small
provisional priority/P2. (Decision aid, not auto-scored.)

Decomposition

Single increment (Feature, small). Slice checklist:

  • Associate each resolved ProposalView with the current-side block range it
    anchors into (reuse F10's positional range pointer).
  • Emit the cw-proposal block at that position in renderReview's output;
    keep unanchored proposals trailing.
  • Unit tests: mid-document placement, multiple proposals in distinct blocks,
    same-block ordering determinism, unanchored fallback, overall determinism.
  • Host E2E spot-check that an accepted in-place proposal still lands + clears.

Solution notes (optional — NOT a design)

Non-binding: F10 already computes a positional ranges/ci mapping for
author-coloring in renderReview (src/trackChangesModel.ts); the same mapping can
locate the block a proposal's anchorStart falls in, and the proposal block can be
emitted right after that block's HTML. Watch determinism for multiple proposals in
one block (stable sort by anchorStart then id).

<!-- Set the type/* label: type/feature --> # Render F10 review proposals inline at their resolved anchor (not as trailing blocks) ## Summary In the F10 review preview, render each **pending Claude proposal at its resolved anchor position within the rendered document** — inline where the change would land — instead of appending all proposals as trailing `cw-proposal` blocks after the diff body. ## Problem / pain F10 shipped (#29) with `renderReview` appending **every** proposal as a trailing block at the bottom of the preview (anchored ones first, then unanchored). The graduated spec (`coauthoring-interactive-review.md` §2 and §6.2) actually calls for a pending proposal to be "rendered **inline at its resolved anchor**" — a blue block *in context*, where the replaced text sits. The trailing-block behavior is a deliberate v1 simplification (chosen for determinism + because inline injection into the block-level diff body is materially more complex), recorded as a deferred decision in session 0030. It is functional but **lower-fidelity**: when several proposals are pending, the reviewer has to mentally map each trailing block back to the place in the document it affects, which is exactly the "where does this change go?" friction the inline-at-anchor design was meant to remove. ## Who feels it The **human coauthor** (PP-1) reviewing a session with **multiple** pending Claude proposals — the more proposals, the more the trailing list loses the spatial "this change happens *here*" cue. ## Desired outcome / value Resolved proposals appear **in place** in the rendered preview — the blue ✓/✗ block sits at the document location it would change, so "what is Claude proposing, and where?" is answered by position, not by cross-referencing a trailing list. Unanchored (unresolvable) proposals keep the trailing-block fallback (never dropped — INV-34). ## Acceptance — "done right" - A pending proposal whose anchor **resolves** renders as a `cw-proposal` block **at its resolved offset** within the rendered body (in the block/paragraph that contains the anchor), carrying its `data-proposal-id` + ✓/✗, the struck replaced text, and the proposed text. - A proposal whose anchor **does not resolve** still renders as a trailing `cw-proposal-unanchored` block (the current fallback — never silently dropped, INV-34 preserved). - `renderReview` stays **pure, vscode-free, and deterministic** (INV-33): same inputs → identical HTML. - ✓/✗ still route only through the F4 seam; the webview never mutates the document (INV-34/INV-21 unchanged). - Unit coverage: a proposal injected mid-document renders at the right block; two proposals in different blocks each render in place; an unanchored proposal still trails; determinism holds. ## Scope / non-goals **In scope:** the positional placement of resolved proposal blocks inside `renderReview`'s output; the unit tests for it. **Non-goals:** any change to the F4 propose/accept model, the accept/reject seam, the sidecar, or `SCHEMA_VERSION`; changing the visual styling of a proposal block; the unanchored-proposal fallback (keep it). ## Assumptions · constraints · dependencies - Builds directly on F10 (#29, shipped). `renderReview` already receives `ProposalView[]` with resolved `anchorStart`/`anchorEnd` offsets — the data needed for placement is already in hand; the work is in the render composition. - Constraint: the block-level diff (`diffBlocks`) emits ops in document order; injecting a proposal "at its block" means associating each resolved offset with the current-side block range it falls in (the same positional mapping F10 uses for author-coloring) and emitting the proposal block adjacent to that block. - Determinism must be preserved — stable ordering when multiple proposals fall in the same block. ## Source / signal Self-originated — **capture session vscode-cowriting-plugin-0031** (2026-06-12), from the **deferred decision** recorded in planning-and-executing session 0030 (F10 ship). Spec: `specs/coauthoring-interactive-review.md` §2 / §6.2 ("rendered inline at its resolved anchor") and §9 (open items). Builds on F10 `#29`. ## Priority (WSJF) Provisional: Value **medium** (sharpens the just-shipped review UX; the trailing-block v1 is functional, so this is fidelity, not a fix) · Time-criticality **low** · Opportunity **medium** ÷ Size **small** → **provisional priority/P2**. (Decision aid, not auto-scored.) ## Decomposition Single increment (Feature, small). Slice checklist: - [ ] Associate each resolved `ProposalView` with the current-side block range it anchors into (reuse F10's positional range pointer). - [ ] Emit the `cw-proposal` block at that position in `renderReview`'s output; keep unanchored proposals trailing. - [ ] Unit tests: mid-document placement, multiple proposals in distinct blocks, same-block ordering determinism, unanchored fallback, overall determinism. - [ ] Host E2E spot-check that an accepted in-place proposal still lands + clears. ## Solution notes (optional — NOT a design) Non-binding: F10 already computes a positional `ranges`/`ci` mapping for author-coloring in `renderReview` (src/trackChangesModel.ts); the same mapping can locate the block a proposal's `anchorStart` falls in, and the proposal block can be emitted right after that block's HTML. Watch determinism for multiple proposals in one block (stable sort by anchorStart then id).
benstull added the type/featurepriority/P2 labels 2026-06-12 08:09:11 +00:00
Sign in to join this conversation.