--- name: architect:kb-update description: Manuell oppdatering av kunnskapsbasen — poller Microsoft Learn-sitemaps, sammenligner mot lokale `Last updated`-headere, oppdaterer endrede filer og oppdager nye relevante URLer argument-hint: "[valgfritt: --skip-discover | --priorities critical,high,medium,low | --dry-run]" allowed-tools: Bash, Read, Edit, Write, Glob, Grep, mcp__microsoft-learn__microsoft_docs_search, mcp__microsoft-learn__microsoft_docs_fetch, mcp__microsoft-learn__microsoft_code_sample_search model: opus --- # /architect:kb-update — Manuell KB-oppdatering Holder Microsoft AI-kunnskapsbasen i `skills/*/references/` ferskt ved å sammenligne lokale referansefiler mot Microsoft Learn-sitemaps. **Apply (oppdatering av KB-filer) er alltid manuell og kjøres in-session** — den bruker Claude og forblir under operatør-gate. **Deteksjon** (poll → rapport → discovery → skill-livssyklus) er ren node som aldri kontakter Claude/Anthropic, og kan **valgfritt schceduleres** (se «Scheduled deteksjon» under). Skillet er ToS-forankret: Consumer Terms §3.7 begrenser automatisert tilgang til Anthropics tjenester — ikke kjøring av lokale scripts som ikke rører Claude. ## Hva kommandoen gjør 1. **Polle sitemaps:** kjører `node scripts/kb-update/run-weekly-update.mjs --force` for å hente fersk `` for hver Microsoft Learn-URL i registeret 2. **Optional discovery:** med default `--discover` finner nye relevante URLer i sitemap som ikke er i registeret (`scripts/kb-update/discover-new-urls.mjs --limit 500`). Discovery **leser beslutnings-ledgeren** (`data/decisions.json`, lag 2) og utelater alt operatøren allerede har tatt stilling til (policy A: approved/rejected/pending) — så de samme kandidatene drukner ikke gaten kjøring etter kjøring 3. **Generere endringsrapport:** `report-changes.mjs` produserer `data/change-report.json` med per-fil prioritering (critical/high/medium/low) basert på antall endrede kilder + alder på lokal fil 4. **Vise rapporten:** lese rapport, presentere oppsummering til bruker, vente på `go` 5. **Oppdatere filer:** for hver fil i valgt prioritetsbøtte (default: critical + high): - Hente fersk innhold fra alle endrede kildene via `microsoft_docs_fetch` - Oppdatere relevante seksjoner i den lokale `.md`-fila - Oppdatere `Last updated:`-header til dagens dato 6. **Committe:** én git-commit per fil med `chore(ms-ai-architect): refresh KB [skip-docs]`-format (eller én samlet commit om brukeren foretrekker det) ## Argumenter | Flagg | Effekt | |-------|--------| | `--skip-discover` | Hopp over discovery-passet (raskere, ingen nye URLer oppdages) | | `--priorities ` | Komma-separert subset av `critical,high,medium,low`. Default: `critical,high` | | `--dry-run` | Generer rapport, men ikke oppdater filer eller committ | | `--single-commit` | Samle alle filendringer i én commit i stedet for én per fil | ## Scheduled deteksjon (opt-in, Spor C / C1) Deteksjonen kan kjøre automatisk i bakgrunnen ved sesjonsstart — **av som default**. Den er frivillig å sette opp, men virker når aktivert. **Slik aktiverer du den:** opprett `ms-ai-architect.local.md` i plugin-roten (gitignored) med: ```yaml --- scheduled_detection: enabled: true # default false — ingenting kjører før dette er true (Tier 1) interval_days: 7 # kjør deteksjon på nytt når det er ≥ N dager siden sist poll include_skill_lifecycle: true # ta med skill-livssyklus-deteksjon (overlapp/gap/bloat) os_scheduler_cadence: daily # Tier 2: daily (poll hver dag) | interval (throttle på interval_days) --- ``` ### Tier 1 — sesjonsforankret (SessionStart-hook) Når `enabled: true` og det er ≥ `interval_days` siden sist poll, spawner SessionStart-hooken `scripts/kb-update/run-detection.mjs` i bakgrunnen. Den kjører **kun deteksjon** (poll → rapport → discovery → skill-livssyklus) og skriver JSON-rapporter til `data/` — **aldri** til `skills/`, og kaller **aldri** Claude. Ferske signaler surfaces ved neste sesjonsstart («KB: …» + «Skill-signaler: …»), og du kjører `/architect:kb-update` manuelt for å gjennomgå + apply-e gjennom gaten. **Begrensning:** Tier 1 kjører bare når en Claude-sesjon *starter* — starter du aldri en sesjon, kjører deteksjonen aldri. ### Tier 2 — lokal launchd-scheduler (ekte bakgrunn mellom sesjoner) For deteksjon som kjører *uavhengig av sesjoner*, installer en lokal **launchd LaunchAgent** som fyrer samme Claude-frie entrypoint daglig (kl. 03:00): ```bash node scripts/kb-update/scheduler.mjs install # skriv plist til ~/Library/LaunchAgents + last agenten node scripts/kb-update/scheduler.mjs status # er den lastet? node scripts/kb-update/scheduler.mjs run-now # kjør én gang nå (verifiser uten å vente til 03:00) node scripts/kb-update/scheduler.mjs print # vis plisten uten å skrive noe (alias --dry-run) node scripts/kb-update/scheduler.mjs uninstall # avlast + fjern plist ``` - **Kadens (`os_scheduler_cadence`):** `daily` (default) poller hver dag; `interval` throttler til `interval_days` via samme staleness-gate som Tier 1. (Onboarding setter dette senere; daglig er fornuftig default — poll av Microsoft Learn-sitemaps er billig og read-only.) - **Pause = uninstall.** LaunchAgenten kjører uansett `enabled:`-flagget (det styrer *kun* Tier 1/hooken). Vil du stoppe Tier 2, kjør `uninstall` — det er pause-knappen. - **Etter en major `brew upgrade node`:** kjør `install` på nytt. Node-stien bakes inn ved install-tid (launchd har minimalt `PATH`); plistens `PATH` inkluderer `/usr/local/bin` så barneprosessenes `node` overlever, men det er ryddigst å re-installere. - **launchd, ikke cron:** Apple anbefaler launchd; cron krever Full Disk Access og er «not recommended». Vil du heller bruke cron/systemd/CI, peker du den på samme `node scripts/kb-update/run-detection.mjs`. **ToS-garanti (strukturell, begge tiers):** entrypointet (`run-detection.mjs`, og `scheduler.mjs run` som delegerer til det) spawner kun `node` på de allow-listede deteksjons-scriptene + `launchctl` for agent-styring; det kan ikke invokere `claude`. Apply (det eneste Claude-steget) forblir manuelt og in-session. Kjør `node scripts/kb-update/run-detection.mjs --dry-run` (eller `scheduler.mjs print`) for å se hva som ville kjørt uten å kjøre noe. ## Instruksjoner til assistenten ### 1. Pre-flight - `pwd` — bekreft at du står i `plugins/ms-ai-architect/` (eller delegere via absolutt sti) - `git status --porcelain | grep -E '\.md$' && echo "WARN: ucommittede skill-endringer — kommandoen vil blande dem inn"` — advar bruker hvis det finnes lokale skill-endringer - Parse argumenter ### 2. Kjør pollingsfasen ```bash node scripts/kb-update/run-weekly-update.mjs --force${ARG_DISCOVER} ``` Hvor `${ARG_DISCOVER}` er `--discover` med mindre `--skip-discover` ble gitt. Output forventes å skrive `data/change-report.json` og evt. nye registry-entries hvis discovery kjørte. ### 3. Vis rapport-oppsummering ```bash node scripts/kb-update/report-changes.mjs | head -40 ``` Presenter til bruker: - Antall filer per prioritet - Hvilke prioriteter som blir behandlet (default: critical + high) - Estimert antall `microsoft_docs_fetch`-kall (≈ sum av endrede kilder per fil) - Spør: "Fortsett med oppdatering? (y/n)" Hvis `--dry-run`: stopp her, ikke oppdater filer. ### 3b. Discovery-gate — nye URLer via decision-ledger (lag 2) Kjørte discovery (default), lukk løkken her. Dette er **operatør-gaten** — det eneste stedet beslutninger om nye URLer skrives. a. **Les kandidatene:** `data/discovery-report.json` → `candidates[]`. Listen er allerede dedupet av scriptet mot både registry og `data/decisions.json`, så alt her er genuint ubesluttet. Feltet `deduped_by_ledger` viser hvor mange tidligere beslutninger som ble utelatt. b. **Presenter for operatør**, gruppert per `suggested_skill` (vis `url`, `suggested_category`, `lastmod`). For hver kandidat (eller batch): **approve** (ta inn i KB), **reject** (irrelevant — ikke vis igjen), eller **utsett** (`pending` — ikke avgjort, vises i pending-bøtta men re-foreslås ikke). c. **Skriv beslutningene til ledgeren — ENESTE skrivevei.** Bruk `lib/decisions-io.mjs` (ikke håndskriv JSON): ``` import { loadDecisions, recordDecision, saveDecisions } from './scripts/kb-update/lib/decisions-io.mjs'; let led = loadDecisions(); led = recordDecision(led, url, { status: 'rejected', decided_at: '', suggested_skill, suggested_category, note }); // ...én recordDecision per beslutning... saveDecisions(led); ``` `decided_at` settes til dagens dato (caller-injisert — `recordDecision` er ren). `decisions.json` er tracket i git (overlever, til forskjell fra de genererte rapportene). d. **For `approved`:** registrér URLen i `url-registry` (gated) så den fanges av polling heretter — via `lib/registry-io.mjs` `saveRegistry`. Bruk `suggested_skill`/`suggested_category` fra kandidaten. Deretter kjør **transformasjonslaget (lag 4)** for å lage KB-fila: 1. `microsoft_docs_fetch` på den godkjente URLen → kildedokument. 2. Destillér via `scripts/kb-update/transform-prompt.md` (doc→KB-fil; status-påstander holdes eksplisitte). Multi-agent parallell fan-out foreslås i produksjon (roadmap §71), speilet på `generate-skills`-mønsteret. 3. **Født-verifisert (Spor 3 Port 2):** kjør claim-judgen (`scripts/kb-eval/judge-claim-prompt-v3.1.md`) over brødtekstens maskin-verifiserbare påstander mot den godkjente URLen → `verdict = {pass}`. Så `meta = stampVerifiedMeta({title, status, category, source: , lastUpdated: }, verdict, )` — stempler `type='reference'` + `verified` + `verified_by='judge-v3.1'` **kun** ved `pass`; ellers KASTER (ingen fil, flagg for menneske). Deretter `content = composeKbFile(meta, body)` (`lib/transform.mjs`) — header + (store filer >100 linjer) en deterministisk `## Innhold`-TOC + brødtekst. **`Status` + `Source` + `Verified` + `Verified by` er obligatoriske** — `Source` i header-blokka (øverste 500 bytes) er det som lar lag 3 backfille `authority_source`. TOC-en fødes inn så regenerering ikke stripper den (Fase 1c). 4. `validateKbFile(content)` MÅ være `valid: true` (title + Last updated + Status + Source + **Verified + Verified by**; **store filer også TOC**) før noe gates videre (ellers be modellen fylle manglende felt). 5. For hver status-/load-bearing-påstand: `buildChange({...})` → **lag 5** `classifyChange(...)` (samme gate som §4 c2). Status-påstander er alltid `flagged`. 6. `resolveTargetPath(tax, category, filename)` → eierskill-sti via taksonomien (`null` = ukjent kategori → flagg for operatør, ikke skriv). 7. **Create-guard FØR skriving (to sibling-gater):** kjør BEGGE (exit ≠0 på noen av dem ⇒ ikke skriv): - `node scripts/kb-update/validate-kb-file.mjs ` — kontrakt (Source/født-verifisert/TOC). - `node scripts/kb-update/scan-adversarial-content.mjs ` — **Layer B ingestion-gate (G6 §8 / R6 punkt d):** deterministisk adversariell-innhold-skann (unicode/injection/base64, prosa + fenced code blocks) via de delte llm-security-detektorene. **exit 1 = BLOCK** (aldri skriv — karantene, operatør adjudiserer); **exit 2 = WARN** (flagg → samme menneske-i-loop som en status-påstand; ikke auto-committ). Gaten er ortogonal til korrekthets-judgen: en faktakorrekt men forgiftet fil blokkeres likevel. Først etter operatør-gate + BEGGE create-guards grønne: atomisk skriving (`lib/atomic-write.mjs` + `lib/backup.mjs`). **`transform.mjs` skriver aldri selv** — verifisert av `tests/kb-update/test-transform.test.mjs` (import-invariant). Kriterium verifisert av `tests/kb-eval/test-transform-criterion.test.mjs` (regenerer 1 fil → eval ≥ baseline). Layer B verifisert av `tests/kb-update/test-adversarial-scan.test.mjs` + `test-scan-adversarial-content.test.mjs` + `test-adversarial-detect-integration.test.mjs`. e. **Invariant:** `discover-new-urls.mjs` (deteksjon) skriver **aldri** ledgeren — den kun leser. Bare denne gaten skriver. Verifisert av `tests/kb-update/test-discover-invariant.test.mjs`. ### 3c. Kurs-deteksjon-gate — nye/endrede kurs via decision-ledger (courses-kolleksjon, Spor C / C3) Operatør-gaten for kurs-sporet. **Et kurs-lead er et *signal* om at et tema finnes — aldri en doc-side som ingestes.** Å godkjenne et lead **henter/transformerer/skriver ingen KB-fil** (det ville vært auto-ingest, et eksplisitt ikke-mål) — det registrerer kun operatørens beslutning i den UID-nøklede `courses`-kolleksjonen. Dette er ENESTE skrivevei for kurs-beslutninger. a. **Sjekk rapporten:** `data/course-detection-report.json`. Produsert av den Claude-frie detektoren (`detect-courses.mjs`) — enten schedulert (Tier 1/2) eller manuelt: `node scripts/kb-update/detect-courses.mjs`. Mangler fila, eller `status:"skipped"` (Keychain-creds mangler — C3 er opt-in inni opt-in): **hopp over denne gaten stille**. `status:"error"`: rapportér kort og hopp over. b. **Les leads:** `new[]` + `updated[]` (hver `{uid, title, url, products, suggested_skill, suggested_category, updated_at}`). `removed[]` er **kun et informasjonssignal** — vis det som en notis, aldri som en beslutning (et retirert kurs er ikke et tema å dekke; spec §4.2). c. **Dedup mot ledgeren (policy A):** utelat leads operatøren allerede har tatt stilling til. Bruk `isCourseLeadDecided` (UID er den stabile nøkkelen — URL kan endres): ``` import { loadDecisions, isCourseLeadDecided } from './scripts/kb-update/lib/decisions-io.mjs'; const led = loadDecisions(); const fresh = [...report.new, ...report.updated].filter((l) => !isCourseLeadDecided(led, l.uid)); ``` d. **Presenter `fresh`** for operatør, gruppert per `suggested_skill` (vis `title`, `url`, `products`, `kind` new/updated). For hvert lead (eller batch): **approve** (relevant — verdt å dekke i KB-en), **reject** (ikke relevant — ikke vis igjen), eller **utsett** (`pending` — ikke avgjort). Hvis `--dry-run`: stopp etter presentasjonen — **ikke skriv ledgeren**. e. **Skriv beslutningene til ledgeren — ENESTE skrivevei.** Bruk `recordCourseLead` (ikke håndskriv JSON): ``` import { loadDecisions, recordCourseLead, saveDecisions } from './scripts/kb-update/lib/decisions-io.mjs'; let led = loadDecisions(); led = recordCourseLead(led, uid, { kind, status: 'approved', title, url, products, suggested_skill, suggested_category, updated_at, detected_at, decided_at: '', note, }); // ...én recordCourseLead per beslutning... saveDecisions(led); ``` `decided_at` settes til dagens dato (caller-injisert — `recordCourseLead` er ren). `decisions.json` er tracket i git. f. **Ingen ingest — strukturell invariant.** Et godkjent kurs-lead blir værende i `courses`-kolleksjonen og leses av **ingen** av apply-pathene: `apply-skill-op.mjs` leser kun `actions`, `discover-new-urls.mjs` kun `decisions`. En kurs-beslutning trigger derfor **aldri** fetch/transform/KB-skriving. Skal et kurs' tema faktisk dekkes med en KB-side, er det en **separat, bevisst** doc-discovery-handling gjennom §3b (finn doc-URLen) — ikke en automatisk konsekvens av å godkjenne kurset. Et godkjent kurs surfaces ved sesjonsstart (C3.6: «N nye / M endrede kurs i dekkede produkter») som en påminnelse til operatøren. ### 4. Per-fil oppdatering (etter brukerens `y`) For hver fil i valgte prioriteter: a. **Les nåværende fil:** `Read` på filstien b. **Hent oppdaterte kilder:** for hver URL i `change-report.json[file].changed_urls`, kjør `microsoft_docs_fetch` på URLen c. **Identifiser endringer:** sammenlign hentet markdown mot eksisterende seksjoner i fila. Fokuser på faktuelle endringer (ny info, oppdaterte features, deprecation-varsler) — ikke små formuleringsendringer c1. **Autoritetskilde-binding (lag 3) — header-as-truth.** `authority_source = resolveAuthority()` (`lib/authority.mjs`) — fila sin `**Source:**`-header ER den utpekte autoriteten for dens påstander. `null` hvis fila mangler headeren (vokser etter hvert som lag 4 regenererer filer). Dette er inngangsdataen til lag-5 regel 3 (autoritets-mismatch): er en status-/load-bearing-endrings `source_url` ≠ `authority_source`, flagges den. Aldri gjett en autoritet fra fila sine siterte URLer (251/303 filer siterer 4–10 → ville vært gjetning). c2. **Verifisering-ut (lag 5) — FØR du skriver.** Hver kandidat-endring fra (c) går gjennom `lib/verify-out.mjs` `classifyChange({field, old_value, new_value, source_url, authority_source, refutations})` med `authority_source` fra (c1). Dette fanger regresjons-klassen (en status-påstand «korrigert» mot en tilfeldig sitert side og stille auto-applyet — agentic-retrieval-regresjonen): - **`flagged`** → IKKE auto-skriv. Vis endringen til operatør med `reasons[]`; operatør avgjør om den tas inn. **Status-påstander (GA/preview/versjon/pris) er ALLTID `flagged`** (spec §21) — uansett hvor sikker kilden ser ut. - **`auto-applied`** → trygt å ta inn i (d) (benign, ikke-status, ingen motbevis, autoritets-match). - **Adversarial motbevis-panel (LLM-runtime):** for status-/load-bearing-påstander, kjør et lite panel som *prøver å motbevise* `new_value` mot den utpekte `authority_source`, og mat resultatene inn som `refutations[]` (`[{refuted, reason}]`). Multi-agent foreslås når lag 4/5 kjøres i produksjon (roadmap §71). - **Invariant:** `verify-out.mjs` skriver aldri — den returnerer kun en verdict. Selve skrivingen skjer i (d), gated. Verifisert av `tests/kb-update/test-verify-out.test.mjs`. d. **Oppdater fila:** `Edit` med endringer som er `auto-applied` eller eksplisitt godkjent av operatør i (c2). Behold "For Cosmo"-seksjonen og overordnet struktur. Oppdater `Last updated: YYYY-MM-DD`-header til dagens dato. **Lag-4-kontrakt:** kjør `validateKbFile()` (`lib/transform.mjs`) før skriving — den skal være `valid: true`. Mangler fila et `**Source:**`-header, legg det til i header-blokka med den utpekte autoritets-URLen for hovedkilden — da fanger lag 3 (`resolveAuthority` + `build-registry`) den som `authority_source`, og lag-5 regel 3 blir virksom for fila. Mangler en **stor fil (>100 linjer)** en `## Innhold`-TOC, generer den med `buildToc()` og legg den inn rett etter header-`---` — `validateKbFile` krever den nå for store filer, så in-place-oppdateringer backfiller TOC inkrementelt (Fase 1c). d1. **Layer B FØR skriving/commit (ingestion-gate, G6 §8):** kjør `node scripts/kb-update/scan-adversarial-content.mjs ` på den oppdaterte fila. exit 1 (BLOCK) ⇒ ikke skriv/committ — karantene, operatør adjudiserer; exit 2 (WARN) ⇒ flagg for operatør, ikke auto-committ. Deterministisk adversariell-innhold-skann via delte llm-security-detektorer; ortogonal til korrekthets-gatene i (c1/c2). e. **Committ:** kjør create-guardene (`validate-kb-file.mjs` + `scan-adversarial-content.mjs`) grønne på fila, deretter `git add ` + `git commit -m "chore(ms-ai-architect): refresh KB $(basename ) [skip-docs]"` med mindre `--single-commit` ble gitt ### 5. Single-commit modus Hvis `--single-commit`: skip committer per fil, og lag én samlet commit til slutt. **Layer B FØR commit (ingestion-gate, G6 §8):** skann alle endrede `skills/**/*.md` — en BLOCK (exit 1) tas ut av staging (aldri committet, karantene); en WARN (exit 2) flagges for operatør før commit: ```bash node scripts/kb-update/scan-adversarial-content.mjs $(git diff --name-only --diff-filter=AM -- 'skills/**/*.md') git add skills/ git commit -m "chore(ms-ai-architect): refresh KB — N files [skip-docs]" ``` ### 6. Push (om bruker bekrefter) Spør: "Push til Forgejo origin/main? (y/n)". Per global push-policy er direkte main-push pre-autorisert, men spør likevel her siden dette er en bulk-operasjon. ```bash git push origin main ``` ### 7. Oppsummering Rapporter: - Antall filer oppdatert per prioritet - Antall commits laget - Hvis discovery kjørte: antall nye URLer oppdaget og lagt til registry - Eventuelle filer som ble hoppet over (f.eks. ingen reelle endringer i hentet innhold) - `data/change-report.json` blir værende på disk for diagnose ## Fallgruver - **Sitemap-coverage:** ~69% av URLene matche mot sitemap. ~31% (mest `azure/ai-foundry/openai/`) finnes ikke pga. URL-restrukturering på Microsofts side. Disse rapporteres som "always stale" og må vurderes manuelt - **Microsoft_docs_fetch latency:** hver fetch tar 2-5 sek. 9 critical + 44 high filer × ~1.5 kilder hver = ~80 fetches = ~3-7 minutter - **Modellvalg:** Opus brukes fordi diff-resonnering + tekst-syntese krever nyanse. For enklere "just refresh dates"-oppdateringer er Sonnet tilstrekkelig — bruker kan overstyre med eksplisitt `--model claude-sonnet-4-6` i Claude Code config - **MCP-tilgjengelighet:** kommandoen krever at `microsoft-learn` MCP-serveren er aktiv. Sjekk med `claude mcp list` ved første kjøring ## Når kjøre - **Anbefalt:** ukentlig eller månedlig, avhengig av hvor sensitive prosjektene dine er for KB-ferskhet - **Før viktig vurdering:** kjør med `--priorities critical,high,medium` før en stor `/architect:utredning` eller `/architect:adr` - **Etter Microsoft-events:** Build, Ignite, eller annen større Microsoft-konferanse → forvent mange endringer ## Schedulering Pluginen schedulerer **ingenting før du selv aktiverer det** (alt er opt-in). Du har tre nivåer, alle Claude-frie i deteksjonsfasen: - **Tier 1 (sesjonsstart):** `scheduled_detection.enabled: true` i `ms-ai-architect.local.md` — se «Scheduled deteksjon» over. - **Tier 2 (lokal launchd, ekte bakgrunn):** `node scripts/kb-update/scheduler.mjs install` — se «Scheduled deteksjon» over. - **Egen scheduler:** vil du heller bruke cron / systemd timer / CI, pek den på `node scripts/kb-update/run-detection.mjs` (eller `run-weekly-update.mjs --force --discover` for den eldre poll-flyten) og la den varsle deg om å kjøre `/architect:kb-update` i en interaktiv sesjon. Apply-fasen (oppdatere filer + committe) kan ikke automatiseres innenfor denne pluginen — den krever LLM-resonnering på endringene og menneskelig vurdering, og er bevisst designet for kjøring fra en åpen Claude Code-sesjon.