feat(ms-ai-architect): Spor 3 Port 2 — create-time-guard (born-verified contract) [skip-docs]

Type-aware create-guard for reference-filer (Spor 3 Port 1/Port 2):

- kb-headers.mjs: parseTypeHeader/parseVerifiedHeader/parseVerifiedByHeader
  (samme top-500-byte bold-label-skann som parseSourceHeader).
- transform.mjs: buildKbHeader er type-aware (reference krever
  source+verified+verified_by; non-reference kaster pa MS-Learn-source) +
  emitterer Type/Verified/Verified by; validateKbFile type-aware;
  stampVerifiedMeta = fodt-verifisert-gate (stempler KUN ved bestatt
  judge-verdikt, ellers kaster).
- validate-kb-file.mjs (ny): kjorbar gate generatorer kaller for commit
  (exit != 0 ved kontraktbrudd).
- generate-skills.md + kb-update.md + transform-prompt.md: wiret til
  fodt-verifisert kontrakt (header-felt + judge-steg + create-guard-gate).

Suite 586/586 (33 nye + 1 invariant). Plugin-validering 239/0/0.

Utenfor scope (flagget): generate-skills.sh legacy (sonnet/Cosmo/no-source),
Cosmo-persona i generatorene (S-Cosmo), korpus-migrering av 306 filer (Spor 1).
This commit is contained in:
Kjell Tore Guttormsen 2026-06-29 10:36:21 +02:00
commit 5a0ba1fc9e
10 changed files with 621 additions and 54 deletions

View file

@ -23,13 +23,16 @@ regel 3 (autoritets-mismatch) faktisk fyre.
- `{{CATEGORY}}` — taksonomi-kategorinøkkel (f.eks. `rag-architecture`) — bestemmer eierskill via `resolveTargetPath`
- `{{SOURCE_URL}}` — den **utpekte autoritetskilden** (kanonisk Learn-URL dokumentet ble hentet fra)
- `{{LAST_UPDATED}}``YYYY-MM` (dagens måned, caller-injisert)
- `{{VERIFIED_DATE}}``YYYY-MM-DD` (dagen judgen bekreftet innholdet; settes av `stampVerifiedMeta` etter steg 2, ikke før)
- `{{FETCHED_DOC}}` — den hentede markdownen fra `microsoft_docs_fetch`
- `{{EXISTING_CONTEXT}}` — sammendrag av overlappende eksisterende ref-filer (unngå duplisering — K4)
## Header (deterministisk — IKKE la LLM-en skrive den)
Header-blokka bygges av `buildKbHeader({title, status, category, source, lastUpdated})`,
ikke av modellen. Den ser slik ut og MÅ stå først:
Header-blokka bygges av `buildKbHeader({title, status, category, type, source, lastUpdated,
verified, verified_by})`, ikke av modellen. En `reference`-fil er **født verifisert**
(Spor 3 Port 1/Port 2): den MÅ bære `Type` + `Source` + `Verified` + `Verified by`.
Headeren ser slik ut og MÅ stå først:
```markdown
# {{TITLE}}
@ -37,17 +40,22 @@ ikke av modellen. Den ser slik ut og MÅ stå først:
**Last updated:** {{LAST_UPDATED}}
**Status:** {{STATUS}}
**Category:** {{CATEGORY}}
**Type:** reference
**Source:** {{SOURCE_URL}}
**Verified:** {{VERIFIED_DATE}}
**Verified by:** judge-v2
---
```
Modellen skriver **kun brødteksten under `---`**. `composeKbFile({...}, body)` setter så
sammen hele fila — header + (for **store filer >100 linjer**) en deterministisk
`## Innhold`-TOC fra brødtekstens `##`-seksjoner + brødteksten. Etterpå kjører
`validateKbFile(content)` og MÅ returnere `valid: true` (title + Last updated + Status +
Source til stede; **for store filer også TOC**) før noe gates videre. TOC-en fødes inn så
en senere regenerering ikke stripper den (write-path-regresjon — Fase 1c).
`Verified`/`Verified by` settes IKKE før innholdet har bestått judgen (se «Etter
destillasjon» steg 2). Modellen skriver **kun brødteksten under `---`**.
`composeKbFile(stampVerifiedMeta({...}, verdict, today), body)` setter så sammen hele fila
— header + (for **store filer >100 linjer**) en deterministisk `## Innhold`-TOC fra
brødtekstens `##`-seksjoner + brødteksten. Etterpå kjører `validateKbFile(content)` og MÅ
returnere `valid: true` (title + Last updated + Status + Source + **Verified + Verified by**
til stede; **for store filer også TOC**) før noe gates videre. TOC-en fødes inn så en
senere regenerering ikke stripper den (write-path-regresjon — Fase 1c).
## Prompt (brødtekst)
@ -90,10 +98,19 @@ Ingen innledning eller avslutningskommentar — kun markdown-brødteksten.
## Etter destillasjon (caller, gated)
1. `content = composeKbFile({title, status, category, source, lastUpdated}, body)` — header + (store filer) `## Innhold`-TOC + brødtekst. Erstatter den bare `buildKbHeader(...) + body`-konkateneringen.
2. `validateKbFile(content)` → må være `valid: true` (title + Last updated + Status + Source; **store filer også TOC**) (ellers: be modellen fikse manglende felt).
3. For hver status-/load-bearing-påstand: `buildChange({field, oldValue, newValue, sourceUrl, authoritySource})`
1. **Født-verifisert judge (FØR commit):** kjør claim-judgen (`scripts/kb-eval/judge-claim-prompt-v2.md`)
over brødtekstens maskin-verifiserbare påstander mot `{{SOURCE_URL}}`. Aggreger til `verdict = {pass}`
(alle maskin-verifiserbare påstander `grounded``pass: true`).
2. `meta = stampVerifiedMeta({title, status, category, source, lastUpdated}, verdict, today)`
stempler `type='reference'` + `verified=today` + `verified_by='judge-v2'` **kun** ved `pass: true`;
ellers KASTER den (ingen fil fødes). Deretter `content = composeKbFile(meta, body)`
header + (store filer) `## Innhold`-TOC + brødtekst.
3. `validateKbFile(content)` → må være `valid: true` (title + Last updated + Status + Source +
**Verified + Verified by**; **store filer også TOC**) (ellers: be modellen fikse manglende felt).
4. For hver status-/load-bearing-påstand: `buildChange({field, oldValue, newValue, sourceUrl, authoritySource})`
`classifyChange(...)` (lag 5). `flagged` → operatør avgjør; status-påstander er alltid `flagged`.
4. `resolveTargetPath(tax, category, filename)` → eierskill-sti (null = ukjent kategori → gate, ikke skriv).
5. Først etter operatør-gate: atomisk skriving (`lib/atomic-write.mjs` + `lib/backup.mjs`). Transform-libben skriver aldri selv.
5. `resolveTargetPath(tax, category, filename)` → eierskill-sti (null = ukjent kategori → gate, ikke skriv).
6. **Create-guard FØR skriving:** `node scripts/kb-update/validate-kb-file.mjs <sti>` (exit ≠0 = ikke skriv).
Først etter operatør-gate + grønn create-guard: atomisk skriving (`lib/atomic-write.mjs` +
`lib/backup.mjs`). Transform-libben skriver aldri selv.
```