config-audit/docs/cc-2.1.x-fase4-items-2-3-plan.md
Kjell Tore Guttormsen 66433fee48 feat(skill-listing): add CA-SKL-002 aggregate listing-budget check
Syklus 2 of Fase 4 Items 2+3. Flags when the sum of active skill
descriptions exceeds the listing budget (~2% of context, CC 2.1.32).

Design (operator-confirmed "fact-first, 200k anchor"):
- low severity (estimate) vs medium for the verified 1,536-char cap
- each description counted up to the 1,536 cap (what actually loads in
  the listing) — avoids double-counting the tail CA-SKL-001 flags
- fires when sum > 2% x 200k = 4000 tok; evidence leads with the measured
  sum + a calibration note that the budget scales 5x on 1M-context models
- aggregate emitted after the per-skill loop so the common case reads
  001=cap, 002=aggregate (finding IDs are a sequential counter, not stable
  semantic IDs — tests match on title, never NNN)

Also:
- tailored humanizer static entry for the aggregate title
- fix latent HOME leak in posture-grade-stability.test.mjs: it spawned
  posture.mjs without hermeticEnv(), so a real ~/.claude leaked HOME-scoped
  SKL/COL findings into the baseline grade (Token Efficiency A->B). Now
  isolated like the 8 other CLI-spawning tests.
- docs sync: test count 868->875, scanner-internals, gap-matrix, plan status

Suite 875/875, no snapshot drift, self-audit clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ter3E2JSi1Khgmuf2kady8
2026-06-18 18:06:17 +02:00

101 lines
10 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.

