import { describe, it, expect } from "vitest"; import { emptyArtifact, serializeArtifact, type Artifact } from "../src/model"; import { expectValidSidecar, validateSidecar } from "./helpers/validateSidecar"; function fullArtifact(): Artifact { const a = emptyArtifact("docs/x.md"); a.anchors["a_1"] = { fingerprint: { text: "alpha", before: "", after: " beta", lineHint: 0 } }; a.threads.push({ id: "t_1", anchorId: "a_1", status: "open", messages: [{ id: "m_1", author: { kind: "human", id: "ben" }, body: "hi", createdAt: "2026-06-10T00:00:00.000Z" }], }); a.attributions.push({ id: "at_1", anchorId: "a_1", author: { kind: "agent", id: "claude", agent: { sdk: "@cline/sdk", model: "sonnet", sessionId: "run-1" } }, createdAt: "2026-06-10T00:00:00.000Z", updatedAt: "2026-06-10T00:00:00.000Z", turnId: "turn-1", }); a.proposals.push({ id: "p_1", anchorId: "a_1", replacement: "ALPHA", author: { kind: "agent", id: "claude", agent: { sdk: "@cline/sdk", model: "sonnet", sessionId: "run-1" } }, createdAt: "2026-06-10T00:00:00.000Z", instruction: "shout", }); return a; } describe("coauthoring-sidecar JSON Schema (F5 SLICE-1, PUC-5)", () => { it("validates the empty artifact and a full artifact", () => { expectValidSidecar(emptyArtifact("docs/x.md")); expectValidSidecar(fullArtifact()); }); it("rejects a structurally broken sidecar with error paths", () => { const broken = JSON.parse(serializeArtifact(fullArtifact())); broken.threads[0].status = "closed"; // not in the enum delete broken.schemaVersion; const { valid, errors } = validateSidecar(broken); expect(valid).toBe(false); expect(errors.some((e) => e.includes("schemaVersion"))).toBe(true); }); it("accepts unknown fields everywhere (forward compat, INV-16)", () => { const data = JSON.parse(serializeArtifact(fullArtifact())); data.futureSection = [{ id: "f_1" }]; data.threads[0].futureField = true; data.threads[0].messages[0].reactions = ["+1"]; const { valid } = validateSidecar(data); expect(valid).toBe(true); }); });