feat: Provenance gains email + agent.onBehalfOf — cross-rung identity (F5 SLICE-2, #14)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-10 22:56:45 -07:00
parent 7c4d3ed79f
commit da172a3117
2 changed files with 85 additions and 6 deletions
+46
View File
@@ -57,6 +57,52 @@ describe("serializeArtifact", () => {
});
});
describe("cross-rung identity (F5 SLICE-2)", () => {
it("round-trips email on human provenance, omitted when absent", () => {
const a = emptyArtifact("docs/x.md");
a.anchors["a_1"] = { fingerprint: { text: "alpha", before: "", after: "", lineHint: 0 } };
a.threads.push({
id: "t_1",
anchorId: "a_1",
status: "open",
messages: [
{ id: "m_1", author: { kind: "human", id: "ben", email: "ben@wiggleverse.org" }, body: "hi", createdAt: "2026-06-10T00:00:00.000Z" },
{ id: "m_2", author: { kind: "human", id: "anon" }, body: "yo", createdAt: "2026-06-10T00:00:01.000Z" },
],
});
const out = serializeArtifact(a);
const parsed = JSON.parse(out) as Artifact;
expect(parsed.threads[0].messages[0].author).toEqual({ kind: "human", id: "ben", email: "ben@wiggleverse.org" });
expect("email" in parsed.threads[0].messages[1].author).toBe(false);
expect(serializeArtifact(parsed)).toBe(out);
});
it("round-trips agent.onBehalfOf and omits it when absent", () => {
const a = emptyArtifact("docs/x.md");
a.attributions.push({
id: "at_1",
anchorId: "a_1",
author: {
kind: "agent",
id: "claude",
agent: { sdk: "@cline/sdk", model: "sonnet", sessionId: "run-1", onBehalfOf: { id: "benstull", email: "ben@wiggleverse.org" } },
},
createdAt: "2026-06-10T00:00:00.000Z",
updatedAt: "2026-06-10T00:00:00.000Z",
});
const out = serializeArtifact(a);
const parsed = JSON.parse(out) as Artifact;
const author = parsed.attributions[0].author;
expect(author.kind).toBe("agent");
if (author.kind === "agent") {
expect(author.agent.onBehalfOf).toEqual({ id: "benstull", email: "ben@wiggleverse.org" });
}
expect(out.includes("onBehalfOf")).toBe(true);
const a2 = emptyArtifact("docs/x.md");
expect(serializeArtifact(a2).includes("onBehalfOf")).toBe(false);
});
});
describe("attributions[] (F3 SLICE-1)", () => {
it("round-trips an attribution record through serialize → parse", () => {
const a = emptyArtifact("docs/x.md");