add spec ./specs/coauthoring-diff-view.md (status: graduated)

This commit is contained in:
Ben Stull
2026-06-11 06:58:02 -07:00
parent 57f5827825
commit 36bc4e3d00
+483
View File
@@ -0,0 +1,483 @@
---
status: graduated
---
# Solution Design: Diff-View Toggle (F6)
| | |
| --- | --- |
| **Author(s)** | Ben Stull (with Claude) |
| **Reviewers / approvers** | Ben Stull |
| **Status** | `draft` |
| **Version** | v0.1.0 |
| **Source artifacts** | Feature `benstull/vscode-cowriting-plugin#17` (F6) · Epic `#1` (closed — F6 is the first post-epic UX feature) · Parent specs: `vscode-cowriting-plugin-content/specs/coauthoring-inner-loop.md`, `specs/coauthoring-attribution.md`, `specs/coauthoring-propose-accept.md`, `specs/coauthoring-cross-rung-format.md` + `specs/coauthoring-sidecar-contract.md` (all graduated) · F5 shipped: `#14` (PR #15, session 0015) · Lineage: `ben.stull/rfc-app#48` |
**Change log**
| Date | Version | Change | By |
| --- | --- | --- | --- |
| 2026-06-11 | v0.1.0 | Initial draft — brainstorming session 0017 | Ben Stull + Claude |
---
## 1. Business Context
### 1.1 Executive Summary
F6 gives the human coauthor the review surface the machine already has. F4
made every machine change reviewable-before-landing (proposal diffs); F3
paints who owns the current text; but the operator's **own** contribution is
visible only as final text. F6 adds a one-gesture **diff-view toggle**: flip
the editor into a diff of the document against a **coauthoring baseline**
and back — so "what did I actually change?" is answered in one keystroke
instead of git archaeology. The load-bearing design move is the baseline
itself: it **advances automatically at every machine landing** (every
successful `applyAgentEdit` seam application — the only way machine text ever
enters the document, INV-9/INV-10), so everything the diff shows is
operator-authored *by construction*. A single **pin** gesture gives the
operator an arbitrary epoch when they want one. The baseline lives in
extension workspace storage — never in the repo — so the sidecar, the
cross-rung contract (INV-14..17), and `SCHEMA_VERSION` are untouched.
### 1.2 Background
The inner-loop triad shipped across F2F4 (threads · attribution ·
propose/accept) and F5 made its sidecar a cross-rung contract. Review parity
is now asymmetric in the human's favor toward the *machine's* changes:
machine edits arrive as anchored diffs (F4) and land tinted (F3), while the
operator's own work between machine landings has no diff perspective at all.
Git's diff is against HEAD — the wrong baseline mid-session, where the
meaningful "before" is a coauthoring moment (a machine landing, a chosen
instant), not the last commit. Issue #17 parked the design forks —
**baseline choice** (the load-bearing one), snapshot home, whose-changes,
and the relationship to git's own features — for this session; all are
resolved here (§6.7, §9).
### 1.3 Business Actors / Roles
- **Coauthor (human)** — the writer/engineer (PP-1); F6's sole user: the
diff view exists to make their own contribution self-reviewable.
- **Coauthor (machine)** — Claude via `@cline/sdk`; not a user of F6, but
its landings define the default baseline epoch.
### 1.4 Problem Statement
While coauthoring, the operator cannot see their own changes as a diff. The
editor shows ownership of current text (F3) and machine intent before it
lands (F4), but the human's contribution since the last meaningful
coauthoring moment is only visible as final prose. The only diff available
— git's — is anchored to the wrong moment (HEAD).
### 1.5 Pain Points
- "What did I actually change?" requires git archaeology or memory.
- Self-review during coauthoring is expensive exactly where machine-review
is cheap — an inverted trust asymmetry.
- No way to mark "I'm about to do a risky pass — let me review it as a diff
afterwards" (no operator-chosen baseline moment).
### 1.6 Targeted Business Outcomes
One gesture flips the active document into a diff of the operator's own
changes against a coauthoring-meaningful baseline; the same gesture flips
back; nothing about the document, sidecar, or editor state is disturbed.
Self-review during coauthoring becomes as cheap as accepting a proposal.
### 1.7 Scope (business)
**In scope:** the baseline lifecycle (initialize at first track, advance at
machine landings, pin on demand); persistence of the baseline across window
reloads (extension workspace storage); the toggle command + keybinding; a
native `vscode.diff` view (readonly baseline left, the **live document**
right — editing continues inside the diff view); test-facing surface; host
E2E + unit coverage; manual smoke.
**Out of scope (deferred, not forgotten):** baseline **splicing** (keeping
pre-accept operator edits visible across a baseline advance — §6.7, §9);
multiple named/selectable baselines; any cross-rung sharing of baselines;
diffing against arbitrary git refs; changed-line gutter indicators in the
normal (non-diff) editor view.
**Non-goals (firm, from #17):** replacing git history; a general-purpose
diff tool; changing how proposals render (F4 owns machine diffs).
### 1.8 Assumptions · Constraints · Dependencies
- **Anchor:** Feature #17. Builds on the shipped inner loop (F2 `#4` · F3
`#6` · F4 `#12` · F5 `#14`); Epic #1 is closed.
- The whole design leans on two shipped invariants: **INV-9** (the seam
`applyAgentEdit` is the single machine-edit ingress) and **INV-10**
(proposals never mutate the document) — together: *between machine
landings, every buffer change is operator-authored.*
- F3 attribution is **state, not history** (cannot reconstruct before-text)
— confirmed: F6 needs a snapshot, and takes one; it does not consume
attribution records at all.
- Nothing persisted touches `.threads/`: the cross-rung contract
(INV-14..17) and `SCHEMA_VERSION = 1` are untouched by construction
(INV-19).
- No new credential surface (INV-8 untouched); no LLM anywhere in F6 or its
tests.
### 1.9 Business Use Cases
- **BUC-1 (self-review)** Mid-session, the operator flips into the diff
view and sees exactly what *they* changed since Claude last landed text —
then flips back and keeps writing.
- **BUC-2 (deliberate pass)** Before a risky rewrite pass, the operator
pins the baseline; afterwards they review their whole pass as one diff.
---
## 2. Solution Proposal
Keep one **baseline snapshot** per tracked document: the full document text
captured at an epoch. The epoch is initialized when the document is first
tracked, **advances automatically on every successful seam application**
(`applyAgentEdit` — the moment machine text lands), and can be **pinned to
now** by the operator. Because the seam is the only machine ingress
(INV-9/10), the diff `baseline → current buffer` shows operator-authored
changes by construction (INV-18) — no attribution filtering needed. A
`cowriting.toggleDiffView` command (with default keybinding) opens a native
`vscode.diff` editor: left side a readonly virtual document
(`cowriting-baseline:` scheme) serving the baseline text, right side **the
live document itself** — so the operator keeps editing inside the diff
view, and toggling back simply closes the diff tab. Baselines persist in
**extension workspace storage** via a vscode-free `BaselineStore` (the
`CoauthorStore` pattern), surviving window reloads without ever entering
the repo (INV-19). The machine-landing signal is one additive event on the
seam owner: `AttributionController.onDidApplyAgentEdit`.
---
## 3. Product Personas
- **PP-1 Inner-loop coauthor** — the human writer/engineer (as F2F5); the
only persona F6 serves.
## 4. Product Use Cases
- **PUC-1 (toggle)** In a tracked document, run the toggle (command or
keybinding) → a diff view opens: baseline left, live document right. Run
it again → the diff closes and the normal editor is back. The document
text, sidecar, threads, attributions, and proposals are untouched either
way.
- **PUC-2 (epoch follows the machine)** Accept a pending proposal → the
baseline advances past it: the accepted text does **not** appear as a
change in the diff; the operator's subsequent edits do.
- **PUC-3 (pin)** Run "pin diff baseline" → the diff goes empty (baseline =
now); everything the operator changes from here shows against the pin.
- **PUC-4 (survive reload)** Reload the window mid-session → the baseline
(and therefore the diff) is the same as before the reload.
- **PUC-5 (graceful edges)** An untracked document refuses the toggle with
a warning; a missing/ancient baseline is visible in the diff title
(epoch + when) and one pin gesture recovers; external file changes (e.g.
a git pull) show up as changes — pin recovers there too.
## 5. UX Layout
No bespoke UI surface — native VS Code primitives only (the F2F5
Claude-Code-rubric precedent). The diff view **is** VS Code's diff editor:
readonly baseline on the left, the live, editable document on the right;
all existing decorations and comment threads keep working on the right side
because it is the same `TextDocument`. The diff tab title carries the epoch
story: `<basename> — my changes since <epoch>` where epoch reads `opened
<time>` / `Claude landed <time>` / `pinned <time>`. Two contributed
commands: **"Cowriting: Toggle Diff View"** (`cowriting.toggleDiffView`,
default keybinding `ctrl+alt+d`, `when: editorTextFocus`) and **"Cowriting:
Pin Diff Baseline to Now"** (`cowriting.pinDiffBaseline`). No status-bar
item: the diff tab itself is the indicator.
---
## 6. Technical Design
### 6.1 Invariants
Parent invariants INV-1..INV-17 carry over unchanged. F6 adds:
- **INV-18 (machine-factored baseline)** The baseline advances on every
successful seam application, so **text the machine landed through the
seam never appears as a change in the diff view**. With INV-9/INV-10,
the corollary: everything the diff view shows changed is
operator-authored — modulo changes made to the file from outside the
session (git operations), which are surfaced as changes and recoverable
by pin.
- **INV-19 (pure view, repo-free)** The diff view never mutates the
document, the sidecar, or attribution state; the baseline snapshot lives
in extension workspace storage, never in the repo — `.threads/`, the
cross-rung contract (INV-14..17), and `SCHEMA_VERSION` are untouched.
### 6.2 High-level architecture
One new vscode-free store plus one new controller (the F2/F3/F4 unit
pattern), and one additive event on an existing unit:
- **`BaselineStore`** (`src/baselineStore.ts`, vscode-free) — load/save one
baseline JSON per docPath under a given storage directory (the
`CoauthorStore` shape: Node `fs`, `mkdir -p`, `<dir>/baselines/<docPath>.json`).
- **`DiffViewController`** (`src/diffViewController.ts`, vscode layer) —
owns the baseline lifecycle (initialize / advance / pin), registers the
`cowriting-baseline:` `TextDocumentContentProvider` (with an `onDidChange`
emitter so an open diff refreshes when the baseline advances), implements
toggle open/close via the tab-groups API, and exposes the test-facing
surface.
- **`AttributionController.onDidApplyAgentEdit`** — a new
`vscode.EventEmitter` fired after a successful, non-no-op seam apply
(`src/attributionController.ts:232`); the single machine-landing signal
(INV-9 makes the seam the single source). Carries the document.
- **Everything else reused unchanged:** seam semantics, ProposalController,
ThreadController, CoauthorStore, Anchorer, VersionGuard (F6 never writes
the sidecar, so the guard is not consulted).
```mermaid
flowchart LR
open["tracked doc first seen"] --> init["baseline := buffer text\n(reason: opened)"]
seam["applyAgentEdit succeeds\n(accept / E2E ingress, INV-9)"] -- onDidApplyAgentEdit --> adv["baseline := buffer text\n(reason: machine-landing)"]
pin["cowriting.pinDiffBaseline\n(human)"] --> pinned["baseline := buffer text\n(reason: pinned)"]
init & adv & pinned --> bs["BaselineStore\n(extension workspace storage,\nINV-19: never the repo)"]
toggle["cowriting.toggleDiffView"] --> diff["vscode.diff\nleft: cowriting-baseline (readonly)\nright: the LIVE document"]
bs --> diff
diff -- "toggle again" --> closed["diff tab closed,\nnormal editor back"]
```
### 6.3 Data model & ownership
No sidecar change of any kind (INV-19). The baseline is a private,
per-workspace extension artifact:
```jsonc
// <context.storageUri>/baselines/<repo-relative-docPath>.json
{
"docPath": "notes/chapter-1.md",
"text": "…full document text at the epoch…",
"capturedAt": "ISO-8601",
"reason": "opened" | "machine-landing" | "pinned"
}
```
- **One baseline per document** — the newest epoch wins; no history of past
baselines (state, not history — the F3/F4 precedent).
- **Owner:** the extension alone; the file lives outside the repo (VS
Code's per-workspace storage directory), so it can never be committed,
merged, or read by another rung. Unknown-field preservation is moot.
- **Capture source is the buffer** (`document.getText()`), not the disk —
the baseline must include unsaved text, since seam landings apply to the
buffer.
### 6.4 Interfaces & contracts
- **`BaselineStore`** (vscode-free):
`load(docPath): Baseline | null` · `save(docPath, baseline): void` ·
`baselinePath(docPath): string`.
- **`DiffViewController`**:
`ensureBaseline(document)` — on first sight of a tracked doc (wired into
`extension.ts`'s existing `renderIfOpen` hook); loads the stored baseline
or captures `reason: "opened"`.
`advance(document)` — captures `reason: "machine-landing"`; fires the
provider's `onDidChange` so an open diff refreshes.
`pin(document)` — captures `reason: "pinned"`; same refresh.
`toggle(editorOrTab)` — if the active tab is this doc's baseline diff →
close it and reveal the normal editor; if the active editor is a tracked
doc → `vscode.diff(baselineUri, document.uri, title)`. Untracked →
warning.
Test-facing: `getBaseline(docPath): { text, reason, capturedAt } |
undefined` · `isDiffOpen(docPath): boolean`.
- **`AttributionController`**: adds
`readonly onDidApplyAgentEdit: vscode.Event<{ document: vscode.TextDocument }>`
— fired in `applyAgentEdit` after `applyEdit` succeeds for a real
(non-no-op) change; the seam's signature and return contract are
unchanged.
- **Commands/keybindings** (`package.json` contributions):
`cowriting.toggleDiffView` (palette + `ctrl+alt+d`) ·
`cowriting.pinDiffBaseline` (palette). Both registered as warning stubs
in the no-folder activation path (the #8 precedent).
- **`CowritingApi`** gains `diffViewController` (the E2E handle).
### 6.5 PerProduct-Use-Case design
- **PUC-1 (toggle):** `toggle` inspects `vscode.window.tabGroups` for a
`TabInputTextDiff` whose `original.scheme === "cowriting-baseline"` and
whose `modified` equals the document's URI. Present → close that tab and
`showTextDocument` the file. Absent → `ensureBaseline` then execute
`vscode.diff` with `{ preview: false }`. The right side is the same
`TextDocument` the normal editor shows — no content is copied, so
"without losing editor state" holds trivially: the normal editor tab is
left in place; the diff is an additional tab on the same buffer.
- **PUC-2 (epoch follows the machine):** `DiffViewController` subscribes to
`onDidApplyAgentEdit` at activation. On fire: `advance(document)`
synchronous capture + `BaselineStore.save` + provider `onDidChange`
(an open diff's left side updates in place). Both seam drivers are
covered by the one event: accept (`ProposalController.accept`) and the
hidden E2E command (`cowriting.applyAgentEdit`).
- **PUC-3 (pin):** command → guard tracked → `pin(activeEditor.document)`
→ if the diff is open it visibly empties (left = right).
- **PUC-4 (survive reload):** `ensureBaseline` prefers the stored file; a
baseline written at the last landing/pin is what the reloaded window
diffs against. (The toggle's *tab* does not need to survive reload — the
baseline does.)
- **PUC-5 (graceful edges):** untracked / non-file schemes → warning, no
diff (the F2 `isTracked` rule). Storage unavailable
(`context.storageUri` undefined or write failure) → degrade to in-memory
baselines + one warning (the session still works; reload survival is
lost). Ancient baseline (workspace reopened weeks later) → the diff
title shows `since opened <date>` — self-describing — and pin recovers.
External-to-session file changes appear as changes (INV-18's stated
modulo); pin recovers.
### 6.6 Non-functional requirements & cross-cutting concerns
Capture cost is O(document) at three rare moments (first track, machine
landing, pin) — trivial at inner-loop scale. Storage is one document-sized
JSON per tracked doc in VS Code's workspace storage (local machine, same
trust domain as the working tree; baseline text is operator content, the
document's own sensitivity class). The content provider serves from the
in-memory copy (disk only on load). No LLM, no network, no new credential
surface (INV-8). Skip non-text/oversized files (the F2 rule, via
`isTracked`). The diff editor is native — no rendering machinery to
maintain.
### 6.7 Key decisions & alternatives considered
| Decision | Chosen | Alternatives rejected |
| --- | --- | --- |
| **Baseline epoch (the load-bearing fork)** | **Machine-landinganchored auto-baseline**: initialize at first track, advance on every successful seam application, plus a human **pin** gesture. INV-9/10 make the resulting diff operator-authored by construction (INV-18) | **turn start** (under F4 propose-by-default the document doesn't change at turn start — the epoch collapses into the accept; not a distinct moment anymore); **last save** (duplicates native `compareWithSaved` / local history / timeline — #17's complement-not-duplicate rule); **fixed session-start** (machine landings bleed into the "my changes" diff — breaks INV-18); **multiple selectable baselines** (a selector UI for epochs nobody asked for — YAGNI; pin covers the operator-chosen case; selectable is additive later) |
| Pre-accept operator edits absorbed by an advance | Accepted trade-off: the diff reads "my changes since the machine last landed", not "my whole session"; pin gives manual control | **baseline splicing** (apply the accepted replacement *into* the baseline so older operator edits stay visible — needs current→baseline range mapping with murky edge cases when the machine edits operator-authored-since-baseline text; deferred additive refinement, §9) |
| Snapshot home | **Extension workspace storage** (`context.storageUri`) via vscode-free `BaselineStore`; survives reload; zero repo/contract footprint (INV-19) | **sidecar `.threads/`** (full document text per accept dwarfs the artifact and churns git diffs — INV-2; drags INV-14..17 into a local view-state concern no other rung needs); **gitignored repo cache** (litters the operator's working tree; needs `.gitignore` management; risks accidental commit); **in-memory only** (baseline dies at reload — the post-reload diff is empty and lies) |
| Whose changes the diff shows | Whole-document diff; operator-authorship is INV-18's corollary, **no filtering** | **F3-attribution-filtered diff** (F3 is state-not-history — cannot reconstruct before-text; #17 itself notes this); **per-author diff lenses** (no user story; heavy) |
| View primitive | **Native `vscode.diff`**: readonly `cowriting-baseline:` virtual doc left, the **live document** right (editable in place; decorations/threads keep working) | **decoration-based inline diff** in the normal editor (re-implements diff rendering VS Code already has; F4's fenced-diff primitive is for proposals, not whole documents); **webview diff** (bespoke UI against the F2F5 native-primitives precedent) |
| Machine-landing signal | Additive `onDidApplyAgentEdit` event on the seam owner — INV-9's single ingress is the single signal source | **call-site wiring** (ProposalController + the hidden command both call `advance` — two sites today, N tomorrow, and each new seam driver must remember); **watching attribution records** (indirect, save-coupled, reconstructive) |
| Toggle keybinding | `ctrl+alt+d` on all platforms (unbound by default in stock VS Code; user-remappable) | `cmd+alt+d` (macOS Dock toggle); `cmd+k d` (taken: compare-with-saved) |
### 6.8 Testing strategy
- **Unit (vitest, vscode-free):** `BaselineStore` round-trip (save → load,
nested docPaths, overwrite-in-place, missing → null); baseline JSON shape
stability; storage-dir path mapping.
- **Host E2E (`@vscode/test-electron`, the F2F5 pattern, no LLM):**
toggle on a tracked doc → diff tab open (original scheme
`cowriting-baseline`), provider text = open-time text, document + sidecar
untouched (INV-19) → type → baseline unchanged, buffer changed →
programmatic propose + accept → baseline advanced past the accepted text
(INV-18: `getBaseline().text` contains the replacement; `reason:
"machine-landing"`) → operator edit after the landing → baseline ≠
buffer (the operator delta) → pin → baseline = buffer, `reason:
"pinned"` → toggle again → diff tab closed, normal editor active →
baseline file present on disk under the storage dir with the expected
content (the reload-survival half is the store's unit-tested round-trip)
→ untracked doc → toggle warns, no diff tab.
- **Live smoke (manual, documented — `docs/MANUAL-SMOKE-F6.md`):** open a
tracked doc → edit → toggle → see own changes → ask Claude to edit a
selection → accept → toggle: the accepted text is *not* shown as a
change → pin → diff empties.
### 6.9 Failure modes, rollback & flags
Storage unavailable / write failure → in-memory fallback + one warning
(reload survival degrades; nothing else). Crash between a seam landing and
the baseline write → the stored baseline is one landing behind: on reload
the machine's last text shows as a change (a visible, self-describing
stale-epoch state, the F4-husk spirit) — one pin recovers; the capture is
synchronous in the event handler to keep this window minimal. Ancient
baseline → self-describing title + pin. External file changes → shown as
changes (INV-18's modulo), pin recovers. No feature flag: the feature is a
pure view (INV-19) — not toggling it is the off state; rollback is
reverting the PR with zero data migration (the baseline cache is
disposable by design).
---
## 7. Delivery Plan
### 7.1 Approach / strategy
One planning-and-executing session (F6 = #17), plan written just-in-time
from this spec — the F2F5 precedent.
### 7.2 Slicing plan
- **SLICE-1** `BaselineStore` (vscode-free) + unit tests (round-trip,
paths, overwrite).
- **SLICE-2** Seam event (`onDidApplyAgentEdit`, additive) +
`DiffViewController` lifecycle: ensure/advance/pin, content provider with
refresh, storage-unavailable fallback.
- **SLICE-3** Toggle UX: `vscode.diff` open/close via tab groups, epoch
titles, `package.json` contributions (commands + keybinding + no-folder
stubs), `CowritingApi.diffViewController` + test-facing surface.
- **SLICE-4** Host E2E per §6.8 + `docs/MANUAL-SMOKE-F6.md` + README
pointer.
E2E are first-class plan tasks (handbook §9/§4; this app's required tier is
host E2E — a VS Code extension has no browser surface or deploy stage, the
F2F5 precedent).
### 7.3 Rollout / launch plan
Still non-shippable (no marketplace publish). "Done" = issue #17 acceptance
met: toggle there-and-back without losing editor state, against a
coauthoring-meaningful baseline; unit + host E2E green; live smoke
performed once on this machine.
### 7.4 Risks & mitigations
| Risk | Mitigation |
| --- | --- |
| Baseline advance absorbs pre-accept operator edits (operator surprise) | Diff title names the epoch ("since Claude landed <time>"); pin gives manual control; splicing is the documented additive follow-up (§9) |
| `ctrl+alt+d` collides with an operator's keymap | Default keybinding only — remappable; command palette always works |
| Storage dir missing/unwritable | In-memory fallback + one warning; feature degrades to session-scoped, never errors out of the toggle |
| Diff left side stale while baseline advances | Provider `onDidChange` fired on every advance/pin — the open diff refreshes in place |
| Future seam drivers forget the baseline | They can't — the advance subscribes to the seam's own event (INV-9: one ingress, one signal) |
| Operator expects "my whole session" semantics | §6.7 records the trade-off; the epoch is visible in the title; pin-at-session-start reproduces session semantics manually |
---
## 8. Traceability matrix
| Requirement (issue #17 acceptance) | Use case | Design | Slice |
| --- | --- | --- | --- |
| Toggle (command and/or keybinding) editor ↔ diff, and back | PUC-1 | §6.4, §6.5 | SLICE-3 |
| Diff is the operator's own changes | PUC-2 | INV-18, §6.2 | SLICE-2 |
| Against a coauthoring-meaningful baseline | PUC-2/3 | §6.7 (epoch fork) | SLICE-2 |
| Without losing editor state | PUC-1 | §6.5 (live doc is the right side) | SLICE-3 |
| Sidecar/contract untouched if persisted artifacts exist | — | INV-19, §6.3 | all |
| Complement, not duplicate, git's diff/timeline | — | §6.7 (save rejected) | — |
| Unit + host E2E coverage, no LLM in CI | — | §6.8 | SLICE-1/4 |
## 9. Open Questions & Decisions log
- **RESOLVED (this session — the forks routed by #17):**
(a) **baseline** = machine-landinganchored auto-baseline (init at first
track, advance on every successful seam application) + human pin; turn
start collapses into accept under F4, save duplicates native features,
fixed session-start breaks INV-18, selectable-multi is YAGNI;
(b) **snapshot home** = extension workspace storage via vscode-free
`BaselineStore` — never the sidecar, no contract impact (INV-19);
(c) **whose changes** = whole-document diff, operator-authored by
construction (INV-18) — no attribution filtering;
(d) **git relationship** = complement: this diffs against coauthoring
moments, git diffs against commits; save-points stay native;
(e) **view primitive** = native `vscode.diff`, live document on the
right; (f) **landing signal** = `onDidApplyAgentEdit` on the seam owner.
- **OPEN → later:** baseline **splicing** (keep pre-accept operator edits
visible across an advance — needs current→baseline range mapping);
multiple named baselines / epoch selector (additive on the same store);
changed-line gutter indicators in the normal view (a decoration consumer
of the same baseline); whether a future rung wants to *share* a pinned
baseline (would move it into the contract — deliberately not now).
- **Deferred decisions (autonomous-mode calls logged for the operator):**
the absorb-on-advance semantics (vs. splicing) and the `ctrl+alt+d`
default keybinding were decided without operator input — both are
cheap to revisit.
## 10. Glossary & References
- **Baseline** — the full text of a tracked document captured at an epoch;
the diff view's left side. **Epoch** — the moment the baseline was
captured: `opened`, `machine-landing`, or `pinned`. **Machine landing**
a successful `applyAgentEdit` seam application (the only way machine text
enters the document, INV-9/10). **Pin** — the human gesture resetting the
baseline to now. **Advance** — the automatic baseline re-capture at a
machine landing (INV-18).
- Feature #17 (F6) · Epic #1 (closed) · F5 #14 (PR #15) · F4 #12 (PR #13) ·
F3 #6 (PR #7) · F2 #4 (PR #5) · parent specs `coauthoring-inner-loop.md`,
`coauthoring-attribution.md`, `coauthoring-propose-accept.md`,
`coauthoring-cross-rung-format.md`, `coauthoring-sidecar-contract.md` ·
lineage `ben.stull/rfc-app#48`.