feat(f8): SidecarStore interface + GlobalSidecarStore (sha256 key, untitled in-memory)

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>
This commit is contained in:
Ben Stull
2026-06-11 13:06:08 -07:00
parent 568139d548
commit 515acb4868
3 changed files with 227 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
/**
* SidecarStore — the storage surface the three authoring controllers depend on
* (F8 spec §6.2/§6.4). One per-document `Artifact` keyed by a string `key` (the
* document key — repo-relative path in-workspace, URI string otherwise; the
* router's keyOf is the single source). Two implementations:
* - CoauthorStore (src/store.ts) — the committable repo `.threads/` sidecar,
* keyed by repo-relative path (INV-2, byte-for-byte unchanged; conforms
* structurally — not modified for F8).
* - GlobalSidecarStore (src/globalSidecarStore.ts) — out-of-workspace `file:`
* and `untitled:` docs, in VS Code GLOBAL storage keyed by sha256(uri)
* (INV-19/24/25; untitled in-memory only).
* SidecarRouter (src/sidecarRouter.ts) implements this AND adds keyOf/sidecarPath;
* the controllers receive the router.
*/
import type { Artifact } from "./model";
export interface SidecarStore {
/** Load the artifact for `key`, or null if none persisted. */
load(key: string): Artifact | null;
/** Overwrite the artifact for `key` (newest wins, no history). */
save(key: string, artifact: Artifact): void;
/**
* Read-modify-write: load (or empty), apply `mutate`, prune anchors referenced
* by no thread/attribution/proposal, persist, return the result. MUST be
* synchronous (the F3 co-ownership contract). Throws on a newer-major sidecar
* (INV-16 backstop).
*/
update(key: string, mutate: (artifact: Artifact) => void): Artifact;
/**
* Decrement-and-report whether `fsPath` is a sidecar this store just wrote
* (repo sidecars only; the global store is outside the `.threads/` watcher so
* its impl is a no-op returning false).
*/
consumeSelfWrite(fsPath: string): boolean;
}