F8: out-of-workspace authoring — hybrid sidecar persistence (#25) #26

Merged
benstull merged 8 commits from f8-out-of-workspace into main 2026-06-11 20:18:55 +00:00
Owner

Implements F8 (Feature #25, out-of-workspace authoring) from the graduated Solution Design specs/coauthoring-out-of-workspace.md (brainstorming session 0024). Planning-and-executing session 0025.

What changes

"Ask Claude to Edit Selection" (and F2 threads / F3 attribution / F4 propose-accept) now work on any document the editor shows — saved in-folder, saved out-of-folder, or untitled — matching the already-universal F6 diff / F7 preview. Commands are live even with no folder open.

Hybrid persistence (one SidecarStore abstraction, routed per-document by #24's isUnderRoot):

  • in-workspace file: → the committable repo .threads/<repo-rel>.json sidecar (CoauthorStore, byte-for-byte unchanged, INV-2);
  • out-of-workspace file: + untitled: → global-storage sidecar keyed by sha256(uri) (GlobalSidecarStore, mirroring F6's BaselineStore, INV-19/24); untitled is in-memory only.

The Artifact shape + SCHEMA_VERSION are identical either home, so mergeArtifacts and the F5 cross-rung contract are untouched. A global artifact is not a committed file → never cross-rung-shareable (INV-25); URI-rename orphaning accepted & documented.

How

  • New: sidecarStore.ts (interface), globalSidecarStore.ts, sidecarRouter.ts (keyOf + routing); isAuthorable in workspacePath.ts.
  • Re-pointed the three controllers + VersionGuard onto the router; gate isInRoot/isTrackedisAuthorable; key via store.keyOf.
  • extension.ts: construct the router; removed the no-root early-return + command stubs (folder-less authoring); renderIfOpen gated by isAuthorable.
  • model.ts, mergeArtifacts.ts, store.ts (CoauthorStore) untouched — the INV-2 byte-for-byte guarantee by construction.

Tests

  • Unit (vitest): 150 passing (130 prior + new GlobalSidecarStore, SidecarRouter, isAuthorable/selectionRejection).
  • Host E2E (@vscode/test-electron, no LLM): 36 passing with-workspace (incl. 3 new F8: out-of-folder file, untitled, in-workspace byte-for-byte regression) + 5 passing no-workspace (folder-less authoring). All F2–F7 regressions green.

Docs: docs/MANUAL-SMOKE-F8.md, README F8 section.

Plan: docs/superpowers/plans/2026-06-11-f8-out-of-workspace.md.

🤖 Generated with Claude Code

Implements **F8 (Feature #25, out-of-workspace authoring)** from the graduated Solution Design `specs/coauthoring-out-of-workspace.md` (brainstorming session 0024). Planning-and-executing session 0025. ## What changes "Ask Claude to Edit Selection" (and F2 threads / F3 attribution / F4 propose-accept) now work on **any** document the editor shows — saved in-folder, saved out-of-folder, or untitled — matching the already-universal F6 diff / F7 preview. Commands are live even with **no folder open**. **Hybrid persistence** (one `SidecarStore` abstraction, routed per-document by `#24`'s `isUnderRoot`): - in-workspace `file:` → the committable repo `.threads/<repo-rel>.json` sidecar (`CoauthorStore`, **byte-for-byte unchanged**, INV-2); - out-of-workspace `file:` + `untitled:` → global-storage sidecar keyed by `sha256(uri)` (`GlobalSidecarStore`, mirroring F6's `BaselineStore`, INV-19/24); untitled is **in-memory only**. The `Artifact` shape + `SCHEMA_VERSION` are identical either home, so `mergeArtifacts` and the F5 cross-rung contract are untouched. A global artifact is **not a committed file** → never cross-rung-shareable (INV-25); URI-rename orphaning accepted & documented. ## How - New: `sidecarStore.ts` (interface), `globalSidecarStore.ts`, `sidecarRouter.ts` (`keyOf` + routing); `isAuthorable` in `workspacePath.ts`. - Re-pointed the three controllers + `VersionGuard` onto the router; gate `isInRoot`/`isTracked` → `isAuthorable`; key via `store.keyOf`. - `extension.ts`: construct the router; removed the no-root early-return + command stubs (folder-less authoring); `renderIfOpen` gated by `isAuthorable`. - **`model.ts`, `mergeArtifacts.ts`, `store.ts` (CoauthorStore) untouched** — the INV-2 byte-for-byte guarantee by construction. ## Tests - Unit (vitest): **150 passing** (130 prior + new `GlobalSidecarStore`, `SidecarRouter`, `isAuthorable`/`selectionRejection`). - Host E2E (`@vscode/test-electron`, no LLM): **36 passing** with-workspace (incl. 3 new F8: out-of-folder file, untitled, in-workspace byte-for-byte regression) + **5 passing** no-workspace (folder-less authoring). All F2–F7 regressions green. Docs: `docs/MANUAL-SMOKE-F8.md`, README F8 section. Plan: `docs/superpowers/plans/2026-06-11-f8-out-of-workspace.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
benstull added 8 commits 2026-06-11 20:18:32 +00:00
Spec §6.2/§6.4: out-of-workspace file: → <globalStorage>/sidecars/<sha256(uri)>.json
(INV-19/24); untitled: → in-memory only (F6 degrade). update() carries the INV-16
newer-major throw + anchor prune; consumeSelfWrite is a no-op (outside the .threads
watcher). Mirrors src/baselineStore.ts; vscode-free, unit-tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Spec §6.4: replace the scheme!=file / !isUnderRoot rejections with a single
!isAuthorable branch. isUnderRoot retained as a routing input. Per-condition
messaging (#24) kept for no-editor / empty-selection / non-{file,untitled}.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Routes by #24's isUnderRoot: in-workspace file: → CoauthorStore (repo-relative key,
.threads/), out-of-folder file: + untitled: → GlobalSidecarStore (URI-string key).
keyOf is the single document identity. vscode-free; CoauthorStore conforms
structurally (unmodified, INV-2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Spec §6.4: construct GlobalSidecarStore + SidecarRouter (CoauthorStore when root);
remove the no-root early-return + command stubs so F2/F3/F4 are real folder-less;
renderIfOpen gated by isAuthorable; editor-context menus widened to untitled;
export sidecarRouter on CowritingApi. activate now always returns the API.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
benstull merged commit 7e17ff8b23 into main 2026-06-11 20:18:55 +00:00
benstull deleted branch f8-out-of-workspace 2026-06-11 20:18:59 +00:00
Sign in to join this conversation.