Commit Graph

8 Commits

Author SHA1 Message Date
Ben Stull 2f6008ba2b #47 (SLICE-2, review): per-block document proposals + word-precise accept
Document-edit flow SLICE-2 (specs/coauthoring-document-edit-flow.md §7.2,
INV-39/40/41 — P1 "too much to review"). A whole-document rewrite now proposes
ONE F4 proposal per CHANGED BLOCK (the unit a human reviews), but accepting a
block reconciles attribution at WORD granularity (the unit F3 records). Block =
decision unit; word = attribution unit. Supersedes INV-37's per-word cut for
document edits; selection edits unchanged.

- trackChangesModel.ts: new pure diffToBlockHunks(current, rewritten) — block-key
  alignment (reusing diffArrays/diffBlocks keying): an isolated changed block →
  one block-aligned hunk → the rewritten block raw (a code/mermaid fence is one
  atomic whole-fence hunk, INV-23); insert/delete runs → one gap-span hunk over
  the inter-anchor region (separators included) so reconstruction stays exact; a
  zero-width gap-span is anchored (INV-41). Also split diffToHunks into the raw,
  un-anchored wordEditHunks + the anchoring wrapper (the anchoring could grow an
  insertion to overlap an adjacent hunk, corrupting a batch apply — a latent bug
  that only surfaced once hunks are applied as a batch).
- trackChangesPreview.ts: runEditAndPropose document branch uses diffToBlockHunks
  and tags each proposal granularity:"block".
- model.ts / proposalModel.ts: additive optional Proposal.granularity
  ("block"|"single"; absent ⇒ single, back-compat — no migration).
- proposalController.ts: accept of a block proposal runs an intra-block word
  sub-diff (wordEditHunks, disjoint) and applies one applyAgentEdit per changed
  run, descending offset — only the words Claude changed land Claude-attributed;
  unchanged spans keep prior authorship (INV-40).
- Tests: diffToBlockHunks unit (reconstruction + fence atomic + add/remove);
  f12Review host E2E (M blocks→M proposals, unchanged→none, fence atomic, INV-40
  attribution, INV-41 insertion accept); updated the f11 document-path E2E to
  per-block (INV-39 supersedes INV-37); MANUAL-SMOKE-F12 §2.

Seam note: pendingEdits.matchEvent resolves one registration per change event, so
INV-40's per-run attribution is sequential applyAgentEdit calls (N undo steps),
not one multi-replace WorkspaceEdit — see transcript Deferred decisions.

214 unit + 69/5 host E2E green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 08:01:16 -07:00
Ben Stull fe45569218 F10 SLICE-3: ProposalController.listProposals + onDidChangeProposals + keyFor (#29)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 00:17:26 -07:00
Ben Stull 743de78994 F10 SLICE-1: remove F4 in-editor proposal threads/decorations; bookkeeping via live/unresolved (#29)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 23:59:50 -07:00
Ben Stull 7387366e28 refactor(f8): controllers depend on SidecarRouter + isAuthorable (key via keyOf)
Spec §6.4: constructor store CoauthorStore→SidecarRouter, rootDir→string|undefined;
isInRoot/isTracked gate → isAuthorable(scheme); per-doc key via store.keyOf(docIdentity).
Seam + artifact logic unchanged. currentAuthor omits git email when no root (fail-open).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 13:11:39 -07:00
Ben Stull 0bdee1c286 fix(edit-selection): accurate per-condition warnings + path-boundary workspace check
cowriting.editSelection collapsed four distinct failures (no editor / no
selection / unsaved / outside the workspace folder) into one misleading
"select some text in a workspace document first" warning — so editing a
selection in a file OUTSIDE the EDH workspace root (e.g. a content-repo file
while the EDH opens sandbox/) was rejected as if no text were selected.

- New pure, vscode-free src/workspacePath.ts: isUnderRoot() (separator-bounded
  membership, fixing a latent startsWith() prefix collision where a sibling
  whose name prefixes the root — e.g. vscode-cowriting-plugin-content vs
  vscode-cowriting-plugin — falsely matched) + selectionRejection() (one
  message per condition). Unit-tested (8 cases), incl. the reported bug.
- Wire isUnderRoot into all five membership checks: extension.ts (editSelection
  guard + renderIfOpen) and the thread/attribution/proposal controllers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 12:12:18 -07:00
Ben Stull 4c947acc43 fix: proposal threads disable the reply input — decide-only, INV-12
Playing with F4, the operator hit VS Code's default "Reply…" box on a
proposal thread: no submit command is wired for cowriting.proposals, so
typed text had nowhere to go. Proposals are decide-only (accept/reject in
the title bar); discussion belongs in a coauthoring thread. Set
canReply=false so the dead input never renders, and expose canReply on the
test-facing RenderedProposal with an E2E assertion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 23:26:11 -07:00
Ben Stull ef7d9403c0 feat: VersionGuard — newer-major sidecars are read-only with one warning (F5 SLICE-2, INV-16/PUC-4, #14)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 23:01:30 -07:00
Ben Stull 4efd34ffda F4 SLICE-2/3: ProposalController — propose ingress, resolve-or-flag, diff threads, accept/reject via the seam (#12)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 22:04:19 -07:00