feat(ms-ai-architect): R20 — Category-backfill på 25 kategorisløse ref-filer via insertMetaField [skip-docs]

Ny testet primitiv transform.insertMetaField (anker + 500B-back-off som insertHeaderFields, idempotent på eksakt label, body byte-identisk; 6 tester) + backfill-category.mjs (deterministisk folder->category-regel, hard per-fil-invariant, insert-only, aborterer for skriving ved avvik).

Fordeling: 14 Solution Architecture & Advisory (architecture/), 6 Microsoft AI Platforms (platforms/+development/), 4 Responsible AI & Governance, 1 MLOps & GenAIOps. Label = engelsk Category (322 vs 42 Kategori). Eksisterende recommended-mcp-servers/rag-maturity-model urort.

Roadmap: R20 done + R21 (Status/Last-updated not-due) encoded. Verifisering: 0 kategorislose filer (var 25); diff +25/-0; idempotent re-run; suite 764/764 exit 0.
This commit is contained in:
Kjell Tore Guttormsen 2026-07-06 07:39:54 +02:00
commit a583599ab1
29 changed files with 262 additions and 1 deletions

View file

@ -1,4 +1,4 @@
# Plugin-roadmap: sesjonsplan R0R18 (2026-07)
# Plugin-roadmap: sesjonsplan R0R21 (2026-07)
_Styrende sesjonsplan for ms-ai-architect fra 2026-07-02, basert på full statusanalyse (visjon/planer, ground-truth-audit m/testkjøring, Spor 1-planlesing). Erstatter IKKE eksisterende planer (korrekthetsprogrammet, Spor 1-planen, briefer) — **sekvenserer** dem og fyller hullene analysen avdekket. Operatør-detaljer som ikke hører hjemme i repoet ligger i en LOCAL-ONLY-annex (`.claude/projects/2026-07-02-helhetlig-roadmap/`)._
@ -27,6 +27,7 @@ R0 (tillit+hygiene+klargjøring)
└─→ R13 → R14 (S-Cosmo, sist i programmet)
└─→ R15 (skill-eval v2 plan) → R16+ (eksekvering)
Fleksible (uavhengige, kan interleaves): R17 (dekningsaudit), R18 (onboarding-uplift)
Ungated base-felt-pre-pass (⊥ R7, i R7-vente-vinduet): R20 (Category-backfill ✅ 2026-07-06) → R21 (Status/Last-updated not-due)
Parkert (eget operatør-go): OKF second brain · pris/unsourced-verifisering
```
@ -110,6 +111,24 @@ Maskineriet er bygget (krav 1+2+3 + per-kategori-hint); målingen er aldri kjør
**Utsatt / betinget — IKKE del av R0R18-programmet.** ms-ai-architects Layer B (adversarial-content-skann) kjører i dag på `../llm-security`-*pluginens* JS-scannere (grønt, urørt — bekreftet 2026-07-05). Målet (`docs/ingestion-security-brief-2026-07.md` §6) er å konvergere på den delte deterministiske scanneren i `llm-ingestion-pipeline-security` (det utpekte delte aktivumet; repo/remote `open/llm-ingestion-pipeline-security`, internt pakkenavn `llm-ingestion-guard`). **Utløser (reell driver, ikke spekulasjon):** når operatør vil migrere Layer B *bort* fra llm-security-pluginen. **Koordinerte beslutninger (m/ library-sesjonen 2026-07-05, relayet):** (1) biblioteket forblir ÉTT repo → polyglot (modell A) når Node bygges, aldri splitt; (2) Node-porten er bibliotekets **v0.2-milepæl**, med ms-ai-architect-konvergensen som driver; (3) **datasett-først** — det delte lexicon/charset-datasettet er den billigste, høyest-verdi, lavest-drift konvergensen (§6); implementasjons-deling (CLI-shell-out vs. tynne native matchere) er en separat senere beslutning; (4) disposisjon uendret (§5: høy-severity i lav-trust chunk → quarantine). **ms-ai-architect-datapunkt (verifisert herfra):** konvergens-flaten er **Node** (hele scan-stien er `.mjs`; to perifere Python-verktøy — `export-pdf.py`, `kb-eval/ref-file-audit.py` — er utenfor Layer B). **Åpen brikke som låser modell A:** `claude-code-llm-wiki`s språk (Python → polyglot tvingende; Node → Node-primært bibliotek levedyktig) — uverifisert herfra (repo ikke i `~/repos`), library-sesjonen settler. **Koordinasjonsmekanisme (hub-and-spoke):** kanonisk konvergens-kontrakt bor i hub-en (`llm-ingestion-pipeline-security`); denne R19 er ms-ai-architects spoke-peker. Utgående brief levert til hub 2026-07-05. **Verifisering (når utløst):** Layer B importerer den delte scanneren + delte lexicon-datasettet; `node --test tests/kb-update/*.test.mjs tests/kb-eval/*.test.mjs` exit 0; korpus byte-urørt; nøyaktig ÉN lexicon-kilde (ingen andre-kopi).
## R20 — Base-felt pre-pass del 1: **Category:**-backfill (1 økt — ungated, ⊥ R7) ✅ 2026-07-06
**Kontekst:** 25 ref-filer manglet enhver kategori-label. 24/25 er **not-due** (ikke i R7s 243) → kjøres i R7-vente-vinduet, disjunkt fra judge-passet, ingen header-budsjett-kollisjon. Utledningen viste at kun 5/25 var mekanisk avledbare (enstemmig mappe-taksonomi); de 20 øvrige (advisor toolkit + plattform-referanser) krevde en operatør-godkjent sjanger-/taksonomibeslutning (2026-07-06): ta dem inn i header-kontrakten med to nye kategorier.
**Utført:** ny testet primitiv `transform.insertMetaField(content, label, value)` — samme anker + 500B-back-off som `insertHeaderFields`, idempotent på eksakt label, body byte-identisk (6 tester) — driver `scripts/kb-update/backfill-category.mjs` (deterministisk folder→category-regel, hard per-fil-invariant «nøyaktig én linje satt inn», insert-only, aborterer FØR skriving ved avvik). Fordeling: **14** «Solution Architecture & Advisory» (architecture/), **6** «Microsoft AI Platforms» (platforms/ + development/), **4** «Responsible AI & Governance», **1** «MLOps & GenAIOps». Label = engelsk `**Category:**` (322 filer vs 42 `Kategori`). Eksisterende `recommended-mcp-servers` («Architecture») + `rag-maturity-model` («RAG Architecture & Decision Framework») urørt — begge forsvarlige, ingen feilpasting fantes (korrigert antakelse).
**Verifisering:** `grep -rLE '\*\*(Category|Kategori):\*\*' skills/*/references` → 0; git diff +25/0 (én linje/fil, body byte-identisk); idempotent re-run = 0 planned; suite 764/764 exit 0.
## R21 — Base-felt pre-pass del 2: **Status:** + **Last updated:** på not-due-filer (1 økt — ungated, ⊥ R7)
**Mål:** lukk resten av base-felt-restansen på **not-due**-filene. Due-filenes Status/Last-updated FOLDES inn i R7 (Status er en faktapåstand judge-passet verifiserer mot live kilde; `**Verified:**`-stempelet er freshness-signalet) — backfilles ALDRI her.
**Scope (ground-truth 2026-07-06):** ~21 not-due mangler `**Status:**`, ~22 not-due mangler engelsk `Last updated:` (alle 48 datoløse er template/metodikk/rubrikk-filer — 0 har norsk dato å relabele fra). **Verdikilder:** Status = beskrivende klasse for toolkit/metodikk (`Established Practice`/`Reference`), produkt-tilstand for plattform-filer; Last updated = **git-avledet** (siste innholds-commit på fila), ALDRI «i dag». Reuse `insertMetaField` + manifest-drevet applier med samme per-fil-invariant.
**Åpne del-beslutninger:** (a) plattform-filenes Status er produkt-fakta — vurder om de heller skal vente på judge-verifisering som due-filene, ikke gjettes her; (b) 42 filer bruker norsk `**Kategori:**`/`Sist oppdatert:` — label-normalisering til engelsk er en egen residual (logg, ikke bland inn i R21).
**Verifisering:** not-due uten Status/Last-updated → 0 (eller eksplisitt utsatt-liste m/begrunnelse); git diff kun header-innsettinger; body byte-identisk; suite exit 0.
## Parkert (krever eget operatør-go)
- **OKF second brain** (spec v0.1 ratifisert; bygging = separat go).

View file

@ -0,0 +1,82 @@
#!/usr/bin/env node
// Backfill **Category:** on the reference files that carry NO category label (R20).
//
// Deterministic folder→category rule (operator-approved taxonomy 2026-07-06): insert-only
// via the tested `insertMetaField` primitive, with a hard per-file invariant asserted BEFORE
// any write — exactly one line is inserted, it is the Category line, and the body is
// byte-identical. If any target falls outside the approved folder rule, or any file would
// change by more than that single line, the run aborts and writes nothing. Idempotent:
// files that already carry **Category:**/**Kategori:** are skipped, so a re-run is a no-op.
//
// Usage: node scripts/kb-update/backfill-category.mjs [--dry]
import { readFileSync, writeFileSync } from 'node:fs';
import { execSync } from 'node:child_process';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { insertMetaField } from './lib/transform.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
const PLUGIN_ROOT = join(__dirname, '..', '..');
const dry = process.argv.includes('--dry');
// Immediate-parent-directory basename → Category. Operator-approved 2026-07-06.
const FOLDER_CATEGORY = {
architecture: 'Solution Architecture & Advisory',
platforms: 'Microsoft AI Platforms',
development: 'Microsoft AI Platforms',
'responsible-ai': 'Responsible AI & Governance',
'mlops-genaiops': 'MLOps & GenAIOps',
};
const RE_CAT = /^\s*\*\*(Category|Kategori):\*\*/im;
const files = execSync("find skills/*/references -name '*.md'", { cwd: PLUGIN_ROOT, encoding: 'utf8' })
.trim()
.split('\n');
const targets = files.filter((rel) => !RE_CAT.test(readFileSync(join(PLUGIN_ROOT, rel), 'utf8')));
const planned = [];
const unmatched = [];
for (const rel of targets) {
const folder = rel.split('/').slice(-2, -1)[0];
const cat = FOLDER_CATEGORY[folder];
if (!cat) {
unmatched.push(`${rel} (folder=${folder})`);
continue;
}
const old = readFileSync(join(PLUGIN_ROOT, rel), 'utf8');
const out = insertMetaField(old, 'Category', cat);
// Hard invariant: exactly one line inserted (the Category line), body byte-identical.
const o = old.split('\n');
const n = out.split('\n');
if (n.length !== o.length + 1) throw new Error(`ABORT ${rel}: not exactly one line inserted`);
let d = 0;
while (d < o.length && o[d] === n[d]) d++;
if (n[d] !== `**Category:** ${cat}`) {
throw new Error(`ABORT ${rel}: inserted line mismatch → ${JSON.stringify(n[d])}`);
}
if (o.slice(d).join('\n') !== n.slice(d + 1).join('\n')) {
throw new Error(`ABORT ${rel}: body not byte-identical below the insertion`);
}
planned.push({ rel, cat, out });
}
if (unmatched.length) {
console.error(`ABORT — ${unmatched.length} target(s) outside the approved folder rule:`);
unmatched.forEach((u) => console.error(' ' + u));
process.exit(1);
}
const byCat = {};
for (const p of planned) byCat[p.cat] = (byCat[p.cat] || 0) + 1;
console.log(`Reference files: ${files.length} | without a category: ${targets.length} | planned: ${planned.length}`);
console.log('By category:');
for (const [c, n] of Object.entries(byCat).sort()) console.log(` ${n} ${c}`);
if (dry) {
console.log('\n(dry run — no writes)');
process.exit(0);
}
for (const p of planned) writeFileSync(join(PLUGIN_ROOT, p.rel), p.out);
console.log(`\nWrote ${planned.length} files.`);

View file

@ -359,6 +359,65 @@ export function insertHeaderFields(content, meta) {
return lines.join('\n');
}
/**
* Surgically insert ONE bold-label meta line `**<label>:** <value>` into an EXISTING KB
* file that lacks it: the R20/R21 base-field backfill primitive (**Category:** in R20;
* **Status:** / **Last updated:** in R21). Reuses insertHeaderFields' anchor + 500-byte
* back-off discipline the line lands immediately after the last line of the first
* contiguous bold-label meta run that still leaves room in the window, with a title fallback
* for the "None" dialect (no meta lines). Idempotent on the EXACT label: a file already
* carrying `**<label>:**` anywhere in the header region is returned unchanged. The body (from
* the first `## ` section) is byte-identical.
*
* Unlike insertHeaderFields (Port-1 Type/Source, with type validation) this is field-agnostic
* it keys on the label shape only, so it never re-derives a value or touches other lines.
*
* @param {string} content full existing file content
* @param {string} label the bold label WITHOUT the `**`/`:` decoration, e.g. 'Category'
* @param {string} value the field value
* @returns {string} content with the meta line inserted, or unchanged if the label is present
* @throws if label or value is blank
*/
export function insertMetaField(content, label, value) {
const s = String(content ?? '');
const lab = String(label ?? '').trim();
const val = String(value ?? '').trim();
if (lab === '') throw new Error('insertMetaField: label is required');
if (val === '') throw new Error('insertMetaField: value is required');
const lines = s.split('\n');
const esc = lab.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const labelRe = new RegExp('^\\s*\\*\\*' + esc + ':\\*\\*', 'i');
// Idempotent: if the exact label already appears in the header region, return unchanged.
for (const line of lines) {
if (/^##\s/.test(line) || /^---\s*$/.test(line)) break; // reached body / rule
if (labelRe.test(line)) return s;
}
// Anchor: last line of the first contiguous meta run whose insertion point still fits the
// 500-byte window; title fallback for the "None" dialect (mirrors insertHeaderFields).
let anchorIdx = -1;
let titleIdx = -1;
let cum = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (/^##\s/.test(line) || /^---\s*$/.test(line)) break; // reached body / rule
if (titleIdx === -1 && /^#\s+\S/.test(line)) titleIdx = i;
const nextCum = cum + line.length + 1; // byte offset just AFTER this line
if (RE_META_LINE.test(line)) {
if (nextCum > HEADER_REGION_BYTES) break; // back off — insertion point past the window
anchorIdx = i;
} else if (anchorIdx !== -1) {
break; // first non-meta line after the run — the contiguous run is over
}
cum = nextCum;
}
const at = (anchorIdx !== -1 ? anchorIdx : titleIdx) + 1; // -1 + 1 = 0 → very top if no title
lines.splice(at, 0, `**${lab}:** ${val}`);
return lines.join('\n');
}
/**
* Surgically insert-or-update ONLY the `**Verified:**` and `**Verified by:**` header lines
* on an EXISTING KB file the R7R10 born-verified STAMP primitive. Unlike composeKbFile

View file

@ -2,6 +2,7 @@
**Last updated:** 2026-06-24 (research via microsoft-learn MCP)
**Format:** MADR (Markdown Any Decision Records) v3.0
**Category:** Solution Architecture & Advisory
---

View file

@ -3,6 +3,7 @@
**Sist oppdatert:** 2026-06-24 (v1.0)
**Målgruppe:** Løsningsarkitekter, prosjektledere og beslutningstagere i norsk offentlig sektor
**Format:** Strukturert utredningsmal basert på utredningsinstruksen, Digdirs arkitekturprinsipper, rammeverk for digital samhandling og EU AI Act
**Category:** Solution Architecture & Advisory
---

View file

@ -3,6 +3,7 @@
**Sist oppdatert:** 2026-02 (v1.0)
**Målgruppe:** Arkitekter som gjennomfører AI-arkitekturutredninger for norsk offentlig sektor
**Regulatorisk forankring:** Utredningsinstruksen (2016), DFOs veileder til utredningsinstruksen
**Category:** Solution Architecture & Advisory
---

View file

@ -3,6 +3,7 @@
**Sist oppdatert:** 2026-02 (v1.0)
**Målgruppe:** Arkitekter og prosjektledere som vurderer gjennomførbarhet av AI-prosjekter i norsk offentlig sektor
**Formål:** Gi konkrete benchmarks for kompetansevurdering, tidsplanvalidering, risikovurdering og MVP-avgrensning
**Category:** Solution Architecture & Advisory
---

View file

@ -3,6 +3,7 @@
**Last updated:** 2026-06 (token-pris-tabell strippet → kanonisk register; scenario-utregninger korrigert)
**Disclaimer:** Prices change frequently. Always verify at azure.microsoft.com/pricing
**Prissannhet:** Rå token-priser eies av `deterministic-cost-calculation-model.md` §1.1 — ikke dupliser tabeller her.
**Category:** Solution Architecture & Advisory
---

View file

@ -1,4 +1,5 @@
# Decision Trees for Microsoft AI
**Category:** Solution Architecture & Advisory
Akkumulerte beslutningstrær og arkitekturmønstre for Microsoft AI-stakken.

View file

@ -3,6 +3,7 @@
**Sist oppdatert:** 2026-02 (v1.0)
**Målgruppe:** diagram-generation-agent
**Format:** Prompt-maler for `mcp__mcp-image__generate_image` (Imagen 3 / Nano Banana Pro)
**Category:** Solution Architecture & Advisory
---

View file

@ -2,6 +2,7 @@
**Last updated:** 2026-06-24 (research via microsoft-learn MCP)
**Disclaimer:** Licensing changes frequently. Verify at microsoft.com/licensing
**Category:** Solution Architecture & Advisory
---

View file

@ -1,6 +1,7 @@
# Migration Patterns - Microsoft AI Platforms
**Last updated:** 2026-06-24 (research via microsoft-learn MCP)
**Category:** Solution Architecture & Advisory
---

View file

@ -1,6 +1,7 @@
# POC Template - Microsoft AI Projects
**Last updated:** 2026-06-24 (research via microsoft-learn MCP)
**Category:** Solution Architecture & Advisory
---

View file

@ -2,6 +2,7 @@
**Last updated:** 2026-06-24 (research via microsoft-learn MCP + WebSearch)
**Målgruppe:** Arkitekter som rådgiver norske offentlige virksomheter
**Category:** Solution Architecture & Advisory
---

View file

@ -3,6 +3,7 @@
**Sist oppdatert:** 2026-06-24 (v1.0)
**Målgruppe:** Arkitekter som verifiserer Azure-tjenestetilgjengelighet for norsk offentlig sektor
**Datakilde:** Microsoft Learn (MCP-verifisert 2026-06-24), Azure Products by Region
**Category:** Solution Architecture & Advisory
---

View file

@ -1,6 +1,7 @@
# Security for Microsoft AI Solutions
**Last updated:** 2026-06-24 | Verified: MCP 2026-06-24
**Category:** Solution Architecture & Advisory
Omfattende guide til sikkerhet, compliance og governance for AI-løsninger i Microsoft-økosystemet.

View file

@ -3,6 +3,7 @@
**Sist oppdatert:** 2026-02 (v1.0)
**Målgruppe:** Arkitekter som utarbeider AI-arkitekturvurderinger og utredninger
**Formål:** Sikre sporbarhet, transparens og etterprøvbarhet i arkitekturvurderinger
**Category:** Solution Architecture & Advisory
---

View file

@ -2,6 +2,7 @@
**Last updated:** 2026-06-24
**Status:** Generelt tilgjengelig (kjerne-SDK). Multi-agent workflows/orkestrering er under aktiv utvikling — deler er i preview/experimental.
**Category:** Microsoft AI Platforms
> **Verifisert 2026-06-24 mot Microsoft Learn (`learn.microsoft.com/agent-framework/`).** Hele API-flaten er korrigert: tidligere utgaver av denne filen brukte oppdiktede imports (`azure.ai.agent`, `azure.ai.foundry.FoundryClient/FoundryTools`, `Swarm`/`Handoff`, `Memory`, `@checkpoint`, `enable_tracing`). Det korrekte SDK-et er pakken `agent-framework` (Python) / `Microsoft.Agents.AI` (.NET).

View file

@ -3,6 +3,7 @@
**Last updated:** 2026-06-24 (modellkatalog reconciled mot `model-catalog-2026.md`; øvrig innhold research via microsoft-learn MCP juni 2026)
**Status:** GA (General Availability) — rebrandet til Microsoft Foundry desember 2025
**Source:** https://learn.microsoft.com/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure
**Category:** Microsoft AI Platforms
---

View file

@ -2,6 +2,7 @@
**Last updated:** 2026-06-19 (research via microsoft-learn MCP)
**Status:** GA (General Availability)
**Category:** Microsoft AI Platforms
---

View file

@ -2,6 +2,7 @@
**Last updated:** 2026-06-19 (research via microsoft-learn MCP)
**Status:** GA (General Availability)
**Category:** Microsoft AI Platforms
---

View file

@ -2,6 +2,7 @@
**Last updated:** 2026-06-18 (live-verifisert mot Microsoft Learn region-tabeller)
**Status:** Aktiv — dekker modeller i Microsoft Foundry per 2026-06; Norway East-tilgjengelighet verifisert per deployment-type mot «Region availability for Foundry Models sold by Azure»
**Category:** Microsoft AI Platforms
---

View file

@ -2,6 +2,7 @@
**Last updated:** 2026-06-19
**Status:** GA (General Availability)
**Category:** Microsoft AI Platforms
---

View file

@ -6,6 +6,7 @@
**Konfidensgrad:** ⚡️⚡️⚡️ Høy (basert på Microsoft Learn + offisielle code samples)
**Type:** reference
**Source:** https://learn.microsoft.com/azure/machine-learning/concept-model-management-and-deployment
**Category:** MLOps & GenAIOps
**Last updated:** 2026-04

View file

@ -1,5 +1,6 @@
# EU AI Act — Systematisk Klassifiseringsmetodikk
**Type:** regulatory
**Category:** Responsible AI & Governance
Last updated: 2026-06-18
Status: GA

View file

@ -1,5 +1,6 @@
# EU AI Act — Forpliktelser for Brukere (Deployers)
**Type:** regulatory
**Category:** Responsible AI & Governance
Last updated: 2026-06-18
Status: GA

View file

@ -1,5 +1,6 @@
# FRIA-mal — Fundamental Rights Impact Assessment (Art. 27)
**Type:** template
**Category:** Responsible AI & Governance
Last updated: 2026-02
Status: GA

View file

@ -1,5 +1,6 @@
# EU AI Act — Forpliktelser for Tilbydere (Providers)
**Type:** regulatory
**Category:** Responsible AI & Governance
Last updated: 2026-06-18
Status: GA

View file

@ -25,6 +25,7 @@ import {
composeKbFile,
insertToc,
insertHeaderFields,
insertMetaField,
insertVerifiedFields,
normalizeStaleVerified,
stampVerifiedMeta,
@ -690,6 +691,81 @@ test('insertHeaderFields rejects an unknown type', () => {
assert.throws(() => insertHeaderFields(HDR_CATEGORY, { type: 'blogpost' }), /type/i);
});
// --- insertMetaField: the R20/R21 base-field backfill primitive ------------------
// Inserts ONE bold-label meta line (**<Label>:** <value>) into an EXISTING file that
// lacks it, using the same anchor + 500-byte back-off discipline as insertHeaderFields
// (last line of the first contiguous meta run that fits the window; title fallback for
// the "None" dialect). Body byte-identical; idempotent on the exact label. Serves R20
// (**Category:**) and R21 (**Status:**, **Last updated:**).
// A file with a meta run but NO **Category:** (the R20 target shape).
const NOCAT =
'# Decision Trees\n\n' +
'**Last updated:** 2026-06-19\n' +
'**Status:** Established Practice\n\n' +
'---\n\n' +
'## Introduksjon\n\nBrødtekst.\n';
// The "None" dialect: a platform ref with NO bold-label meta line at all.
const NOMETA =
'# Azure AI Foundry\n\n' +
'Foundry er Microsofts plattform.\n\n' +
'## Oversikt\n\nBrødtekst.\n';
test('insertMetaField inserts **Category:** after the meta run, before the rule, body byte-identical', () => {
const out = insertMetaField(NOCAT, 'Category', 'Solution Architecture & Advisory');
assert.match(out, /\*\*Category:\*\* Solution Architecture & Advisory/);
// lands inside the meta run: after Status, before the `---` rule
assert.ok(out.indexOf('**Status:**') < out.indexOf('**Category:**'));
assert.ok(out.indexOf('**Category:**') < out.indexOf('\n---'));
const tail = (s) => s.slice(s.indexOf('## Introduksjon'));
assert.equal(tail(out), tail(NOCAT));
});
test('insertMetaField anchors on the title for the "None" dialect (no meta lines), before the first ## section', () => {
const out = insertMetaField(NOMETA, 'Category', 'Microsoft AI Platforms');
assert.match(out, /\*\*Category:\*\* Microsoft AI Platforms/);
assert.ok(out.indexOf('# Azure AI Foundry') < out.indexOf('**Category:**'));
assert.ok(out.indexOf('**Category:**') < out.indexOf('## Oversikt'));
const tail = (s) => s.slice(s.indexOf('## Oversikt'));
assert.equal(tail(out), tail(NOMETA));
});
test('insertMetaField is idempotent on the exact label (a re-run inserts nothing)', () => {
const once = insertMetaField(NOCAT, 'Category', 'Solution Architecture & Advisory');
const twice = insertMetaField(once, 'Category', 'Solution Architecture & Advisory');
assert.equal(twice, once);
assert.equal((once.match(/\*\*Category:\*\*/g) || []).length, 1);
});
test('insertMetaField keeps the field inside the 500-byte window when a meta line is itself >500B (anchor backs off)', () => {
const giant =
'# Big\n\n' +
'**Status:** ' + 'GA; '.repeat(140) + 'slutt\n\n' + // single meta line ~560 bytes
'---\n\n' +
'## A\n\ntekst\n';
const out = insertMetaField(giant, 'Category', 'X');
// backed off to the title anchor — Category lands BEFORE the giant Status line, in-window
assert.ok(out.indexOf('# Big') < out.indexOf('**Category:**'));
assert.ok(out.indexOf('**Category:**') < out.indexOf('**Status:**'));
assert.ok(out.indexOf('**Category:**') < 500, 'Category fell outside the 500-byte window');
const tail = (s) => s.slice(s.indexOf('## A'));
assert.equal(tail(out), tail(giant));
});
test('insertMetaField preserves a pipe-delimited meta row (siblings untouched)', () => {
const out = insertMetaField(HDR_PIPE, 'Type', 'reference');
assert.match(out, /\*\*Opprettet:\*\* 2026-04 \| \*\*Sist oppdatert:\*\* 2026-06-19/);
assert.ok(out.indexOf('**Confidence:**') < out.indexOf('**Type:**'));
const tail = (s) => s.slice(s.indexOf('## Introduksjon'));
assert.equal(tail(out), tail(HDR_PIPE));
});
test('insertMetaField rejects a blank label or blank value', () => {
assert.throws(() => insertMetaField(NOCAT, '', 'x'), /label/i);
assert.throws(() => insertMetaField(NOCAT, 'Category', ' '), /value/i);
});
test('insertToc(insertHeaderFields(large)) → Type+Source+TOC, passes real checkN4, body byte-identical except header + one ## Innhold', () => {
const withHdr = insertHeaderFields(HDR_LARGE, { type: 'reference', source: MS_URL });
const out = insertToc(withHdr);