refactor(ms-ai-architect): R13b — nøytraliser de mekaniske persona-forekomstene utenfor headinger, etter at ordrens egen korrigerte bøttegrense ble målt usann

Ordren ba meg behandle sitt eget premiss som et premiss: «PM-ens 87/45 er korrigert
til 85/47 ved maaling ... Behandle ogsaa 85/47 som et premiss og re-maal det foer du
bygger.» Re-målt over de 389 ref-filene holder 85/47 heller ikke — og feilen ligger i
KLASSIFIKATOREN, ikke i tellingen. Summen 132 står; fordelingen er 62/70.

TRE FUNN, alle pinnet i test:

1. FJORTEN bold-etiketter er DIALOG-ATTRIBUSJONER, ikke etiketter. Dialog-bøtta
   matchet bare kursivformen `*Cosmo:*`. Fetformen står foran sitert tale —
   `**Naar kunden sier:** "..."` / `**Cosmo svarer:** "..."` — så å slette den
   etterlater replikken uten taler. Redaksjonelt, ikke mekanisk.
2. TI tabellceller er PROVENIENSPÅSTANDER i kildekvalitets-kolonnen. Celleposisjon
   deler de 26 tabellforekomstene i tre klasser, ikke én: 12 radetiketter (kolonne 0),
   4 kolonneoverskrifter, 10 proveniensverdier (`Moenstre er Cosmo-design`,
   `Raadgivende innhold basert paa Cosmo-persona`). Å skrive om en av dem er å avgi en
   ny påstand om hvor innholdet kommer fra, og den siste har ikke noe slettemål i det
   hele tatt. Seks av ordrens ni sammensetninger ligger helt inne i denne klassen —
   ordrens egen advarsel traff, målingen lokaliserte den.
3. Lekkasje ANDRE veien, +1 mekanisk: `- **For arkitekten (Cosmo):** ...` i
   reasoning-models-o1-o3-optimization.md:549 er nøyaktig målklassen, men et
   linjestart-forankret nett ser den ikke bak `- ` og bokførte den som prosa.

Operatøren ratifiserte 2026-09-15 alt. A: kjør de 62, hold dialog og proveniens for
R14. Begge tilbakeholdte klasser henger på #R14-persona-ramme som aldri er besvart;
de 62 gjør ikke det, fordi R13 ALT har kjørt `For Cosmo` -> `For arkitekten` over 401
headinger — dette gjør bare etiketter og tabellrader konsistente med en beslutning
som allerede er utført.

UTFØRT: 62 forekomster i 50 filer (ordren sa 47) — 46 etiketter i 16 ratifiserte
varianter, 16 celler i 4. Diffen er 62 fjernet = 62 lagt til, ren in-place-erstatning;
hver endret linje klassifisert, ANNET = 0. Ingen måltekst innfører et ord kilden ikke
hadde, utover R13s sanksjonerte `arkitekten` (samme invariant-test, gjenbrukt).

GATEN, DEKOMPONERT I TRE KLAUSULER, hver validert BEGGE veier før den ble konsumert:
  R1 REFERENT      mekaniske sites 62 -> 0, produkt 451 uendret.
                   Kjent-pos: `**For Cosmo:**` OG `| Cosmos raad |` (genitiven er den
                   et bold-only nett mister) feller begge. Kjent-neg:
                   `**Cosmos DB-anbefaling:**` — ser ut som persona, er produkt —
                   og `| Azure Cosmos DB |` passerer urørt.
  R2 REKKEVIDDE    0 umålte varianter, 0 utenfor rekkevidde. Kjent-pos: en ukjent
                   etikettform rapporteres som umålt OG transformen KASTER, den
                   hopper ikke stille over. Kjent-neg: dialog + proveniens bokføres
                   utenfor scope og er byte-identiske.
  R3 HVA SOM STÅR  16 redaksjonelle etiketter + 10 celler uendret, heading/TOC
                   fortsatt 0 (R13 ikke regradert), prosa 132 -> 70. Kjent-pos: et
                   fjernet tegn i `Cosmos DB` feller produkt-differansen. Kjent-neg:
                   alle kun-produkt-filer byte-identiske gjennom transformen.

Gaten ble kjørt FØR transformen og felte R1 (exit 1) — et nullresultat som aldri er
tvunget til det andre svaret er ingen måling. R13b har egen baseline-fil;
cosmo-gate-baseline.json er et referansepunkt-artefakt (personaHeadings 401) og er
IKKE re-emittert. Ny .gitignore-negasjon (6 entries), aldri `git add -f`.

Målt underveis: 0 av de 62 målinjene ligger i en kodeblokk, så fence-agnostisk
transform er trygg her — samme konklusjon R13 nådde for headinger. Nøyaktig 1 linje
bærer begge klasser; dens `Cosmo-persona` står igjen, som den skal.

Suite 1134/1134 (1120 + 14 nye), validate-plugin 250/0/0, transform idempotent,
R13-driveren fortsatt no-op. RX-OPS1 adversarial-scan kjørt MANUELT på de 56 stagede
filene (OK, exit 0) — `core.hooksPath` skygger repoets pre-commit, fiksen eies av
`.claude`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-15 21:09:00 +02:00
commit 1c9af82daa
56 changed files with 793 additions and 63 deletions

1
.gitignore vendored
View file

