feat(opt): optimization lens Chunk 2b — opus prose-judgment analyzer + /config-audit optimize

The recall+precision halves of the CA-OPT hybrid motor for the three
mechanism-fit cases the deterministic OPT scanner (2a) deliberately skips:
lifecycle→hook (BP-MECH-001), unscoped path-specific→rule (BP-MECH-002),
absolute "never"→permission (BP-MECH-004).

New:
- scanners/lib/lens-prefilter.mjs — cheap, recall-oriented line scan of the
  CLAUDE.md body; detector names mirror the register lensCheck fields; skips
  fenced code, gates the path class on an instruction verb. Pure + 13 tests.
- scanners/optimize-lens-cli.mjs — discovery + OPT scanner + pre-filter; attaches
  only the CONFIRMED register entry to each candidate (unverifiable → dropped,
  Verifiseringsplikt); emits {deterministic, candidates, register, counts}.
- agents/optimization-lens-agent.md — opus precision gate (7th agent, orange):
  reads the real CLAUDE.md, drops low-confidence candidates, keeps only genuine
  opportunities, cites register id + source.
- commands/optimize.md — /config-audit optimize orchestrates pre-filter→agent→report.

Agent-driven → deliberately NOT byte-stable (own command, outside the snapshot
suite). No new orchestrated scanner → scanner count stays 15. Counts: agents
6→7, commands 18→19, suite 1055→1068. Self-audit A/A unchanged, readmeCheck
passed (clean HOME). Plan: docs/v5.7-optimization-lens-plan.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-06-21 14:01:18 +02:00
commit 7b3b487d26
9 changed files with 687 additions and 13 deletions

View file

