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:
parent
06713ed7b8
commit
1c9af82daa
56 changed files with 793 additions and 63 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
18
scripts/kb-update/data/cosmo-labels-baseline.json
Normal file
18
scripts/kb-update/data/cosmo-labels-baseline.json
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
|
||||||
126
scripts/kb-update/neutralize-cosmo-labels.mjs
Normal file
126
scripts/kb-update/neutralize-cosmo-labels.mjs
Normal 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();
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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?"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.**
|
||||||
|
|
|
||||||
|
|
@ -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).
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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`
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) |
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) |
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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."
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
199
tests/kb-update/test-cosmo-labels.test.mjs
Normal file
199
tests/kb-update/test-cosmo-labels.test.mjs
Normal 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',
|
||||||
|
);
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue