From 6c933d034bf8df20991c38573a61a6f6355ffeef Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 16 Sep 2026 18:31:52 +0200 Subject: [PATCH] Spell the transcript separator as an escape, not a raw byte The unit separator that joins protocol ids sat in the source as a literal U+001F: once in `cultmesh-authority.ts` and twice in its test. Every editor, every diff and every review renders it as nothing, so `join("")` reads as a join on the empty string and the transcript looks wrong to anyone checking it against the C# reference, which spells the same separator as an escape. The bytes were right, which is exactly why nothing caught it. The three become escapes. The bytes do not move: the C#-written and the TypeScript-written route vectors both still verify through the shared module, and the sorted-transcript test still builds the same expected bytes. A new test reads both files and refuses any byte below 0x20 that is not LF or CR, so the class recurs loudly rather than silently. It earned its keep on the first run by catching a literal U+001F in the comment introducing it. Co-Authored-By: Claude Fable 5.1 --- packages/cultnet-ts/src/cultmesh-authority.ts | 2 +- .../test/cultmesh-authority.test.ts | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/cultnet-ts/src/cultmesh-authority.ts b/packages/cultnet-ts/src/cultmesh-authority.ts index 10c9d187..d61f6072 100644 --- a/packages/cultnet-ts/src/cultmesh-authority.ts +++ b/packages/cultnet-ts/src/cultmesh-authority.ts @@ -205,7 +205,7 @@ export function canonicalRoute(route: CultMeshAuthorityRouteView): Uint8Array { route.verseId, authorityRuntimeId, endpoint, - cleanProtocolIds(route.protocolIds).join(""), + cleanProtocolIds(route.protocolIds).join("\u001f"), String(route.priority ?? 0), // A blank generation is not blank in C#: the route constructor substitutes // the runtime id and endpoint, already cleaned, joined by "@". diff --git a/packages/cultnet-ts/test/cultmesh-authority.test.ts b/packages/cultnet-ts/test/cultmesh-authority.test.ts index 3017300f..dbf5a8d7 100644 --- a/packages/cultnet-ts/test/cultmesh-authority.test.ts +++ b/packages/cultnet-ts/test/cultmesh-authority.test.ts @@ -278,7 +278,7 @@ test("protocol ids are sorted ordinally into the transcript, whatever order the route.verseId, route.authorityRuntimeId, route.endpoint, - "cultmesh.content.v1cultmesh.documents.v1cultmesh.realtime.v1", + "cultmesh.content.v1\u001fcultmesh.documents.v1\u001fcultmesh.realtime.v1", "0", route.generation, certificate.providerKey.keyId, @@ -655,3 +655,22 @@ test("the shared module keeps the browser-safe surface: no node: import", () => const source = readFileSync(join(__dirname, "..", "..", "src", "cultmesh-authority.ts"), "utf8"); assert.doesNotMatch(source, /from "node:|require\("node:/); }); + +// The transcript separator is U+001F, and it sat in these two files as a literal +// byte: invisible in every editor and diff, indistinguishable from the empty +// string in `join("")`. The bytes were right, which is why nothing caught it. +// Any control character in a string literal has the same problem, so none of +// them may be spelled literally here; a backslash-u escape is the only spelling. +for (const [label, path] of [ + ["the shared module", join(__dirname, "..", "..", "src", "cultmesh-authority.ts")], + ["this test", join(__dirname, "..", "..", "test", "cultmesh-authority.test.ts")], +] as const) test(`${label} spells control characters as escapes, never as literal bytes`, () => { + const bytes = readFileSync(path); + const offenders: string[] = []; + for (const [offset, byte] of bytes.entries()) { + if (byte >= 0x20 || byte === 0x0a || byte === 0x0d) continue; + const line = bytes.subarray(0, offset).toString("utf8").split("\n").length; + offenders.push(`0x${byte.toString(16).padStart(2, "0")} at byte ${offset} (line ${line})`); + } + assert.deepEqual(offenders, []); +});