ms-ai-architect/docs/kb-mechanism-redesign-plan.md
Kjell Tore Guttormsen 797179fc5f docs(ms-ai-architect): Sesjon 7 SPOR A-resultater i roadmap + STATE
Roadmap «Sesjon 7 — RESULTATER» (refTall + dead-path + K5, deterministisk del
FERDIG). STATE peker på NESTE = SPOR A beslutning-gated rester (6×5-vekting,
K9, K4, K1 — krever operatør-input). Telling oppdatert: kb-integrity 181/181,
K5 navngitte per skill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REiKFhP4w6xGXXqWKpPCJJ
2026-06-20 05:47:26 +02:00

281 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Spec — Redesign av KB-kunnskapsmekanismen (ms-ai-architect)
_Godkjent plan (operatør, 2026-06-19). Avløser planleggings-input i `kb-mechanism-redesign-brief.md`. Implementeres én sesjon per økt; STATE.md peker på neste sesjon._
## Context
Kunnskapen pluginen leverer (389 referansefiler over 5 skills) er dens kjerneverdi; de 29 kommandoene/12 agentene er bare leveringsmekanismer oppå den. Dagens `kb-update`-mekanisme er **URL- og metadata-forankret**: den ser bare endring i sider den *allerede* siterer, måler «endret» på sitemap-`lastmod` (ikke faktisk innhold), og forankrer status-påstander (GA/preview/versjon) mot en *tilfeldig sitert* side — som ga en reell regresjon (agentic-retrieval-status ble «korrigert» feil; hovedkontekst måtte rette manuelt).
Empiri fra søk-først kvantifiserer problemet:
- **35 % av KB-en (468/1344 URLer) er `not_in_sitemap`** → «alltid stale». For Azure AI Foundry er det **100 % (164/164)** fordi MS restrukturerte URL-rommet — kjerne-produktet gir aldri et currency-signal.
- **0 % av filene har en maskinlesbar `Source:`-header** → en forankret per-fil-verifisering har ingen inngangsdata i dag.
- **Discovery er en dead-end:** `discover-new-urls.mjs` skriver `discovery-report.json` som *leses av ingenting*; nye sider registreres aldri. Kurs (`/training/`) er eksplisitt ekskludert.
- **Fire divergerende taksonomier** styrer i dag hva som er «i domenet» (poll-prefikser, discovery-INCLUDE, skill-gen-kategorier, prioritets-regex) — allerede inkonsistente.
**Mål:** en mekanisme som holder de eksisterende skills *oppdatert* (currency), *komplette* (nye sider + kurs) og *optimaliserte* (målbar kvalitet), med **auto-deteksjon (scheduled) og operatør-gate på all skriving**. Utfall: pluginen «står på egne ben» — mindre avhengig av manuell hovedkontekst-verifisering, uten å miste operatør-kontroll over innholdet.
### Låst scope og retning (operatør, 2026-06-19)
- **I scope:** krav 1 currency · 2 completeness (nye sider + kurs) · 4 optimalisering av eksisterende skills.
- **UTE av scope:** krav 5 (lage nye skills) · krav 6 (slå sammen/slette skills). Innhold kan *vokse* (nye ref-filer ok), men **ingen destruktive ops**.
- **Autonomi:** deteksjon kjører automatisk → rapport; **all** KB-skriving + URL-registrering går gjennom operatør-gate.
- **Verifisering:** hver faktapåstand forankres mot en *utpekt* autoritetskilde; status-påstander (GA/preview/versjon/pris) flagges **alltid** for operatør.
- **Kurs:** Microsoft Learn **Platform API** direkte (krever Entra ID app-registrering; verifisert: app-only, scope `https://learn.microsoft.com/.default`, `api-version=2023-11-01-preview` obligatorisk, 100 calls/min, ingen betaling). Legacy Catalog API er deprecated «through June 2026» → kill-switch-fallback.
## Arkitektur — 6 lag oppå delt registry
```
0. TAKSONOMI data/domain-taxonomy.json — én sannhetskilde for "i domenet"
(poll, discovery, skill-gen, prioritet leser HERFRA)
1. DETEKSJON currency + completeness + kurs → produserer KUN rapporter
2. DECISION-LEDGER data/decisions.json — eneste skrive-autoriserte bro;
/ OPERATØR-GATE dedup så avviste kandidater ikke re-foreslås
3. VERIFISERING-INN autoritetskilde-binding på godkjente kandidater
4. TRANSFORMASJON doc→KB-fil (LLM-destillasjon, status-felt obligatorisk)
5. VERIFISERING-UT adversarial "prøv-å-motbevis" + status-gate
(fanger regresjons-klassen — kjøres ETTER transformasjon)
6. EVAL/OPTIMALISERING rubrikk K1K9 (uavhengig av 15)
```
**Lag 0** og **lag 2** er ikke valgfrie: uten konsolidert taksonomi blir completeness til silent misrouting; uten decision-ledger + dedup drukner gaten operatøren → autonomien blir teater (primær-fallgruve). Verifisering er bevisst splittet i **inn** (lag 3) og **ut** (lag 5) fordi den kjente regresjonen er en *post*-transformasjons-feil.
## Roadmap (multi-sesjon, én økt per sesjon)
| Sesjon | Leveranse | Verifiserbart kriterium |
|---|---|---|
| **1** | **Eval-baseline + ai-foundry-probe** (read-only) | `node eval.mjs --json \| jq '.skills\|length' == 5`; 10 ai-foundry-URLer kategorisert {redirect/dead/ny-sti} med beslutning |
| **2** | Taksonomi-konsolidering (lag 0) + registry-skjema (`authority_source`, `status`, kurs-felt) + ai-foundry signal-fiks | `grep -c "TARGET_PREFIXES = \[" scripts/kb-update/*.mjs == 0`; ai-foundry `not_in_sitemap` < 50 % |
| **3** | Decision-ledger (lag 2) + lukk discovery-løkken gjennom gaten | Discovery 2× re-foreslår ikke en `rejected` URL (dedup-diff) |
| **4** ✅ | Verifisering-ut (lag 5) + adversarial regresjonstest | ✅ Kjent agentic-retrieval-regresjon som fixtur → `flagged:true`, ikke `auto-applied` (se Sesjon 4-resultater) |
| **5** | Transformasjonslag (lag 4) + 2 carry-forward cleanups (foldet inn 2026-06-19) | Regenerer 1 fil → eval-score ≥ baseline |
| **Parallelt spor** | Kurs / Platform API-spike (Entra-app + token + date/product-filter) — **av kritisk vei** | `poll-training.mjs` ≥1 kurs m/ `last_modified`; ELLER kill-switch→legacy dokumentert |
Scheduled deteksjon (`/schedule` eller hook) + wiring i `commands/kb-update.md` = siste integrasjonssteg etter lag 13.
## Sesjon 1 (detaljert)
**Del A — Eval-baseline (lag 6, flyttet fram):** nytt read-only script som kjører K2/K3/K5/K6 (grep/wc) på alle 5 `SKILL.md` + refs, og LLM-judge (Opus) for K1/K4/K7/K8/K9 på de 5 `SKILL.md`. Output `eval-baseline.json` (skrives kun etter operatør-gate) = referansen alt senere måles mot.
**Del B — ai-foundry signal-probe (riskeste antakelse, ren read):** 10 `not_in_sitemap` ai-foundry-URLer → `microsoft_docs_fetch` → kategoriser {finnes/redirect/ny-sti}. Avgjør om Sesjon 2 er en poll-fiks eller en re-mapping-oppgave.
### Rubrikk K1K9 (krav 4, «optimalisert skill»)
- K1 trigger-presisjon (LLM, 20 test-prompts ≥90 %/≤10 %) · K2 tredjeperson/format i description (regex) · K3 SKILL.md ≤500 linjer (wc) · K4 ingen SKILL.md↔ref-duplisering (LLM ≥4/5) · K5 progressive disclosure: navngitte filer ikke mapper (grep, ratio ≥20 %) · K6 kjernefil-routing-tabell finnes (grep) · K7 imperativ-stil (LLM ≥80 %) · K8 kildehenvisning i ref-filer (LLM ≥80 %) · K9 ingen tid-sensitiv info i SKILL.md body (LLM).
## Gjenbruk vs. nybygg (navngitte filer)
**Gjenbruk direkte:** `scripts/kb-update/lib/sitemap-stream.mjs`, `lib/url-normalize.mjs`, `lib/registry-io.mjs`, `lib/atomic-write.mjs` + `lib/backup.mjs` (← for **all** gated skriving), `poll-sitemaps.mjs`-kjernen, `generate-skills.md` 5-parallelle fan-out + `prompt-template.md`.
**Må endres:** `discover-new-urls.mjs` (INCLUDE → poll-paritet + ikke-bare-engineering; skriv decision-ledger-input); `report-changes.mjs` (`getFilePriority` leser taksonomi; datoløse filer = «uverifisert», ikke critical-støy); `build-registry.mjs` (les `Source:`-header → `authority_source`); `commands/kb-update.md` (apply leser decision-ledger); `scripts/skill-gen/category-skill-map.json` (inn i taksonomien).
**Nytt:** `data/domain-taxonomy.json`, `data/decisions.json`, `data/training-registry.json`, `poll-training.mjs`, eval-script (K1K9) + `eval-baseline.json`, doc→KB-destillasjons-prompt, adversarial motbevis-prompt.
**Multi-agent gir kvalitet (senere):** lag 5 (adversarial motbevis-panel per status-påstand), lag 4 (parallell doc→KB-transformasjon). Foreslås når fasene starter.
## Risiko (rangert) og håndtering
1. **Gaten drukner operatøren → autonomi blir teater** (primær). → decision-ledger + dedup (lag 2); riktig klassifisering av datoløse/`not_in_sitemap`-filer (Sesjon 2).
2. **ai-foundry = re-mapping, ikke poll-fiks** (35 % av KB). → testes først (Sesjon 1 Del B).
3. **Platform API-onboarding blokkerer** (ekstern). → av kritisk vei + kill-switch→legacy.
4. **Taksonomi-drift → misrouting.** → lag 0 før discovery lukkes.
5. **Transformasjon innfører regresjon.** → eval-baseline før-måling; lag 5 adversarial-test før vi stoler på laget.
## Verification (overordnet)
- Per-sesjon kriterier (tabell) er kjørbare sjekker.
- Pluginens gates ved hver commit: `validate` (239+), `kb-integrity` (115/115), `gitleaks` clean.
- Arkitektur-invariant: deteksjon skriver *aldri* til KB direkte — kun via `decisions.json` etter gate (deteksjon-scripts importerer ikke write-utils).
## Inneværende scope vs. fremtidig roadmap
**Inneværende mekanisme-arbeid (Sesjon 15 + kurs-spor):** krav 1 currency · 2 completeness · 4 optimalisering — på de eksisterende 5 skills. Innhold kan vokse (nye ref-filer).
**Utsatt til senere faser — på roadmap, IKKE forkastet** (operatørens valg 2026-06-19 var «krav 14 nå»; krav 5/6 var merket «ikke nå», dvs. utsatt):
- **Krav 5 — lage nye skills profesjonelt.** Naturlig neste utvidelse når deteksjonen (lag 1) begynner å avdekke domener uten dekning (fra domene-taksonomien: f.eks. Power Platform AI / AI Builder / Power Automate, Dynamics365). Forutsetter lag 03.
- **Krav 6 — slå sammen/slette skills (livssyklus, destruktive ops).** Krever egne guardrails (aldri tap av kuratert verdi) + obligatorisk operatør-gate. Vurderes etter at lag 16 står.
Annet utsatt arbeid sporet her: **Low-currency-bøtta (51 filer)** — parkert bak redesignet; kjøres med den nye mekanikken (eller som siste enkle bøtte hvis redesignet drar ut).
> Denne seksjonen ER roadmapen for fremtidige faser. Den bor i `docs/` (ikke en egen `ROADMAP.md`) per den globale «ingen lokale kontinuitets-mekanismer»-regelen, og refereres fra `STATE.md`.
---
## Sesjon 1 — resultater (2026-06-19) ✅ FERDIG
Kriterier møtt: `node scripts/kb-eval/eval.mjs --json | jq '.skills|length'` = 5; 10 ai-foundry-URLer kategorisert. Tester: **kb-eval 13 PASS · kb-update 42 PASS (regresjon) · validate 239 PASS**. Node 25 krever eksplisitte filstier (`tests/X/*.test.mjs`), ikke katalog.
### Eval-baseline (referanse for senere optimalisering — lag 6)
Verktøy: `scripts/kb-eval/eval.mjs` (determ. K2/K3/K5/K6 + ref-tall) + operatør-gated LLM-judge (`judge-prompt.md``data/judge-results.json`), flettet til `data/eval-baseline.json`. Tester i `tests/kb-eval/`.
Pass-matrise (K2/K3/K7/K8 ✓ for alle 5):
- **K5 (progressive disclosure): 3/5 FAIL** — advisor (1,6 %), engineering (0 %), security (16 %) bruker mappehenvisninger; infrastructure (97 %) er forbildet å kopiere.
- **K9 (tid-sensitiv info i SKILL.md body): 4/5 FAIL** — GA/preview-status, modellversjoner, SLA-/perf-tall ligger i body (kun advisor ren). Hører i ref-filer (currency-sporet).
- **K4 (duplisering): 2/5 FAIL** — governance (3/5: DPIA/AI-Act/Digdir dupliserer ref-filer), security (3/5).
- **refTall-konsistens: 2/5 FAIL** — engineering (agent-orch tabell 20 vs faktisk 24; total 149 vs 153), governance (29 vs 30).
**To konkrete bugs (operatør-beslutning kreves; fikses i optimaliserings-fase, ikke nå):**
1. **security 6×5-vekting motstrider kanonisk rubrikk:** SKILL.md body (Identity 20/Network 15/Data 20/Content 20/Compliance 15/Mon 10) ≠ `security-scoring-rubrics-6x5.md` (Compliance 25/Data 20/Identity 20/Content 15/Network 10/Mon 10). Påvirker scoring-output. → hvilken er kanonisk?
2. **governance-SKILL.md linje 191** peker på ikke-eksisterende `drift-detection-automated-retraining.md` (faktisk: `model-performance-drift-detection.md`).
K1-presisjon (0,951,0) er **PROVISORISK** — operatør må kuratere 20 trigger-prompts/skill. Flagg: governance-description mangler Schrems II-trigger; advisor-description er bred nok til å over-trigge mot søsken-kommandoer.
### ai-foundry signal-probe → BESLUTNING for Sesjon 2
10 URLer: **8 LIVE · 0 MOVED · 2 DEAD**. Rotårsak: MS rebrandet «Azure AI Foundry» → «Microsoft Foundry»; nye canonical-URLer under `/azure/foundry/` (i `azure_en-us_7`-sitemap), gamle `/azure/ai-foundry/` serverer fortsatt innhold men matcher aldri sitemapet → 100 % `not_in_sitemap`.
**→ POLL-FIKS (ikke re-arkitektur av signalet):** remap registry `azure/ai-foundry/*``azure/foundry/*` + 2 DEAD-remappinger (`concepts/evaluation-evaluators``concepts/built-in-evaluators`; `agent-service``agents/overview`). Sitemap-`lastmod` fra `azure_en-us_7` er tilstrekkelig signal; ingen innholdshash nødvendig.
---
## Sesjon 2 — konkret startplan (lag 0 + registry-skjema + ai-foundry poll-fiks)
**Mål:** konsolidér de fire divergerende taksonomiene til ÉN sannhetskilde, utvid registry-skjemaet, og fiks ai-foundry-blindsonen (poll-fiks). **TDD: skriv test FØR kode.** Invariant: deteksjon skriver aldri til KB; ikke rør krav 5/6.
**Rekkefølge:**
1. **Les først:** `scripts/kb-update/{poll-sitemaps,discover-new-urls,report-changes,build-registry}.mjs`, `lib/{registry-io,url-normalize,atomic-write,backup}.mjs`, `data/url-registry.json` (skjema), og `scripts/skill-gen/{categories.json,category-skill-map.json}`.
2. **`data/domain-taxonomy.json`** (lag 0, ny): samle dagens 4 taksonomier til én — (a) sitemap-prefikser (fra `poll-sitemaps.mjs` `TARGET_PREFIXES`), (b) discover INCLUDE/EXCLUDE (utvid til poll-paritet: mangler `microsoftteams`/`sharepoint`/`microsoft-365`/`training`/`cloud-computing`/`privacy`; og ikke-bare-engineering-skill-mapping), (c) skill+kategori-mapping (`category-skill-map.json`), (d) prioritet (`report-changes.mjs` `getFilePriority`).
3. **Refaktorer** `poll-sitemaps.mjs` + `discover-new-urls.mjs` + `report-changes.mjs` til å LESE taksonomien. **Kriterium:** `grep -c "TARGET_PREFIXES = \[" scripts/kb-update/*.mjs` == 0.
4. **Registry-skjema** (bakoverkompatibelt): legg til `authority_source`, `status`, kurs-felt. `build-registry.mjs` leser `**Source:**`/`**Primary source:**`-header når den finnes (i dag 0 % dekning — ikke krav å backfille alle nå).
5. **ai-foundry poll-fiks:** remap registry `azure/ai-foundry/*``azure/foundry/*` + 2 DEAD-remappinger (over). Bruk `lib/backup.mjs` + `lib/atomic-write.mjs` for skriving. **Kriterium:** ai-foundry `not_in_sitemap` < 50 % (fra 100 %).
6. **Tester** (`tests/kb-update/` el. ny `tests/kb-eval/`): taksonomi-lasting + remap-logikk. Node 25 → kjør med eksplisitt filsti.
7. **Avslutt:** `validate` 239 + alle enhetstester grønne · commit per logisk enhet · push (i vindu 2023 hverdag) · oppdater STATE.md (Sesjon 3 neste) + denne roadmapen (Sesjon 2-resultater).
**Ikke i Sesjon 2:** de 2 bug-fiksene (6×5-vekting, governance ref-path) — de hører i optimaliserings-fasen og venter på operatør-beslutning (kanonisk vekting).
---
## Sesjon 2 — RESULTATER (FERDIG, 2026-06-19)
**Begge akseptansekriterier møtt:** `grep -c "TARGET_PREFIXES = \["` == 0 ✅ · foundry `not_in_sitemap` **38,4 %** (63/164, fra 100 %) < 50 % ✅. Tester: validate 239 · kb-update 66 (+24) · kb-eval 13 — alle grønne. 3 commits (8bea3a2 lag 0 · eb2a002 skjema · d3af3f7 remap) + docs.
**Levert:**
- **Lag 0:** `data/domain-taxonomy.json` (tracket via gitignore-negasjon `data/*` + `!domain-taxonomy.json`; generert registry/rapporter forblir ignorert). `lib/taxonomy.mjs` laster + kompilerer. `poll-sitemaps`/`discover-new-urls`/`report-changes` LESER taksonomien — ingen embeddede kopier.
- **Registry-skjema (minimal-reservert, operatør-valg):** `authority_source:null` + `course:null` på alle 1343 entries. `build-registry` leser `**Source:**`-header via `lib/kb-headers.mjs` og logger dekning (0/N i dag). Eksisterende poll-`status` urørt (unngikk kollisjon).
- **ai-foundry-fiks:** `lib/registry-migrate.mjs` + one-shot `migrate-ai-foundry.mjs` (backup + atomisk). 164 remappet, 2 DEAD eksplisitt, 1 kollisjon merget, remappede → `unpolled`.
**Konsolideringsfunn (binding for Sesjon 3):**
- **`category-skill-map.json` er stale i 4 entries** (`copilot-extensibility`, `monitoring-observability`, `performance-scalability`, `prompt-engineering` → sier `engineering`; **disk** sier advisor/governance/security/advisor). Disk er kanon; taksonomien bruker disk-sannhet. Map-en er **ukonsumert av kode** (kun README) → flagget, ikke rørt. **Sesjon 3-kandidat:** korriger/avskilt `category-skill-map.json` + README, ELLER bekreft taksonomien som eneste kilde.
- **Datoløse-filer-reklassifisering** (getFilePriority: datoløs = «uverifisert» i stedet for critical-støy): **utsatt** (ekskludert fra Sesjon 2 per operatør-valg — adferdsbevarende refaktor nå). **Sesjon 3-kandidat.**
- **foundry/openai-tail:** 63 gjenstående `not_in_sitemap` er nesten utelukkende `/azure/foundry/openai/*` (egen URL-struktur, ikke probet). Ingen gjettede remaps. Kandidat for målrettet probe senere.
---
## Sesjon 3 — konkret startplan (lag 2: decision-ledger + lukk discovery-løkken)
**Mål:** bygg `data/decisions.json` som **eneste skrive-autoriserte bro** mellom deteksjon og KB/registry, med **dedup** så avviste kandidater aldri re-foreslås. Dette løser primær-fallgruven (gaten drukner operatøren → autonomi blir teater). **TDD: skriv test FØR kode.** **Invariant (§83): deteksjon-scripts importerer ALDRI write-utils** (`atomic-write`/`backup`/`saveRegistry`) — de kun LESER ledger for å filtrere; skriving til ledger skjer kun via operatør-gate.
**Rekkefølge:**
1. **Les først:** `scripts/kb-update/discover-new-urls.mjs` (kandidat-output `discovery-report.json`: `{url, lastmod, sitemap, suggested_skill, suggested_category}`), `commands/kb-update.md` (apply-flyt = gaten), `lib/registry-io.mjs` (`saveReport`/atomisk-mønster å speile), `lib/taxonomy.mjs` + `lib/registry-migrate.mjs` (Sesjon 2-mønstre for ren-funksjon-+-tynn-script-split).
2. **`data/decisions.json`-skjema** (lag 2, nytt — tracket som `domain-taxonomy.json`, IKKE generert): ledger keyet på normalisert URL. Per entry: `{status: pending|approved|rejected, decided_at, suggested_skill, suggested_category, note?}` + toppnivå `version`/`updated_at`. **Beslutningspunkt:** dedup ekskluderer KUN `rejected`, eller alle `decided` (approved teller som «allerede håndtert»)? Anbefalt: ekskluder alle `decided` fra re-forslag; `pending` beholdes til operatør avgjør.
3. **`lib/decisions-io.mjs`** (ren I/O, speil `registry-io`): `loadDecisions`/`saveDecisions` (atomisk), `isDecided(ledger, url)`, `recordDecision(ledger, url, decision)`. TDD-test først.
4. **discover dedup:** `discover-new-urls.mjs` LESER `decisions.json`, filtrerer ut alt som er `isDecided` FØR kandidater emitteres. Nye → `pending` (eller fravær = ubesluttet). **Kriterium:** kjør discover 2×, en `rejected`-URL re-foreslås IKKE (dedup-diff-test mot fixture).
5. **Gate-wiring (`commands/kb-update.md` apply):** apply-steget viser `pending`-kandidater → operatør approve/reject → skriver `decisions.json` (ENESTE skrivevei) → `approved` registreres i `url-registry` (gated, via `registry-io`). Script-siden leverer ledger-I/O + «pending»-visning; selve interaktive apply er LLM-drevet (slash-command). Dokumentér flyten.
6. **Invariant-guard-test:** assert at `discover-new-urls.mjs` IKKE importerer `atomic-write`/`backup`/`saveRegistry` (grep-test). + decisions-io + dedup-tester. Node 25 → eksplisitt filsti.
7. **Avslutt:** `validate` 239 + alle enhetstester grønne · commit per logisk enhet · push (vindu 2023 hverdag) · oppdater STATE.md (Sesjon 4 neste) + denne roadmapen (Sesjon 3-resultater).
**Også vurder i Sesjon 3 (Sesjon 2-funn, operatør-beslutning):** (a) `category-skill-map.json` stale i 4 entries — korriger/avskilt + README, ELLER bekreft taksonomi som eneste kilde; (b) datoløse-filer-reklassifisering i `getFilePriority` (datoløs = «uverifisert», ikke critical-støy). Begge er små; ta dem hvis tid, ellers eksplisitt til Sesjon 4.
**Ikke i Sesjon 3:** lag 3 (verifisering-inn), lag 4/5 (transformasjon/verifisering-ut), kurs-sporet, de 2 SKILL.md-bugene (6×5-vekting, governance ref-path — venter på kanonisk-vekting-beslutning).
---
## Sesjon 3 — RESULTATER (FERDIG, 2026-06-19)
**Akseptansekriterium møtt:** dedup-diff — en `rejected` URL foreslås i runde 1, men **ikke** i runde 2 (test `dedup-diff — a rejected URL is re-proposed in round 1 but NOT in round 2`). Tester grønne: validate **239** · kb-update **82** (+16) · kb-eval **13** · kb-integrity **115/115**.
**Dedup-policy = A (operatør-valg 2026-06-19):** `isDecided(url)` = enhver ledger-entry. Alt operatøren har tatt stilling til — approved, rejected *og* utsatt (pending) — dedupes; discovery re-foreslår kun URLer som er **helt fraværende** fra ledgeren. Renest å resonnere om, og den eneste som fullt nøytraliserer primær-fallgruven (gate-støy). Pending vises i egen bøtte fra ledgeren, ikke som påtrengende re-forslag.
**Levert:**
- **`data/decisions.json` (lag 2):** tracket via gitignore-negasjon (som `domain-taxonomy.json`; generert registry/rapporter forblir ignorert). Skjema: `{version, updated_at, decisions: {[normalisertUrl]: {status, decided_at, suggested_skill, suggested_category, note?}}}`.
- **`lib/decisions-io.mjs`** (speiler `registry-io`-stil, null avhengigheter): `createLedger` · `loadDecisions` · `saveDecisions` (atomisk .tmp→rename) · `isDecided` (policy A) · `recordDecision` (ren, muterer ikke) · `filterUndecided`. TDD: 10 tester FØR kode.
- **discover-wiring:** `discover-new-urls.mjs` LESER ledger (`loadDecisions`+`isDecided`), filtrerer i kandidat-løkken, rapporterer `deduped_by_ledger` i `discovery-report.json` + logg. **Importerer ALDRI write-utils.**
- **Gate dokumentert** i `commands/kb-update.md` §3b («Discovery-gate»): eneste skrivevei = `recordDecision`+`saveDecisions`; `approved``url-registry` (gated via `saveRegistry`); `decided_at` caller-injisert. Apply er LLM-drevet per design (ikke eget apply-script — lib gir deterministisk skrivevei).
- **Invariant-guard-test** (`test-discover-invariant.test.mjs`, 6 tester): import-spesifikk sjekk at discover IKKE importerer `saveDecisions`/`saveRegistry`/`atomic-write`/`backup` (kommentarer kan ikke gi falske positiver) + positiv sjekk at `loadDecisions`/`isDecided` faktisk er wiret.
**Binding for Sesjon 4:**
- **IKKE tatt** (var «hvis tid»): `category-skill-map.json`-stale (4 entries) og datoløse-fil-reklassifisering i `getFilePriority`. Prioriterte kjerneleveransen + sporet onboarding-redesign i stedet. **Eksplisitt til Sesjon 4.**
- **Nytt sporet sidespor (operatør-flagget 2026-06-19):** onboarding-/virksomhetskontekst-mekanismen oppfyller ikke «ambient i enhver interaksjon» + «overlever oppgraderinger» + mangler fritekst-felt → egen brief `docs/onboarding-redesign-brief.md` (task #26). Utenfor KB-redesignet; design godkjennes separat.
- **Neste — Sesjon 4:** verifisering-ut (lag 5) + adversarial regresjonstest. Kriterium: kjent agentic-retrieval-regresjon som fixtur → `flagged:true`, ikke `auto-applied`.
---
## Sesjon 4 — RESULTATER (FERDIG, 2026-06-19)
**Akseptansekriterium møtt:** den kjente agentic-retrieval-regresjonen som fixtur → `verdict: 'flagged'`, `status_claim: true`, IKKE `auto-applied` (test `agentic-retrieval regression fixture → flagged, NOT auto-applied`). Tester grønne: validate **239** · kb-update **95** (+13) · kb-eval **13** · kb-integrity **115/115**. gitleaks: 3 PRE-EKSISTERENDE i `playground/vendor/` (utenfor scope); nye filer rene.
**Hva lag 5 er:** verifisering-UT kjører ETTER transformasjon (lag 4) og FØR en kandidat-endring skrives til en KB-fil. Den fanger regresjons-klassen — en status-påstand «korrigert» mot en *tilfeldig sitert* side og stille auto-applyet. Den faktiske regresjonen ligger i KB: `agentic-rag-patterns.md` beskriver «delvis GA via REST `2026-04-01`; answer synthesis + multi-turn forblir preview» — en naiv auto-correct ville flatet dette til «GA» og slettet nyansen.
**Regel (spec §21):** status-påstander (GA/preview/versjon/pris) flagges **ALLTID**, aldri auto-applyet — uansett hvor sikker evidensen ser ut. Denne regelen alene bærer kriteriet.
**Levert:**
- **`lib/verify-out.mjs`** (ren klassifiserer, null avhengigheter — speiler `decisions-io`): `detectStatusClaim(text)``{isStatus, kinds[]}` (release-stage / version / price; `\bGA\b` er case-SENSITIV for å unngå norsk «ga»; bare kalenderdato ≠ status, kun `-preview`-versjoner teller). `classifyChange(change)``{verdict: 'flagged'|'auto-applied', status_claim, reasons[]}` med tre flag-regler: (1) **status-gate** (§21), (2) **adversarial refutering** (`refutations[].refuted===true`), (3) **autoritets-mismatch** (`source_url` ≠ utpekt `authority_source` — regresjonens rotårsak; inert mens `authority_source` er null pre-lag-3). Konservativ med vilje: ved tvil flagges. **SKRIVER ALDRI** — returnerer kun en verdict.
- **Fixtur** `tests/fixtures/kb-update/agentic-retrieval-regression.json`: den kjente regresjonens FORM (flat «generally available (GA)» mot nyansert «delvis GA … resten preview», forankret mot ikke-autoritativ `whats-new`-side). Verdiene er illustrative for regresjonen, ikke en currency-påstand (verifiseringsplikt).
- **TDD:** 13 tester FØR kode (`tests/kb-update/test-verify-out.test.mjs`), inkl. import-invariant (`verify-out.mjs` importerer ingen write-utils — ren klassifiserer).
- **Wiret i `commands/kb-update.md` §4 c2:** lag 5 kjører mellom (c) identifiser-endring og (d) skriv. `flagged` → operatør avgjør; `auto-applied` → trygt. Adversarial motbevis-panel er LLM-runtime (multi-agent foreslås i produksjon, §71).
**Binding for Sesjon 5:**
- **FOLDET INN i Sesjon 5** (operatør-valg 2026-06-19) — to carry-forward cleanups, bevisst holdt utenfor lag-5-kriteriet:
- **(A) `category-skill-map.json` — 4 stale entries:** `copilot-extensibility`/`monitoring-observability`/`performance-scalability`/`prompt-engineering` sier `engineering` i map-fila, men disk-sannhet er advisor/governance/security/advisor (Sesjon 2-funn §151). Map-en er **ukonsumert av kode** (kun README). → korriger map + README, ELLER bekreft `domain-taxonomy.json` som eneste kilde og deprecér/slett map-fila.
- **(B) datoløse-fil-reklassifisering i `getFilePriority` (`report-changes.mjs`):** datoløse filer skal være «uverifisert»-prioritet, ikke critical-støy (Sesjon 2-funn §152). Adferdsendrende refaktor → TDD, test FØR kode.
- **`authority_source`-feltet er fortsatt 0 % dekning** (registry); lag 5-regel 3 (autoritets-mismatch) er derfor inert i praksis i dag — den aktiveres når **lag 3 verifisering-INN** (egen fremtidig fase) backfiller feltet. Status-gaten (regel 1) er full virksom uavhengig.
- **Neste — Sesjon 5:** transformasjonslaget (lag 4, doc→KB-fil, LLM-destillasjon med obligatorisk status-felt) **+ cleanup (A) og (B) over**. Kriterium (lag 4): regenerer 1 fil → eval-score ≥ baseline. Lag 5 (Sesjon 4) er motbevis-gaten lag 4 mates gjennom før skriving.
---
## Sesjon 5 — RESULTATER (FERDIG, 2026-06-19)
**Akseptansekriterium møtt:** «regenerer 1 fil → eval-score ≥ baseline» — test `regenerate 1 real file → valid, dated, source-anchored, body preserved, same path` + `live deterministic eval ≥ baseline for ms-ai-infrastructure` (`tests/kb-eval/test-transform-criterion.test.mjs`). Tester grønne: **validate 239 · kb-update 111 (+16) · kb-eval 15 (+2) · kb-integrity 115/115**. gitleaks: rent på changeset (3 pre-eksisterende i `playground/vendor/` urørt).
**Lag 4 levert (`lib/transform.mjs`, ren lib, SKRIVER ALDRI — speiler decisions-io/verify-out):**
- `buildKbHeader({title, status, category, source, lastUpdated})` → deterministisk header. **`Status` + `Source` obligatoriske** (kaster ved manglende felt). **Nøkkelfunn/-fiks:** den gamle `prompt-template.md` la kilder i en bunn-seksjon, men `build-registry`/`kb-headers.parseSourceHeader` skanner kun **øverste 500 bytes** → derfor `authority_source`-dekning = 0 %. Lag 4 legger `**Source:**` i header-blokka → det er dette som lar lag 3 (fremtidig) backfille `authority_source` og lag-5 regel 3 fyre.
- `validateKbFile(content)``{valid, missing[]}` (title/last_updated/status/source); bruker `parseSourceHeader` så «hva er en Source-header» har én sannhetskilde med build-registry.
- `buildChange({field, oldValue, newValue, sourceUrl, authoritySource, refutations})` → eksplisitt lag-4→lag-5-bro; mater rett inn i `classifyChange` (verifisert: status-endring → `flagged`, ikke-status → `auto-applied`).
- `resolveTargetPath(tax, category, filename)` → ruter via taksonomiens `getCategorySkill` (disk-sann); `null` = ukjent kategori → gate, ikke skriv.
- **Prompt:** `scripts/kb-update/transform-prompt.md` (doc→KB-destillasjon; gjenbruker husformatet fra `prompt-template.md`, men Source i header + status-påstander eksplisitt for lag-5-gaten). Deterministisk header bygges av lib-en, ikke modellen.
- **Wiret i `commands/kb-update.md`:** §3.d (ny godkjent URL → fetch → destillér → buildKbHeader → validateKbFile → buildChange→lag 5 → resolveTargetPath → operatør-gate → atomisk skriving) + §4.d (oppdateringer må også passere `validateKbFile`; legg Source-header der pre-lag-4-filer mangler den). `eval.mjs` eksporterer nå `evalSkill` for kriterium-testen.
- **TDD:** 12 lib-tester (`test-transform.test.mjs`) inkl. import-invariant (ingen write-utils) + 2 kriterium-tester FØR kode.
**Carry-forward A — `category-skill-map.json` (operatør-valg 2026-06-19: SLETT + rydd README):** premiss verifisert mot kode — fila var ukonsumert av all runtime (`docs/development.md:60` erklærte allerede taksonomien som eneste kilde; kun `README.md:654` kalte den feilaktig «used by generate-skills»). Slettet (`git rm`); README peker nå på `domain-taxonomy.json` via `lib/taxonomy.mjs` `getCategorySkill`. Fjerner en divergerende taksonomi helt (redesignets mål). `test-taxonomy.test.mjs`-kommentarer beholdt som historikk (leser kun taksonomien, ikke map-fila).
**Carry-forward B — datoløse filer (TDD):** rotårsak: datoløs fil fikk `effectiveDate='0000-01-01'` (alltid «stale») **+** path-basert prioritet (kunne bli `critical`) → flommet critical-bøtta. Fix: ny **`unverified`-tier** deklarert i `domain-taxonomy.json` (lag 0). `makePriorityFn` tar nå valgfri `hasDate` (default `true` → bakoverkompatibelt); `report-changes` sender `Boolean(fileDate)`. `unverified` rangerer sist i sorteringen (metadata-hygiene, ikke currency-hast). Verifisert mot ekte registry: `Unverified: 1` skilt ut fra path-bøttene. TDD: 4 tester FØR kode (`test-priority-unverified.test.mjs`).
**Binding for neste fase (numerert roadmap Sesjon 15 er nå FERDIG):**
- 6-lags-arkitekturen står strukturelt: lag 0 (Sesjon 2) · lag 1 currency+completeness (eksisterende + Sesjon 3) · lag 2 (Sesjon 3) · lag 3 (Sesjon 6) · lag 4 (Sesjon 5) · lag 5 (Sesjon 4) · lag 6 eval (Sesjon 1).
- **Gjenstår (ikke numerert — operatør prioriterer):** ~~(1) lag 3 verifisering-INN~~ ✅ FERDIG Sesjon 6. (2) **Optimaliserings-fase (krav 4-anvendelse):** fiks de målte eval-FAIL-ene (K5 progressive disclosure advisor/eng/security, K9 tid-sensitiv i body, K4 duplisering, refTall) + de 2 SKILL.md-bugene (6×5-vekting kanonisk? + governance ref-path) — krever operatør-beslutning på kanonisk vekting. (3) **Kurs-spor** (task #25, parallelt, av kritisk vei). (4) **Scheduled deteksjon** + siste integrasjon. (5) **Low-currency-bøtta (51 filer).**
---
## Sesjon 6 — RESULTATER (FERDIG, 2026-06-19)
**Akseptansekriterier møtt:** `authority_source`-dekning **0,22 % (3/1353)** fra 0 ✅ · lag-5 regel 3 demonstrert virksom ende-til-ende med ekte data (test `rule 3 fires end-to-end on a real authority: source_url ≠ header authority → flagged`, `test-authority.test.mjs`) ✅. Tester grønne: **validate 239 · kb-update 122 (+11) · kb-eval 15 · kb-integrity 115/115**. gitleaks: rent på changeset.
**Designvalg (operatør 2026-06-19): HEADER-AS-TRUTH.** En fils `**Source:**`-header ER den utpekte autoriteten for dens påstander. Vi gjetter ALDRI autoritet fra en fils mange siterte URLer — datagrunnlaget viste **251/303 ref-filer siteres av 410 URLer hver** (ingen entydig kilde å utlede; en gjettet autoritet ville fått regel 3 til å bomme + 303 operatør-beslutninger = primær-fallgruven). Dekning vokser organisk når lag 4 regenererer filer (lag 4 skriver alltid Source i header).
**Levert:**
- **`lib/authority.mjs`** (ren lib, SKRIVER ALDRI — speiler decisions-io/verify-out/transform; importerer kun read-only `kb-headers` + `url-normalize`): `resolveAuthority(content)` → deklarert `**Source:**`-URL (validert som http(s); fri tekst avvises — verifiseringsplikt; ikke-MS-autoritet som EU-forordning tillates) · `collectDeclaredSources(contents[])` → normalisert Set (kun MS Learn, matcher registry-nøkler) · `markUrlAuthority(url, set)` → url|null · `authorityCoverage(registry)``{withAuthority,total,ratio}`.
- **`build-registry.mjs` wiret:** samler deklarerte kilder fra headere, setter per URL-entry `authority_source = url` iff en fil deklarerer den (recomputes fra headere = sannhet, ikke carry-over), logger dekning.
- **`commands/kb-update.md` §4 c1 (ny):** `authority_source = resolveAuthority(<fil>)` mates inn i lag-5 `classifyChange` (c2). §4.d/§3.d oppdatert: lag 3 er live (ikke «senere»).
- **Bootstrap (gated KB-skriving):** `**Source:**`-header skrevet til de **3 entydige** (1-sitert-URL) filene med rent topisk treff: `late-chunking-patterns.md` → embeddings-tutorial · `ai-red-team-operations-practical.md` → ai-red-team/training · `batch-api-usage-optimization.md` → batch-blob-storage.
- **TDD:** 11 tester FØR kode inkl. import-invariant + ende-til-ende regel 3.
**Verifiseringsfunn (binding):** den 4. «entydige» fila — `ai-act-annex-iii-checklist.md``purview/compliance-manager-assessments` — ble **bevisst utelatt** fra bootstrap: dens egentlige autoritet er EU-forordningen selv (fila deklarerer allerede `**Hjemmel:** Regulation (EU) 2024/1689, Annex III`), ikke en MS Purview compliance-tool-side. Å sette Purview-URLen som autoritet ville vært en gjetning som får regel 3 til å bomme. **Kandidat for senere:** sett `**Source:**` til EUR-Lex-URLen (`resolveAuthority` aksepterer ikke-MS-autoriteter; den normaliseres bare ikke inn i den MS-keyede registryen — som er riktig).
- **`url-registry.json` er gitignored (generert):** `authority_source` er derfor avledet on-demand fra de committede `**Source:**`-headerne (sannhetskilden), ikke en committet verdi. `build-registry --merge` regenererte registryen à jour (1343→1353 URLer; build-registry er det utpekte verktøyet).
---
## Sesjon 7 (SPOR A — krav 4-optimalisering, deterministisk del) — RESULTATER (FERDIG, 2026-06-20)
**Akseptansekriterier møtt:** K5 PASS for advisor/eng/security · refTall PASS for eng+gov · eval ellers ikke regredert (`eval --json` = akseptansemål). Tester grønne: **validate 239 · kb-eval 15 · kb-update 122 · kb-integrity 181/181** (opp fra 115). gitleaks: rent på changeset.
**Steg 1 — refTall-konsistens (commit `3c097f5`):** engineering `agent-orchestration` 20→24 (faktisk på disk) + total 149→153; fjernet inkonsistent 24/20-dobbeltsitering. governance `norwegian-public-sector-governance` 29→30. Verifisert: eval `refCountConsistency` PASS for alle 5.
**Steg 2 — døde ref-paths (commit `3c097f5`):** governance §4 monitoring-bullets pekte på ikke-eksisterende filer. STATE flagget kun L191; **verifiseringsfunn:** L190 var også død. Begge fikset til ekte disk-filer: `azure-monitor-ai-services-setup``azure-monitor-setup-ai-workloads`, `drift-detection-automated-retraining``model-performance-drift-detection`. **Løser den ene av de «2 SKILL.md-bugene» (governance ref-path).**
**Steg 3 — K5 progressive disclosure (commit `abac74f`, operatør-gated kuratering godkjent 2026-06-20):** fulgte infrastructure-forbildet (0,97) — full-sti `references/<mappe>/<fil>.md`-pekere til kjernefiler.
- **Nøkkelfunn:** security + advisor hadde filene navngitt allerede, men som **bare-filnavn uten `references/`-prefiks** → ufanget av både eval-regex OG kb-integrity Check 2 (samme klasse som de døde ref-paths). K5-«failure» for disse to var i stor grad et sti-format-artefakt. Fiks = konverter bare→full sti (konservativt, bruker eksisterende kuratering).
- **security:** 10→16 navngitte (0,16→0,26). Konverterte 5 perf-filer (§3) + owasp (§1). Beholdt eksisterende kuratering (inkl. `gpu-compute-sizing` m/ `/architect:cost --capacity`-note) i stedet for å bytte sett.
- **advisor:** 1→25 navngitte (0,016→0,40). Konverterte ~18 bare-filnavn i Kunnskapsbasen + la til `model-catalog-2026` og entry-points for `copilot-extensibility`/`prompt-engineering` (40 filer som manglet ALLE navngitte pekere).
- **engineering:** 0→35 navngitte (0,0→0,23). Genuint 0 før (kun mappe-refs); la til `> **Kjernefiler:**`-linje med 4-6 kuraterte filer per §1-7.
- **Bivirkning (positiv):** kb-integrity Check 2 valideringer 115→181 (de nye full-sti-refsene fanges nå), orphan-warnings 260→223.
**Gjenstår i SPOR A (beslutning-gated — krever operatør-input, tas «når vi når dem»):**
1. **6×5-vekting (den andre SKILL.md-bug-en):** security SKILL.md body-vekting (Identity 20/Network 15/Data 20/Content 20/Compliance 15/Mon 10) ≠ kanonisk `security-scoring-rubrics-6x5.md` (Compliance 25/Data 20/Identity 20/Content 15/Network 10/Mon 10). **HVILKEN er kanonisk?** Påvirker scoring-output.
2. **K9 (tid-sensitiv info i SKILL.md body): 4/5 FAIL** — GA/preview-status, modellversjoner, SLA-/perf-tall i body. Hører i ref-filer (overlapper currency-sporet). Body-rydding = adferdsendrende → TDD.
3. **K4 (duplisering): 2/5 FAIL** — governance + security dupliserer ref-filer i body. Krever vurdering av hva som flyttes ut.
4. **K1 (trigger-presisjon):** PROVISORISK — operatør må kuratere 20 trigger-prompts/skill. Flagg: governance-description mangler Schrems II-trigger; advisor-description over-trigger mot søsken-kommandoer.
**Spor B (senere, samme tema):** scheduled deteksjon · kurs-spor (task #25) · low-currency-bøtta (51 filer) · bredere `**Source:**`-backfill.