config-audit/docs/scanner-internals.md
Kjell Tore Guttormsen e3b044a476 feat(ost): v5.6 C — output-style scanner (CA-OST, count 13→14)
New orchestrated scanner output-style-scanner.mjs — first new family since
SKL. Three findings, each pinned to a CONFIRMED V-row of the steering-model
plan + re-verified against code.claude.com/docs/en/output-styles:

- CA-OST-001 (medium, V10): user/project custom style missing
  keep-coding-instructions:true (default false) → silently strips built-in
  software-engineering instructions when active. Scoped to user/project.
- CA-OST-002 (low, V11): plugin style with force-for-plugin:true overrides the
  user's selected outputStyle. Verifiseringsplikt correction — the plan bullet
  said "project/user style," but force-for-plugin is plugin-styles-only per the
  docs, so the check keys on source==='plugin'.
- CA-OST-003 (medium): settings outputStyle matching no built-in
  (Default/Explanatory/Learning/Proactive, case-insensitive) nor discovered
  custom style → dead config.

Byte-stability — a scanner addition, not a field addition. Growing the
scanners array + scanners_ok cannot be hidden by a field strip, but re-seeding
frozen v5.0.0 (the SKL precedent) would now bake in B2's hotspot triple +
claudeMd drift. So, per the B2 lesson, frozen v5.0.0 snapshots are PRESERVED
and the OST entry is stripped at compare time via new
tests/helpers/strip-added-scanner.mjs (wired into json/raw-backcompat + the
Step 5/6 humanizer tests); only SC-5 default-output is regenerated (additive
OST entry, diff reviewed). OST is fixture-gated (no output styles on
marketplace-medium / hermetic HOME → silent).

Wiring: orchestrator; humanizer (OST→Configuration mistake) + humanizer-data
OST family (title-coupled); scoring (OST→Settings, keeps 10 areas). Suite
1012→1023 (+11). Badges: scanners 14, tests 1023, TRANSLATIONS families 15.
Lore swept: README, CLAUDE.md, scanner-internals, humanizer.md. self-audit A/A.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:02:44 +02:00

78 lines
8 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.