@ -20,6 +20,7 @@ Analyzes and optimizes Claude Code configuration across three pillars:
| `/config-audit tokens` | prompt-cache-aware token hotspots (6 patterns: cache-breaking, redundant perms, deep imports, oversized cascade, bloated SKILL.md desc, MCP tool-schema budget), each ranked hotspot tagged with its load pattern (always / on-demand / external) — optional `--accurate-tokens` API calibration, `--with-telemetry-recipe` cache-hit recipe pointer |
| `/config-audit manifest` | Ranked table of every token source (CLAUDE.md, rules, agents, skills, output styles, MCP, hooks) sorted by estimated tokens, each tagged with its load pattern (always-loaded / on-demand / external) + an always-loaded subtotal ("tokens that enter context every turn") |
| `/config-audit feature-gap` | Context-aware feature recommendations grouped by impact (incl. a conditional `disableBundledSkills` lever when the active skill listing is over budget — remediation companion to SKL `CA-SKL-002`) |
| `/config-audit optimize` | Optimization lens (mechanism-fit) — config that works but fits a better mechanism: procedure→skill (CA-OPT-001, deterministic), lifecycle→hook / unscoped path→rule / "never"→permission (prose-judgment via opus `optimization-lens-agent`). Hybrid motor; every finding cites a best-practices-register rule. Agent-driven, **not byte-stable** |
| `/config-audit fix` | Auto-fix deterministic issues with backup + verification |
| `/config-audit rollback` | Restore configuration from backup |
| `/config-audit plan` | Create action plan from audit findings |
@ -49,6 +50,7 @@ Analyzes and optimizes Claude Code configuration across three pillars:
| implementer-agent | Execute changes | sonnet | magenta | Read, Write, Edit, Bash, Glob |
| verifier-agent | Verify results | sonnet | purple | Read, Glob, Grep |
| feature-gap-agent | Context-aware feature recommendations | opus | green | Read, Glob, Grep, Write |
| optimization-lens-agent | Mechanism-fit precision gate (prose-judgment lens cases) | opus | orange | Read, Glob, Grep, Write |
## Hooks
@ -109,7 +111,7 @@ Default: auto-detects scope from git context. Override with `/config-audit full|
node --test 'tests/**/*.test.mjs'
```
1055 tests across 59 test files (18 lib + 31 scanner + 1 hook + 1 agent + 3 commands + 1 knowledge + 4 top-level). Test fixtures in `tests/fixtures/`. Top-level humanizer tests: `json-backcompat.test.mjs`, `raw-backcompat.test.mjs`, `scenario-read-test.test.mjs`, `snapshot-default-output.test.mjs`.
1068 tests across 60 test files (19 lib + 31 scanner + 1 hook + 1 agent + 3 commands + 1 knowledge + 4 top-level). Test fixtures in `tests/fixtures/`. Top-level humanizer tests: `json-backcompat.test.mjs`, `raw-backcompat.test.mjs`, `scenario-read-test.test.mjs`, `snapshot-default-output.test.mjs`.
### active-config-reader — load-pattern model + rule/agent/output-style enumeration (v5.6 Foundation)
@ -366,11 +368,30 @@ First detector of the «optimal?» axis (vs «correct?»). `optimization-lens-sc
best-practices register and flags config that works but fits a better mechanism. **`CA-OPT-001`**
(low, *Missed opportunity*): a CLAUDE.md procedure (≥6 consecutive numbered steps) that belongs in a
skill — recommendation/provenance from register `BP-MECH-003`. Conservative (negative corpus = null
false-positive); 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
`OPT`, SC-5 regenerated (additive). **Test-isolation note:** the TOK test reads the real `~/.claude`
(non-hermetic) — run the suite with a clean `HOME` for deterministic results (pre-existing).
false-positive); prose-judgment cases (lifecycle→hook, unscoped path→rule, «never»→permission) are
handled by the Chunk 2b opus analyzer (below). Wiring mirrors OST: orchestrator entry, humanizer
`OPT:'Missed opportunity'` + family, scoring `OPT:'CLAUDE.md'` (existing area → no new posture row →
byte-stable), strip-helper `OPT`, SC-5 regenerated (additive).
### Optimization lens Chunk 2b — opus analyzer (prose-judgment half, `/config-audit optimize`)
The hybrid motor's recall + precision halves for the three cases the deterministic OPT scanner skips.
**Pre-filter** (`scanners/lib/lens-prefilter.mjs`, pure + tested): cheap, recall-oriented line scan
of CLAUDE.md body for lifecycle phrasing (`BP-MECH-001`→hook), unscoped path-specific instructions
(`BP-MECH-002`→rule), and absolute «never» prohibitions (`BP-MECH-004`→permission); skips fenced
code, gates the path class on an instruction verb. Detector names = the register `lensCheck` fields.
**CLI** (`optimize-lens-cli.mjs`, `-cli` → not a scanner): runs discovery + OPT scanner + pre-filter,
attaches the **confirmed** register entry to each candidate (unverifiable → dropped, Verifiseringsplikt),
emits `{deterministic, candidates, register, counts}`. **Agent** (`optimization-lens-agent`, opus,
orange — the 7th agent, **precision gate**): reads the real CLAUDE.md, drops low-confidence candidates,
keeps only genuine opportunities, cites register id + source. **Command** `/config-audit optimize`
orchestrates pre-filter→agent→report. **Agent-driven → deliberately NOT byte-stable** (own command,
outside the snapshot suite); the pre-filter lib *is* unit-tested (13 tests). No new orchestrated
scanner → scanner count stays 15; agents 6→7, commands 18→19, suite 1055→1068.
**Test-isolation fix (this session):** `token-hotspots.test.mjs` `runScanner` now wraps `scan()` in
the shared `withHermeticHome` helper — the suite is green on BOTH a real and a clean `HOME` (the OPT
section's old «run with clean HOME» caveat is resolved). Snapshot/byte tests were already hermetic.
## Gotchas

View file

@ -9,10 +9,10 @@
![Version](https://img.shields.io/badge/version-5.6.0-blue)
![Platform](https://img.shields.io/badge/platform-Claude_Code_Plugin-purple)
![Scanners](https://img.shields.io/badge/scanners-15-cyan)
![Commands](https://img.shields.io/badge/commands-18-green)
![Agents](https://img.shields.io/badge/agents-6-orange)
![Commands](https://img.shields.io/badge/commands-19-green)
![Agents](https://img.shields.io/badge/agents-7-orange)
![Hooks](https://img.shields.io/badge/hooks-4-red)
![Tests](https://img.shields.io/badge/tests-1055+-brightgreen)
![Tests](https://img.shields.io/badge/tests-1068+-brightgreen)
![License](https://img.shields.io/badge/license-MIT-lightgrey)
A Claude Code plugin that checks configuration health, suggests context-aware improvements, and auto-fixes issues — `CLAUDE.md`, `settings.json`, hooks, rules, MCP servers, `@imports`, and plugins. 15 deterministic scanners across 10 quality areas, context-aware feature recommendations, auto-fix with backup/rollback, a prompt-cache-aware Token Hotspots scanner with optional API-calibrated `--accurate-tokens` mode, plus cache-prefix stability, dead-tool, cross-plugin collision, and output-style detection. Zero external dependencies.
@ -278,6 +278,7 @@ Your team configuration changes over time. Track it:
| `/config-audit tokens` | prompt-cache-aware token hotspots — ranked by estimated waste, each tagged with its load pattern (always / on-demand / external); 6 patterns + optional `--accurate-tokens` API calibration |
| `/config-audit manifest` | Ranked table of every token source (CLAUDE.md, rules, agents, skills, output styles, MCP, hooks) sorted by estimated tokens — each tagged with its **load pattern** (always-loaded / on-demand / external) plus an **always-loaded subtotal** ("≈X tokens enter context every turn before you type"). Component-level: no coarse plugin roll-up (it would double-count) |
| `/config-audit feature-gap` | Context-aware feature recommendations grouped by impact |
| `/config-audit optimize` | Optimization lens (mechanism-fit): config that works but fits a better mechanism — procedure→skill, lifecycle→hook, unscoped path→rule, "never"→permission. Hybrid motor (deterministic pre-filter + opus precision gate), every finding cites a best-practices-register rule |
| `/config-audit fix` | Auto-fix deterministic issues with backup + verification |
| `/config-audit rollback` | Restore configuration from a previous backup |
| `/config-audit plan` | Generate prioritized action plan from audit findings |
@ -325,7 +326,7 @@ By default, `/config-audit` auto-detects scope from your git context. Override w
| `collision-scanner.mjs` | COL | Cross-plugin skill name collisions; user-vs-plugin overlaps |
| `skill-listing-scanner.mjs` | SKL | Skill-listing token budget: a single skill description over the ~1,536-char listing cap Claude Code truncates (`CA-SKL-001`), and the summed active-skill descriptions exceeding the ~2%-of-context listing budget (`CA-SKL-002`) |
| `output-style-scanner.mjs` | OST | Output-style validation: a custom (user/project) style missing `keep-coding-instructions: true` that silently strips built-in software-engineering instructions (`CA-OST-001`), a plugin style with `force-for-plugin: true` overriding the user's selected `outputStyle` (`CA-OST-002`), and a settings `outputStyle` resolving to no built-in or custom style — dead config (`CA-OST-003`) |
| `optimization-lens-scanner.mjs` | OPT | Optimization lens (mechanism-fit): a multi-step procedure in CLAUDE.md that would fit better as a skill (`CA-OPT-001`) — reads the machine-readable best-practices register, framed as an opportunity, not a failure. The deterministic half of the lens; prose-judgment cases (lifecycle→hook, "never"→permission) are handled by the Chunk 2b analyzer |
| `optimization-lens-scanner.mjs` | OPT | Optimization lens (mechanism-fit): a multi-step procedure in CLAUDE.md that would fit better as a skill (`CA-OPT-001`) — reads the machine-readable best-practices register, framed as an opportunity, not a failure. The deterministic half of the lens; prose-judgment cases (lifecycle→hook, unscoped path→rule, "never"→permission) are judged by the opus `optimization-lens-agent` via `/config-audit optimize` |
> **Cross-scanner remediation — diagnosis meets the fix.** SKL diagnoses an over-budget
> skill listing (`CA-SKL-002`); GAP prescribes the remedy. When the active skill listing
@ -432,6 +433,7 @@ Six specialized agents collaborate through the audit workflow, each matched to a
| **implementer-agent** | Sonnet | Change execution with mandatory backups | Read, Write, Edit, Bash, Glob |
| **verifier-agent** | Sonnet | Post-implementation verification | Read, Glob, Grep |
| **feature-gap-agent** | Opus | Context-aware feature recommendations | Read, Glob, Grep, Write |
| **optimization-lens-agent** | Opus | Mechanism-fit precision gate — judges prose-judgment lens candidates (lifecycle→hook, path→rule, never→permission), cites the best-practices register | Read, Glob, Grep, Write |
### Orchestration Flow
@ -585,8 +587,8 @@ Reference documents that inform the feature-gap agent and context-aware recommen
**Machine-readable register (`best-practices.json`).** Alongside the human-readable documents
above, `knowledge/best-practices.json` is a provenance-stamped, schema-validated register of
best-practice claims and mechanism-fit rules — each entry carries `source.url`, a `verified`
date, and a `confidence`. It is the source of truth for the forthcoming optimization lens; the
Markdown files remain the human-readable mirror. Loaded and validated by
date, and a `confidence`. It is the source of truth for the optimization lens (OPT scanner +
`/config-audit optimize`); the Markdown files remain the human-readable mirror. Loaded and validated by
`scanners/lib/best-practices-register.mjs` (zero-dependency, native JSON). See
`docs/v5.7-optimization-lens-plan.md`.

View file

@ -0,0 +1,120 @@
---
name: optimization-lens-agent
description: |
Judges CLAUDE.md mechanism-fit for the v5.7 optimization lens (CA-OPT). Reads
deterministic pre-filter candidates and decides, with prose judgement, whether
each is a genuine "you use mechanism X, but Y fits this better" opportunity —
lifecycle phrasing → hook, unscoped path-specific instruction → path-scoped
rule, absolute "never" prohibition → permission. Precision-gated: cites the
best-practices register rule + source, and stays silent when unsure.
model: opus
color: orange
tools: ["Read", "Glob", "Grep", "Write"]
---
# Optimization Lens Agent
You are the **precision gate** of the optimization lens's hybrid motor. A cheap
deterministic pre-filter (`lens-prefilter`) has already surfaced candidate lines
in CLAUDE.md that *might* fit a better mechanism. Your job is to read each
candidate **in its real context** and keep only the genuine opportunities.
This is the "is the config **optimal?**" axis, not "is it **correct?**" — every
finding is a *Missed opportunity*, never a mistake. The config works as written;
you are pointing at a mechanism that would fit the content better.
## The judgement you make
For each candidate, the register rule names the better-fit mechanism. Decide
whether the line is *really* that kind of instruction:
| lensCheck | Register | Keep it ONLY if the line is… | Better mechanism |
|---|---|---|---|
| `claude-md-lifecycle-phrasing` | BP-MECH-001 | a recurring automation the model is *told* to perform ("after every commit, run X") — something that should happen deterministically, not at the model's discretion | a **hook** (PreToolUse / PostToolUse / Stop) |
| `unscoped-path-specific-instruction` | BP-MECH-002 | a constraint that only applies when a *specific* file/path/glob is touched, sitting in root CLAUDE.md where it loads every turn regardless | a **path-scoped rule** (`.claude/rules/` with `paths:` frontmatter) |
| `never-instruction` | BP-MECH-004 | an *absolute* prohibition — something that must NEVER happen, where relying on the model to remember is the wrong guarantee | a **permission deny rule** or PreToolUse hook |
## Input
You receive an `optimize-lens` payload (JSON) with:
- `target` — the repo path.
- `deterministic` — OPT scanner findings already confirmed (CA-OPT-001:
procedure → skill). Report these **as-is**; do not re-judge them.
- `candidates` — pre-filter candidates, each with `file`, `line`, `lensCheck`,
`mechanism`, `signalText`, and a `register` block (`id`, `claim`,
`recommendation`, `severity`, `source`). Only CONFIRMED register rules reach
you.
- `register` — the full confirmed prose-judgment entries, for reference.
Always **Read the actual CLAUDE.md file(s)** named in the candidates before
judging — `signalText` is one line out of context; the surrounding lines decide
whether it is really lifecycle/path-specific/prohibition phrasing.
## Precision rules (non-negotiable)
1. **Keep only high-confidence opportunities.** When the line is ambiguous,
rhetorical, an example, a heading, or already correctly placed (e.g. it is
*inside* a path-scoped rule, or already references a hook) — **drop it**. A
missed suggestion is far cheaper than a wrong one (Verifiseringsplikt).
2. **Never invent a recommendation.** Use the `register.recommendation` and cite
`register.id` + `register.source.url`. If a candidate has no register block,
skip it.
3. **De-duplicate.** If one line yields two candidates (e.g. "never edit
src/config.ts"), pick the single mechanism that fits best and say why,
rather than emitting two findings for one line.
4. **No false urgency.** These are LOW-severity opportunities. Do not imply the
config is broken.
## Output
Write `optimization-lens-report.md` to the session directory (≤120 lines).
```markdown
# Optimization Lens — mechanism-fit
**Date:** YYYY-MM-DD | **Target:** {repo}
**Confirmed opportunities:** {N kept} · **Candidates reviewed:** {M} · **Dropped (low confidence):** {M-N}
> The config works as written. These are places where a different Claude Code
> mechanism would fit the content better — usually cheaper per turn or more
> reliable.
## Procedures → skills (deterministic)
{For each `deterministic` finding — render title/recommendation verbatim, cite CA-OPT-001 + BP-MECH-003.}
## Lifecycle → hooks
{Kept BP-MECH-001 findings. For each:}
**{file}:{line}** — {one-line restatement of the line}
Why: {register.claim, condensed}
Move to: {register.recommendation}
Source: {register.source.url}
## Path-specific → scoped rules
{Kept BP-MECH-002 findings, same shape.}
## Absolute prohibitions → permissions
{Kept BP-MECH-004 findings, same shape.}
## What I deliberately left alone
{Brief, honest: candidates you dropped and why — "line 22 mentions a path but is
a cross-reference, not an instruction." This is the precision gate showing its
work. Keep to a few lines.}
```
Omit any section with zero kept findings (except keep the "left alone" note when
you dropped anything). If nothing survived the gate, say so plainly — a clean
CLAUDE.md is a good outcome, not a failure to find problems.
## Guidelines
- Frame everything as *opportunities*, never failures.
- Cite the register rule id + source URL on every finding — provenance is the
product.
- Be concrete: name the file and line, and what the replacement mechanism is.
- Prefer dropping a borderline candidate over stretching to keep it.
- Do not recommend a mechanism the project already uses for that exact content.

View file

@ -1,7 +1,7 @@
---
name: config-audit
description: Claude Code Configuration Intelligence - audit, analyze, and optimize your configuration
argument-hint: "[posture|tokens|manifest|feature-gap|fix|rollback|plan|implement|help|discover|analyze|interview|drift|plugin-health|whats-active|status|cleanup]"
argument-hint: "[posture|tokens|manifest|feature-gap|optimize|fix|rollback|plan|implement|help|discover|analyze|interview|drift|plugin-health|whats-active|status|cleanup]"
allowed-tools: Read, Write, Glob, Grep, Bash, Agent, AskUserQuestion
model: opus
---
@ -17,6 +17,7 @@ If a subcommand is provided, route to it:
- `tokens``/config-audit:tokens`
- `manifest``/config-audit:manifest`
- `feature-gap``/config-audit:feature-gap`
- `optimize``/config-audit:optimize`
- `fix``/config-audit:fix`
- `rollback``/config-audit:rollback`
- `plan``/config-audit:plan`

View file

@ -35,6 +35,7 @@ if echo "$ARGUMENTS" | grep -q -- "--raw"; then RAW_FLAG="--raw"; fi
| `/config-audit tokens` | prompt-cache token hotspots; optional `--accurate-tokens` API calibration |
| `/config-audit manifest` | Ranked table of every system-prompt token source |
| `/config-audit feature-gap` | Deep analysis of features you're not using |
| `/config-audit optimize` | Optimization lens — config that works but fits a better mechanism (procedure→skill, lifecycle→hook, path→rule, never→permission) |
| `/config-audit fix` | Auto-fix deterministic issues; a copy of every changed file is saved first so you can roll back with one command |
| `/config-audit rollback` | Restore configuration from a saved copy |

123
commands/optimize.md Normal file
View file

@ -0,0 +1,123 @@
---
name: config-audit:optimize
description: Optimization lens — config that works but would fit a better mechanism (procedure→skill, lifecycle→hook, path→rule, never→permission)
argument-hint: "[path]"
allowed-tools: Read, Write, Glob, Grep, Bash, Agent
model: opus
---
# Config-Audit: Optimization Lens
The "is the config **optimal?**" axis (vs. the health scanners' "is it
**correct?**"). It finds configuration that *works* but uses a mechanism a
better one would fit — and frames every one as a *Missed opportunity*, never a
mistake.
Mechanism-fit rules come from the provenance-stamped best-practices register
(`knowledge/best-practices.json`); only CONFIRMED rules are surfaced. The motor
is hybrid: a cheap deterministic pre-filter finds candidates, then the opus
`optimization-lens-agent` judges each in context (precision-gated).
## What the user gets
- **Procedures → skills** (deterministic, CA-OPT-001)
- **Lifecycle phrasing → hooks** (BP-MECH-001)
- **Unscoped path-specific instructions → path-scoped rules** (BP-MECH-002)
- **Absolute "never" prohibitions → permissions / hooks** (BP-MECH-004)
Each finding cites its register rule + source URL. A clean CLAUDE.md returns "no
opportunities" — that is a good result, not a failure.
## Implementation
### Step 1: Determine target
Split `$ARGUMENTS` into a path (first non-flag argument; default: current working
directory) and flags. Recognized flags: `--global` (include the user `~/.claude`
cascade in discovery).
Tell the user:
```
## Optimization Lens
Looking for configuration that works but would fit a better Claude Code mechanism...
```
### Step 2: Run the lens CLI
Generate a session ID (`YYYYMMDD_HHmmss`) if no active session exists.
```bash
mkdir -p ~/.claude/config-audit/sessions/{session-id} 2>/dev/null
GLOBAL_FLAG=""
if echo "$ARGUMENTS" | grep -q -- "--global"; then GLOBAL_FLAG="--global"; fi
node ${CLAUDE_PLUGIN_ROOT}/scanners/optimize-lens-cli.mjs <target-path> --output-file ~/.claude/config-audit/sessions/{session-id}/optimize-lens.json $GLOBAL_FLAG 2>/dev/null; echo $?
```
Exit code 0 is normal. Only exit code 3 is a real error → "The lens couldn't run.
Check that the path exists and contains a CLAUDE.md."
### Step 3: Read the payload
Read `~/.claude/config-audit/sessions/{session-id}/optimize-lens.json` with the
Read tool. It has `deterministic` (already-confirmed OPT findings), `candidates`
(pre-filter candidates with register provenance), `register`, and `counts`.
**Early exit:** if `counts.deterministic === 0` and `counts.candidates === 0`,
skip the agent and tell the user plainly:
```
✓ No mechanism-fit opportunities found.
Your CLAUDE.md holds facts, not procedures/automation/prohibitions that would be
better as skills, hooks, rules, or permissions. Nothing to change here.
```
Then go to Step 5.
### Step 4: Spawn the precision gate
Tell the user what's happening and set expectations:
```
Found {counts.candidates} candidate line(s) + {counts.deterministic} deterministic finding(s).
Asking the optimization-lens agent to judge each in context (~20-40 seconds)...
```
Spawn the `optimization-lens-agent` (Agent tool) with:
- the full payload from Step 3 (deterministic + candidates + register),
- the session directory path so it can write `optimization-lens-report.md`.
The agent reads the actual CLAUDE.md, drops low-confidence candidates, and keeps
only genuine opportunities — each citing its register rule + source.
### Step 5: Present results
Read the agent's `optimization-lens-report.md` and present it formatted
(markdown tables / grouped sections). Follow the UX rules: never show raw JSON or
scanner progress; lead with a one-sentence summary of what was found before the
detail. Make clear these are LOW-severity *opportunities*.
If the agent kept nothing from the candidates (all dropped) but there were
deterministic findings, show those; if it kept nothing at all, show the clean
result from Step 3.
### Step 6: Next steps
End with context-sensitive next steps, explaining WHY each is useful:
- `/config-audit plan` — turn the kept opportunities into an action plan with
backups before you change anything.
- `/config-audit feature-gap` — the complementary lens: features you *don't* use
yet (this command is about mechanisms you *do* use that could fit better).
- Re-run `/config-audit optimize` anytime after editing CLAUDE.md.
## Notes
- This command is **agent-driven and not byte-stable** — its output is a
human-facing report, deliberately outside the deterministic snapshot suite.
- The deterministic half (CA-OPT-001) also rides in the normal orchestrated
audit; this command adds the prose-judgment half on top.
- No files are modified. To act on a finding, use `/config-audit plan`
`/config-audit implement` (backup + rollback) or edit by hand.

View file

@ -0,0 +1,114 @@
/**
* lens-prefilter deterministic, recall-oriented candidate generator for the
* v5.7 optimization lens (CA-OPT) hybrid motor, Chunk 2b.
*
* The OPT *scanner* (Chunk 2a) handles the one mechanism-fit case it can decide
* deterministically with high precision (a long numbered procedure skill). The
* other three cases in the register are PROSE-JUDGMENT calls whether a line is
* really lifecycle automation, a path-specific constraint, or an absolute
* prohibition depends on reading intent, which a regex cannot settle. So the
* hybrid motor splits the work:
*
* pre-filter (this module, CHEAP, recall-oriented)
* surfaces candidate lines tagged with the register rule they might fit
* opus optimization-lens-agent (PRECISION gate)
* reads each candidate in context, keeps only genuine mechanism-fit
* opportunities, cites the register rule + source
*
* Therefore this pre-filter deliberately errs toward recall: a false candidate
* costs the agent a moment's judgement, a missed line is never recoverable. It
* does, however, avoid the two obvious noise sources fenced code blocks and
* (when the caller passes the parsed body) YAML frontmatter and it requires an
* imperative-looking line for the path-specific class so plain "see docs/x.md"
* references don't flood the candidate list.
*
* The detector names mirror the `lensCheck` fields of the register entries
* (knowledge/best-practices.json), so the agent can map each candidate straight
* back to its provenance.
*
* Zero external dependencies. Pure: input text candidate array.
*/
/**
* The three prose-judgment detectors, keyed to their register entries.
* `mechanism` is the better-fit mechanism the register recommends.
*/
export const LENS_DETECTORS = Object.freeze([
{ lensCheck: 'claude-md-lifecycle-phrasing', registerId: 'BP-MECH-001', mechanism: 'hook' },
{ lensCheck: 'unscoped-path-specific-instruction', registerId: 'BP-MECH-002', mechanism: 'rule' },
{ lensCheck: 'never-instruction', registerId: 'BP-MECH-004', mechanism: 'permission' },
]);
// Lifecycle automation phrased as an instruction: "after every commit", "before
// each push", "every time you …", "whenever you …", "always run". Recall-first.
const LIFECYCLE_RE =
/\b(?:after (?:every|each)|before (?:every|each)|on (?:every|each)|every time|each time|always run|whenever)\b/i;
// Absolute prohibition: a standalone "never" followed by an action word. Kept
// permissive (recall); the agent decides whether it is a real hard rule.
const NEVER_RE = /\bnever\s+[a-z]/i;
// A concrete path / glob / known-extension filename anywhere in the line.
const PATH_RE =
/(?:(?:\.{0,2}\/)?[\w.-]+\/[\w.*/-]+|\*\*?\/[\w.*-]+|\b[\w-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|md|json|ya?ml|toml|go|rs|java|rb|php|c|cpp|h|hpp|sh|sql|css|scss|html|env)\b)/;
// An imperative / modal verb that marks a line as an instruction rather than a
// bare cross-reference. Gates the path-specific class to cut "see foo/bar.md".
const INSTRUCTION_RE =
/\b(?:use|edit|run|always|must|should|put|place|write|add|modify|update|format|lint|test|name|store|keep|never|generate|build|deploy|commit)\b/i;
const getDetector = (lensCheck) => LENS_DETECTORS.find((d) => d.lensCheck === lensCheck);
function candidate(lensCheck, lineNo, lineText) {
const d = getDetector(lensCheck);
return {
lensCheck,
registerId: d.registerId,
mechanism: d.mechanism,
line: lineNo,
text: lineText.trim(),
};
}
/**
* Scan CLAUDE.md text for prose-judgment mechanism-fit candidates.
*
* Pass the file body (frontmatter stripped) for clean line numbers; the caller
* is then responsible for offsetting `line` by the body's start line. Raw text
* also works fenced code is skipped either way.
*
* @param {string} text
* @returns {Array<{lensCheck:string, registerId:string, mechanism:string, line:number, text:string}>}
*/
export function prefilterClaudeMd(text) {
const lines = String(text == null ? '' : text).split('\n');
const out = [];
let inFence = false;
for (let i = 0; i < lines.length; i++) {
const raw = lines[i];
const lineNo = i + 1;
// Toggle fenced code blocks (``` or ~~~). Fence lines themselves are skipped.
if (/^\s*(?:```|~~~)/.test(raw)) {
inFence = !inFence;
continue;
}
if (inFence) continue;
const trimmed = raw.trim();
if (trimmed === '') continue;
if (LIFECYCLE_RE.test(raw)) {
out.push(candidate('claude-md-lifecycle-phrasing', lineNo, raw));
}
if (NEVER_RE.test(raw)) {
out.push(candidate('never-instruction', lineNo, raw));
}
if (PATH_RE.test(raw) && INSTRUCTION_RE.test(raw)) {
out.push(candidate('unscoped-path-specific-instruction', lineNo, raw));
}
}
return out;
}

View file

@ -0,0 +1,160 @@
#!/usr/bin/env node
/**
* optimize-lens CLI feeds the v5.7 optimization lens (CA-OPT) `/config-audit
* optimize` command. It produces the two halves of the hybrid motor as one JSON
* payload:
*
* 1. `deterministic` the OPT scanner's high-precision findings (CA-OPT-001:
* a long numbered procedure in CLAUDE.md skill). Already part of the
* orchestrated audit; surfaced here so /optimize is a complete view.
* 2. `candidates` recall-oriented prose-judgment candidates from the
* lens-prefilter (lifecycle hook, unscoped path-specific rule, "never"
* permission), each stamped with the CONFIRMED register entry it might fit
* (claim / recommendation / source / severity). The opus
* optimization-lens-agent is the precision gate over these.
*
* Only CONFIRMED register entries are attached (Verifiseringsplikt); a candidate
* whose register rule is missing or unconfirmed is dropped, so the agent never
* sees an unverifiable recommendation.
*
* Usage:
* node optimize-lens-cli.mjs [path] [--output-file <path>] [--global]
*
* Exit codes: 0=ok, 3=unrecoverable error. Zero external dependencies.
*/
import { resolve } from 'node:path';
import { writeFile, readFile, stat } from 'node:fs/promises';
import { discoverConfigFiles } from './lib/file-discovery.mjs';
import { resetCounter } from './lib/output.mjs';
import { parseFrontmatter } from './lib/yaml-parser.mjs';
import { loadRegister, getEntry } from './lib/best-practices-register.mjs';
import { prefilterClaudeMd, LENS_DETECTORS } from './lib/lens-prefilter.mjs';
import { scan as optScan } from './optimization-lens-scanner.mjs';
/** Confirmed register entry for `id`, or null. */
function confirmedEntry(register, id) {
const e = getEntry(register, id);
return e && e.confidence === 'confirmed' ? e : null;
}
async function main() {
const args = process.argv.slice(2);
let targetPath = '.';
let outputFile = null;
let includeGlobal = false;
for (let i = 0; i < args.length; i++) {
if (args[i] === '--global') includeGlobal = true;
else if (args[i] === '--output-file' && args[i + 1]) outputFile = args[++i];
else if (!args[i].startsWith('-')) targetPath = args[i];
}
const absPath = resolve(targetPath);
try {
const s = await stat(absPath);
if (!s.isDirectory()) {
process.stderr.write(`Error: ${absPath} is not a directory\n`);
process.exit(3);
}
} catch {
process.stderr.write(`Error: path does not exist: ${absPath}\n`);
process.exit(3);
}
// Load the register once; tolerate its absence (deterministic half still runs).
let register = null;
try {
register = loadRegister();
} catch {
register = null;
}
resetCounter();
const discovery = await discoverConfigFiles(absPath, { includeGlobal });
// ── Deterministic half: the OPT scanner (CA-OPT-001) ──
const opt = await optScan(absPath, discovery);
// ── Recall half: prose-judgment candidates from the pre-filter ──
const claudeMdFiles = (discovery.files || []).filter((f) => f.type === 'claude-md');
const candidates = [];
for (const file of claudeMdFiles) {
let content;
try {
content = await readFile(file.absPath, 'utf-8');
} catch {
continue;
}
const parsed = parseFrontmatter(content);
const body = parsed.body || content;
const bodyStartLine = parsed.bodyStartLine || 1;
for (const cand of prefilterClaudeMd(body)) {
const entry = register ? confirmedEntry(register, cand.registerId) : null;
if (!entry) continue; // never surface an unverifiable recommendation
candidates.push({
file: file.relPath || file.absPath,
line: bodyStartLine - 1 + cand.line,
lensCheck: cand.lensCheck,
mechanism: cand.mechanism,
signalText: cand.text,
register: {
id: entry.id,
claim: entry.claim,
recommendation: entry.recommendation || null,
severity: entry.severity || 'low',
source: entry.source,
},
});
}
}
// The CONFIRMED prose-judgment entries, so the agent has full provenance even
// for a detector class that produced no candidates this run.
const registerEntries = register
? LENS_DETECTORS.map((d) => confirmedEntry(register, d.registerId))
.filter(Boolean)
.map((e) => ({
id: e.id,
lensCheck: e.lensCheck,
claim: e.claim,
recommendation: e.recommendation || null,
mechanism: e.mechanism || null,
severity: e.severity || 'low',
source: e.source,
}))
: [];
const payload = {
status: 'ok',
target: absPath,
deterministic: opt.findings || [],
candidates,
register: registerEntries,
counts: {
deterministic: (opt.findings || []).length,
candidates: candidates.length,
byLensCheck: candidates.reduce((acc, c) => {
acc[c.lensCheck] = (acc[c.lensCheck] || 0) + 1;
return acc;
}, {}),
},
};
const json = JSON.stringify(payload, null, 2);
if (outputFile) {
await writeFile(outputFile, json, 'utf-8');
}
if (!outputFile) {
process.stdout.write(json + '\n');
}
}
const isDirectRun = process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname);
if (isDirectRun) {
main().catch((err) => {
process.stderr.write(`Fatal: ${err.message}\n`);
process.exit(3);
});
}

View file

@ -0,0 +1,132 @@
/**
* lens-prefilter tests the deterministic, recall-oriented half of the v5.7
* optimization-lens hybrid motor (Chunk 2b).
*
* The pre-filter is intentionally CHEAP and recall-oriented: it surfaces
* candidate lines for the three prose-judgment mechanism-fit classes that the
* deterministic OPT scanner (Chunk 2a) deliberately skips. The opus
* optimization-lens-agent is the PRECISION gate downstream so these tests
* assert the pre-filter (a) detects the obvious signals and tags them with the
* right register id / lensCheck / mechanism, (b) reports correct 1-based line
* numbers, and (c) does not fire inside fenced code blocks. They do NOT demand
* zero false positives that is the agent's job, by design.
*/
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { prefilterClaudeMd, LENS_DETECTORS } from '../../scanners/lib/lens-prefilter.mjs';
const byCheck = (candidates, lensCheck) => candidates.filter((c) => c.lensCheck === lensCheck);
describe('lens-prefilter — detector table', () => {
it('exposes the three prose-judgment detectors keyed by register id + lensCheck', () => {
const map = new Map(LENS_DETECTORS.map((d) => [d.lensCheck, d]));
assert.deepEqual(map.get('claude-md-lifecycle-phrasing')?.registerId, 'BP-MECH-001');
assert.deepEqual(map.get('claude-md-lifecycle-phrasing')?.mechanism, 'hook');
assert.deepEqual(map.get('unscoped-path-specific-instruction')?.registerId, 'BP-MECH-002');
assert.deepEqual(map.get('unscoped-path-specific-instruction')?.mechanism, 'rule');
assert.deepEqual(map.get('never-instruction')?.registerId, 'BP-MECH-004');
assert.deepEqual(map.get('never-instruction')?.mechanism, 'permission');
});
});
describe('lens-prefilter — lifecycle phrasing (BP-MECH-001 → hook)', () => {
it('flags "After every commit, run the linter."', () => {
const c = byCheck(prefilterClaudeMd('After every commit, run the linter.'), 'claude-md-lifecycle-phrasing');
assert.equal(c.length, 1);
assert.equal(c[0].registerId, 'BP-MECH-001');
assert.equal(c[0].mechanism, 'hook');
assert.equal(c[0].line, 1);
assert.match(c[0].text, /After every commit/);
});
it('flags several lifecycle markers (before each, every time, whenever)', () => {
const text = [
'Before each push, run the test suite.',
'Every time you edit a config file, re-validate it.',
'Whenever you finish a task, update STATE.md.',
].join('\n');
assert.equal(byCheck(prefilterClaudeMd(text), 'claude-md-lifecycle-phrasing').length, 3);
});
it('does not flag a plain fact with no lifecycle phrasing', () => {
assert.equal(
byCheck(prefilterClaudeMd('This project uses TypeScript and pnpm.'), 'claude-md-lifecycle-phrasing').length,
0,
);
});
});
describe('lens-prefilter — never-instruction (BP-MECH-004 → permission)', () => {
it('flags "Never commit secrets to the repository."', () => {
const c = byCheck(prefilterClaudeMd('Never commit secrets to the repository.'), 'never-instruction');
assert.equal(c.length, 1);
assert.equal(c[0].registerId, 'BP-MECH-004');
assert.equal(c[0].mechanism, 'permission');
});
it('flags a bulleted "never" directive', () => {
const c = byCheck(prefilterClaudeMd('- Never push directly to main.'), 'never-instruction');
assert.equal(c.length, 1);
});
});
describe('lens-prefilter — unscoped path-specific instruction (BP-MECH-002 → rule)', () => {
it('flags an instruction referencing a glob', () => {
const c = byCheck(prefilterClaudeMd('Always format src/**/*.ts with prettier.'), 'unscoped-path-specific-instruction');
assert.equal(c.length, 1);
assert.equal(c[0].registerId, 'BP-MECH-002');
assert.equal(c[0].mechanism, 'rule');
});
it('flags an instruction referencing a concrete filename', () => {
const c = byCheck(prefilterClaudeMd('Run migrations from db/schema.sql before tests.'), 'unscoped-path-specific-instruction');
assert.equal(c.length, 1);
});
it('does not flag a bare path reference with no instruction verb', () => {
assert.equal(
byCheck(prefilterClaudeMd('See docs/architecture.md for the overview.'), 'unscoped-path-specific-instruction').length,
0,
);
});
});
describe('lens-prefilter — structural rules', () => {
it('reports correct 1-based line numbers', () => {
const text = ['# Title', '', 'Some facts here.', 'Never delete the .env file.'].join('\n');
const c = byCheck(prefilterClaudeMd(text), 'never-instruction');
assert.equal(c.length, 1);
assert.equal(c[0].line, 4);
});
it('ignores signals inside fenced code blocks', () => {
const text = [
'Real prose: never run this in production.',
'```bash',
'# after every deploy, never skip the smoke test',
'echo "every time"',
'```',
'More prose after the fence.',
].join('\n');
const all = prefilterClaudeMd(text);
// only the first prose line's "never" should match; nothing inside the fence
assert.ok(all.every((c) => c.line === 1), `expected only line 1; got lines ${all.map((c) => c.line).join(',')}`);
});
it('returns an empty array for a clean facts-only document', () => {
const text = [
'# Project',
'A web API written in Go. Data lives in Postgres.',
'The team deploys via GitHub Actions.',
].join('\n');
assert.deepEqual(prefilterClaudeMd(text), []);
});
it('a single line can yield candidates for two distinct detectors', () => {
// "never" + a concrete path → both the prohibition and the path-scope angle.
const c = prefilterClaudeMd('Never edit src/config.ts by hand.');
const checks = new Set(c.map((x) => x.lensCheck));
assert.ok(checks.has('never-instruction'));
assert.ok(checks.has('unscoped-path-specific-instruction'));
});
});