@ -35,6 +35,7 @@ scripts/kb-update/data/*
!scripts/kb-update/data/ai-act-deadlines.json !scripts/kb-update/data/ai-act-deadlines.json
!scripts/kb-update/data/layerb-allowlist.json !scripts/kb-update/data/layerb-allowlist.json
!scripts/kb-update/data/cosmo-gate-baseline.json !scripts/kb-update/data/cosmo-gate-baseline.json
!scripts/kb-update/data/cosmo-labels-baseline.json
# Generated skill-lifecycle detection report (Spor B / B1) — regenerated on demand, # Generated skill-lifecycle detection report (Spor B / B1) — regenerated on demand,
# like the kb-update reports above. The detector script + curated inputs are tracked. # like the kb-update reports above. The detector script + curated inputs are tracked.
scripts/kb-eval/data/skill-lifecycle-report.json scripts/kb-eval/data/skill-lifecycle-report.json

View file

@ -26,11 +26,17 @@ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
import { join, dirname, relative } from 'node:path'; import { join, dirname, relative } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { globSync } from 'node:fs'; import { globSync } from 'node:fs';
import { classifyCosmo, findDeadAnchors } from './lib/cosmo-persona.mjs'; import {
classifyCosmo, findDeadAnchors, classifyPersonaSites, neutralizeLabels,
} from './lib/cosmo-persona.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url));
const PLUGIN_ROOT = join(__dirname, '..', '..'); const PLUGIN_ROOT = join(__dirname, '..', '..');
const BASELINE_PATH = join(__dirname, 'data', 'cosmo-gate-baseline.json'); const BASELINE_PATH = join(__dirname, 'data', 'cosmo-gate-baseline.json');
// R13b lives in its OWN baseline file. The R13 one records pre-transform reference points
// (personaHeadings 401) that regenerating would destroy — never build over a reference-point
// artefact, so R13b adds a file rather than re-emitting that one.
const LABELS_BASELINE_PATH = join(__dirname, 'data', 'cosmo-labels-baseline.json');
/** Every reference file in the corpus, as repo-relative paths, sorted. */ /** Every reference file in the corpus, as repo-relative paths, sorted. */
function refFiles() { function refFiles() {
@ -121,6 +127,8 @@ function check() {
if (now.persona !== 0) fails.push(`G3: ${f} har faatt ${now.persona} persona-forekomst(er)`); if (now.persona !== 0) fails.push(`G3: ${f} har faatt ${now.persona} persona-forekomst(er)`);
} }
const labelLines = checkLabels(fails);
console.log('=== R13 persona-gate (ratifisert 2026-09-12, alt. A) ==='); console.log('=== R13 persona-gate (ratifisert 2026-09-12, alt. A) ===');
console.log(`ref-filer : ${m.files.length} (baseline ${b.refFiles})`); console.log(`ref-filer : ${m.files.length} (baseline ${b.refFiles})`);
console.log(`G1 persona paa heading-linjer : ${m.totals.heading} (baseline ${b.personaHeadings})`); console.log(`G1 persona paa heading-linjer : ${m.totals.heading} (baseline ${b.personaHeadings})`);
@ -131,6 +139,8 @@ function check() {
+ `(${exempt.size} unntatt, ${newDead.length} nye, ${healed.length} forsvunnet)`); + `(${exempt.size} unntatt, ${newDead.length} nye, ${healed.length} forsvunnet)`);
console.log(`G3 produkt-forekomster : ${m.totals.product} (baseline ${b.productOccurrences})`); console.log(`G3 produkt-forekomster : ${m.totals.product} (baseline ${b.productOccurrences})`);
console.log(` kun-produkt-filer urørt : ${Object.keys(b.productOnlyFileCounts).length}`); console.log(` kun-produkt-filer urørt : ${Object.keys(b.productOnlyFileCounts).length}`);
console.log('--- R13b etikett/celle-gate (ratifisert 2026-09-15, alt. A) ---');
for (const l of labelLines) console.log(l);
if (fails.length) { if (fails.length) {
console.log('\nGATE FAILED'); console.log('\nGATE FAILED');
for (const f of fails) console.log(` x ${f}`); for (const f of fails) console.log(` x ${f}`);
@ -178,11 +188,190 @@ function validateNet() {
else console.log(`ok KJENT-NEGATIV: ${f} — 0 persona, ${c.product} produkt`); else console.log(`ok KJENT-NEGATIV: ${f} — 0 persona, ${c.product} produkt`);
} }
console.log('--- R13b klausulene, begge veier ---');
bad += validateLabelsNet();
console.log(bad === 0 ? '\nNETTET VALIDERT BEGGE VEIER' : `\nNETT-VALIDERING FEILET (${bad})`); console.log(bad === 0 ? '\nNETTET VALIDERT BEGGE VEIER' : `\nNETT-VALIDERING FEILET (${bad})`);
process.exit(bad === 0 ? 0 : 1); process.exit(bad === 0 ? 0 : 1);
} }
// ================================ R13b: the three decomposed clauses ========================
//
// R1 REFERENT — has the search string more than one referent? Zero persona in
// MECHANICAL bold labels and table cells, while product stays untouched.
// R2 REKKEVIDDE — does the operation reach every occurrence it claims? An in-class site
// in neither ratified table must be REPORTED, never silently skipped.
// R3 HVA SOM STAAR — the 70 editorial occurrences unchanged in count AND content, product
// 451 unchanged, R13's headings and TOC still zero (not regraded).
//
// Each clause carries BOTH a known-positive and a known-negative in --validate-net. A gate
// never forced to the other answer is not a measurement.
/** Per-corpus measurement of the R13b sites. */
function measureLabels() {
const files = refFiles();
const t = {
mechLabel: 0, mechCell: 0, edLabel: 0, edCell: 0, prose: 0, product: 0,
unmapped: [], mechFiles: [], unreachable: [],
};
for (const rel of files) {
const src = readFileSync(join(PLUGIN_ROOT, rel), 'utf8');
const s = classifyPersonaSites(src);
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}`);
const c = classifyCosmo(src);
t.prose += c.persona.prose;
t.product += c.product;
// R2: a site the tables know but the transform does not actually rewrite is out of reach.
if (s.label.mechanical + s.table.mechanical > 0) {
t.mechFiles.push(rel);
let after;
try { after = neutralizeLabels(src, { relPath: rel }).content; } catch (e) {
t.unreachable.push(`${rel}: ${e.message}`); continue;
}
const a = classifyPersonaSites(after);
const left = a.label.mechanical + a.table.mechanical;
if (left) t.unreachable.push(`${rel}: ${left} site(r) utenfor transformens rekkevidde`);
}
}
return t;
}
function emitLabelsBaseline() {
const t = measureLabels();
const baseline = {
_comment: 'Ratified R13b label/cell baseline (operator 2026-09-15, alt. A). Measured BEFORE '
+ 'the label transform. R1 drives mechanical -> 0; R3 pins editorial and product as '
+ 'immovable. The 85/47 split the dispatching order carried was re-measured to 62/70 — '
+ 'the classifier was wrong, not the count. Regenerate ONLY on a ratified scope change.',
measured: new Date().toISOString().slice(0, 10),
refFiles: refFiles().length,
mechanicalLabels: t.mechLabel,
mechanicalCells: t.mechCell,
mechanicalTotal: t.mechLabel + t.mechCell,
mechanicalFiles: t.mechFiles.length,
editorialLabels: t.edLabel,
editorialCells: t.edCell,
editorialTotal: 70,
personaProseBefore: t.prose,
personaProseAfter: t.prose - (t.mechLabel + t.mechCell),
productOccurrences: t.product,
mixedLines: [
'skills/ms-ai-advisor/references/prompt-engineering/reasoning-models-o1-o3-optimization.md:549',
],
};
writeFileSync(LABELS_BASELINE_PATH, `${JSON.stringify(baseline, null, 2)}\n`, 'utf8');
console.log(`R13b-baseline skrevet: ${relative(PLUGIN_ROOT, LABELS_BASELINE_PATH)}`);
console.log(JSON.stringify(baseline, null, 2));
}
/** Evaluate R1-R3, pushing onto the shared fails list; returns the printable lines. */
function checkLabels(fails) {
if (!existsSync(LABELS_BASELINE_PATH)) return ['(R13b: ingen baseline — klausulene hoppes over)'];
const b = JSON.parse(readFileSync(LABELS_BASELINE_PATH, 'utf8'));
const t = measureLabels();
const mech = t.mechLabel + t.mechCell;
// --- R1 REFERENT ---
if (mech !== 0) {
fails.push(`R1: ${mech} mekanisk(e) persona-site(r) staar igjen `
+ `(${t.mechLabel} etiketter, ${t.mechCell} celler; baseline ${b.mechanicalTotal})`);
}
if (t.product !== b.productOccurrences) {
fails.push(`R1/R3: produkt ${t.product} != baseline ${b.productOccurrences}`);
}
// --- R2 REKKEVIDDE ---
if (t.unmapped.length) {
fails.push(`R2: ${t.unmapped.length} persona-site(r) i INGEN ratifisert tabell — `
+ `umaalt variant: ${t.unmapped.slice(0, 5).join(' | ')}`);
}
if (t.unreachable.length) {
fails.push(`R2: ${t.unreachable.length} site(r) utenfor transformens rekkevidde: `
+ `${t.unreachable.slice(0, 5).join(' | ')}`);
}
// --- R3 HVA SOM SKAL STAA ---
if (t.edLabel !== b.editorialLabels || t.edCell !== b.editorialCells) {
fails.push(`R3: REDAKSJONELT scope roert — etiketter ${t.edLabel} `
+ `(baseline ${b.editorialLabels}), celler ${t.edCell} (baseline ${b.editorialCells})`);
}
if (t.prose !== b.personaProseAfter && t.prose !== b.personaProseBefore) {
fails.push(`R3: persona i proesa/tabell ${t.prose} er hverken foer-verdien `
+ `${b.personaProseBefore} eller etter-verdien ${b.personaProseAfter}`);
}
const done = mech === 0;
return [
`R1 mekaniske sites (etikett+celle) : ${mech} (baseline ${b.mechanicalTotal} `
+ `= ${b.mechanicalLabels}+${b.mechanicalCells} i ${b.mechanicalFiles} filer)`,
`R2 umaalte varianter / utenfor rekkevidde : ${t.unmapped.length} / ${t.unreachable.length}`,
`R3 redaksjonelt holdt for R14 : ${t.edLabel} etiketter + ${t.edCell} celler `
+ `(baseline ${b.editorialLabels} + ${b.editorialCells})`,
` persona i proesa/tabell : ${t.prose} (${b.personaProseBefore} foer R13b, `
+ `${b.personaProseAfter} etter)${done ? ' — R13b UTFOERT' : ' — R13b IKKE kjoert'}`,
];
}
/** Force each R13b clause to the other answer. */
function validateLabelsNet() {
let bad = 0;
const ok = (msg) => console.log(`ok ${msg}`);
const no = (msg) => { console.log(`x ${msg}`); bad++; };
// R1 known-positive — BOTH forms, because the genitive table variant is precisely what a
// bold-only net misses.
const victim = refFiles()[0];
const base = readFileSync(join(PLUGIN_ROOT, victim), 'utf8');
const inj = classifyPersonaSites(`${base}\n**For Cosmo:** x\n\n| A | Cosmos råd |\n`);
const pre = classifyPersonaSites(base);
if (inj.label.mechanical !== pre.label.mechanical + 1) no('R1 KJENT-POSITIV: injisert "**For Cosmo:**" ble IKKE sett');
else ok('R1 KJENT-POSITIV: injisert "**For Cosmo:**" felte R1');
if (inj.table.mechanical !== pre.table.mechanical + 1) no('R1 KJENT-POSITIV: injisert "| Cosmos råd |" (genitiv) ble IKKE sett');
else ok('R1 KJENT-POSITIV: injisert "| Cosmos råd |" (genitiv) felte R1');
// R1 known-negative — the second case is the dangerous one: it LOOKS like a persona label.
const prod = classifyPersonaSites('**Cosmos DB-anbefaling:** bruk autoscale\n| Azure Cosmos DB | 8000 NOK |\n');
if (prod.label.mechanical + prod.table.mechanical !== 0 || prod.unmapped.length) {
no(`R1 KJENT-NEGATIV: produkt feilklassifisert (${JSON.stringify(prod)})`);
} else ok('R1 KJENT-NEGATIV: "**Cosmos DB-anbefaling:**" og "| Azure Cosmos DB |" passerer urort');
// R2 known-positive — an in-class site the tables do not know must be REPORTED.
const hole = classifyPersonaSites('**Cosmo-hjørnet:** noe tekst\n');
if (hole.unmapped.length !== 1) no('R2 KJENT-POSITIV: ukjent etikettform ble IKKE rapportert');
else ok('R2 KJENT-POSITIV: ukjent etikettform "**Cosmo-hjørnet:**" rapportert som umaalt');
let threw = false;
try { neutralizeLabels('**Cosmo-hjørnet:** noe tekst\n'); } catch { threw = true; }
if (!threw) no('R2 KJENT-POSITIV: transformen hoppet STILLE over en ukjent form'); else ok('R2 KJENT-POSITIV: transformen KASTER paa ukjent form, hopper ikke stille over');
// R2 known-negative — the editorial classes must be reported OUT of scope and left alone.
const ed = '**Cosmo svarer:**\n| A | B | Cosmo-syntese av verified sources |\n';
const edC = classifyPersonaSites(ed);
if (edC.label.editorial !== 1 || edC.table.editorial !== 1) no('R2 KJENT-NEGATIV: redaksjonell form ikke bokfoert som utenfor scope');
else if (neutralizeLabels(ed).content !== ed) no('R2 KJENT-NEGATIV: redaksjonell form ble ENDRET');
else ok('R2 KJENT-NEGATIV: dialog + proveniens bokfoert utenfor scope OG byte-identiske');
// R3 known-positive — break one product occurrence and the product clause must fell.
const broken = classifyCosmo('| Azure Cosmo DB | 8000 |\n');
const intact = classifyCosmo('| Azure Cosmos DB | 8000 |\n');
if (!(intact.product === 1 && broken.product === 0)) no(`R3 KJENT-POSITIV: fjernet tegn i "Cosmos DB" felte IKKE produkt-differansen (${intact.product} -> ${broken.product})`);
else ok('R3 KJENT-POSITIV: fjernet tegn i "Cosmos DB" felte produkt-differansen (1 -> 0)');
// R3 known-negative — the product-only files must be byte-identical through the transform.
for (const f of productOnlyFiles(measure())) {
const src = readFileSync(join(PLUGIN_ROOT, f), 'utf8');
if (neutralizeLabels(src, { relPath: f }).content !== src) no(`R3 KJENT-NEGATIV: kun-produkt-fila ${f} ble endret`);
else ok(`R3 KJENT-NEGATIV: ${f} byte-identisk gjennom transformen`);
}
return bad;
}
const arg = process.argv[2]; const arg = process.argv[2];
if (arg === '--emit-baseline') emitBaseline(); if (arg === '--emit-baseline') emitBaseline();
else if (arg === '--emit-labels-baseline') emitLabelsBaseline();
else if (arg === '--validate-net') validateNet(); else if (arg === '--validate-net') validateNet();
else check(); else check();

View file

@ -0,0 +1,18 @@
{
"_comment": "Ratified R13b label/cell baseline (operator 2026-09-15, alt. A). Measured BEFORE the label transform. R1 drives mechanical -> 0; R3 pins editorial and product as immovable. The 85/47 split the dispatching order carried was re-measured to 62/70 — the classifier was wrong, not the count. Regenerate ONLY on a ratified scope change.",
"measured": "2026-09-15",
"refFiles": 389,
"mechanicalLabels": 46,
"mechanicalCells": 16,
"mechanicalTotal": 62,
"mechanicalFiles": 50,
"editorialLabels": 16,
"editorialCells": 10,
"editorialTotal": 70,
"personaProseBefore": 132,
"personaProseAfter": 70,
"productOccurrences": 451,
"mixedLines": [
"skills/ms-ai-advisor/references/prompt-engineering/reasoning-models-o1-o3-optimization.md:549"
]
}

View file

@ -322,3 +322,200 @@ export function neutralizeContent(content, { relPath = '' } = {}) {
return { content: out.join('\n'), changes }; return { content: out.join('\n'), changes };
} }
// ===========================================================================================
// R13b — the MECHANICAL persona sites OUTSIDE headings: bold labels and table cells.
//
// THE SPLIT IS A MEASUREMENT, NOT THE ORDER'S PREMISE. The dispatching order carried
// "MEKANISK 85 / REDAKSJONELT 47" — itself a correction of an earlier "87/45" — and asked for
// it to be re-measured before anything was built. Re-measured over the 389 reference files, it
// is wrong again, and wrong in the CLASSIFIER rather than the count:
//
// 1. FOURTEEN bold labels are DIALOGUE SPEAKER ATTRIBUTIONS. The dialogue bucket only ever
// matched the italic `*Cosmo:*`; the bold form introduces quoted speech —
// `**Naar kunden sier:** "..."` / `**Cosmo svarer:** "..."` — so deleting it leaves the
// quote with no speaker. That is editorial work, not a scripted rewrite.
// 2. TEN table cells are PROVENANCE CLAIMS in the source-quality column. Cell position splits
// the 26 table occurrences into three classes, not one: 12 row labels (column 0),
// 4 column headers, 10 provenance values such as `Moenstre er Cosmo-design` and
// `Raadgivende innhold basert paa Cosmo-persona`. Rewriting one asserts something new
// about where the content came from, and the last of them 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: `- **For arkitekten (Cosmo):** ...` in
// reasoning-models-o1-o3-optimization.md:549 is exactly the target class, but a
// line-start-anchored net cannot see it behind the `- ` marker and books it as prose.
//
// MEASURED: MEKANISK 62 (46 labels + 16 cells) / REDAKSJONELT 70; sum 132 unchanged.
// Operator-ratified 2026-09-15, alternative A: execute the 62, hold dialogue and provenance
// for R14. Both held-back classes depend on the never-answered #R14-persona-ramme (neutral
// frame / new named persona / none); the 62 do not, because R13 has ALREADY executed
// `For Cosmo` -> `For arkitekten` across 401 headings, so this only brings labels and table
// rows into line with a decision that has already run.
//
// KEYED ON EXACT TEXT, like HEADING_MAP, and for the same reason: every rewrite is then a
// table entry a human ratified, and an unmeasured variant THROWS instead of being guessed at.
// Exact keying is also what keeps the two table classes apart without any row-role heuristic —
// the header cell is `Cosmo-anbefaling`, the provenance cell is
// `Cosmo-anbefaling basert paa verified data`, and they simply are different keys.
/**
* The ratified bold-label rewrites: 16 variants, 46 occurrences. Keys are the text INSIDE the
* `**...**` span, colon included where the source has it inside the bold.
* No target introduces a word its source did not carry, except R13's sanctioned `arkitekten`.
*/
export const LABEL_MAP = {
// --- the bulk: the three forms that converge on R13's ratified heading vocabulary ---
'For Cosmo:': 'For arkitekten:',
'For Cosmo Skyberg:': 'For arkitekten:',
'For arkitekten (Cosmo):': 'For arkitekten:',
// --- compound nouns: the persona is the first element, so the head stands alone ---
'Cosmo-anbefaling:': 'Anbefaling:',
"Cosmo's anbefaling:": 'Anbefaling:',
'Cosmo Skybergs anbefaling:': 'Anbefaling:',
'Cosmo-svar:': 'Svar:',
'Cosmo-spørsmål:': 'Spørsmål:',
'Cosmo-checkpoint:': 'Checkpoint:',
"Cosmo's one-liner:": 'One-liner:',
"Cosmo's rule of thumb:": 'Rule of thumb:',
// --- parenthetical or prepositional: the name drops out and the label survives intact ---
'Owner (Cosmo):': 'Owner:',
'Note til Cosmo:': 'Note:',
'Oppsummering for Cosmo': 'Oppsummering',
'Oppsummering for Cosmo:': 'Oppsummering:',
// --- addressed TO the architect, so the role takes the name's place ---
'Til Cosmo:': 'Til arkitekten:',
};
/**
* Bold labels that carry persona but are OUT of R13b's scope: speaker attributions in front of
* quoted speech. Enumerated rather than pattern-matched, so that a variant in NEITHER table is
* a hole in the net and throws, instead of being silently skipped. 6 variants, 14 occurrences.
*/
export const EDITORIAL_LABELS = new Set([
'Cosmo:', // 5 — `**Cosmo:** "Jeg forstaar at dere er klare for produksjon..."`
'Cosmo svarer:', // 4 — paired with `**Naar kunden sier:**`
'Cosmo forklarer:', // 2 — same dialogue frame
'Cosmo spør:', // 1 — same dialogue frame
'Mitt svar (Cosmo):', // 1 — first person, inside a scripted exchange
'Cosmo Skyberg tipset:', // 1 — past-tense attribution of advice to a person
// The two the ORDER already booked as editorial, and the reason its predecessor undercounted
// provenance: both are whole sentences that merely happen to be bold, and the first also
// points at `/architect`, an R14 delivery surface. Listed here so the net has no hole.
'For spørsmål om NLU-konfigurasjon, kontakt Cosmo Skyberg via `/architect`.',
'Sist oppdatert av Cosmo Skyberg, Microsoft AI Solution Architect',
]);
/** The ratified table-cell rewrites: 4 variants, 16 occurrences (12 row labels, 4 headers). */
export const TABLE_CELL_MAP = {
'For arkitekten (Cosmo)': 'For arkitekten',
'**For arkitekten (Cosmo)**': '**For arkitekten**',
'Cosmo-anbefaling': 'Anbefaling',
'Cosmos råd': 'Råd',
};
/** Provenance cells held back for R14's editorial pass. 10 occurrences, measured. */
export const EDITORIAL_CELLS = new Set([
'Cosmo-syntese av verified sources',
'Cosmo-anbefaling basert på verified data',
'Cosmo-anbefalinger basert på best practices',
"Cosmo's domain expertise + maturity model",
"Cosmo's domain knowledge + norsk lovverk",
"Cosmo's expertise + best practices fra docs",
'Rådgivende innhold basert på Cosmo-persona',
'Mønstre er Cosmo-design, PowerShell-kode er Verified',
'Tabeller og anbefalinger basert på Cosmo-erfaring + Microsoft docs',
'GDPR/AI Act-mapping er Cosmo-tolkninger (ikke Microsoft-spesifikk)',
]);
/** A bold label at the start of a line, optionally behind a list marker. */
const LABEL_RE = /^(\s*(?:[-*+]\s+)?)\*\*([^*\n]+)\*\*/;
/** A markdown table row. */
const TABLE_RE = /^\s*\|/;
/**
* Where do this document's non-heading persona occurrences sit, and is each one in a ratified
* table? Counts only the SITES R13b acts on bold labels and table cells; prose, dialogue
* outside bold labels and provenance lines are R14's and are not counted here.
*
* `unmapped` is the fail-safe: a persona label or cell in neither the mechanical nor the
* editorial table is an unmeasured variant, and the driver refuses to run rather than guess.
*
* @param {string} content
* @returns {{label: {mechanical: number, editorial: number},
* table: {mechanical: number, editorial: number}, unmapped: string[]}}
*/
export function classifyPersonaSites(content) {
const label = { mechanical: 0, editorial: 0 };
const table = { mechanical: 0, editorial: 0 };
const unmapped = [];
for (const line of String(content ?? '').split('\n')) {
if (/^#{1,6}\s+/.test(line)) continue;
if (TABLE_RE.test(line)) {
const segs = line.split('|');
for (let i = 1; i < segs.length - 1; i++) {
const cell = segs[i].trim();
if (!hasPersona(cell)) continue;
if (TABLE_CELL_MAP[cell] !== undefined) table.mechanical++;
else if (EDITORIAL_CELLS.has(cell)) table.editorial++;
else unmapped.push(`cell: ${JSON.stringify(cell)}`);
}
continue;
}
const m = LABEL_RE.exec(line);
if (!m || !hasPersona(m[2])) continue;
if (LABEL_MAP[m[2]] !== undefined) label.mechanical++;
else if (EDITORIAL_LABELS.has(m[2])) label.editorial++;
else unmapped.push(`label: ${JSON.stringify(m[2])}`);
}
return { label, table, unmapped };
}
/**
* Neutralise the ratified bold labels and table cells. Pure: returns new content, writes
* nothing. Surgical only the label span or the single matching cell is rewritten, so the
* rest of the line, including any persona R14 still owns, stays byte-identical. Splitting and
* rejoining a row on `|` is lossless, so cell padding survives untouched.
*
* Throws never writes a guess on a persona label or cell in neither ratified table.
* Idempotent: a neutral target matches no key.
*
* @param {string} content
* @param {{relPath?: string}} [opts]
* @returns {{content: string, changes: Array<{kind: string, line: number, from: string, to: string}>}}
*/
export function neutralizeLabels(content, { relPath = '' } = {}) {
const where = (i) => `${relPath || '<ukjent fil>'}:${i + 1}`;
const changes = [];
const out = String(content ?? '').split('\n').map((line, i) => {
if (/^#{1,6}\s+/.test(line)) return line;
if (TABLE_RE.test(line)) {
const segs = line.split('|');
let touched = false;
for (let c = 1; c < segs.length - 1; c++) {
const cell = segs[c].trim();
if (!hasPersona(cell)) continue;
const to = TABLE_CELL_MAP[cell];
if (to === undefined) {
if (EDITORIAL_CELLS.has(cell)) continue;
throw new Error(`ukjent persona-tabellcelle i ${where(i)}: ${JSON.stringify(cell)}`);
}
segs[c] = segs[c].replace(cell, to);
changes.push({ kind: 'cell', line: i + 1, from: cell, to });
touched = true;
}
return touched ? segs.join('|') : line;
}
const m = LABEL_RE.exec(line);
if (!m || !hasPersona(m[2])) return line;
const to = LABEL_MAP[m[2]];
if (to === undefined) {
if (EDITORIAL_LABELS.has(m[2])) return line;
throw new Error(`ukjent persona-etikett i ${where(i)}: ${JSON.stringify(m[2])}`);
}
changes.push({ kind: 'label', line: i + 1, from: m[2], to });
return `${m[1]}**${to}**${line.slice(m[0].length)}`;
});
return { content: out.join('\n'), changes };
}

View file

@ -0,0 +1,126 @@
#!/usr/bin/env node
// neutralize-cosmo-labels.mjs — R13b: apply the ratified neutralisation to the MECHANICAL
// persona sites that sit OUTSIDE headings — bold labels and table cells — in one per-file
// operation, behind the same plan-then-write discipline as R13's heading driver.
//
// SCOPE (operator-ratified 2026-09-15, alternative A; measured, not inherited). IN: the 62
// mechanical occurrences — 46 bold labels in 16 ratified variants, 16 table cells in 4.
// OUT, and deliberately so: the 70 editorial ones, which R14 owns. The dispatching order
// carried "MEKANISK 85 / REDAKSJONELT 47" and asked for that premise to be re-measured before
// anything was built; re-measured, it is wrong in the CLASSIFIER, not the count — 14 bold
// labels are dialogue speaker attributions standing in front of quoted speech, 10 table cells
// are provenance claims in the source-quality column, and one list-item label hidden behind a
// `- ` marker leaks the other way INTO scope. Full derivation: lib/cosmo-persona.mjs.
//
// Also out, unchanged from R13: the 4 SKILL.md, the 23 commands, CLAUDE.md / README / NOTICE,
// and docs/ — all R14 — and R13's own headings and TOC, which are green and are not regraded.
//
// INVARIANTS asserted per file BEFORE any write (a throw aborts the whole run and writes
// nothing):
// - zero MECHANICAL label/cell sites remain
// - the editorial counts are untouched — if one moved, the scope has been breached
// - product occurrences unchanged (the 451 Azure Cosmos DB mentions)
// - persona on headings and in TOC entries still zero (R13 not regraded)
// - line count unchanged; no NEW dead fragment link
// - only lines the transform claims to have touched differ from the original
// Idempotent: a re-run matches no key and is a no-op, so an interrupted run is re-runnable.
//
// Usage: node scripts/kb-update/neutralize-cosmo-labels.mjs [--dry]
import { readFileSync, realpathSync, globSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { atomicWriteSync } from './lib/atomic-write.mjs';
import { classifyCosmo, classifyPersonaSites, findDeadAnchors, neutralizeLabels } from './lib/cosmo-persona.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
const PLUGIN_ROOT = join(__dirname, '..', '..');
/** Throws on the first violation, so the run aborts before a single byte is written. */
function assertInvariant(rel, before, after, changes) {
const sb = classifyPersonaSites(before);
const sa = classifyPersonaSites(after);
const left = sa.label.mechanical + sa.table.mechanical;
if (left !== 0) throw new Error(`${rel}: ${left} mekanisk(e) persona-site(r) staar igjen`);
if (sa.unmapped.length) throw new Error(`${rel}: ukjent persona-site: ${sa.unmapped.join(', ')}`);
if (sa.label.editorial !== sb.label.editorial || sa.table.editorial !== sb.table.editorial) {
throw new Error(`${rel}: REDAKSJONELT scope roert — etiketter `
+ `${sb.label.editorial} -> ${sa.label.editorial}, celler `
+ `${sb.table.editorial} -> ${sa.table.editorial}`);
}
const b = classifyCosmo(before);
const a = classifyCosmo(after);
if (a.product !== b.product) throw new Error(`${rel}: produkt endret ${b.product} -> ${a.product}`);
if (a.persona.heading !== 0 || a.persona.toc !== 0) {
throw new Error(`${rel}: R13 regradert — heading ${a.persona.heading}, toc ${a.persona.toc}`);
}
const lb = before.split('\n');
const la = after.split('\n');
if (lb.length !== la.length) throw new Error(`${rel}: linjeantall endret ${lb.length} -> ${la.length}`);
const deadBefore = new Set(findDeadAnchors(before).map((d) => d.anchor));
const newDead = findDeadAnchors(after).map((d) => d.anchor).filter((x) => !deadBefore.has(x));
if (newDead.length) throw new Error(`${rel}: nye doede ankre: ${newDead.join(', ')}`);
const touched = new Set(changes.map((c) => c.line));
for (let i = 0; i < lb.length; i++) {
if (lb[i] === la[i]) continue;
if (!touched.has(i + 1)) {
throw new Error(`${rel}:${i + 1}: linje endret uten aa staa i endringslista\n`
+ ` foer: ${lb[i]}\n etter: ${la[i]}`);
}
}
}
function main() {
const dry = process.argv.includes('--dry');
const files = globSync('skills/*/references/**/*.md', { cwd: PLUGIN_ROOT }).sort();
const planned = [];
let labelChanges = 0;
let cellChanges = 0;
let heldBack = 0;
for (const rel of files) {
const before = readFileSync(join(PLUGIN_ROOT, rel), 'utf8');
heldBack += (() => { const s = classifyPersonaSites(before); return s.label.editorial + s.table.editorial; })();
const { content: after, changes } = neutralizeLabels(before, { relPath: rel });
if (after === before) continue;
assertInvariant(rel, before, after, changes);
labelChanges += changes.filter((c) => c.kind === 'label').length;
cellChanges += changes.filter((c) => c.kind === 'cell').length;
planned.push({ rel, out: after, changes });
}
console.log(`ref-filer: ${files.length} | aa endre: ${planned.length} | urørt: ${files.length - planned.length}`);
console.log(`etikett-endringer: ${labelChanges} | tabellcelle-endringer: ${cellChanges} `
+ `| SUM ${labelChanges + cellChanges}`);
console.log(`redaksjonelle sites holdt tilbake til R14: ${heldBack}`);
if (dry) {
const byTarget = new Map();
for (const p of planned) {
for (const c of p.changes) {
const k = `${c.kind.padEnd(5)} ${c.from} -> ${c.to}`;
byTarget.set(k, (byTarget.get(k) || 0) + 1);
}
}
console.log('\n(dry run — ingen skriving)\n--- mapping som vil brukes ---');
for (const [k, v] of [...byTarget.entries()].sort((x, y) => y[1] - x[1])) {
console.log(`${String(v).padStart(4)} ${k}`);
}
return;
}
for (const p of planned) atomicWriteSync(join(PLUGIN_ROOT, p.rel), p.out);
console.log(`\nSkrev ${planned.length} filer.`);
}
const isMain = (() => {
try {
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
} catch {
return false;
}
})();
if (isMain) main();

View file

@ -445,4 +445,4 @@ Disse mønstrene kan kombineres med ethvert nivå og er ikke del av den lineære
--- ---
**For Cosmo:** Bruk denne modellen som standard rammeverk når kunder spør om "RAG-strategi" eller "hva er neste steg for vår RAG-løsning". Start alltid med å plassere kunden på riktig nivå gjennom spørsmålene over, deretter anbefal neste 1-2 nivåer. Aldri anbefal nivå 9-11 som første steg. **For arkitekten:** Bruk denne modellen som standard rammeverk når kunder spør om "RAG-strategi" eller "hva er neste steg for vår RAG-løsning". Start alltid med å plassere kunden på riktig nivå gjennom spørsmålene over, deretter anbefal neste 1-2 nivåer. Aldri anbefal nivå 9-11 som første steg.

View file

@ -493,4 +493,4 @@ Get-MgBetaReportMicrosoft365CopilotUserCountSummary `
--- ---
**For Cosmo:** Når kunde spør om "hvordan måle Copilot-bruk", start med deres primary goal (adoption vs. ROI vs. compliance). De fleste trenger IKKE Viva Insights Advanced Copilot Dashboard + Admin center dekker 80% av use cases. Sentiment surveys er gull for early-stage adoption. Aldri lov ROI-estimatet alene kombiner med qualitative feedback. Offentlig sektor: vær krystallklar på at Purview audit logs kan inneholde sensitive prompts → access controls er kritisk. **For arkitekten:** Når kunde spør om "hvordan måle Copilot-bruk", start med deres primary goal (adoption vs. ROI vs. compliance). De fleste trenger IKKE Viva Insights Advanced Copilot Dashboard + Admin center dekker 80% av use cases. Sentiment surveys er gull for early-stage adoption. Aldri lov ROI-estimatet alene kombiner med qualitative feedback. Offentlig sektor: vær krystallklar på at Purview audit logs kan inneholde sensitive prompts → access controls er kritisk.

View file

@ -655,4 +655,4 @@ Agent kan detektere brukerens språk i hver melding og bytte språk dynamisk gje
Localization og globalization i Copilot-plattformen handler om å velge riktig balanse mellom enkelhet, kostnad og brukeropplevelse. **Browser-based localization er utgangspunktet** for de fleste scenarioer, mens **dynamic language switching** er en kraftig, men kostbar, løsning for spesialiserte behov. Med generative orchestration får du automatisk oversettelse av generativt innhold, noe som drastisk reduserer vedlikeholdsbyrden. For norsk offentlig sektor: Start med bokmål og engelsk, valider, og skaler deretter basert på faktisk behov. Localization og globalization i Copilot-plattformen handler om å velge riktig balanse mellom enkelhet, kostnad og brukeropplevelse. **Browser-based localization er utgangspunktet** for de fleste scenarioer, mens **dynamic language switching** er en kraftig, men kostbar, løsning for spesialiserte behov. Med generative orchestration får du automatisk oversettelse av generativt innhold, noe som drastisk reduserer vedlikeholdsbyrden. For norsk offentlig sektor: Start med bokmål og engelsk, valider, og skaler deretter basert på faktisk behov.
**Cosmo Skybergs anbefaling:** Gjør det enkelt først, skaler smart, og dokumenter valgene i en ADR. **Anbefaling:** Gjør det enkelt først, skaler smart, og dokumenter valgene i en ADR.

View file

@ -436,7 +436,7 @@ Følgende informasjon er basert på modellens treningsdata (januar 2025) og bekr
--- ---
**For Cosmo:** **For arkitekten:**
Når du rådgir om topics og entities, vurder: Når du rådgir om topics og entities, vurder:
1. **Topic granularity:** Hvor mange topics trenger løsningen? (Tommelfingerregel: 1 topic per høynivå-brukerforspørsel) 1. **Topic granularity:** Hvor mange topics trenger løsningen? (Tommelfingerregel: 1 topic per høynivå-brukerforspørsel)

View file

@ -348,7 +348,7 @@ SharePoint Copilot Agents respekterer **eksisterende SharePoint-permissions og s
| Integrasjon med Microsoft-stakken | **Verified** | Microsoft Learn (MCP) | | Integrasjon med Microsoft-stakken | **Verified** | Microsoft Learn (MCP) |
| Offentlig sektor (Norge) | **Baseline** (legal compliance basert på Microsoft docs + norsk lov) | Microsoft Learn + juridisk tolkning | | Offentlig sektor (Norge) | **Baseline** (legal compliance basert på Microsoft docs + norsk lov) | Microsoft Learn + juridisk tolkning |
| Kostnad og lisensiering | **Verified** | Microsoft Learn (MCP) + Azure pricing | | Kostnad og lisensiering | **Verified** | Microsoft Learn (MCP) + Azure pricing |
| For arkitekten (Cosmo) | **Baseline** (praktiske spørsmål og fallgruver) | Praktisk erfaring | | For arkitekten | **Baseline** (praktiske spørsmål og fallgruver) | Praktisk erfaring |
--- ---

View file

@ -464,7 +464,7 @@ Chat-historikk i Teams/Copilot er underlagt arkiveringskrav for offentlig sektor
| Integrasjon med Microsoft-stakken | Verified | MCP microsoft_docs_search | | Integrasjon med Microsoft-stakken | Verified | MCP microsoft_docs_search |
| Offentlig sektor (Norge) | Baseline | Modellkunnskap (juridisk kontekst) | | Offentlig sektor (Norge) | Baseline | Modellkunnskap (juridisk kontekst) |
| Kostnad og lisensiering | Baseline | Offentlige prislister + erfaring | | Kostnad og lisensiering | Baseline | Offentlige prislister + erfaring |
| For arkitekten (Cosmo) | Baseline | Best practices fra feltet | | For arkitekten | Baseline | Best practices fra feltet |
**Unike kilder:** 6 Microsoft Learn-artikler **Unike kilder:** 6 Microsoft Learn-artikler
**Dato verifisert:** 2026-02-04 **Dato verifisert:** 2026-02-04

View file

@ -586,7 +586,7 @@ Basert på testing (Azure OpenAI dokumentasjon):
--- ---
**Cosmo's anbefaling:** **Anbefaling:**
*Start med Azure OpenAI On Your Data + RAG for domener med dokumentasjon. Bruk GPT-4 med high strictness (4-5) og inScope=true for compliance-kritiske domener. Test grundig med representative queries før produksjon. For offentlig sektor: alltid inkluder disclaimers, logging og PII-filtering.* *Start med Azure OpenAI On Your Data + RAG for domener med dokumentasjon. Bruk GPT-4 med high strictness (4-5) og inScope=true for compliance-kritiske domener. Test grundig med representative queries før produksjon. For offentlig sektor: alltid inkluder disclaimers, logging og PII-filtering.*
--- ---

View file

@ -707,7 +707,7 @@ User Request
--- ---
**For Cosmo Skyberg:** **For arkitekten:**
Bruk denne referansen når kunden spør om: Bruk denne referansen når kunden spør om:
- "Hvordan håndterer vi feil fra Azure OpenAI?" - "Hvordan håndterer vi feil fra Azure OpenAI?"
- "Hva gjør vi hvis vi får 429 rate limit errors?" - "Hva gjør vi hvis vi får 429 rate limit errors?"

View file

@ -462,6 +462,6 @@ Function calling påvirker kostnaden på flere måter:
| Integrasjon med Microsoft-stakken | **Verified** | Microsoft Learn cross-references | | Integrasjon med Microsoft-stakken | **Verified** | Microsoft Learn cross-references |
| Offentlig sektor (Norge) | **Baseline** | Standard compliance-krav (generelt, ikke Azure-spesifikt) | | Offentlig sektor (Norge) | **Baseline** | Standard compliance-krav (generelt, ikke Azure-spesifikt) |
| Kostnad og lisensiering | **Verified** | Azure pricing + Microsoft Learn token counting | | Kostnad og lisensiering | **Verified** | Azure pricing + Microsoft Learn token counting |
| For arkitekten (Cosmo) | **Baseline** | Syntese av dokumentasjon + praktisk erfaring | | For arkitekten | **Baseline** | Syntese av dokumentasjon + praktisk erfaring |
**Siste verifikasjon:** 2026-02-04 via MCP microsoft-learn server **Siste verifikasjon:** 2026-02-04 via MCP microsoft-learn server

View file

@ -546,6 +546,6 @@ Denne kunnskapsreferansen er basert på offisiell Microsoft Learn-dokumentasjon
- **Integrasjon med Microsoft-stakken:** Verified (MCP + baseline) - **Integrasjon med Microsoft-stakken:** Verified (MCP + baseline)
- **Offentlig sektor (Norge):** Baseline (legal/compliance-kunnskap + Azure docs) - **Offentlig sektor (Norge):** Baseline (legal/compliance-kunnskap + Azure docs)
- **Kostnad og lisensiering:** Baseline (pricing estimert, lisensiering verifisert via MCP) - **Kostnad og lisensiering:** Baseline (pricing estimert, lisensiering verifisert via MCP)
- **For arkitekten (Cosmo):** Baseline (erfaring + Cosmo-persona) - **For arkitekten:** Baseline (erfaring + Cosmo-persona)
**Merk:** Priser er omtrentlige og basert på global Azure-prising. Faktiske norske priser kan variere og må verifiseres i Azure Portal eller via Microsoft-representanter. **Merk:** Priser er omtrentlige og basert på global Azure-prising. Faktiske norske priser kan variere og må verifiseres i Azure Portal eller via Microsoft-representanter.

View file

@ -581,7 +581,7 @@ Alle kilder verifisert: **2026-02-04**
--- ---
**For Cosmo:** **For arkitekten:**
Dette dokumentet gir deg komplett oversikt over generation parameters. Bruk tabellene og decision trees aktivt i arkitekturrådgivning. Husk at **temperature og top_p ikke skal brukes samtidig** — dette er den vanligste feilen jeg ser i kundeimplementasjoner. Dette dokumentet gir deg komplett oversikt over generation parameters. Bruk tabellene og decision trees aktivt i arkitekturrådgivning. Husk at **temperature og top_p ikke skal brukes samtidig** — dette er den vanligste feilen jeg ser i kundeimplementasjoner.
Når du rådgir om offentlig sektor, vekt konsistens og reproducibility (men vær ærlig om at seed ikke garanterer 100% determinisme). Kombiner alltid parameter-tuning med solid prompt engineering — parametere alene løser ikke dårlige prompts. Når du rådgir om offentlig sektor, vekt konsistens og reproducibility (men vær ærlig om at seed ikke garanterer 100% determinisme). Kombiner alltid parameter-tuning med solid prompt engineering — parametere alene løser ikke dårlige prompts.

View file

@ -555,7 +555,7 @@ results = mlflow.genai.evaluate(
| Integrasjon med Microsoft-stakken | Verified | Fra Foundry, Semantic Kernel, Prompt Flow, Application Insights docs | | Integrasjon med Microsoft-stakken | Verified | Fra Foundry, Semantic Kernel, Prompt Flow, Application Insights docs |
| Offentlig sektor (Norge) | Baseline | GDPR/AI Act vurdering basert på modellkunnskap + Azure residency facts | | Offentlig sektor (Norge) | Baseline | GDPR/AI Act vurdering basert på modellkunnskap + Azure residency facts |
| Kostnad og lisensiering | Baseline | Prisestimater basert på Azure OpenAI pricing (feb 2026) + observability costs | | Kostnad og lisensiering | Baseline | Prisestimater basert på Azure OpenAI pricing (feb 2026) + observability costs |
| For arkitekten (Cosmo) | Baseline | Synthesized fra verified sources + praktisk erfaring | | For arkitekten | Baseline | Synthesized fra verified sources + praktisk erfaring |
--- ---

View file

@ -481,4 +481,4 @@ def update_citizen_record(ssn: str, field: str, value: str) -> str:
--- ---
**For Cosmo:** Dette dokumentet dekker både grunnleggende og avanserte mønstre for function calling. Bruk det til å velge riktig tilnærming basert på klientens behov (antall funksjoner, kompleksitet, compliance-krav). Husk alltid: **Start enkelt (Basic), skalér til Auto-invocation, og bygg modulært med Agent-as-Tool når kompleksiteten vokser.** **For arkitekten:** Dette dokumentet dekker både grunnleggende og avanserte mønstre for function calling. Bruk det til å velge riktig tilnærming basert på klientens behov (antall funksjoner, kompleksitet, compliance-krav). Husk alltid: **Start enkelt (Basic), skalér til Auto-invocation, og bygg modulært med Agent-as-Tool når kompleksiteten vokser.**

View file

@ -367,6 +367,6 @@ Kombiner OCR med LLM for intelligent dokumentforståelse:
| Integrasjon med Microsoft-stakken | Verified | MCP microsoft_docs_search (AI Search skill, Synapse tutorial, code samples) | | Integrasjon med Microsoft-stakken | Verified | MCP microsoft_docs_search (AI Search skill, Synapse tutorial, code samples) |
| Offentlig sektor (Norge) | Baseline | Modellkunnskap + GDPR/WCAG-standarder | | Offentlig sektor (Norge) | Baseline | Modellkunnskap + GDPR/WCAG-standarder |
| Kostnad og lisensiering | Baseline | Modellkunnskap (priser endres hyppig, sjekk Azure Pricing Calculator) | | Kostnad og lisensiering | Baseline | Modellkunnskap (priser endres hyppig, sjekk Azure Pricing Calculator) |
| For arkitekten (Cosmo) | Baseline | Arkitekturveiledning basert på Microsoft Learn best practices | | For arkitekten | Baseline | Arkitekturveiledning basert på Microsoft Learn best practices |
**Merknad:** Alle tekniske detaljer om API-er, kapabiliteter, input-krav, språkstøtte, og JSON-responser er verifisert mot Microsoft Learn-dokumentasjon via MCP-research (februar 2026). Prisopplysninger er estimater og bør verifiseres mot Azure Pricing Calculator. Offentlig sektor-spesifikke anbefalinger er basert på norsk regulatorisk kontekst (GDPR, Offentlighetsloven, WCAG 2.1). **Merknad:** Alle tekniske detaljer om API-er, kapabiliteter, input-krav, språkstøtte, og JSON-responser er verifisert mot Microsoft Learn-dokumentasjon via MCP-research (februar 2026). Prisopplysninger er estimater og bør verifiseres mot Azure Pricing Calculator. Offentlig sektor-spesifikke anbefalinger er basert på norsk regulatorisk kontekst (GDPR, Offentlighetsloven, WCAG 2.1).

View file

@ -560,10 +560,10 @@ Custom model ekstraherer:
| Integrasjon med Microsoft-stakken | **Verified** | Power Automate/Logic Apps connector docs | | Integrasjon med Microsoft-stakken | **Verified** | Power Automate/Logic Apps connector docs |
| Offentlig sektor (Norge) | **Baseline** | Modellkunnskap om norske standarder (EHF, NOARK5) + Azure compliance docs | | Offentlig sektor (Norge) | **Baseline** | Modellkunnskap om norske standarder (EHF, NOARK5) + Azure compliance docs |
| Kostnad og lisensiering | **Verified** | Azure Pricing Calculator + Document Intelligence pricing page | | Kostnad og lisensiering | **Verified** | Azure Pricing Calculator + Document Intelligence pricing page |
| For arkitekten (Cosmo) | **Baseline** | Modellkunnskap + arkitekturerfaring | | For arkitekten | **Baseline** | Modellkunnskap + arkitekturerfaring |
**Unike kilder:** 7 Microsoft Learn-URLer **Unike kilder:** 7 Microsoft Learn-URLer
--- ---
**Til Cosmo:** Når en kunde spør om "faktura-automatisering" eller "kvitterings-scanning", start med å verifisere at prebuilt-modellene dekker deres feltbehov (bruk schema-lenker over). Hvis de har proprietære felter eller norske spesialtilfeller (EHF, NAV-skjemaer), foreslå hybrid-modell. Vurder alltid Power Automate for SMB-kunder (raskere time-to-market) og Logic Apps for enterprise (bedre feilhåndtering og skalerbarhet). Ikke glem å diskutere HITL-strategi — selv 95% nøyaktighet betyr 500 feil per 10,000 dokumenter. **Til arkitekten:** Når en kunde spør om "faktura-automatisering" eller "kvitterings-scanning", start med å verifisere at prebuilt-modellene dekker deres feltbehov (bruk schema-lenker over). Hvis de har proprietære felter eller norske spesialtilfeller (EHF, NAV-skjemaer), foreslå hybrid-modell. Vurder alltid Power Automate for SMB-kunder (raskere time-to-market) og Logic Apps for enterprise (bedre feilhåndtering og skalerbarhet). Ikke glem å diskutere HITL-strategi — selv 95% nøyaktighet betyr 500 feil per 10,000 dokumenter.

View file

@ -421,7 +421,7 @@ Language Services bruker **pay-per-call** modell (per text record).
| Integrasjon med MS-stakken | ✅ Documented | Power BI tutorial + SynapseML docs + Foundry quickstarts | | Integrasjon med MS-stakken | ✅ Documented | Power BI tutorial + SynapseML docs + Foundry quickstarts |
| Offentlig sektor (Norge) | ⚠️ Baseline | GDPR-analyse (modellkunnskap) + Azure datacenter geografi | | Offentlig sektor (Norge) | ⚠️ Baseline | GDPR-analyse (modellkunnskap) + Azure datacenter geografi |
| Kostnad og lisensiering | ⚠️ Baseline | Generell Azure pricing structure (ikke eksakte NOK-priser hentet) | | Kostnad og lisensiering | ⚠️ Baseline | Generell Azure pricing structure (ikke eksakte NOK-priser hentet) |
| For arkitekten (Cosmo) | ⚠️ Baseline | Arkitekturerfaringer (modellkunnskap), ikke dokumentert av Microsoft | | For arkitekten | ⚠️ Baseline | Arkitekturerfaringer (modellkunnskap), ikke dokumentert av Microsoft |
**Notater:** **Notater:**
- Prisestimater er basert på generell Azure-prisstruktur — alltid sjekk [Azure Pricing Calculator](https://azure.microsoft.com/en-us/pricing/calculator/) for eksakte priser. - Prisestimater er basert på generell Azure-prisstruktur — alltid sjekk [Azure Pricing Calculator](https://azure.microsoft.com/en-us/pricing/calculator/) for eksakte priser.

View file

@ -478,7 +478,7 @@ Scenario: Daglig batch inference (1M records, 50 GB data, 30 dager)
### Arkitektur-tradeoffs ### Arkitektur-tradeoffs
| Beslutning | Alternativ A | Alternativ B | Cosmos råd | | Beslutning | Alternativ A | Alternativ B | Råd |
|------------|-------------|-------------|-----------| |------------|-------------|-------------|-----------|
| **Fabric vs. ADF** | Fabric (capacity, OneLake-integrasjon) | ADF (consumption, Azure ML-integrasjon) | Velg Fabric hvis Power BI Premium allerede er i bruk (delt capacity). Velg ADF hvis hybride on-prem-kilder er dominerende. | | **Fabric vs. ADF** | Fabric (capacity, OneLake-integrasjon) | ADF (consumption, Azure ML-integrasjon) | Velg Fabric hvis Power BI Premium allerede er i bruk (delt capacity). Velg ADF hvis hybride on-prem-kilder er dominerende. |
| **Pipeline vs. Airflow** | Pipeline (low-code UI) | Airflow (Python DAGs) | Start med Pipeline. Migrer til Airflow hvis >5 data engineers trenger git-basert versjonskontroll og Python-flexibility. | | **Pipeline vs. Airflow** | Pipeline (low-code UI) | Airflow (Python DAGs) | Start med Pipeline. Migrer til Airflow hvis >5 data engineers trenger git-basert versjonskontroll og Python-flexibility. |
@ -746,7 +746,7 @@ Invoke-FabricPipelineUpgrade `
--- ---
**For Cosmo:** **For arkitekten:**
Denne referansen dekker orkestreringsaspektet av AI-pipelines. For dypdykk i: Denne referansen dekker orkestreringsaspektet av AI-pipelines. For dypdykk i:
- **Feature engineering:** Se `feature-store-architecture.md` og `dataflow-gen2-transformations.md` - **Feature engineering:** Se `feature-store-architecture.md` og `dataflow-gen2-transformations.md`
- **Model lifecycle:** Se `azure-ml-pipelines.md` og `mlops-ci-cd.md` - **Model lifecycle:** Se `azure-ml-pipelines.md` og `mlops-ci-cd.md`

View file

@ -367,4 +367,4 @@ Bruk **Fabric Capacity Metrics App** for:
--- ---
**For Cosmo:** Denne referansen er klar for å brukes i arkitekturrådgivning. Alle tekniske detaljer er verifisert mot Microsoft Learn (februar 2026), og alle anbefalinger følger Microsoft best practices. Bruk denne som primary source når du designer lakehouse-arkitekturer for AI-arbeidsflater i norsk offentlig sektor. **For arkitekten:** Denne referansen er klar for å brukes i arkitekturrådgivning. Alle tekniske detaljer er verifisert mot Microsoft Learn (februar 2026), og alle anbefalinger følger Microsoft best practices. Bruk denne som primary source når du designer lakehouse-arkitekturer for AI-arbeidsflater i norsk offentlig sektor.

View file

@ -730,6 +730,6 @@ CI/CD pipelines for ML krever compute for training og deployment:
| Integrasjon med Microsoft-stakken | Verified | MCP: Multiple Azure ML integration docs | | Integrasjon med Microsoft-stakken | Verified | MCP: Multiple Azure ML integration docs |
| Offentlig sektor (Norge) | Baseline | Modellkunnskap (NSM, GDPR, AI Act) | | Offentlig sektor (Norge) | Baseline | Modellkunnskap (NSM, GDPR, AI Act) |
| Kostnad og lisensiering | Baseline | Modellkunnskap (2026 prising estimert) | | Kostnad og lisensiering | Baseline | Modellkunnskap (2026 prising estimert) |
| For arkitekten (Cosmo) | Baseline | Beste praksiser + MLOps maturity model | | For arkitekten | Baseline | Beste praksiser + MLOps maturity model |
**Overall Konfidens:** 85% (majoriteten av innhold er verifisert via Microsoft Learn MCP-kilde, offentlig sektor og prising er basert på modellkunnskap og er merket som "Baseline") **Overall Konfidens:** 85% (majoriteten av innhold er verifisert via Microsoft Learn MCP-kilde, offentlig sektor og prising er basert på modellkunnskap og er merket som "Baseline")

View file

@ -379,7 +379,7 @@ MLflow Tracing provides end-to-end observability for GenAI applications:
--- ---
**For Cosmo Skyberg:** **For arkitekten:**
Denne kunnskapsfilen dekker det **operasjonelle rammeverket** for GenAI-løsninger — hvordan du går fra prototype til production med repeatable processes. Fokus er på **Microsoft-spesifikke verktøy** (Microsoft Foundry, Prompt Flow, MLflow, Databricks Mosaic AI), men prinsippene er portable til andre platforms. Denne kunnskapsfilen dekker det **operasjonelle rammeverket** for GenAI-løsninger — hvordan du går fra prototype til production med repeatable processes. Fokus er på **Microsoft-spesifikke verktøy** (Microsoft Foundry, Prompt Flow, MLflow, Databricks Mosaic AI), men prinsippene er portable til andre platforms.

View file

@ -580,5 +580,5 @@ az ml model list --registry-name my-registry --query "[?created<'$cutoff_date'].
--- ---
**Oppsummering for Cosmo:** **Oppsummering:**
Model versioning og registry management er fundamentet for skalerbar MLOps. Azure ML Registry + MLflow gir et kraftig, standardbasert økosystem som støtter alt fra single-team experimentation til enterprise-scale governance. For offentlig sektor i Norge er lineage tracking og audit trail-capabilities kritiske for å oppfylle AI Act og GDPR. Start med workspace registry for eksperimentering, migrer til Azure ML Registry når modeller skal deles på tvers av teams eller deployes til separate production-miljøer. Automatiser model promotion via CI/CD for å redusere feil og øke hastighet. Model versioning og registry management er fundamentet for skalerbar MLOps. Azure ML Registry + MLflow gir et kraftig, standardbasert økosystem som støtter alt fra single-team experimentation til enterprise-scale governance. For offentlig sektor i Norge er lineage tracking og audit trail-capabilities kritiske for å oppfylle AI Act og GDPR. Start med workspace registry for eksperimentering, migrer til Azure ML Registry når modeller skal deles på tvers av teams eller deployes til separate production-miljøer. Automatiser model promotion via CI/CD for å redusere feil og øke hastighet.

View file

@ -623,7 +623,7 @@ jobs:
### Når skal RAI integreres i MLOps? ### Når skal RAI integreres i MLOps?
**Cosmo's rule of thumb:** **Rule of thumb:**
> "Hvis modellen tar beslutninger som kan påvirke enkeltpersoners liv, økonomi eller rettigheter — integrer RAI fra dag 1. Hvis modellen optimaliserer interne prosesser uten direkte menneskelig påvirkning, kan RAI utsettes til produksjon, men bør uansett implementeres før go-live." > "Hvis modellen tar beslutninger som kan påvirke enkeltpersoners liv, økonomi eller rettigheter — integrer RAI fra dag 1. Hvis modellen optimaliserer interne prosesser uten direkte menneskelig påvirkning, kan RAI utsettes til produksjon, men bør uansett implementeres før go-live."
@ -735,7 +735,7 @@ jobs:
--- ---
**For Cosmo Skyberg:** **For arkitekten:**
Dette dokumentet gir deg en komplett arkitekturoversikt over RAI-integrasjon i MLOps. Nøkkelpunktene for deg som arkitekt er: Dette dokumentet gir deg en komplett arkitekturoversikt over RAI-integrasjon i MLOps. Nøkkelpunktene for deg som arkitekt er:

View file

@ -481,7 +481,7 @@ Azure AI Search prises per **search unit** (SU = 1 partition × 1 replica).
--- ---
**For Cosmo:** Denne referansen brukes når kunden snakker om "RAG-implementasjon", "søkeløsning", "Azure AI Search setup", eller spør om SKU-valg. Kombiner med **RAG Core Patterns** for arkitekturveiledning og **Hybrid Search - Full-Text and Vector Combined** for query-optimalisering. **For arkitekten:** Denne referansen brukes når kunden snakker om "RAG-implementasjon", "søkeløsning", "Azure AI Search setup", eller spør om SKU-valg. Kombiner med **RAG Core Patterns** for arkitekturveiledning og **Hybrid Search - Full-Text and Vector Combined** for query-optimalisering.
### Hybrid Search (oppdatert 2026-06) ### Hybrid Search (oppdatert 2026-06)

View file

@ -520,4 +520,4 @@ For teams som ikke har nok treningsdata for fine-tuning, er en domene-tilpasset
--- ---
**For Cosmo:** Bruk denne referansen når kunden nevner "embedding-problemer", "dårlig retrieval-kvalitet på norsk", "for høye Azure AI Search-kostnader" eller "vi vurderer å bytte embedding-modell". Start alltid med å kartlegge språk, domene og volum før du anbefaler modell. Test ALLTID retrieval-kvalitet med kundens egne data før produksjonsutrulling. **For arkitekten:** Bruk denne referansen når kunden nevner "embedding-problemer", "dårlig retrieval-kvalitet på norsk", "for høye Azure AI Search-kostnader" eller "vi vurderer å bytte embedding-modell". Start alltid med å kartlegge språk, domene og volum før du anbefaler modell. Test ALLTID retrieval-kvalitet med kundens egne data før produksjonsutrulling.

View file

@ -316,4 +316,4 @@ GraphRAG introduserer spesifikke personvernrisiki i offentlig sektor:
--- ---
**For Cosmo Skyberg:** Dette dokumentet skal brukes for å vurdere om GraphRAG-patterns er hensiktsmessige for kundens use case. Prioriter alltid spørsmålet: "Trenger vi faktisk graph traversal, eller holder vector search?" — kompleksitet skal forsvares med klare fordeler. Ved tvil, start med hybrid approach (vector + metadata) før full graph commitment. **For arkitekten:** Dette dokumentet skal brukes for å vurdere om GraphRAG-patterns er hensiktsmessige for kundens use case. Prioriter alltid spørsmålet: "Trenger vi faktisk graph traversal, eller holder vector search?" — kompleksitet skal forsvares med klare fordeler. Ved tvil, start med hybrid approach (vector + metadata) før full graph commitment.

View file

@ -535,7 +535,7 @@ $filter=personalDataCategories/any(c: c eq 'Helseopplysninger') and legalBasis n
--- ---
**For Cosmo:** **For arkitekten:**
Metadata management er ofte undervurdert i RAG-prosjekter. Kunder fokuserer på embeddings og vector search, men glemmer at 70% av queries i produksjon inneholder strukturerte filter-kriterier ("bare fra min avdeling", "kun siste år", "høyeste klassifikasjon"). Design metadata-schema tidlig, test med reelle cardinality-tall, og prioritér normalizers og security trimming fra dag 1. I offentlig sektor er compliance non-negotiable — bygg audit trail og retention policies inn fra start. Metadata management er ofte undervurdert i RAG-prosjekter. Kunder fokuserer på embeddings og vector search, men glemmer at 70% av queries i produksjon inneholder strukturerte filter-kriterier ("bare fra min avdeling", "kun siste år", "høyeste klassifikasjon"). Design metadata-schema tidlig, test med reelle cardinality-tall, og prioritér normalizers og security trimming fra dag 1. I offentlig sektor er compliance non-negotiable — bygg audit trail og retention policies inn fra start.

View file

@ -431,7 +431,7 @@ results = search_client.search(
--- ---
**For Cosmo:** Når kunde spør om RAG, start med "Naive vs Advanced vs Agentic"-beslutningstreet. Identifiser data source, query complexity, og latency-krav først. Hvis offentlig sektor: alltid spør om GDPR/Schrems II/AI Act compliance før du foreslår arkitektur. Hvis customer mangler evaluation strategy: stopp og definer retrieval recall/precision targets før du går videre med implementation. **For arkitekten:** Når kunde spør om RAG, start med "Naive vs Advanced vs Agentic"-beslutningstreet. Identifiser data source, query complexity, og latency-krav først. Hvis offentlig sektor: alltid spør om GDPR/Schrems II/AI Act compliance før du foreslår arkitektur. Hvis customer mangler evaluation strategy: stopp og definer retrieval recall/precision targets før du går videre med implementation.
### Hybrid Search — Kjernemønster (oppdatert 2026-04) ### Hybrid Search — Kjernemønster (oppdatert 2026-04)

View file

@ -534,7 +534,7 @@ Authorization: Bearer <user-token>
| Microsoft-stack integrasjon | **Verified** | MCP docs (5, 9) | | Microsoft-stack integrasjon | **Verified** | MCP docs (5, 9) |
| Offentlig sektor (Norge) | **Baseline** | Modellkunnskap (GDPR, AI Act, norsk lov) | | Offentlig sektor (Norge) | **Baseline** | Modellkunnskap (GDPR, AI Act, norsk lov) |
| Kostnad og lisensiering | **Baseline** | Estimater basert på Azure pricing (ikke direkte MCP-verified) | | Kostnad og lisensiering | **Baseline** | Estimater basert på Azure pricing (ikke direkte MCP-verified) |
| For arkitekten (Cosmo) | **Baseline** | Best practices + erfaring | | For arkitekten | **Baseline** | Best practices + erfaring |
--- ---

View file

@ -438,7 +438,7 @@ AppMetrics
### Fallgruver og mitigering ### Fallgruver og mitigering
| Fallgruve | Konsekvens | Cosmo-anbefaling | | Fallgruve | Konsekvens | Anbefaling |
|-----------|------------|------------------| |-----------|------------|------------------|
| **Dashboard-fatigue** | Governance team overveldes av alerts → ignorerer critical issues | Start med TOP 5 critical metrics, utvid gradvis. Bruk severity-based routing (HIGH → immediate alert, MEDIUM → daily digest) | | **Dashboard-fatigue** | Governance team overveldes av alerts → ignorerer critical issues | Start med TOP 5 critical metrics, utvid gradvis. Bruk severity-based routing (HIGH → immediate alert, MEDIUM → daily digest) |
| **Policy without enforcement** | Policies blir "guidelines" ikke "guardrails" → non-compliance fortsetter | Bruk Deny-mode på kritiske policies. Audit-mode kun i pilot-fase. Set deadline for transition (3 mnd pilot → enforcement) | | **Policy without enforcement** | Policies blir "guidelines" ikke "guardrails" → non-compliance fortsetter | Bruk Deny-mode på kritiske policies. Audit-mode kun i pilot-fase. Set deadline for transition (3 mnd pilot → enforcement) |

View file

@ -525,4 +525,4 @@ if ($metrics.Data.Total -eq 0) {
--- ---
**For Cosmo:** Bruk denne guiden for å designe kostnadsstrategien basert på kundens modenhetsnivå. Start alltid med Native Cost Analysis + tags, og bygg ut mot gateway-løsning kun hvis chargeback eller detaljert tracking er nødvendig. Husk at fine-tuning hosting costs er en vanlig kostnadsfelle som må adresseres tidlig i prosjektet. **For arkitekten:** Bruk denne guiden for å designe kostnadsstrategien basert på kundens modenhetsnivå. Start alltid med Native Cost Analysis + tags, og bygg ut mot gateway-løsning kun hvis chargeback eller detaljert tracking er nødvendig. Husk at fine-tuning hosting costs er en vanlig kostnadsfelle som må adresseres tidlig i prosjektet.

View file

@ -517,7 +517,7 @@ Sentralt for norsk offentlig sektor på Microsoft Cloud:
- ❌ Kompleksere compliance (må verifisere begge regioner) - ❌ Kompleksere compliance (må verifisere begge regioner)
- ❌ Risk for misconfiguration → data leakage - ❌ Risk for misconfiguration → data leakage
**Cosmo's anbefaling:** **Anbefaling:**
For norsk offentlig sektor med AI workloads: **Start single-region (Norway East), evaluer multi-region når availability SLA krev det**. Implementer Azure Site Recovery for disaster recovery til Norway West (som også er innenfor EU Data Boundary). For norsk offentlig sektor med AI workloads: **Start single-region (Norway East), evaluer multi-region når availability SLA krev det**. Implementer Azure Site Recovery for disaster recovery til Norway West (som også er innenfor EU Data Boundary).
## Kilder og verifisering ## Kilder og verifisering

View file

@ -413,4 +413,4 @@ Model monitoring er inkludert i Azure Machine Learning workspace, men du betaler
| Integrasjon Microsoft-stakken | High | Verified (Event Grid integration, AI Foundry observability) | | Integrasjon Microsoft-stakken | High | Verified (Event Grid integration, AI Foundry observability) |
| Offentlig sektor (Norge) | Medium | Baseline (AI Act, GDPR) + Verified (Schrems II data residency) | | Offentlig sektor (Norge) | Medium | Baseline (AI Act, GDPR) + Verified (Schrems II data residency) |
| Kostnad og lisensiering | Medium | Baseline (Azure pricing estimates for Norway East, feb 2026) | | Kostnad og lisensiering | Medium | Baseline (Azure pricing estimates for Norway East, feb 2026) |
| For arkitekten (Cosmo) | High | Verified (best practices) + Baseline (enterprise architecture) | | For arkitekten | High | Verified (best practices) + Baseline (enterprise architecture) |

View file

@ -400,4 +400,4 @@ Hvis en kommune bruker en AI-chatbot for saksbehandling, må tilgjengelighetserk
--- ---
**For Cosmo Skyberg:** Dette dokumentet skal brukes når kunde nevner "tilgjengelighet", "universell utforming", "WCAG", "funksjonshemmede brukere", eller når løsningen er for norsk offentlig sektor (der UU er lovpålagt). Kombiner med `eu-ai-act.md` og `norwegian-public-sector-ai-governance.md` for helhetlig vurdering. **For arkitekten:** Dette dokumentet skal brukes når kunde nevner "tilgjengelighet", "universell utforming", "WCAG", "funksjonshemmede brukere", eller når løsningen er for norsk offentlig sektor (der UU er lovpålagt). Kombiner med `eu-ai-act.md` og `norwegian-public-sector-ai-governance.md` for helhetlig vurdering.

View file

@ -264,4 +264,4 @@ Når du veileder norsk offentlig sektor om AI og opphavsrett, vurder disse spør
--- ---
**Note til Cosmo:** Denne kunnskapsbasen reflekterer rettstilstanden per februar 2026, hvor AI Act-implementering i Norge er nært forestående (august 2026) og DSM-direktivet ennå ikke er implementert. Vær oppmerksom på at dette er et juridisk område under rask utvikling. Råd alltid offentlig sektor til å søke juridisk bistand for spesifikke spørsmål om opphavsrett og AI-treningsdata, spesielt i forbindelse med anskaffelser og egenutviklede løsninger. **Note:** Denne kunnskapsbasen reflekterer rettstilstanden per februar 2026, hvor AI Act-implementering i Norge er nært forestående (august 2026) og DSM-direktivet ennå ikke er implementert. Vær oppmerksom på at dette er et juridisk område under rask utvikling. Råd alltid offentlig sektor til å søke juridisk bistand for spesifikke spørsmål om opphavsrett og AI-treningsdata, spesielt i forbindelse med anskaffelser og egenutviklede løsninger.

View file

@ -60,7 +60,7 @@ Beskriv problemet tiltaket skal løse, og hvilke mål som skal nås.
**Eksempel:** **Eksempel:**
"Saksbehandlingstid i NAV for førstegangssøknader er 45 dager. Mål: Redusere til 20 dager ved AI-assistert dokumentklassifisering og informasjonsutvinning." "Saksbehandlingstid i NAV for førstegangssøknader er 45 dager. Mål: Redusere til 20 dager ved AI-assistert dokumentklassifisering og informasjonsutvinning."
**Cosmo-spørsmål:** **Spørsmål:**
- Er problemformuleringen spesifikk nok til å evaluere AI-løsninger? - Er problemformuleringen spesifikk nok til å evaluere AI-løsninger?
- Finnes baseline-data som kan måle effekt? - Finnes baseline-data som kan måle effekt?
@ -87,7 +87,7 @@ For hvert alternativ må du vurdere:
- **Implementeringstid** - **Implementeringstid**
- **Reversibilitet** (kan vi gå tilbake hvis det ikke fungerer?) - **Reversibilitet** (kan vi gå tilbake hvis det ikke fungerer?)
**Cosmo-anbefaling:** **Anbefaling:**
Start alltid med minst tre alternativer (null + to AI-løsninger). Vurder hybridløsninger der AI assisterer, men mennesker tar endelige beslutninger. Start alltid med minst tre alternativer (null + to AI-løsninger). Vurder hybridløsninger der AI assisterer, men mennesker tar endelige beslutninger.
--- ---
@ -158,7 +158,7 @@ Beskriv positive og negative virkninger, varighet og hvem som påvirkes.
**Varighet:** Er effektene midlertidige (pilotfase) eller permanente? Når inntrer gevinster? **Varighet:** Er effektene midlertidige (pilotfase) eller permanente? Når inntrer gevinster?
**Cosmo-spørsmål:** **Spørsmål:**
- Har dere vurdert ikke-intenderte konsekvenser (f.eks. brukere som tilpasser atferd for å "lure" AI)? - Har dere vurdert ikke-intenderte konsekvenser (f.eks. brukere som tilpasser atferd for å "lure" AI)?
- Hvordan måles faktisk virkning post-implementering? - Hvordan måles faktisk virkning post-implementering?
@ -194,7 +194,7 @@ Beskriv involvering av berørte parter.
**Dokumentasjon:** Loggfør hvem som er involvert, når, og hvordan tilbakemeldinger påvirket beslutninger. **Dokumentasjon:** Loggfør hvem som er involvert, når, og hvordan tilbakemeldinger påvirket beslutninger.
**Cosmo-anbefaling:** **Anbefaling:**
Involver alltid sluttbrukere i POC-fase. "AI-optimisme" hos ledelse må balanseres med realisme fra de som skal bruke systemet daglig. Involver alltid sluttbrukere i POC-fase. "AI-optimisme" hos ledelse må balanseres med realisme fra de som skal bruke systemet daglig.
--- ---
@ -239,7 +239,7 @@ Beskriv ressurser, kompetanse, organisering og andre forutsetninger.
- **Fallback:** Kan vi fortsette manuelt hvis AI feiler? - **Fallback:** Kan vi fortsette manuelt hvis AI feiler?
- **Monitorering:** Hvordan overvåkes modellens ytelse over tid? - **Monitorering:** Hvordan overvåkes modellens ytelse over tid?
**Cosmo-checkpoint:** **Checkpoint:**
- Sjekk om alle forutsetninger er **realistiske** (ikke optimistiske antakelser) - Sjekk om alle forutsetninger er **realistiske** (ikke optimistiske antakelser)
- Identifiser **kritiske avhengigheter** (hva kan stoppe prosjektet?) - Identifiser **kritiske avhengigheter** (hva kan stoppe prosjektet?)
@ -414,7 +414,7 @@ Utredningsinstruksen krever at utredning skal være **"så omfattende og grundig
| Offentlig tjeneste (høy-risiko AI Act) | Omfattende: Full utredning, DPIA, samfunnsøkonomi, ekstern kvalitetssikring | | Offentlig tjeneste (høy-risiko AI Act) | Omfattende: Full utredning, DPIA, samfunnsøkonomi, ekstern kvalitetssikring |
| Kritisk infrastruktur (f.eks. helsediagnostikk) | Meget omfattende: Alle analyser + uavhengig validering, kliniske studier | | Kritisk infrastruktur (f.eks. helsediagnostikk) | Meget omfattende: Alle analyser + uavhengig validering, kliniske studier |
**Cosmo-anbefaling:** **Anbefaling:**
Selv ved "lett" utredning, **gjør alltid:** Selv ved "lett" utredning, **gjør alltid:**
1. Risikoklassifisering (AI Act) 1. Risikoklassifisering (AI Act)
2. Personvernssjekk (trenger vi DPIA?) 2. Personvernssjekk (trenger vi DPIA?)
@ -545,7 +545,7 @@ Innbygger → AI-system (regelbasert + ML) → Automatisk vedtak (med innsyn)
Krav: Høy nøyaktighet, transparens, klageadgang Krav: Høy nøyaktighet, transparens, klageadgang
Eksempel: Automatisk utbetaling av barnetrygd (regel-basert med ML-fraud detection) Eksempel: Automatisk utbetaling av barnetrygd (regel-basert med ML-fraud detection)
**Cosmo-anbefaling:** **Anbefaling:**
Start med **hybrid** (menneske i løkken), selv om teknologien kunne gjort det fullt automatisk. Bygg tillit gradvis. Start med **hybrid** (menneske i løkken), selv om teknologien kunne gjort det fullt automatisk. Bygg tillit gradvis.
--- ---
@ -685,7 +685,7 @@ Start med **hybrid** (menneske i løkken), selv om teknologien kunne gjort det f
**Lav konfidens (50%):** **Lav konfidens (50%):**
- Eksakte timelines for AI Act-ikrafttredelse i Norge (avhenger av EØS-prosess) - Eksakte timelines for AI Act-ikrafttredelse i Norge (avhenger av EØS-prosess)
**Cosmo-anbefaling:** **Anbefaling:**
Verifiser alltid aktuelle lover og forskrifter på regjeringen.no og lovdata.no før beslutning. Denne filen er en veiledning, ikke juridisk rådgivning. Verifiser alltid aktuelle lover og forskrifter på regjeringen.no og lovdata.no før beslutning. Denne filen er en veiledning, ikke juridisk rådgivning.
--- ---

View file

@ -524,4 +524,4 @@ Den nye forvaltningsloven (vedtatt 3. juni 2025, Prop. 79 L (2024-2025)) innehol
**Dokumentets status:** GA (Generally Available) **Dokumentets status:** GA (Generally Available)
**Neste oppdatering anbefales:** Q3 2026 (når EU Commission publiserer Art. 6(3) guidelines og norsk AI Act-lov vedtas) **Neste oppdatering anbefales:** Q3 2026 (når EU Commission publiserer Art. 6(3) guidelines og norsk AI Act-lov vedtas)
**Owner (Cosmo):** Oppdater ved nye Nkom-retningslinjer, EU guidelines, eller norsk lovvedtak. **Owner:** Oppdater ved nye Nkom-retningslinjer, EU guidelines, eller norsk lovvedtak.

View file

@ -735,4 +735,4 @@ Tre nivåer av human oversight:
**Dokumentets status:** GA (Generally Available) **Dokumentets status:** GA (Generally Available)
**Neste oppdatering anbefales:** Q3 2026 (når EU Commission publiserer detailed guidelines per Art. 6) **Neste oppdatering anbefales:** Q3 2026 (når EU Commission publiserer detailed guidelines per Art. 6)
**Owner (Cosmo):** Oppdater ved nye Nkom-retningslinjer eller Microsoft-feature launches. **Owner:** Oppdater ved nye Nkom-retningslinjer eller Microsoft-feature launches.

View file

@ -388,10 +388,10 @@ Hvis kunden beskriver noen av disse, **stopp og advare**:
### Typiske misvær ### Typiske misvær
**Misforståelse:** "Vi bruker bare Azure OpenAI, så vi har ingen high-risk AI." **Misforståelse:** "Vi bruker bare Azure OpenAI, så vi har ingen high-risk AI."
**Cosmo-svar:** "Azure OpenAI selv er ikke high-risk, men *bruken* kan være det. Hvis systemet deres tar beslutninger om ansettelse, kreditt, eller helsediagnoser, er det high-risk uavhengig av underliggende teknologi." **Svar:** "Azure OpenAI selv er ikke high-risk, men *bruken* kan være det. Hvis systemet deres tar beslutninger om ansettelse, kreditt, eller helsediagnoser, er det high-risk uavhengig av underliggende teknologi."
**Misforståelse:** "Vi trenger ikke HITL fordi modellen er veldig nøyaktig." **Misforståelse:** "Vi trenger ikke HITL fordi modellen er veldig nøyaktig."
**Cosmo-svar:** "HITL handler ikke bare om nøyaktighet det handler om accountability og compliance. EU AI Act krever human oversight for high-risk systems uavhengig av modellprestasjon." **Svar:** "HITL handler ikke bare om nøyaktighet det handler om accountability og compliance. EU AI Act krever human oversight for high-risk systems uavhengig av modellprestasjon."
### Praktisk verktøy-stack for risk assessment ### Praktisk verktøy-stack for risk assessment

View file

@ -595,4 +595,4 @@ Norge implementerer GDPR gjennom personopplysningsloven. Datatilsynet er tilsyns
--- ---
**Oppsummering for Cosmo**: GDPR-compliance for AI-systemer er ikke valgfritt — det er lovpålagt for alle organisasjoner som behandler personopplysninger fra EU/EØS-borgere. Microsoft Azure AI-stakken tilbyr sterke GDPR-capabilities out-of-the-box (encryption, data residency, no training on customer data), men arkitekten må aktivt designe for data minimization, user rights, transparency, og accountability. Bruk Microsoft Purview for automatisert compliance-monitorering, gjennomfør DPIA for høyrisikobehandling, og implementer zero-trust data access for AI agents. Ved tvil, konsulter juridisk rådgiver og gjennomfør DPIA. **Oppsummering**: GDPR-compliance for AI-systemer er ikke valgfritt — det er lovpålagt for alle organisasjoner som behandler personopplysninger fra EU/EØS-borgere. Microsoft Azure AI-stakken tilbyr sterke GDPR-capabilities out-of-the-box (encryption, data residency, no training on customer data), men arkitekten må aktivt designe for data minimization, user rights, transparency, og accountability. Bruk Microsoft Purview for automatisert compliance-monitorering, gjennomfør DPIA for høyrisikobehandling, og implementer zero-trust data access for AI agents. Ved tvil, konsulter juridisk rådgiver og gjennomfør DPIA.

View file

@ -471,7 +471,7 @@ jobs:
### Trade-offs og gotchas ### Trade-offs og gotchas
| Trade-off | Implikasjon | Cosmos råd | | Trade-off | Implikasjon | Råd |
|-----------|-------------|------------| |-----------|-------------|------------|
| **Automated vs. Manual red teaming** | Automated gir scale, manual gir creativity og edge-case discovery | Start automated (PyRIT), supplement med manual quarterly | | **Automated vs. Manual red teaming** | Automated gir scale, manual gir creativity og edge-case discovery | Start automated (PyRIT), supplement med manual quarterly |
| **Local vs. Cloud** | Local gir data control, cloud gir agentic risk coverage | Hybrid: PyRIT for CI/CD, AI Red Teaming Agent for pre-deployment gates | | **Local vs. Cloud** | Local gir data control, cloud gir agentic risk coverage | Hybrid: PyRIT for CI/CD, AI Red Teaming Agent for pre-deployment gates |

View file

@ -374,5 +374,5 @@ A: Ja for high-risk use cases (biometrics, critical infrastructure). Nei for low
--- ---
**For Cosmo:** **For arkitekten:**
Dette er oversikten du trenger for å veilede kunder om Responsible AI. Bruk de seks prinsippene som utgangspunkt, match dem mot kundens compliance-krav (GDPR, forvaltningsloven), og anbefal konkrete verktøy basert på use case (RAI Dashboard for ML, Content Safety for generative AI, Purview for governance). Husk: RAI er ikke etikk-teater — det er lovpålagt risk management. Dette er oversikten du trenger for å veilede kunder om Responsible AI. Bruk de seks prinsippene som utgangspunkt, match dem mot kundens compliance-krav (GDPR, forvaltningsloven), og anbefal konkrete verktøy basert på use case (RAI Dashboard for ML, Content Safety for generative AI, Purview for governance). Husk: RAI er ikke etikk-teater — det er lovpålagt risk management.

View file

@ -517,7 +517,7 @@ Er det custom AI (ikke bare ferdiglagde features)?
└─ Nei → Valgfritt (men anbefales) └─ Nei → Valgfritt (men anbefales)
``` ```
**Cosmo's one-liner:** **One-liner:**
> "Responsible AI training er ikke en 'nice-to-have' — det er fundamentet for at AI-løsningen ikke skal kollapse under etiske, regulatoriske eller tillitsmessige belastninger." > "Responsible AI training er ikke en 'nice-to-have' — det er fundamentet for at AI-løsningen ikke skal kollapse under etiske, regulatoriske eller tillitsmessige belastninger."
--- ---

View file

@ -792,4 +792,4 @@ Return on investment: Transparency er billigere enn cleanup. Skal vi prioritere
--- ---
**For Cosmo:** Denne kunnskapsbasen dekker både teknisk implementasjon (Azure ML dashboard, Azure OpenAI annotations) og organisatorisk praksis (governance workflows, compliance mapping). Bruk decision trees og scenario-spesifikke recommendations for å guide kunder gjennom transparency-design. Vekt living documentation-prinsippet — transparency er ikke en one-time artifact, men en ongoing practice. **For arkitekten:** Denne kunnskapsbasen dekker både teknisk implementasjon (Azure ML dashboard, Azure OpenAI annotations) og organisatorisk praksis (governance workflows, compliance mapping). Bruk decision trees og scenario-spesifikke recommendations for å guide kunder gjennom transparency-design. Vekt living documentation-prinsippet — transparency er ikke en one-time artifact, men en ongoing practice.

View file

@ -608,4 +608,4 @@ Set-AzSecurityContact -Name "default1" `
--- ---
**For Cosmo:** Dette er et komplett utgangspunkt for å diskutere incident response-strategi med klienter. Start med maturity assessment, map til ett av de fire scenarioene (startup/scale-up/enterprise/offentlig), og tilpass playbooks basert på deres AI-specific risk profile. Husk: Incident response er ikke "set it and forget it" — kontinuerlig tuning og tabletop exercises er essensielt for å holde organisasjonen klar. **For arkitekten:** Dette er et komplett utgangspunkt for å diskutere incident response-strategi med klienter. Start med maturity assessment, map til ett av de fire scenarioene (startup/scale-up/enterprise/offentlig), og tilpass playbooks basert på deres AI-specific risk profile. Husk: Incident response er ikke "set it and forget it" — kontinuerlig tuning og tabletop exercises er essensielt for å holde organisasjonen klar.

View file

@ -525,6 +525,6 @@ Prompt Shield er **obligatorisk** for alle produksjons-AI-systemer med ekstern b
--- ---
**For Cosmo:** **For arkitekten:**
Prompt injection er den mest undervurderte AI-sikkerhetsrisikoen i norsk offentlig sektor. Mange organisasjoner fokuserer på nettverkssikkerhet og access control, men glemmer at LLM-en selv kan bli "hacket" via ondsinnet tekst. Prompt Shields gir et konkret, enkelt å aktivere forsvarslag som direkte adresserer OWASP LLM #1 og er i linje med NSM Grunnprinsipper. Anbefal det alltid — kostnadene er lave, risikoredusjonen er høy. Prompt injection er den mest undervurderte AI-sikkerhetsrisikoen i norsk offentlig sektor. Mange organisasjoner fokuserer på nettverkssikkerhet og access control, men glemmer at LLM-en selv kan bli "hacket" via ondsinnet tekst. Prompt Shields gir et konkret, enkelt å aktivere forsvarslag som direkte adresserer OWASP LLM #1 og er i linje med NSM Grunnprinsipper. Anbefal det alltid — kostnadene er lave, risikoredusjonen er høy.

View file

@ -464,7 +464,7 @@ Basert på offisiell Microsoft Learn-dokumentasjon (sist verifisert 2026-06 via
--- ---
**For Cosmo:** **For arkitekten:**
Security Copilot er et av de mest konkrete AI-verdiforslaget i Microsoft-stakken for sikkerhetsteam. Det er ikke "AI for AI's skyld" — det er direkte tidsbesparelse på veldefinerte oppgaver. Security Copilot er et av de mest konkrete AI-verdiforslaget i Microsoft-stakken for sikkerhetsteam. Det er ikke "AI for AI's skyld" — det er direkte tidsbesparelse på veldefinerte oppgaver.

View file

@ -934,7 +934,7 @@ Denne guiden er basert på følgende Microsoft Learn-dokumentasjon (sist verifis
--- ---
**For Cosmo:** **For arkitekten:**
Når en kunde spør om "Hvordan sikrer jeg AI-tjenester?", nevn denne guiden og gi konkrete anbefalinger basert på deres scenario: Når en kunde spør om "Hvordan sikrer jeg AI-tjenester?", nevn denne guiden og gi konkrete anbefalinger basert på deres scenario:
- Er det service-to-service kommunikasjon? → Managed Identity + Private Endpoint - Er det service-to-service kommunikasjon? → Managed Identity + Private Endpoint

View file

@ -246,7 +246,7 @@ Azure Cost Management aggregerer kostnader per dag, men fakturering skjer måned
### Fallgruver per modenhetsnivå ### Fallgruver per modenhetsnivå
| Modenhetsnivå | Typisk fallgruve | Cosmo-anbefaling | | Modenhetsnivå | Typisk fallgruve | Anbefaling |
|---------------|------------------|------------------| |---------------|------------------|------------------|
| **Beginner** | Oppretter kun ét budsjett for hele subscriptionen, mangler granularitet | Start med resource group-budsjetter per team, bruk templates for konsistens | | **Beginner** | Oppretter kun ét budsjett for hele subscriptionen, mangler granularitet | Start med resource group-budsjetter per team, bruk templates for konsistens |
| **Intermediate** | Ignorerer forecast alerts, reagerer kun på 100%-overskridelse | Implementer forecast alerts (110%) med eskalert respons | | **Intermediate** | Ignorerer forecast alerts, reagerer kun på 100%-overskridelse | Implementer forecast alerts (110%) med eskalert respons |
@ -287,7 +287,7 @@ Azure Cost Management aggregerer kostnader per dag, men fakturering skjer måned
| Beslutningsveiledning | **Verified** | Cost optimization best practices (Well-Architected) | | Beslutningsveiledning | **Verified** | Cost optimization best practices (Well-Architected) |
| Integrasjon med Microsoft-stakken | **Verified** | Official docs (tags, Power BI, Azure Monitor) | | Integrasjon med Microsoft-stakken | **Verified** | Official docs (tags, Power BI, Azure Monitor) |
| Offentlig sektor (Norge) | **Domain Expertise** | KTG/DDT-kontekst, ikke Microsoft-spesifikk | | Offentlig sektor (Norge) | **Domain Expertise** | KTG/DDT-kontekst, ikke Microsoft-spesifikk |
| For arkitekten (Cosmo) | **Baseline + Best Practices** | Syntetisert fra research + field experience | | For arkitekten | **Baseline + Best Practices** | Syntetisert fra research + field experience |
--- ---

View file

@ -575,7 +575,7 @@ Tilgjengelig i deployment workflow:
--- ---
**For Cosmo:** **For arkitekten:**
Denne kunnskapen er kritisk for **cost optimization discussions** med kunder. Viktigste takeaways: Denne kunnskapen er kritisk for **cost optimization discussions** med kunder. Viktigste takeaways:

View file

@ -637,7 +637,7 @@ az webapp create --name webapp-slm-phi4 --resource-group rg-slm-norway --plan pl
| **Integrasjon med Microsoft-stakken** | Verified | MCP (code samples, deployment guides) | | **Integrasjon med Microsoft-stakken** | Verified | MCP (code samples, deployment guides) |
| **Offentlig sektor (Norge)** | Baseline | Domenekunnskap (norsk offentlig sektor) | | **Offentlig sektor (Norge)** | Baseline | Domenekunnskap (norsk offentlig sektor) |
| **Kostnad og lisensiering** | Verified (priseksempler) + Baseline (TCO-kalkulasjoner) | MCP (Azure-priser) + estimering | | **Kostnad og lisensiering** | Verified (priseksempler) + Baseline (TCO-kalkulasjoner) | MCP (Azure-priser) + estimering |
| **For arkitekten (Cosmo)** | Baseline | Erfaringsbaserte anbefalinger | | **For arkitekten** | Baseline | Erfaringsbaserte anbefalinger |
--- ---

View file

@ -0,0 +1,199 @@
// 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',
);
});