feat(opt): optimization lens CA-OPT-001 (procedure→skill) — v5.7 Fase 1 Chunk 2a

First detector of the 'is the config OPTIMAL?' axis (vs the existing 'correct?' scanners). New orchestrated scanner family CA-OPT (count 14->15), the deterministic half of the hybrid optimization lens.

CA-OPT-001 (low, Missed opportunity): a multi-step procedure in CLAUDE.md (>=6 consecutive numbered steps) that belongs in a skill. Reads recommendation + provenance from the best-practices register (BP-MECH-003). Conservative by design; the negative corpus proves null false-positives. Prose-judgment cases (lifecycle->hook, 'never'->permission) are deferred to the Chunk 2b opus analyzer.

Wiring mirrors OST: orchestrator entry, humanizer (OPT->'Missed opportunity' + family), scoring (OPT->'CLAUDE.md', existing area -> no new posture row -> byte-stable), strip-helper (OST,OPT), SC-5 regenerated under hermetic HOME (additive OPT entry only). 10 new tests; suite 1045->1055, self-audit A/A, readmeCheck passed (all verified with a clean HOME).

Note: the pre-existing TOK test reads the real ~/.claude (non-hermetic) -> run the suite with a clean HOME for deterministic results. Tracked as a separate follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-06-20 23:10:03 +02:00
commit e7833b65fc
14 changed files with 350 additions and 38 deletions

View file

@ -24,6 +24,7 @@ Scanner CLI: `node scanners/scan-orchestrator.mjs <path> [--global] [--full-mach
| `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) |
| `optimization-lens-scanner.mjs` | OPT | `CA-OPT-001` (low, *Missed opportunity*): a CLAUDE.md procedure (≥6 consecutive numbered steps) that belongs in a skill (mechanism-fit, `BP-MECH-003`). Reads the machine-readable best-practices register (`best-practices-register.mjs`) for recommendation + provenance. Conservative — negative corpus proves null false-positive; prose-judgment cases (lifecycle→hook, "never"→permission) deferred to the Chunk 2b opus analyzer. Scoring area `CLAUDE.md` (existing → byte-stable). New scanner family in v5.7 Fase 1 Chunk 2a (count 14→15) |
## Scanner Lib (`scanners/lib/`)
@ -43,7 +44,7 @@ Scanner CLI: `node scanners/scan-orchestrator.mjs <path> [--global] [--full-mach
| `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 |
| `humanizer-data.mjs` | TRANSLATIONS table for 16 scanner prefixes (CML/SET/HKV/RUL/MCP/IMP/CNF/COL/TOK/CPS/DIS/GAP/PLH/SKL/OST/OPT). Three-step lookup: exact title → regex pattern → `_default` → fall through to original |
## Action Engines (`scanners/`)