add spec ./specs/coauthoring-attribution.md (status: graduated)
This commit is contained in:
@@ -0,0 +1,397 @@
|
|||||||
|
---
|
||||||
|
status: graduated
|
||||||
|
---
|
||||||
|
# Solution Design: Live Human/Claude Attribution in the Buffer (F3)
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
| **Author(s)** | Ben Stull (with Claude) |
|
||||||
|
| **Reviewers / approvers** | Ben Stull |
|
||||||
|
| **Status** | `draft` |
|
||||||
|
| **Version** | v0.1.0 |
|
||||||
|
| **Source artifacts** | Epic `benstull/vscode-cowriting-plugin#1` · Feature `#6` (F3) · Parent spec: `vscode-cowriting-plugin-content/specs/coauthoring-inner-loop.md` (graduated; §6.3 `attributions[]`/`provenance`, §9 OPEN→F3/F4) · F2 shipped: `#4` (PR #5, session 0004) · Lineage: `ben.stull/rfc-app#48` |
|
||||||
|
|
||||||
|
**Change log**
|
||||||
|
|
||||||
|
| Date | Version | Change | By |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 2026-06-10 | v0.1.0 | Initial draft — brainstorming session 0006 | Ben Stull + Claude |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Business Context
|
||||||
|
|
||||||
|
### 1.1 Executive Summary
|
||||||
|
|
||||||
|
F3 makes the buffer an **honest record of authorship**: as a document is
|
||||||
|
coauthored, the editor shows — live — which spans were written by the **human**
|
||||||
|
and which by **Claude**, and persists that record git-natively in the same
|
||||||
|
sidecar F2 shipped, filling the reserved `attributions[]` extension point on the
|
||||||
|
shared `anchors`/`Provenance` primitives. It also lands the project's **first
|
||||||
|
live `@cline/sdk` turn** — the minimal machine-edit ingress that gives
|
||||||
|
attribution something real to attribute — behind a programmatic apply-edit
|
||||||
|
**seam** that F4's propose/accept will later drive.
|
||||||
|
|
||||||
|
### 1.2 Background
|
||||||
|
|
||||||
|
F2 (#4) gave regions durable *discussion*; nothing yet records who *wrote* what.
|
||||||
|
Epic #1's second acceptance pillar is "see, live in the buffer, which spans are
|
||||||
|
human vs. Claude." The parent spec deliberately reserved `attributions: []`
|
||||||
|
(INV-4) and deferred "where Claude runs + provider/auth" to "the first live
|
||||||
|
`@cline/sdk` turn, F3/F4" — i.e., to exactly this design. Issue #6 routed the
|
||||||
|
open fork (what counts as a machine edit before F4 exists; the `attributions[]`
|
||||||
|
shape; mixed-edit granularity) to this brainstorming session.
|
||||||
|
|
||||||
|
### 1.3 Business Actors / Roles
|
||||||
|
|
||||||
|
- **Coauthor (human)** — the writer/engineer authoring prose/specs in VS Code.
|
||||||
|
- **Coauthor (machine)** — Claude via `@cline/sdk`; **active for the first time**
|
||||||
|
(F2 reserved the `agent` provenance author; F3 uses it).
|
||||||
|
|
||||||
|
### 1.4 Problem Statement
|
||||||
|
|
||||||
|
Once a machine edit lands in the buffer it is indistinguishable from human
|
||||||
|
typing. There is no basis for trust ("did I write this claim or did it?"), no
|
||||||
|
provenance to carry up the ladder, and no substrate for F4's propose/accept to
|
||||||
|
render against.
|
||||||
|
|
||||||
|
### 1.5 Pain Points
|
||||||
|
|
||||||
|
- No visible, live distinction between human-authored and Claude-authored spans.
|
||||||
|
- No durable authorship record: attribution (if any) dies on reload.
|
||||||
|
- No machine-edit pathway at all yet — F2 is human-only by design.
|
||||||
|
|
||||||
|
### 1.6 Targeted Business Outcomes
|
||||||
|
|
||||||
|
Human and Claude spans visibly distinguished as edits happen; the attribution
|
||||||
|
survives further editing (re-anchor) and reload (sidecar persistence); a real
|
||||||
|
Claude edit can be invoked from the editor; F4 inherits both the attribution
|
||||||
|
substrate and a clean apply-edit seam.
|
||||||
|
|
||||||
|
### 1.7 Scope (business)
|
||||||
|
|
||||||
|
**In scope:** live in-buffer rendering of human/Claude span attribution;
|
||||||
|
capturing human keystrokes and applied machine edits as attributed anchored
|
||||||
|
spans; the `attributions[]` shape; character-precise split/merge of mixed edits;
|
||||||
|
attribution survival across edits (re-anchor / orphan); git-native persistence +
|
||||||
|
reload; the programmatic apply-edit seam; a minimal live `@cline/sdk` turn
|
||||||
|
(edit-selection command) on the `claude-code` provider.
|
||||||
|
|
||||||
|
**Out of scope (deferred, not forgotten):** F4 propose/accept (keep/reject UX,
|
||||||
|
inline diff — it will drive this seam); F5 cross-rung format + Gitea round-trip
|
||||||
|
(#46); attribution of edits made outside the session (external editors, git
|
||||||
|
merges) beyond the orphan rule; per-character blame depth (the span is the
|
||||||
|
unit); multi-file orchestration; Anthropic API-key auth (config option later);
|
||||||
|
chat-panel UX; any server.
|
||||||
|
|
||||||
|
### 1.8 Assumptions · Constraints · Dependencies
|
||||||
|
|
||||||
|
- **Parent:** Epic #1. **Blocked by:** F2 #4 (shipped). **Feeds:** F4
|
||||||
|
(propose/accept renders into this substrate and drives the seam); relates to
|
||||||
|
F5.
|
||||||
|
- Builds on the graduated parent spec's primitives: shared `anchors`
|
||||||
|
(INV-4), `Provenance` (`kind: human | agent`), the Anchorer's
|
||||||
|
resolve/orphan ladder (INV-1/INV-3), the per-document sidecar (INV-2).
|
||||||
|
- **F2's no-credentials invariant (parent INV-5) ends here by design** — but
|
||||||
|
with zero key handling: the live turn uses the SDK's built-in **`claude-code`
|
||||||
|
provider** ("Use Claude Code SDK with Claude Pro/Max subscription"), riding
|
||||||
|
the operator's existing local Claude Code login. The extension stores no
|
||||||
|
secret of any kind.
|
||||||
|
- Requires Claude Code installed + signed in for the live turn; everything else
|
||||||
|
(tracking, rendering, persistence, tests) works without it.
|
||||||
|
- Native primitives: `vscode.window.createTextEditorDecorationType` (rendering),
|
||||||
|
`onDidChangeTextDocument` (edit deltas — the Anchorer already consumes it),
|
||||||
|
`WorkspaceEdit` (the seam's application path).
|
||||||
|
|
||||||
|
### 1.9 Business Use Cases
|
||||||
|
|
||||||
|
- **BUC-1** A coauthor watches authorship accumulate honestly as they and Claude
|
||||||
|
write: each author's spans are visibly theirs, live, and the record survives
|
||||||
|
editing and reopening the workspace.
|
||||||
|
- **BUC-2** A coauthor selects a region, asks Claude to edit it, and the
|
||||||
|
resulting machine edit lands visibly Claude-attributed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Solution Proposal
|
||||||
|
|
||||||
|
Fill the sidecar's reserved `attributions[]` with **anchored, author-attributed
|
||||||
|
spans** maintained live by a pure span-algebra tracker: human keystrokes produce
|
||||||
|
human spans; edits applied through a new programmatic **apply-edit seam**
|
||||||
|
produce agent spans. Mixed edits split character-precisely; adjacent same-author
|
||||||
|
spans coalesce. Spans persist via the existing Store/Anchorer (fingerprint on
|
||||||
|
save; resolve-or-orphan on load — never guess). A minimal live turn — an
|
||||||
|
edit-selection command on the SDK's `claude-code` provider — drives the seam so
|
||||||
|
there is a real machine author from day one. Rendering is the native Decorations
|
||||||
|
API: Claude spans tinted, human spans gutter-marked, unattributed text plain.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Product Personas
|
||||||
|
|
||||||
|
- **PP-1 Inner-loop coauthor** — the human writer/engineer (as in F2).
|
||||||
|
- **PP-2 Machine coauthor (Claude)** — now active: authors edits via the live
|
||||||
|
turn; its spans carry `agent` provenance.
|
||||||
|
|
||||||
|
## 4. Product Use Cases
|
||||||
|
|
||||||
|
- **PUC-1** Type in a tracked document → the new/changed span renders
|
||||||
|
human-attributed, live.
|
||||||
|
- **PUC-2** Select a region, run "Ask Claude to edit selection," give an
|
||||||
|
instruction → the replacement lands Claude-attributed, live.
|
||||||
|
- **PUC-3** Edit inside a Claude-attributed span → the span splits
|
||||||
|
character-precisely; the human's characters are human-attributed; the
|
||||||
|
remainder stays Claude's.
|
||||||
|
- **PUC-4** Reopen the workspace → attributed spans reload from the sidecar at
|
||||||
|
their re-resolved anchors; un-resolvable spans surface as orphaned, never
|
||||||
|
silently moved.
|
||||||
|
- **PUC-5** Toggle attribution decorations on/off.
|
||||||
|
|
||||||
|
## 5. UX Layout
|
||||||
|
|
||||||
|
No new UI surface. Attribution renders as **decorations**: a subtle background
|
||||||
|
tint on Claude-attributed spans, a thin gutter bar on human-attributed spans,
|
||||||
|
nothing on unattributed text (text predating tracking — absence of a span is
|
||||||
|
the honest record). Two contributed commands: **"Cowriting: Ask Claude to edit
|
||||||
|
selection"** (selection + instruction input box → machine edit) and
|
||||||
|
**"Cowriting: Toggle attribution"**. Orphaned attributions are dropped from
|
||||||
|
decoration and surfaced as a **status-bar count** (details in the output
|
||||||
|
channel) — visible, flagged, recoverable, per F2's orphan precedent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Technical Design
|
||||||
|
|
||||||
|
### 6.1 Invariants
|
||||||
|
|
||||||
|
Parent-spec invariants INV-1..INV-4 carry over unchanged. F3 adds:
|
||||||
|
|
||||||
|
- **INV-6** An attribution span is **never silently moved or re-authored**: its
|
||||||
|
location is re-resolved by fingerprint (or orphaned), and its author only
|
||||||
|
changes by the explicit split/clip rules of the span algebra.
|
||||||
|
- **INV-7** Attribution is **char-honest**: the span algebra attributes exactly
|
||||||
|
the characters each author produced (split character-precisely; coalesce only
|
||||||
|
adjacent same-author spans). No threshold heuristics, no whole-span flips.
|
||||||
|
- **INV-8** The extension **stores no credentials**. The live turn delegates
|
||||||
|
auth entirely to the local Claude Code installation (`claude-code` provider);
|
||||||
|
if it is absent or signed out, the live turn fails gracefully with guidance —
|
||||||
|
tracking, rendering, and persistence are unaffected.
|
||||||
|
- **INV-9** Machine edits enter the buffer **only through the seam**
|
||||||
|
(`applyAgentEdit`), so attribution can never mistake an agent edit for typing;
|
||||||
|
F4 inherits the same guarantee.
|
||||||
|
|
||||||
|
### 6.2 High-level architecture
|
||||||
|
|
||||||
|
Two new vscode-free units (unit-testable, like the POC's `cline.ts` and F2's
|
||||||
|
model/anchorer) plus thin editor-facing wiring:
|
||||||
|
|
||||||
|
- **`attributions` model** — the typed `attributions[]` shape on the artifact
|
||||||
|
(replaces the `unknown[]` placeholder; `schemaVersion` stays 1 — additive).
|
||||||
|
- **AttributionTracker** — pure span algebra over offset ranges:
|
||||||
|
`applyChange(spans, edit, author) → spans` (shift/split/clip/coalesce).
|
||||||
|
- **Apply-edit seam** — `applyAgentEdit(doc, range, newText, provenance)`:
|
||||||
|
applies a `WorkspaceEdit` and registers it (keyed by document version) so the
|
||||||
|
tracker attributes the resulting change event to the agent. Exposed as an
|
||||||
|
internal API + a command, so tests drive it with no LLM.
|
||||||
|
- **LiveTurn** — the edit-selection command: instruction → `@cline/sdk`
|
||||||
|
(`claude-code` provider, building on the POC's vscode-free `cline.ts`
|
||||||
|
driver) → replacement text → the seam.
|
||||||
|
- **AttributionRenderer** — two `TextEditorDecorationType`s + the toggle;
|
||||||
|
re-renders on tracker change.
|
||||||
|
- **CoauthorStore / Anchorer — reused unchanged.**
|
||||||
|
|
||||||
|
```
|
||||||
|
keystroke ─────────────▶ AttributionTracker (author = human)
|
||||||
|
applyAgentEdit (seam) ─▶ WorkspaceEdit + pending-edit registry ─▶ AttributionTracker (author = agent)
|
||||||
|
live turn ─▶ selection + instruction ─▶ @cline/sdk (claude-code) ─▶ seam
|
||||||
|
document change ───────▶ shift/split/clip/coalesce ─▶ AttributionRenderer (decorations)
|
||||||
|
save ──────────────────▶ buildFingerprint per span ─▶ sidecar attributions[] (CoauthorStore)
|
||||||
|
load / external change ▶ resolve per span ─▶ render | orphaned (Anchorer ladder, INV-1)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 Data model & ownership
|
||||||
|
|
||||||
|
Same sidecar, same `anchors` map, same `Provenance` — `attributions[]` is filled,
|
||||||
|
not reshaped (parent INV-4; no `schemaVersion` bump):
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
"attributions": [
|
||||||
|
{
|
||||||
|
"id": "at1",
|
||||||
|
"anchorId": "a7", // shared anchors map — same Fingerprint primitive
|
||||||
|
"author": { "kind": "agent", "id": "claude",
|
||||||
|
"agent": { "sdk": "@cline/sdk", "model": "…", "sessionId": "…" } },
|
||||||
|
"createdAt": "ISO-8601",
|
||||||
|
"updatedAt": "ISO-8601", // bumped when the span's extent/fingerprint changes
|
||||||
|
"turnId": "turn-…" // optional; groups all spans applied by one live turn
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Orphanhood is not persisted** — exactly like F2 threads, it is a
|
||||||
|
resolve-time outcome: fingerprint fails → orphaned (visible, flagged), never
|
||||||
|
guessed.
|
||||||
|
- **Unattributed text is the absence of a span** — text predating tracking has
|
||||||
|
no attribution record, honestly.
|
||||||
|
- Ownership/format rules unchanged: extension is the sole writer; stable key
|
||||||
|
ordering; ids via `newId()`.
|
||||||
|
|
||||||
|
### 6.4 Interfaces & contracts
|
||||||
|
|
||||||
|
- **AttributionTracker** (pure): `applyChange(spans, edit, author): spans` ·
|
||||||
|
`coalesce(spans): spans` · span = `{ range: OffsetRange, author: Provenance,
|
||||||
|
id, turnId? }`. Reuses `anchorer.shift` semantics for untouched spans.
|
||||||
|
- **Seam**: `applyAgentEdit(doc, range, newText, provenance): Promise<boolean>`
|
||||||
|
— applies + registers; also contributed as an internal command (E2E entry
|
||||||
|
point).
|
||||||
|
- **LiveTurn**: `editSelection(doc, selection, instruction): Promise<void>` —
|
||||||
|
resolves the `claude-code` provider, runs one SDK turn producing replacement
|
||||||
|
text for the selection, calls the seam with `agent` provenance (model +
|
||||||
|
sessionId from the turn).
|
||||||
|
- **AttributionRenderer**: `render(editor, spans)` · `setVisible(boolean)`.
|
||||||
|
- **Store/Anchorer**: existing contracts, now also carrying attributions.
|
||||||
|
|
||||||
|
### 6.5 Per–Product-Use-Case design
|
||||||
|
|
||||||
|
- **PUC-1 (type):** `onDidChangeTextDocument` → for each content change, the
|
||||||
|
tracker shifts spans past the edit, clips/deletes spans overlapping a
|
||||||
|
deletion, splits a different-author span around an insertion, attributes the
|
||||||
|
inserted range to **human** (no pending seam registration for this document
|
||||||
|
version), then coalesces. Render.
|
||||||
|
- **PUC-2 (live turn):** command guards (selection non-empty, document tracked,
|
||||||
|
Claude Code available) → input box → SDK turn (replacement text only —
|
||||||
|
single-range, the selection) → `applyAgentEdit` → the change event arrives
|
||||||
|
with a matching registration → attributed to **agent** with `turnId`. Render.
|
||||||
|
- **PUC-3 (mixed edit):** the insertion-inside-a-span case of PUC-1's algebra:
|
||||||
|
split at the insertion point, new human span between the two agent halves
|
||||||
|
(INV-7). Same rule symmetric for agent edits inside human spans.
|
||||||
|
- **PUC-4 (reload / external change):** on open or sidecar/document external
|
||||||
|
change: `load` → per attribution `resolve(fingerprint)` → live span or
|
||||||
|
orphaned (counted, not rendered). On save: refresh fingerprints from live
|
||||||
|
spans (`updatedAt` bumped only for changed spans), persist.
|
||||||
|
- **PUC-5 (toggle):** flips renderer visibility; tracking continues regardless.
|
||||||
|
|
||||||
|
### 6.6 Non-functional requirements & cross-cutting concerns
|
||||||
|
|
||||||
|
Tracking is O(spans) per change event — fine at human scale; coalescing bounds
|
||||||
|
span growth. Persistence happens on save (not per keystroke), as F2 does.
|
||||||
|
Resolution cost on load matches F2 (O(document) per anchor). No secrets at all
|
||||||
|
(INV-8). Skip non-text/oversized files (F2 rule). Single editor writer.
|
||||||
|
Live-turn latency is user-visible: show progress notification; the buffer is not
|
||||||
|
locked (edits during a turn are handled by the version-keyed registration — a
|
||||||
|
stale turn result fails to apply and reports, rather than corrupting).
|
||||||
|
|
||||||
|
### 6.7 Key decisions & alternatives considered
|
||||||
|
|
||||||
|
| Decision | Chosen | Alternatives rejected |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Machine-edit ingress | Programmatic seam **+** minimal live SDK turn driving it | seam-only (no real machine author; demo is simulated); live-turn-only (couples tracking to the SDK; F4 has no clean ingress to drive) |
|
||||||
|
| Provider/auth | `claude-code` provider (local Claude Code Pro/Max login; zero key handling) | Anthropic API key in SecretStorage (ignores the plan already paid for; adds key-handling surface — deferred as a config option); Cline-account OAuth (couples UX to Cline's auth service); env var (no user story) |
|
||||||
|
| Mixed-edit granularity | Character-precise split + same-author coalescing (INV-7) | whole-span re-attribution (dishonest at the edges); threshold-based (arbitrary, not strictly honest) |
|
||||||
|
| Attribution record | Span-list on shared `anchors` (state, not history) | event log + replay (truthful history but heavy sidecar churn; re-anchoring a log is hard); hybrid (YAGNI) |
|
||||||
|
| Rendering | Claude tint + human gutter bar + plain unattributed; toggle | Claude-only (human record invisible); both tinted (visually heavy prose) |
|
||||||
|
| Live-turn UX | Edit-selection command (selection + instruction → replacement) | whole-document multi-range edits (big bite for "minimal ingress"); chat panel (new surface F4 will reshape) |
|
||||||
|
| Orphan persistence | Resolve-time (not persisted), per F2 threads | persisted status field (duplicates what resolution derives; risks stale state) |
|
||||||
|
|
||||||
|
### 6.8 Testing strategy
|
||||||
|
|
||||||
|
- **Unit (vitest, vscode-free):** AttributionTracker span algebra exhaustively —
|
||||||
|
insert/delete at/around/inside spans, split, clip, coalesce, multi-edit
|
||||||
|
sequences, both author directions; model round-trip (serialize → reload,
|
||||||
|
stable formatting); resolve→orphan for attribution fingerprints.
|
||||||
|
- **Host E2E (`@vscode/test-electron`, F2 pattern):** type → human span → seam
|
||||||
|
command edit → agent span → save → reload → spans restored → edit above →
|
||||||
|
re-anchored → mangle anchored text → orphaned. **No LLM in CI** — E2E drives
|
||||||
|
the seam.
|
||||||
|
- **Live-turn smoke (manual, documented):** select → instruct → Claude edit
|
||||||
|
lands attributed; run on a signed-in Claude Code machine. Failure-path check:
|
||||||
|
signed-out / missing Claude Code → graceful error (INV-8).
|
||||||
|
|
||||||
|
### 6.9 Failure modes, rollback & flags
|
||||||
|
|
||||||
|
Orphaned attributions (INV-1/INV-6) — handled, recoverable (re-resolve if the
|
||||||
|
text reappears). Live-turn failures (no Claude Code, signed out, model error,
|
||||||
|
stale document version) — surfaced as notifications; never partial-applied.
|
||||||
|
Sidecar merge conflicts — plain JSON, human-resolvable (unchanged). No feature
|
||||||
|
flag: additive; decorations have a toggle; absence of `attributions[]` entries
|
||||||
|
is the off state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Delivery Plan
|
||||||
|
|
||||||
|
### 7.1 Approach / strategy
|
||||||
|
|
||||||
|
One planning-and-executing session (F3 = #6), plan written just-in-time from
|
||||||
|
this spec, per the F2 precedent.
|
||||||
|
|
||||||
|
### 7.2 Slicing plan
|
||||||
|
|
||||||
|
- **SLICE-1** Typed `attributions[]` model (+ round-trip tests).
|
||||||
|
- **SLICE-2** AttributionTracker span algebra (+ exhaustive unit tests).
|
||||||
|
- **SLICE-3** Seam (`applyAgentEdit` + registry) and human-edit wiring +
|
||||||
|
decoration rendering (tint/gutter/toggle).
|
||||||
|
- **SLICE-4** Persistence on save; reload / external-change resolve; orphan
|
||||||
|
surfacing.
|
||||||
|
- **SLICE-5** Live `claude-code` turn (edit-selection command) + documented
|
||||||
|
manual smoke + graceful-failure paths.
|
||||||
|
- **SLICE-6** Host E2E (type → attribute → seam edit → attribute → reload →
|
||||||
|
re-anchor → orphan).
|
||||||
|
|
||||||
|
E2E are first-class plan tasks (handbook §4 / parent §6.8), not a follow-up.
|
||||||
|
|
||||||
|
### 7.3 Rollout / launch plan
|
||||||
|
|
||||||
|
Still non-shippable (no marketplace publish). "Done" = issue #6 acceptance met:
|
||||||
|
unit + host E2E green, live smoke performed once on this machine.
|
||||||
|
|
||||||
|
### 7.4 Risks & mitigations
|
||||||
|
|
||||||
|
| Risk | Mitigation |
|
||||||
|
| --- | --- |
|
||||||
|
| Span churn from heavy typing (many tiny spans) | coalescing (INV-7's merge half); persistence only on save |
|
||||||
|
| Misattributing a seam edit to the human (or vice versa) | version-keyed pending-edit registry; seam is the only machine ingress (INV-9); algebra unit-tested both directions |
|
||||||
|
| `claude-code` provider/SDK surface shifts under us (SDK 0.0.x) | provider use isolated in vscode-free LiveTurn module; seam keeps everything else LLM-free |
|
||||||
|
| Buffer edited mid-turn | stale-version application fails gracefully, reports, never partial-applies |
|
||||||
|
| Fingerprint mis-resolution on duplicated text | unchanged F2 ladder: context + lineHint, orphan over guess |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Traceability matrix
|
||||||
|
|
||||||
|
| Requirement (issue #6 acceptance) | Use case | Design | Slice |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Human spans recorded/rendered live | PUC-1 | §6.2, §6.5, INV-7 | SLICE-2/3 |
|
||||||
|
| Machine edits recorded/rendered as Claude's | PUC-2 | seam + LiveTurn, INV-9 | SLICE-3/5 |
|
||||||
|
| Attribution survives edits (re-anchor / orphan) | PUC-4 | Anchorer ladder, INV-1/6 | SLICE-4 |
|
||||||
|
| Git-native persistence + reload | PUC-4 | §6.3, parent INV-2/4 | SLICE-1/4 |
|
||||||
|
| Mixed-edit split/merge | PUC-3 | INV-7, §6.5 | SLICE-2 |
|
||||||
|
| Unit + host E2E coverage | — | §6.8 | SLICE-2/6 |
|
||||||
|
|
||||||
|
## 9. Open Questions & Decisions log
|
||||||
|
|
||||||
|
- **RESOLVED (this session):** ingress = seam + minimal live turn; auth =
|
||||||
|
`claude-code` provider only (no extension-held credentials; Anthropic-key
|
||||||
|
fallback deferred — not needed for testing since tests drive the seam);
|
||||||
|
granularity = character-precise split + coalesce; record = span-list on shared
|
||||||
|
anchors, orphanhood resolve-time; rendering = Claude tint + human gutter +
|
||||||
|
toggle; live-turn UX = edit-selection command.
|
||||||
|
- **OPEN → F4:** propose/accept drives the same seam; accepted diffs render into
|
||||||
|
this attribution substrate. The seam's contract (single-range apply) may grow
|
||||||
|
multi-range then.
|
||||||
|
- **OPEN → F5:** cross-rung persistence format may re-home `attributions[]`
|
||||||
|
alongside the rest of the sidecar.
|
||||||
|
- **OPEN → later:** attribution of out-of-session edits (git merges, external
|
||||||
|
editors) beyond the orphan rule; span-level → finer blame; Anthropic API-key
|
||||||
|
config option for machines without Claude Code.
|
||||||
|
|
||||||
|
## 10. Glossary & References
|
||||||
|
|
||||||
|
- **Attribution span** — an anchored, author-attributed range of a document; the
|
||||||
|
unit of provenance. **Seam** — `applyAgentEdit`, the single programmatic
|
||||||
|
ingress for machine edits (INV-9). **Unattributed** — text with no span (it
|
||||||
|
predates tracking). **Live turn** — one `@cline/sdk` invocation producing a
|
||||||
|
machine edit. **`claude-code` provider** — the SDK's built-in provider that
|
||||||
|
rides the local Claude Code Pro/Max login.
|
||||||
|
- Epic #1 · Feature #6 (F3) · F2 #4 (PR #5) · parent spec
|
||||||
|
`coauthoring-inner-loop.md` · POC #2 · rfc-app#48/#46 (lineage).
|
||||||
Reference in New Issue
Block a user