// test-cosmo-labels.test.mjs — R13b: neutralise the MECHANICAL persona occurrences that // sit OUTSIDE headings in the reference corpus — bold labels and table cells. // // THE SCOPE IS A MEASUREMENT, NOT THE ORDER'S PREMISE. The dispatching order carried // "MEKANISK 85 / REDAKSJONELT 47", itself a correction of an earlier "87/45". Re-measured // 2026-09-12 over skills/*/references (389 files), that split is wrong too — and wrong in // the CLASSIFIER, not in the count. Three findings, all pinned by tests below: // // 1. FOURTEEN bold labels are DIALOGUE SPEAKER ATTRIBUTIONS, not labels. The dialogue // bucket only ever matched the italic form `*Cosmo:*`; the bold form sits in front of // quoted speech — **Naar kunden sier:** "..." / **Cosmo svarer:** "..." — and // deleting it leaves the quote with no speaker. Editorial, not mechanical. // 2. TEN table cells are PROVENANCE CLAIMS in the source-quality column ("Moenstre er // Cosmo-design", "Raadgivende innhold basert paa Cosmo-persona"). Cell position splits // the 26 table occurrences into three classes, not one: 12 row labels (column 0), // 4 column headers, 10 provenance. Rewriting a provenance claim asserts something new // about where the content came from; one of the ten has no deletion target at all. // Six of the order's nine compound forms live entirely inside this class. // 3. Leaking the OTHER way, +1 mechanical: reasoning-models-o1-o3-optimization.md:549 // `- **For arkitekten (Cosmo):** ...` is exactly the target class, but a line-start // anchored regex cannot see it behind the `- ` list marker, so it was booked as prose. // // MEASURED SPLIT: MEKANISK 62 (46 labels + 16 table cells) / REDAKSJONELT 70. Sum 132 // unchanged. Operator-ratified 2026-09-15: run the 62; dialogue and provenance go to R14's // editorial pass, because both hang on the never-answered #R14-persona-ramme (neutral // frame / new named persona / none) while the 62 do not — R13 already executed // `For Cosmo` -> `For arkitekten` across 401 headings, so this only makes labels and table // rows consistent with a decision that has ALREADY run. import { test } from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync, globSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { LABEL_MAP, EDITORIAL_LABELS, TABLE_CELL_MAP, EDITORIAL_CELLS, classifyPersonaSites, neutralizeLabels, } from '../../scripts/kb-update/lib/cosmo-persona.mjs'; const PLUGIN_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..'); const refFiles = () => globSync('skills/*/references/**/*.md', { cwd: PLUGIN_ROOT }).sort(); // ------------------------------------------------------------------ the ratified tables test('LABEL_MAP is the 16 ratified bold-label variants', () => { assert.equal(Object.keys(LABEL_MAP).length, 16); assert.equal(LABEL_MAP['For Cosmo:'], 'For arkitekten:'); assert.equal(LABEL_MAP['For Cosmo Skyberg:'], 'For arkitekten:'); assert.equal(LABEL_MAP['For arkitekten (Cosmo):'], 'For arkitekten:'); assert.equal(LABEL_MAP['Cosmo-anbefaling:'], 'Anbefaling:'); assert.equal(LABEL_MAP['Owner (Cosmo):'], 'Owner:'); assert.equal(LABEL_MAP['Til Cosmo:'], 'Til arkitekten:'); assert.equal(LABEL_MAP["Cosmo's rule of thumb:"], 'Rule of thumb:'); }); test('TABLE_CELL_MAP is the 4 ratified cell variants, bold form kept bold', () => { assert.equal(Object.keys(TABLE_CELL_MAP).length, 4); assert.equal(TABLE_CELL_MAP['For arkitekten (Cosmo)'], 'For arkitekten'); assert.equal(TABLE_CELL_MAP['**For arkitekten (Cosmo)**'], '**For arkitekten**'); assert.equal(TABLE_CELL_MAP['Cosmo-anbefaling'], 'Anbefaling'); assert.equal(TABLE_CELL_MAP['Cosmos raad'.replace('raad', 'råd')], 'Råd'); }); test('no target invents a word absent from its source (R13 invariant, reused)', () => { // Identical rule to HEADING_MAP's: dropping words is allowed, inventing one is not, // except the two substitutions the operator sanctioned for the persona token. const SANCTIONED = new Set(['arkitekten', 'å']); const words = (s) => s.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean); for (const [from, to] of Object.entries({ ...LABEL_MAP, ...TABLE_CELL_MAP })) { const src = new Set(words(from)); for (const w of words(to)) { assert.ok(src.has(w) || SANCTIONED.has(w), `target invents "${w}": "${from}" -> "${to}"`); } } }); test('every mapped target is itself persona-free, and the two sets are disjoint', () => { for (const [from, to] of Object.entries({ ...LABEL_MAP, ...TABLE_CELL_MAP })) { assert.ok(!/cosmo/i.test(to), `target keeps persona: "${from}" -> "${to}"`); } for (const k of EDITORIAL_LABELS) assert.equal(LABEL_MAP[k], undefined, `"${k}" in both sets`); for (const k of EDITORIAL_CELLS) assert.equal(TABLE_CELL_MAP[k], undefined, `"${k}" in both sets`); }); test('the editorial sets are the measured dialogue and provenance classes', () => { assert.equal(EDITORIAL_LABELS.size, 8); assert.ok(EDITORIAL_LABELS.has('Cosmo svarer:')); assert.ok(EDITORIAL_LABELS.has('Cosmo:')); assert.equal(EDITORIAL_CELLS.size, 10); assert.ok(EDITORIAL_CELLS.has('Rådgivende innhold basert på Cosmo-persona')); }); // ------------------------------------------------------------------ KLAUSUL 2: reach test('the corpus carries ZERO mechanical sites, and the editorial ones are all still there', () => { // Asserts the state AFTER the transform. The pre-transform reference point — 62 mechanical // in 50 files — lives in cosmo-labels-baseline.json and is read from there rather than // restated here: a number asserted in two places drifts, and the baseline is the artefact // the gate compares against. The test below pins that the two agree. const t = { mechLabel: 0, mechCell: 0, edLabel: 0, edCell: 0, unmapped: [] }; for (const rel of refFiles()) { const s = classifyPersonaSites(readFileSync(join(PLUGIN_ROOT, rel), 'utf8')); t.mechLabel += s.label.mechanical; t.mechCell += s.table.mechanical; t.edLabel += s.label.editorial; t.edCell += s.table.editorial; for (const u of s.unmapped) t.unmapped.push(`${rel}: ${u}`); } assert.deepEqual(t.unmapped, [], 'an unmeasured persona site exists — the net has a hole'); assert.equal(t.mechLabel + t.mechCell, 0, 'R13b is executed: no mechanical site remains'); assert.equal(t.edLabel, 16, 'dialogue (14) + the order\u2019s own 2 sentence forms, held for R14'); assert.equal(t.edCell, 10, 'provenance cells held back for R14'); }); test('the ratified tables and the gate baseline describe the SAME 62 occurrences', () => { // The baseline is a reference-point artefact: it records what the corpus looked like BEFORE // the transform, and must never be silently re-emitted. This pins its arithmetic against the // tables that produced it, so a regenerated-over baseline shows up as a failing test. const b = JSON.parse(readFileSync(join(PLUGIN_ROOT, 'scripts/kb-update/data/cosmo-labels-baseline.json'), 'utf8')); assert.equal(b.mechanicalLabels, 46); assert.equal(b.mechanicalCells, 16); assert.equal(b.mechanicalTotal, 62); assert.equal(b.mechanicalFiles, 50); // The 70 editorial occurrences split 16 labels + 10 cells + 44 that sit at no label or // cell site at all (plain prose, italic dialogue, provenance footers) — R14's, every one. assert.equal(b.editorialLabels + b.editorialCells, 26); assert.equal(b.editorialTotal - b.editorialLabels - b.editorialCells, 44); assert.equal(b.personaProseBefore - b.mechanicalTotal, b.personaProseAfter); assert.equal(b.personaProseBefore, 132, 'R13 left 132 persona occurrences outside headings'); assert.equal(b.personaProseAfter, 70, 'R13b leaves 70 for R14'); assert.equal(b.productOccurrences, 451, 'the Azure Cosmos DB mentions R1/R3 protect'); }); test('reach: the list-item label behind a "- " marker is INSIDE scope', () => { // Finding 3. A line-start-anchored net books this as prose and silently misses it. const src = '- **For arkitekten (Cosmo):** Baseline (erfaring + Cosmo-persona)\n'; const s = classifyPersonaSites(src); assert.equal(s.label.mechanical, 1); const { content } = neutralizeLabels(src); assert.equal(content, '- **For arkitekten:** Baseline (erfaring + Cosmo-persona)\n'); }); test('reach: a label form the tables do not know is REPORTED, never silently skipped', () => { // KLAUSUL 2's known-positive: construct an in-class occurrence the transform cannot // handle and assert it surfaces as unmapped rather than passing through untouched. const s = classifyPersonaSites('**Cosmo-hjørnet:** noe tekst\n'); assert.deepEqual(s.unmapped, ['label: "Cosmo-hjørnet:"']); assert.throws(() => neutralizeLabels('**Cosmo-hjørnet:** noe tekst\n'), /ukjent persona-etikett/); }); // ------------------------------------------------------------------ KLAUSUL 1: referent test('known-negative: a product label and a product table row must NOT be touched', () => { // The second is the dangerous one: it LOOKS like a persona label and is product. const src = '**Cosmos DB-anbefaling:** bruk autoscale\n\n| Azure Cosmos DB | 8000 NOK |\n'; const s = classifyPersonaSites(src); assert.equal(s.label.mechanical + s.table.mechanical, 0); assert.deepEqual(s.unmapped, []); assert.equal(neutralizeLabels(src).content, src, 'product content was rewritten'); }); test('known-positive: both injected forms trip the net, genitive included', () => { const s = classifyPersonaSites('**For Cosmo:** x\n\n| Trade-off | Cosmos råd |\n'); assert.equal(s.label.mechanical, 1); assert.equal(s.table.mechanical, 1); }); // ------------------------------------------------------------------ KLAUSUL 3: what stays test('neutralizeLabels leaves dialogue and provenance byte-identical', () => { const src = [ '**Når kunden sier:** "Vi må teste sikkerheten"', '**Cosmo svarer:**', '1. Start med PyRIT', '', '| Beslutningsveiledning | **Baseline** | Cosmo-syntese av verified sources |', '', ].join('\n'); assert.equal(neutralizeLabels(src).content, src); }); test('neutralizeLabels is idempotent and never changes the line count', () => { const src = '**For Cosmo:** x\n| For arkitekten (Cosmo) | Baseline | y |\n'; const once = neutralizeLabels(src).content; assert.equal(neutralizeLabels(once).content, once); assert.equal(once.split('\n').length, src.split('\n').length); assert.equal(once, '**For arkitekten:** x\n| For arkitekten | Baseline | y |\n'); }); test('a table row rewrites only the mapped cell, leaving its siblings byte-identical', () => { const src = '| For arkitekten (Cosmo) | **Baseline** | Best practices + erfaring |\n'; assert.equal( neutralizeLabels(src).content, '| For arkitekten | **Baseline** | Best practices + erfaring |\n', ); });