/** * vscode-free mutations on the Artifact (spec ยง6.5). Keeping thread logic here * (not in the vscode controller) keeps it unit-testable; ThreadController is a * thin adapter that calls these and persists via CoauthorStore. */ import { newId, type Artifact, type Fingerprint, type Provenance, type ThreadStatus, } from "./model"; export interface NewMessage { author: Provenance; body: string; } function message(m: NewMessage) { return { id: newId("m"), author: m.author, body: m.body, createdAt: new Date().toISOString() }; } /** Lazily-created anchor + thread for a new region discussion (PUC-1). */ export function addThread( artifact: Artifact, fingerprint: Fingerprint, first: NewMessage, ): { threadId: string; anchorId: string } { const anchorId = newId("a"); const threadId = newId("t"); artifact.anchors[anchorId] = { fingerprint }; artifact.threads.push({ id: threadId, anchorId, status: "open", messages: [message(first)] }); return { threadId, anchorId }; } /** Append a reply (PUC-2). Throws if the thread is unknown. */ export function appendMessage(artifact: Artifact, threadId: string, m: NewMessage): void { const t = artifact.threads.find((x) => x.id === threadId); if (!t) throw new Error(`unknown thread id: ${threadId}`); t.messages.push(message(m)); } /** Resolve/reopen a thread (PUC-2). Throws if the thread is unknown. */ export function setStatus(artifact: Artifact, threadId: string, status: ThreadStatus): void { const t = artifact.threads.find((x) => x.id === threadId); if (!t) throw new Error(`unknown thread id: ${threadId}`); t.status = status; }