# Plan — Fase 4 Items 2 + 3 (skill-listing token-styring)
**Skrevet:** 2026-06-18 · **Scope:** kun `config-audit/` · **Status:****LEVERT 2026-06-18 (alle 4 sykluser).** Designvalg bekreftet: A=fold inn i SKL, B=**Syklus 2 NÅ LEVERT** — `CA-SKL-002` aggregat-budsjett implementert med «faktum-først, 200k-anker» (operatør-bekreftet 2026-06-18): low severity, sum av beskrivelser (hver tellet opp til 1 536-cap) > 2 %×200k = 4000 tok → funn m/ CALIBRATION_NOTE om 5×-skalering på 1M-kontekst. C=`skillOverrides` i KNOWN_KEYS (IKKE TYPE_CHECKS — verdien er objekt; avvik fra §C-skissen, verify-først). Suite **875/875**. Detaljer i `STATE.md`.
**Syklus 2-notat (avvik fra §B-skissen, verify-først):** (1) Aggregatet teller hver beskrivelse **capped på 1 536** (= det som faktisk lastes i listingen; unngår dobbelttelling av halen `CA-SKL-001` allerede flagger). (2) Finding-IDs er **sekvensiell teller** (`output.mjs:33`), ikke stabil semantisk ID — tester matcher på **tittel**, aldri NNN; aggregatet emitteres ETTER cap-loopen så vanlig-tilfellet leser 001=cap, 002=aggregat. (3) Eksponerte en latent **HOME-lekkasje** i `posture-grade-stability.test.mjs` (kjørte CLI uten hermetisk HOME → ekte ~/.claude lekket inn → Token Efficiency falt A→B); fikset med `hermeticEnv()` (samme mønster som 8 andre CLI-tester). (4) Egen tilpasset humanizer-`static`-entry for aggregat-tittelen (ikke generisk `_default`).
**Forrige:** Fase 4 Item 1 (4.7→modell-nøytral) levert som `8376dab`. Baseline **856/856 grønn**.
## Mål
Lukke matrise-radene 167 + 169 (skill-listing token-styring): gjøre config-audit i stand til å anbefale/varsle om skill-listing-budsjett før CC selv trunkerer det. Item 2(a) (false-positive) er **allerede gjort** (Batch 1).
---
## Verifiserte fakta (changelog-cache, CC 2.1.181 — re-verifisert 2026-06-18 FØR skriving)
| Fakta | Versjon | Changelog-linje |
|---|---|---|
| `disableBundledSkills` + `CLAUDE_CODE_DISABLE_BUNDLED_SKILLS` — skjuler bundled skills/workflows/built-in slash commands fra modellen | **2.1.169** | L176 |
| Skill-listing-truncation-varsel viser nå *antall* berørte beskrivelser | **2.1.178** | L64 |
| Truncation-varsel flyttet fra startup til `/doctor` | 2.1.144 | L666 |
| `skillOverrides`: `off` / `user-invocable-only` / `name-only` (kollapser beskrivelse) | 2.1.129 | L995 |
| Per-beskrivelse listing-cap hevet 250 → **1 536 tegn** + startup-varsel ved truncation | 2.1.105 | L1502 |
| Skill-tegnbudsjett skalerer med kontekstvindu = **2 % av kontekst** | 2.1.32 | L2860 |
**Matrise-korreksjon (verify-først):** rad 170s hypotetiske settings-nøkler `skillListingBudgetFraction` / `maxSkillDescriptionChars` **finnes ikke** som nøkler. Budsjettet (2 % kontekst) og cap-en (1 536 tegn) er interne CC-mekanismer. Ekte config-levere er `skillOverrides` (2.1.129) + `disableBundledSkills` (2.1.169).
---
## Item 2 — `disableBundledSkills` som token-lever (matrise-rad 166 + 167)
### 2(a) — false-positive-fiks → ✅ ALLEREDE GJORT (Batch 1)
`disableBundledSkills` ligger allerede i `KNOWN_KEYS` (`scanners/settings-validator.mjs:25`) + `TYPE_CHECKS` boolean (:53), og dekkes av testen `NEW_KEYS` (`tests/scanners/settings-validator.test.mjs:133,176-180`). **Ingen kode.** Matrise-rad 166 markert DONE.
### 2(b) — anbefaling (matrise-rad 167)
**Designfunn:** GAP-checks (`scanners/feature-gap-scanner.mjs:91` `GAP_CHECKS`) er binære `{id,tier,title,recommendation,check}` der finding fyres når `check()` = false. En ren binær «`disableBundledSkills` ikke satt»-check ville fyre for nær alle brukere → **støy + dårlig råd** (lever-en fjerner ALLE bundled skills; bare verdt det ved faktisk listing-press).
**Anbefalt (DESIGNVALG A — confirm):** IKKE en frittstående GAP-check. Folde `disableBundledSkills`-anbefalingen inn som en **remediation-lever i Item 3s SKL-scanner** (fyres bare ved kvantitativt budsjett-press). Substansen i Item 2(b) leveres dermed via Item 3. Matrise-rad 167 omklassifiseres «folded into SKL (Item 3)».
**Alternativ:** frittstående tier-4 GAP-check gated på antall aktive skills (krever skill-enumerering → samme kobling som Item 3; mer kode, mer støyrisiko).
---
## Item 3 — SKL-scanner: skill-listing-budsjett (matrise-rad 169)
**Ny scanner** `scanners/skill-listing-scanner.mjs`, prefiks **`SKL`** (ledig; bekreftet mot alle eksisterende prefikser). Finding-IDs `CA-SKL-NNN`.
### Hva den gjør
1. **Primær (høy konfidens):** flagg hver aktiv skill-beskrivelse > **1 536 tegn** — verifisert hard cap → CC trunkerer den. Severity medium. Dette er rock-solid (ikke estimat).
2. **Aggregat (estimat, lavere konfidens):** sum av skill-beskrivelse-tegn på tvers av ALLE aktive skills (user + plugin + bundled). Varsle når summen nærmer seg/overskrider budsjett. Budsjett = 2 % av kontekst → **krever kontekstvindu-antakelse** (se DESIGNVALG B). Merk med `CALIBRATION_NOTE`-mønster (jf. TOK) og ±-usikkerhet — IKKE lat som vi vet brukerens kontekstvindu.
3. **Remediation (alle funn):** trim beskrivelse < 1 536; `skillOverrides: name-only` (kollaps); `disableBundledSkills: true` (fjern bundled). ← her bor Item 2(b).
### Gjenbruk (ikke gjenoppfinn)
- **Aktiv-skill-enumerering:** `enumerateSkills(pluginList)` i `scanners/lib/active-config-reader.mjs:465``{name, source:'user'|'plugin', pluginName, path, bytes, estimatedTokens}`. Dekker user + plugin skills (bredere enn project-local).
- **Beskrivelse-uttrekk:** `parseFrontmatter(content)?.frontmatter?.description` (`scanners/lib/yaml-parser.mjs`), nøyaktig som TOK pattern F (`token-hotspots.mjs:381-411`). Les fil via `readTextFile` (`lib/file-discovery.mjs`).
- **`estimateTokens()`** (`active-config-reader.mjs:35`) — markdown = bytes/4. Brukes hvis budsjett uttrykkes i tokens.
### Avgrensning mot eksisterende TOK pattern F
TOK pattern F flagger project-local `skill-md` med beskrivelse > **500 tegn** (strukturell «bloat»-heuristikk, `SKILL_DESCRIPTION_THRESHOLD`, `token-hotspots.mjs:53`). SKL er en **annen linse**: ALLE aktive skills (ikke bare project-local) + den **verifiserte 1 536-cap-en** (hard truncation) + aggregat-budsjett. Dokumentér de to linsene eksplisitt i begge scanner-headere så det ikke leses som duplikat.
---
## ⚠️ Åpne designvalg — BEKREFT med operatør FØR kode (per syklus)
- **A — Item 2(b)-plassering:** anbefalt = folde inn i SKL-remediation (ikke frittstående GAP-check). [Alternativ: tier-4 GAP-check.]
- **B — kontekstvindu-antakelse for aggregat-budsjettet:** anbefalt = konservativ default **200k tokens** (2 % ⇒ budsjett), eksplisitt merket estimat m/ `CALIBRATION_NOTE`. Eventuelt: dropp aggregat-sjekken i v1 og lever KUN den verifiserte 1 536-cap-en (høyest konfidens, null gjetting) — minst risiko. [Anbefaler å starte med per-beskrivelse-cap; aggregat som stretch.]
- **C (valgfri relatert quick-win):** `skillOverrides` mangler i `KNOWN_KEYS` → egen unknown-key false-positive (matrise-rad 170, L/S). Hvis SKL anbefaler `skillOverrides`, bør nøkkelen samtidig legges i `KNOWN_KEYS` + `TYPE_CHECKS` (`'string'`) for konsistens. Egen mini-syklus.
---
## Utførelsesrekkefølge (hver = ratifisert failing-test-først, Iron Law)
**Syklus 1 — Item 3 SKL-scanner (per-beskrivelse 1 536-cap):**
1. Failing test: `tests/scanners/skill-listing-scanner.test.mjs` + fixture med en skill-beskrivelse > 1 536 tegn → forvent `CA-SKL-001`. (RED: scanner finnes ikke.)
2. Skriv `scanners/skill-listing-scanner.mjs` (gjenbruk enumerateSkills + parseFrontmatter).
3. Registrer: import + `SCANNERS`-entry i `scanners/scan-orchestrator.mjs:19-65`; humanizer-entry i `lib/humanizer-data.mjs` (`SKL.static` + `_default`) + `SCANNER_TO_CATEGORY` i `lib/humanizer.mjs:30-43` (kategori `'Wasted tokens'`).
4. README-badge `scanners-12``13` (validert av `self-audit.mjs countScannerShape()`); CLAUDE.md scanner-tabell + `docs/scanner-internals.md`.
5. Snapshot-reseed (HERMETISK): `SEED_SNAPSHOT=1` (v5.0.0/) + `UPDATE_SNAPSHOT=1` (default-output/) — scan-orchestrator + posture endres hvis fixturen `marketplace-medium` trigges. Verifiser `git diff tests/snapshots/` + kontaminerings-grep.
**Syklus 2 — Item 3 aggregat-budsjett (HVIS designvalg B = inkluder):** failing test for sum-over-budsjett → `CA-SKL-002`; CALIBRATION_NOTE; kontekstvindu-konstant.
**Syklus 3 — Item 2(b) remediation:** verifiser at SKL-funn lister `disableBundledSkills` + `skillOverrides` + trim som levere (test på recommendation-tekst).
**(Valgfri) Syklus 4 — designvalg C:** `skillOverrides` i `KNOWN_KEYS`+`TYPE_CHECKS` + utvid `NEW_KEYS`-testen.
---
## Verifisering (testbare kriterier)
- `node --test 'tests/**/*.test.mjs'` grønn (baseline **856** + nye SKL-tester).
- Failing-test-først bevist per syklus (RED → GREEN logget).
- Fixture med beskrivelse > 1 536 tegn → nøyaktig `CA-SKL`-funn; fixture med korte beskrivelser → 0 SKL-funn.
- `node scanners/self-audit.mjs --check-readme` ren (badge 13 matcher).
- `git diff tests/snapshots/` reseedet bevisst, kontaminerings-grep ren (sadhguru/vegnorm/pluginCount).
- Ingen ny `Opus 4.x`-æra-stempling i ny scanner (gjenbruk modell-nøytral framing fra Item 1).
## Scope-gjerde
Kun `config-audit/`. Item 2(a) ferdig. Items 2(b)/3 = ny kode → ratifisert design (A+B) + failing-test-først per syklus. IKKE auto-eskalér; bekreft A/B FØR første scanner-linje.
## Risiko
- **Aggregat-budsjett er et estimat** (ukjent kontekstvindu) — håndteres med CALIBRATION_NOTE + å lede med den verifiserte 1 536-cap-en. Ved tvil: lever KUN per-beskrivelse-cap i v1.
- **Snapshot-drift:** ny orkestrert scanner endrer scan-orchestrator/posture-snapshots → MÅ reseedes hermetisk (`hermeticEnv()`), ALDRI rå `node scanners/...`.
- **Matrise er agent-generert** (2 kjente versjonsfeil) — fakta over er re-verifisert mot changelog; re-verifiser nye påstander før skriving.
## Pekere
- Kartlegging (denne sesjonen): scanner-registrering, GAP-mekanisme, enumerateSkills, snapshot-mønster — alt ankret i koden over.
- Gap-matrise: `docs/cc-2.1.x-gap-matrix.md` rad 166 (✅ DONE), 167 (Item 2b), 169 (Item 3), 170 (skillOverrides quick-win).
- Mønster-katalog: `knowledge/prompt-cache-patterns.md` (rad 167/169 retargetet hit).