feat(linkedin-studio): SB-S1 ingest data layer — record grammar + content-id [skip-docs]
PublishedRecord file-per-post grammar (fixed 5-line header + --- sentinel + verbatim body, no YAML) with parse∘serialize identity, and mintContentId = sha256(VERBATIM body)[:12] — byte-identity dedup so two structurally-different posts never collide (avoids the normalizeContent silent-data-loss path). parsePublishedRecord rejects a published/ record whose provenance != published (corruption signal). 10 grammar tests incl. the B1 edge battery (empty body, body starting with ---, mid-text \n---\n, header-shaped first line) + 4 id tests. Pure layer only; IO + CLI + voice-trainer wiring follow. brain 34→48 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
This commit is contained in:
parent
f549d9fdf3
commit
3e3990f36e
3 changed files with 194 additions and 0 deletions
84
scripts/brain/tests/ingest.test.ts
Normal file
84
scripts/brain/tests/ingest.test.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { describe, test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { mintContentId } from "../src/id.js";
|
||||
import {
|
||||
parsePublishedRecord,
|
||||
serializePublishedRecord,
|
||||
type PublishedRecord,
|
||||
} from "../src/ingest.js";
|
||||
|
||||
const baseRec = (body: string): PublishedRecord => ({
|
||||
id: mintContentId(body),
|
||||
provenance: "published",
|
||||
published_date: "2026-05-26",
|
||||
captured_at: "2026-06-23",
|
||||
source: "manual",
|
||||
body,
|
||||
});
|
||||
|
||||
/** Round-trip helper: parse∘serialize must equal the original record. */
|
||||
function roundTrip(rec: PublishedRecord): PublishedRecord {
|
||||
return parsePublishedRecord(serializePublishedRecord(rec));
|
||||
}
|
||||
|
||||
describe("mintContentId — verbatim-body content hash (SC3)", () => {
|
||||
test("deterministic: same body → same id", () => {
|
||||
const body = "Jeg lærte noe om dømmekraft i dag.\n\nDel 1 av serien.";
|
||||
assert.equal(mintContentId(body), mintContentId(body));
|
||||
});
|
||||
|
||||
test("returns a 12-hex id", () => {
|
||||
assert.match(mintContentId("any body"), /^[0-9a-f]{12}$/);
|
||||
});
|
||||
|
||||
test("different bodies → different ids", () => {
|
||||
assert.notEqual(mintContentId("post A"), mintContentId("post B"));
|
||||
});
|
||||
|
||||
test("whitespace/case differences mint DIFFERENT ids (no normalize-collision, B2 guard)", () => {
|
||||
// Under specifics-bank normalizeContent these would collapse to the SAME id —
|
||||
// proving the post-body hash is byte-identity, not normalized.
|
||||
assert.notEqual(mintContentId("Hello World"), mintContentId("hello world"));
|
||||
assert.notEqual(
|
||||
mintContentId("Line one\nLine two"),
|
||||
mintContentId("Line one Line two"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PublishedRecord grammar — parse∘serialize identity (SC2, B1 edge battery)", () => {
|
||||
const bodies: Record<string, string> = {
|
||||
"plain multi-paragraph": "Hook line.\n\nBody paragraph one.\n\nBody paragraph two.",
|
||||
"empty body": "",
|
||||
"single newline": "\n",
|
||||
"trailing newlines": "post text\n\n",
|
||||
"body starting with ---": "---\nlooks like a separator but is body",
|
||||
"body containing a \\n---\\n sentinel mid-text": "before\n---\nafter the fake separator",
|
||||
"header-shaped first line": "id: 0123456789ab\nprovenance: published",
|
||||
"special chars ] | quotes": 'value with ] and | and "quotes" inside',
|
||||
};
|
||||
|
||||
for (const [label, body] of Object.entries(bodies)) {
|
||||
test(`round-trips: ${label}`, () => {
|
||||
const rec = baseRec(body);
|
||||
assert.deepEqual(roundTrip(rec), rec);
|
||||
});
|
||||
}
|
||||
|
||||
test("serialized form has the fixed 5-line header then the --- sentinel", () => {
|
||||
const text = serializePublishedRecord(baseRec("hi"));
|
||||
const [header, ...rest] = text.split("\n---\n");
|
||||
assert.equal(header.split("\n").length, 5, "exactly 5 header lines before the sentinel");
|
||||
assert.equal(rest.join("\n---\n"), "hi", "body is everything after the FIRST sentinel");
|
||||
assert.match(text, /^id: [0-9a-f]{12}\nprovenance: published\n/);
|
||||
});
|
||||
|
||||
test("a published/ record with non-published provenance is rejected (corruption signal)", () => {
|
||||
const corrupt = serializePublishedRecord(baseRec("x")).replace(
|
||||
"provenance: published",
|
||||
"provenance: ai-draft",
|
||||
);
|
||||
assert.throws(() => parsePublishedRecord(corrupt), /provenance/i);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue