// test-cosmo-persona.test.mjs — R13 Cosmo del 1: persona-classification + heading // neutralisation in the reference corpus. The gate this pins is the RATIFIED one // (operator 2026-09-12, alternative A), NOT the roadmap's `grep -rl "Cosmo" -> 0`, // which was measured false twice over: it would delete 451 Azure Cosmos DB // occurrences, and it is unreachable under a heading-only scope because 132 persona // occurrences live in prose, tables and code blocks that heading neutralisation // cannot reach. // // The three clauses (measured baselines 2026-09-12 over skills/*/references, 389 files): // G1 persona occurrences on markdown heading lines 401 -> 0 (374 files, 40 variants) // G2 every fragment link resolves to a real heading 1 dead -> 1 dead // (agent-framework.md#pattern-3-human-in-the-loop is pre-existing and exempt) // G3 product occurrences unchanged 451 -> 451 // // WHY exact-string keying and NOT fence-awareness: measured, both toggle rules are // wrong on this corpus. A naive ``` toggle leaves chain-of-thought-prompting.md // unbalanced (19 markers) and hides the real heading at line 403; the CommonMark // info-string rule instead unbalances service-level-documentation-dr.md (nested // ```markdown examples). Measured resolution: fence-agnostic detection finds 401 // heading-like persona lines in EXACTLY the same 40 variants as fence-aware // detection finds 400 — no code-block line is byte-identical to a persona heading. // So the transform keys on the 40 enumerated heading texts and ignores fences, which // is both correct and auditable. An unmapped persona heading THROWS: content is never // auto-rewritten by pattern. import { test } from 'node:test'; import assert from 'node:assert/strict'; import { isPersonaOccurrence, classifyCosmo, HEADING_MAP, FILE_HEADING_OVERRIDES, neutralizeContent, slugifyHeading, findDeadAnchors, } from '../../scripts/kb-update/lib/cosmo-persona.mjs'; // ---------------------------------------------------------------- classification test('isPersonaOccurrence — bare Cosmo is persona', () => { assert.equal(isPersonaOccurrence('Cosmo anbefaler X'), true); assert.equal(isPersonaOccurrence("Cosmo's Cheat Sheet"), true); assert.equal(isPersonaOccurrence('Cosmo-tilnærming'), true); assert.equal(isPersonaOccurrence('Cosmo Skyberg, arkitekt'), true); }); test('isPersonaOccurrence — lowercase anchor fragment is persona', () => { // The 327 TOC anchors: `](#for-arkitekten-cosmo)` — persona, and invisible to a // case-sensitive net. This is why the roadmap gate could not even see them. assert.equal(isPersonaOccurrence('cosmo)'), true); assert.equal(isPersonaOccurrence('cosmo-skyberg)'), true); }); test('isPersonaOccurrence — Azure Cosmos DB and SDK identifiers are product', () => { assert.equal(isPersonaOccurrence('Cosmos DB for NoSQL'), false); assert.equal(isPersonaOccurrence('Cosmos-DB"'), false); assert.equal(isPersonaOccurrence('CosmosClient(connectionString)'), false); assert.equal(isPersonaOccurrence('CosmosAIGraph'), false); assert.equal(isPersonaOccurrence('cosmosdb show'), false); assert.equal(isPersonaOccurrence('cosmos_primary_monthly'), false); assert.equal(isPersonaOccurrence('Cosmos;'), false); // Microsoft.Azure.Cosmos; assert.equal(isPersonaOccurrence('cosmos-db/continuous-backup-restore)'), false); }); test('isPersonaOccurrence — Norwegian genitive "Cosmos " is persona', () => { // The discriminator is NOT the letter s: 9 measured occurrences are the persona's // genitive, e.g. `### Cosmos tonalitet`. assert.equal(isPersonaOccurrence('Cosmos tonalitet'), true); assert.equal(isPersonaOccurrence('Cosmos veiledningsstrategi'), true); assert.equal(isPersonaOccurrence('Cosmos råd |'), true); assert.equal(isPersonaOccurrence('Cosmos anbefalinger'), true); assert.equal(isPersonaOccurrence('Cosmos erfaringsbaserte anbefalinger)'), true); }); test('isPersonaOccurrence — a function word after Cosmos keeps it product', () => { // "Redis for hot, Cosmos for warm data" — product prose, not a genitive. assert.equal(isPersonaOccurrence('Cosmos for warm data)'), false); assert.equal(isPersonaOccurrence('Cosmos og Kusto'), false); assert.equal(isPersonaOccurrence('Cosmos i DR-region'), false); }); test('classifyCosmo — buckets persona by syntactic location and counts product', () => { const doc = [ '# Tittel', '', '- [For Cosmo](#for-cosmo)', // TOC line: 2 persona (text + anchor) '', '## For Cosmo', // heading: 1 persona '', '**For Cosmo:** bruk denne.', // prose: 1 persona '', '### Azure Cosmos DB for NoSQL', // heading: product only '', 'Bruk `CosmosClient` mot Cosmos DB.', // prose: 2 product '', ].join('\n'); const c = classifyCosmo(doc); assert.equal(c.persona.heading, 1); assert.equal(c.persona.toc, 2); assert.equal(c.persona.prose, 1); assert.equal(c.product, 3); // Azure Cosmos (heading) + CosmosClient + Cosmos DB (prose) assert.equal(c.personaTotal, 4); }); // ---------------------------------------------------------------- the mapping table test('HEADING_MAP covers all 40 measured persona heading variants', () => { assert.equal(Object.keys(HEADING_MAP).length, 40); }); test('HEADING_MAP — every neutral target is free of persona', () => { for (const [from, to] of Object.entries(HEADING_MAP)) { assert.equal( classifyCosmo(`## ${to}`).personaTotal, 0, `target for "${from}" still carries persona: "${to}"`, ); } }); test('HEADING_MAP — no target invents a word absent from its source', () => { // The order forbids new or changed Cosmo content beyond the neutralisation itself. // Enforced word-by-word: every word of a neutral target must already occur in the // source heading, EXCEPT the two sanctioned substitutions for the persona token — // "arkitekten" (the role Cosmo played) and "å" (when a name was the sentence's // subject, e.g. "Spørsmål Cosmo bør stille" -> "Spørsmål å stille"). Dropping words // is allowed; introducing any other word is not. 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(HEADING_MAP)) { const src = new Set(words(from)); for (const w of words(to)) { assert.ok( src.has(w) || SANCTIONED.has(w), `target invents the word "${w}": "${from}" -> "${to}"`, ); } } }); test('FILE_HEADING_OVERRIDES resolves both measured in-file slug collisions', () => { // Swept over all 389 files: exactly two documents where the ratified mapping would // make two headings share one slug. One collides with a sibling persona heading, the // other with the document's own H1 title. const collision1 = 'skills/ms-ai-engineering/references/mlops-genaiops/feedback-loops-continuous-improvement.md'; const collision2 = 'skills/ms-ai-governance/references/monitoring-observability/application-insights-llm-monitoring.md'; assert.equal(FILE_HEADING_OVERRIDES[collision1]['For Cosmo'], 'Nøkkelpunkter for rådgivning'); assert.equal( FILE_HEADING_OVERRIDES[collision2]['For Cosmo Skyberg: Application Insights for LLM Monitoring'], 'For arkitekten', ); assert.equal(Object.keys(FILE_HEADING_OVERRIDES).length, 2); }); test('FILE_HEADING_OVERRIDES only ever redirects a variant HEADING_MAP already knows', () => { // An override is a collision remedy, never a back door for an unmeasured heading: // every key must already be in the shared table, and every target persona-free. for (const [rel, map] of Object.entries(FILE_HEADING_OVERRIDES)) { for (const [from, to] of Object.entries(map)) { assert.ok(HEADING_MAP[from] !== undefined, `${rel}: "${from}" is not a known variant`); assert.notEqual(to.trim(), '', `${rel}: empty target for "${from}"`); assert.equal(classifyCosmo(`## ${to}`).personaTotal, 0, `${rel}: target keeps persona: "${to}"`); } } }); // ---------------------------------------------------------------- slug parity test('slugifyHeading matches transform.mjs slug semantics (æøå survive)', () => { assert.equal(slugifyHeading('For arkitekten (Cosmo)'), 'for-arkitekten-cosmo'); assert.equal(slugifyHeading('Spørsmål å stille kunden'), 'spørsmål-å-stille-kunden'); assert.equal(slugifyHeading('For Cosmo: Beslutningsveiledning'), 'for-cosmo-beslutningsveiledning'); }); // ---------------------------------------------------------------- the transform test('neutralizeContent rewrites the heading AND its TOC entry and anchor together', () => { const before = [ '# Tittel', '', '## Innhold', '', '- [Introduksjon](#introduksjon)', '- [For arkitekten (Cosmo)](#for-arkitekten-cosmo)', '', '## Introduksjon', '', '## For arkitekten (Cosmo)', '', 'Tekst.', '', ].join('\n'); const { content, changes } = neutralizeContent(before, { relPath: 'x.md' }); assert.match(content, /^## For arkitekten$/m); assert.doesNotMatch(content, /Cosmo/i); assert.match(content, /^- \[For arkitekten\]\(#for-arkitekten\)$/m); assert.equal(changes.length, 2, 'one heading + one TOC entry'); }); test('neutralizeContent leaves product headings and product prose byte-identical', () => { const before = [ '# Tittel', '', '### Azure Cosmos DB for GraphRAG', '', 'Bruk `CosmosClient` mot Cosmos DB; se `cosmos_primary_monthly`.', '', '### Cosmos DB Autoscale', '', ].join('\n'); const { content, changes } = neutralizeContent(before, { relPath: 'x.md' }); assert.equal(content, before); assert.equal(changes.length, 0); }); test('neutralizeContent preserves the product occurrence count exactly', () => { const before = [ '## For Cosmo', '', 'Azure Cosmos DB med `CosmosClient` og cosmosdb-CLI.', '', ].join('\n'); const productBefore = classifyCosmo(before).product; const { content } = neutralizeContent(before, { relPath: 'x.md' }); assert.equal(classifyCosmo(content).product, productBefore); assert.equal(classifyCosmo(content).personaTotal, 0); }); test('neutralizeContent rewrites a heading that a fence toggle would hide', () => { // The chain-of-thought-prompting.md case: an unbalanced fence means a naive // toggle believes line 4 is inside a code block. Exact-string keying must not care. const before = [ '```python', 'x = 1', '```python', // stray opener — leaves a naive toggle "inside" forever '', '## For arkitekten (Cosmo)', '', ].join('\n'); const { content } = neutralizeContent(before, { relPath: 'x.md' }); assert.match(content, /^## For arkitekten$/m); }); test('neutralizeContent is idempotent — a second run changes nothing', () => { const before = [ '- [For Cosmo](#for-cosmo)', '', '## For Cosmo', '', ].join('\n'); const once = neutralizeContent(before, { relPath: 'x.md' }).content; const twice = neutralizeContent(once, { relPath: 'x.md' }); assert.equal(twice.content, once); assert.equal(twice.changes.length, 0); }); test('neutralizeContent THROWS on an in-file slug collision', () => { // Two persona headings mapping to the same neutral text would produce a duplicate // heading and an ambiguous anchor. Fail loud; never write blind. const before = [ '## For arkitekten (Cosmo)', '', '## For Cosmo', '', ].join('\n'); assert.throws( () => neutralizeContent(before, { relPath: 'x.md' }), /kollisjon/i, ); }); test('neutralizeContent THROWS on an unmapped persona heading', () => { // Content is never auto-rewritten by pattern: an unseen variant is an operator // decision, not a silent guess. const before = ['## For Cosmo: en helt ny variant ingen har målt', ''].join('\n'); assert.throws( () => neutralizeContent(before, { relPath: 'x.md' }), /ukjent persona-heading/i, ); }); test('neutralizeContent does not touch persona in prose, tables or dialogue', () => { // Explicitly OUT of the ratified R13 scope — the 132 residual occurrences booked // for R13b/R14. A transform that reached them would exceed its mandate. const before = [ '**For Cosmo:** bruk denne modellen.', '', '| For arkitekten (Cosmo) | Baseline |', '', '*Cosmo:* "La oss starte med risikoprofilen."', '', ].join('\n'); const { content, changes } = neutralizeContent(before, { relPath: 'x.md' }); assert.equal(content, before); assert.equal(changes.length, 0); }); // ---------------------------------------------------------------- G2: dead anchors test('findDeadAnchors reports a fragment link with no matching heading', () => { const doc = [ '- [Finnes](#finnes)', '- [Finnes ikke](#finnes-ikke)', '', '## Finnes', '', ].join('\n'); const dead = findDeadAnchors(doc); assert.equal(dead.length, 1); assert.equal(dead[0].anchor, 'finnes-ikke'); }); test('findDeadAnchors returns empty when every anchor resolves', () => { const doc = ['- [Spørsmål å stille](#spørsmål-å-stille)', '', '## Spørsmål å stille', ''].join('\n'); assert.deepEqual(findDeadAnchors(doc), []); }); test('a neutralized document has no dead anchors', () => { // The whole point of rewriting heading and TOC entry in one operation. const before = [ '- [For Cosmo: Quick Reference Card](#for-cosmo-quick-reference-card)', '', '## For Cosmo: Quick Reference Card', '', ].join('\n'); const { content } = neutralizeContent(before, { relPath: 'x.md' }); assert.deepEqual(findDeadAnchors(content), []); assert.match(content, /^- \[Quick Reference Card\]\(#quick-reference-card\)$/m); });