# Config-Audit — Scanner internals
Detailed scanner inventory, lib modules, action engines, knowledge base. Imported from `CLAUDE.md` via pointer.
## Deterministic Scanners
Node.js scanners (zero external dependencies), run via `node scanners/scan-orchestrator.mjs <path>`.
Posture CLI: `node scanners/posture.mjs <path> [--json] [--global] [--full-machine] [--output-file path]`.
Scanner CLI: `node scanners/scan-orchestrator.mjs <path> [--global] [--full-machine] [--no-suppress]`.
| Scanner | Prefix | Detects |
|---------|--------|---------|
| `claude-md-linter.mjs` | CML | Structure, length, sections, @imports, duplicates, TODOs |
| `settings-validator.mjs` | SET | Schema, unknown/deprecated keys, type mismatches, permissions |
| `hook-validator.mjs` | HKV | Format, script existence, event validity, timeouts |
| `rules-validator.mjs` | RUL | Glob matching, orphan rules, deprecated fields, unscoped rules |
| `mcp-config-validator.mjs` | MCP | Server types, env vars, unknown fields |
| `import-resolver.mjs` | IMP | Broken @imports, circular refs, deep chains, tilde paths |
| `conflict-detector.mjs` | CNF | Settings conflicts, permission contradictions, hook duplicates |
| `feature-gap-scanner.mjs` | GAP | 25 feature checks across 4 tiers — shown as opportunities, not grades |
| `token-hotspots.mjs` | TOK | Cache-breaking volatile content, redundant tool permissions, deep import chains, oversized cascade, bloated SKILL.md descriptions, MCP tool-schema budget (prompt-cache patterns) |
| `cache-prefix-scanner.mjs` | CPS | Volatile content in lines 31150 of CLAUDE.md cascade (beyond Pattern A's top-30 window) |
| `disabled-in-schema-scanner.mjs` | DIS | Dead/ineffective permission entries (low). (1) Tools in BOTH `permissions.deny` AND `permissions.allow` — deny wins; dominance is param-aware and treats the `Tool(*)` deny-all glob as equivalent to a bare deny (covers a bare allow). (2) Unanchored allow wildcards (`*`, `B*`, `mcp__*`) that Claude Code silently skips — CC accepts allow globs only after a literal glob-free `mcp__<server>__` prefix. Predicates shared with CNF live in `lib/permission-rules.mjs` |
| `collision-scanner.mjs` | COL | Cross-plugin skill name collisions (low); user-vs-plugin overlaps (medium); `details.namespaces` payload |
| `skill-listing-scanner.mjs` | SKL | (1) `CA-SKL-001` (medium): active skill descriptions over the verified 1,536-char listing cap (CC 2.1.105) → silently truncated in the model's skill listing. (2) `CA-SKL-002` (low): sum of active descriptions (each counted up to the cap) over the listing budget (~2% of context, CC 2.1.32), anchored on a conservative 200k window with a calibration note that the budget scales 5× on 1M-context models — leads with the measured sum, an estimate not telemetry. HOME-scoped (all user + plugin skills). Remediation surfaces `disableBundledSkills` / `skillOverrides` / trim. Distinct lens from TOK pattern F (project-local 500-char bloat heuristic) |
| `output-style-scanner.mjs` | OST | (1) `CA-OST-001` (medium): a user/project custom output style not setting `keep-coding-instructions: true` (defaults false) → silently strips Claude Code's built-in software-engineering instructions when active (V10). (2) `CA-OST-002` (low): a **plugin** style with `force-for-plugin: true` auto-applies and overrides the user's `outputStyle` (V11; plugin-styles-only per docs). (3) `CA-OST-003` (medium): a settings `outputStyle` matching no built-in (Default/Explanatory/Learning/Proactive, case-insensitive) nor discovered custom style → dead config (CC falls back to default). Reads each style's frontmatter via `parseFrontmatter`; fixture-gated (silent with no output styles). New scanner family in v5.6 C (count 13→14) |
## Scanner Lib (`scanners/lib/`)
| Module | Purpose |
|--------|---------|
| `severity.mjs` | Severity constants, risk scoring, verdict logic, `WEIGHTS` named export (v5 F3) |
| `output.mjs` | Finding objects (CA-XXX-NNN format), scanner results, envelope, optional `details` payload (v5 N6) |
| `file-discovery.mjs` | Config file discovery: single-path, multi-path (`discoverConfigFilesMulti`), full-machine (`discoverFullMachinePaths`) |
| `yaml-parser.mjs` | Frontmatter parsing, JSON parsing, @import/section extraction |
| `string-utils.mjs` | Line counting, truncation, similarity, key extraction |
| `scoring.mjs` | Severity-weighted `scoreByArea` (v5 F3), health scorecard, dedup-by-area (v5 N3), `scoringVersion: 'v5'` |
| `backup.mjs` | Backup creation, manifest parsing, checksum verification |
| `diff-engine.mjs` | Drift diffing: diffEnvelopes(), formatDiffReport() |
| `baseline.mjs` | Baseline save/load/list/delete for drift detection |
| `report-generator.mjs` | Unified markdown reports: posture, drift, plugin health |
| `suppression.mjs` | .config-audit-ignore parsing, finding suppression, audit trail |
| `active-config-reader.mjs` | Read-only inventory: readActiveConfig(), detectGitRoot(), walkClaudeMdCascade(), readClaudeJsonProjectSlice() (longest-prefix match), enumeratePlugins(), enumerateSkills(), readActiveHooks(), readActiveMcpServers() (with cache → package.json tool-count fallback), estimateTokens() (v5: `'mcp'` kind = 500 + toolCount × 200) |
| `tokenizer-api.mjs` | Anthropic `count_tokens` wrapper for `--accurate-tokens` (v5 N5); 5s AbortController timeout, exponential 429 backoff, key masking |
| `humanizer.mjs` | Plain-language output translator (v5.1.0): `humanizeFinding`, `humanizeFindings`, `humanizeEnvelope`, `computeRelevanceContext`. Pure functions; never mutate inputs. Adds `userImpactCategory`, `userActionLanguage`, `relevanceContext` fields and replaces title/description/recommendation when a translation exists. Bypassed by `--raw` and `--json` paths. |
| `humanizer-data.mjs` | TRANSLATIONS table for 15 scanner prefixes (CML/SET/HKV/RUL/MCP/IMP/CNF/COL/TOK/CPS/DIS/GAP/PLH/SKL/OST). Three-step lookup: exact title → regex pattern → `_default` → fall through to original |
## Action Engines (`scanners/`)
| Module | Purpose |
|--------|---------|
| `fix-engine.mjs` | planFixes(), applyFixes(), verifyFixes() — 9 fix types |
| `rollback-engine.mjs` | listBackups(), restoreBackup(), deleteBackup() |
| `fix-cli.mjs` | CLI: `node fix-cli.mjs <path> [--apply] [--json] [--global]` |
| `drift-cli.mjs` | CLI: `node drift-cli.mjs <path> [--save] [--baseline name] [--json]` |
| `whats-active.mjs` | CLI: `node whats-active.mjs <path> [--json] [--verbose] [--suggest-disables]` — read-only active-config inventory |
| `token-hotspots-cli.mjs` | CLI: `node token-hotspots-cli.mjs <path> [--json] [--global] [--output-file path] [--accurate-tokens] [--with-telemetry-recipe]` — prompt-cache token hotspots ranking (each hotspot tagged with its load pattern, v5.6 B2) with optional API calibration |
| `manifest.mjs` | CLI: `node manifest.mjs <path> [--json]` — ranked component-level token-source table, each source tagged with its load pattern + an always-loaded subtotal (v5 N2; load-pattern accounting v5.6 B) |
## Standalone Scanner
| Module | Prefix | Purpose |
|--------|--------|---------|
| `plugin-health-scanner.mjs` | PLH | Plugin structure, frontmatter, cross-plugin conflicts (runs independently) |
| `self-audit.mjs` | — | Runs all scanners + plugin health on this plugin itself |
## Knowledge Base (`knowledge/`)
| File | Content |
|------|---------|
| `claude-code-capabilities.md` | Feature register: 18 config surfaces, Anthropic guidance, relevance table |
| `configuration-best-practices.md` | Per-layer best practices (v5: cache-stability guidance replaces Sonnet-era 200-line rule) |
| `anti-patterns.md` | Common mistakes mapped to scanner IDs |
| `hook-events-reference.md` | All 28 hook events with details |
| `feature-evolution.md` | Feature timeline for staleness detection |
| `gap-closure-templates.md` | Config-specific templates for closing gaps |
| `prompt-cache-patterns.md` | Token-cost dynamics (prompt-cache patterns) — patterns powering the TOK scanner |
| `cache-telemetry-recipe.md` | Manual `jq` recipe for verifying prompt-cache hit rate from session transcripts (v5 M7) |