From 1e5d0bd58cb8463f4cda5741086642a398e9ac56 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Tue, 30 Jun 2026 13:25:09 -0800 Subject: [PATCH] crm: fix blank-node collision merging authorship across works MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jsonld.toRDF restarts blank-node labels at _:b0 per call; concatenating the per-entity N-Triples unified every doc's Production/TimeSpan/Right node into one, silently merging authorship (3110 paintings → one shared production event). Prefix each doc's blank nodes with a per-doc counter so they stay distinct. Verified: two paintings now yield separate production nodes (_:d0b1 vs _:d1b1). --- system/backend/rdf.mjs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/system/backend/rdf.mjs b/system/backend/rdf.mjs index 31ceae5577..7de6787d8e 100644 --- a/system/backend/rdf.mjs +++ b/system/backend/rdf.mjs @@ -29,12 +29,24 @@ async function documentLoader(url) { throw new Error(`refusing to fetch remote document: ${url}`); } -// One Linked Art doc → N-Triples string (default graph). +// jsonld.toRDF restarts blank-node labels at _:b0 on every call. Concatenating +// many docs' N-Triples into one document would then UNIFY every doc's _:b0 +// (Production events, time-spans, rights) into a single node — silently merging +// authorship across works. So we make each doc's blank nodes globally unique by +// prefixing them with a per-doc counter before concatenation. +let docCounter = 0; + +function uniquifyBlankNodes(nt, id) { + return nt.replace(/_:(b\d+)/g, `_:d${id}$1`); +} + +// One Linked Art doc → N-Triples string with blank nodes scoped to this doc. export async function docToNTriples(doc) { - return jsonld.toRDF(doc, { format: "application/n-quads", documentLoader }); + const nt = await jsonld.toRDF(doc, { format: "application/n-quads", documentLoader }); + return uniquifyBlankNodes(nt, docCounter++); } -// Many docs → a single concatenated N-Triples dump. +// Many docs → a single concatenated N-Triples dump (blank nodes stay distinct). export async function docsToNTriples(docs) { const chunks = []; for (const doc of docs) chunks.push(await docToNTriples(doc)); -- 2.51.2