config-audit/docs/v5.4.0-release-plan.md
Kjell Tore Guttormsen d77c18aa53 docs(plan): mark v5.4.0 Feature 3 DONE — Session B complete [skip-docs]
CA-SET autoMode shipped (3633571). All 3 Session B features built+pushed;
suite 936->949. Premise #3 confirmed against primary source (per-file-scope
gate passed). Next: Session C release (needs operator GO).

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

18 KiB

v5.4.0 Release — Multi-Session Plan

Status: PLANNED. No version bump yet. plugin.json = 5.3.0. All work below is gated on explicit operator GO per session. Plans live next to STATE per the continuity system. Mirrors the structure of docs/v5.3.0-release-plan.md.

Goal

Release v5.4.0 (minor, backward-compatible): three net-new additive scanner findings that extend the plugin-hygiene (PLH) and settings (SET) surfaces, mirroring real Claude Code warnings/validation that config-audit does not yet detect. Unlike v5.3.0 (which documented work already on main), the features here do not exist yet — Session B is the core build, not a conditional. No bugs/false-positives are involved; all three are enhancements with confirmed CC premises.

Theme: plugin-hygiene & settings-validation hardening.

Pre-verified facts (do NOT re-derive)

  • Last release: v5.3.0 = commit fe686b6, tag v5.3.0, 2026-06-19. git rev-list --count v5.3.0..HEAD = 0 → working tree is at the tag; v5.4.0 builds net-new code.
  • Suite at HEAD: 936/936 (clean tree, unchanged since the v5.3.0 release). self-audit at v5.3.0: configGrade A 97, pluginGrade A 100, readmeCheck.passed.
  • Scanner count = 13 (authoritative source countScannerShape, scanners/self-audit.mjs:56; README badge scanners-13). All three approved features extend existing scanners (PLH, SET) — no new scanner file, so the badge, the scanner table, and countScannerShape need no change. (An early grep of export async function scan counted 15 shapes — that set includes lib/non-badge shapes like CNF/IMP/SAL and is NOT the badge count. Do not bump the badge.)
  • PLH currently emits CA-PLH-001..CA-PLH-014 (014 = namespace collision, 013 = command-name ambiguity, both shipped v5.3.0). Next free: CA-PLH-015, CA-PLH-016.
  • Edit-target files (v5.3.0 release pattern): .claude-plugin/plugin.json, README.md (badges + What's New + version-history row + TOC), CHANGELOG.md (## [5.4.0] above [5.3.0]), docs/cc-2.1.x-gap-matrix.md, the two target scanners, their tests, and 3 knowledge entries.
  • CC premises primary-source verified (code.claude.com/docs, 2026-06-19) — see Session A reconciliation. The matrix is NOT ground truth; row 175 was found to be framed backwards.

Session A — Reconciliation & scope decision (read/plan; this session) DONE

No code changes. Re-reconcile the gap-matrix against HEAD, verify each candidate is still open AND that its CC premise is real, classify ship/defer/wontfix, get operator GO on the ship-list, draft the changelog. Output recorded below + a ## v5.4.0 reconciliation block appended to docs/cc-2.1.x-gap-matrix.md.

Verifisering (testbar):

  • Every v5.4 candidate has a recorded code-state verdict (IMPLEMENTED/OPEN/PARTIAL) with file:line.
  • Every candidate has a CC-premise verdict (CONFIRMED/REFUTED/UNVERIFIABLE) with a source.
  • Ship-list has a recorded operator GO before any build.
  • The refuted premise (#3) is corrected in the gap-matrix.

Session B — Implement the 3 approved features (CORE build)

One feature per chunk. Iron Law: failing test FIRST (/tdd), then minimal implementation. Re-confirm each feature's exact CC behavior against primary docs at the start of its chunk (Verifiseringsplikt — premises were verified in Session A but the exact field lists / warning text must be pinned before coding). Checkpoint STATE + this plan after each feature.

Feature 1 — PLH shadow-folder (CA-PLH-015) DONE (commit 7abc5a1)

  • Verifiseringsplikt correction (2026-06-19): the field set was pinned against code.claude.com/docs/.../path-behavior-rules. Only the replaces category truly shadows: shipped set = commands/agents/outputStyles. skills excluded — it adds to the default skills/ scan (both load, never a shadow); hooks/mcpServers/lspServers excluded — own merge rules, not a folder-shadow. Experimental themes/monitors omitted (docs warn their schema may change). Explicit-address exception honored ("commands": ["./commands/x.md"] not flagged). +5 tests (936→941), suite green, self-audit A/A, count 13, gitleaks clean. Pushed.
  • What: flag when a plugin.json component-path key (commands/agents/outputStyles) points at a custom path while the default folder of that name also exists in the plugin, so the default is silently ignored. Mirrors CC's warning in /doctor & claude plugin list (~2.1.140). (Original plan listed commands/agents/skills/hooks — corrected above.)
  • Where: scanners/plugin-health-scanner.mjs (per-plugin loop; the scanner already reads plugin.json but only checks name/description/version). Add component-path-key parsing.
  • Severity: MEDIUM — silently-shadowed components = dead config (a whole command/skill/agent set goes unreachable). category: 'plugin-hygiene'.
  • Tests: fixture plugin with both "commands": "custom/" AND a commands/ folder → flagged; fixture with only one of the two → not flagged; fixture with no component keys → not flagged.

Feature 2 — PLH skills:-array validation (CA-PLH-016) DONE (commit 76d5eda)

  • Verifiseringsplikt correction (2026-06-19): the plan's claim that the CC error "suggests the parent directory when an entry points at a file" is not in the primary docs — dropped. The finding asserts only the four primary-source-verified conditions. Escape backed by docs' path-traversal rule ("Installed plugins cannot reference files outside their directory … such as ../shared-utils"). string|array normalized, so a non-string top-level value (e.g. 42) is caught as one non-string entry. +3 tests (941→944), suite green, self-audit A/A, count 13.
  • What: when plugin.json has a skills: field (string|array), validate each entry resolves to an existing directory inside the plugin root. One finding per bad entry, problem ∈ {non-string, escapes-root, not-found, not-a-directory}. Mirrors claude plugin validate.
  • Where: scanners/plugin-health-scanner.mjs (read parsed.skills after the parse-success guard).
  • Severity: MEDIUM (broken/partly-broken plugin manifest). category: 'plugin-hygiene'.
  • Tests: fixture skills: ["valid-dir", "points-to-file.md", "missing", "../escape"] → one finding per bad entry, none for valid-dir; fixture with no skills: key → not flagged.

Feature 3 — SET autoMode structure + dead-config (CA-SET-NNN) DONE (commit 3633571)

  • Premise CONFIRMED against primary source (code.claude.com/docs/en/auto-mode-config) — the plan was correct this time (unlike #1/#2). 4 sub-keys verbatim; "$defaults" valid; exact scope exclusion quote confirmed. Per-file-scope gate PASSED: ConfigFile already carries scope (classifyScope'project' for shared .claude/settings.json), so the dead-config sub-check shipped (no fallback to structure-only needed). SET is in the orchestrator → SC-5 re-checked, byte-equal (snapshot fixture has no autoMode). +5 tests (944→949), suite green, self-audit A/A, count 13, gitleaks clean. Fixtures force-added (.claude/ gitignored).
  • What (two sub-checks):
    1. Structure validationautoMode, if present, must be an object whose only keys are environment, allow, soft_deny, hard_deny, each a string array (entries are prose rules; "$defaults" is a valid literal). Flag unknown sub-keys and wrong value types.
    2. Dead-configautoMode placed in shared project settings (.claude/settings.json) is ignored by CC ("Not read from shared project settings" — official docs). Flag it as dead config; valid scopes are user (~/.claude/settings.json), local (.claude/settings.local.json), and managed.
  • Where: scanners/settings-validator.mjs (autoMode is already in KNOWN_KEYS:21 but has no TYPE_CHECKS / nested validation). The dead-config sub-check needs the settings file's scope; confirm the scanner already knows per-file scope (it validates user/project/local files).
  • Severity: structure = LOW/MEDIUM (malformed config); dead-config = LOW (dead config).
  • Tests: valid autoMode in user settings → clean; unknown sub-key / non-array value → flagged; autoMode in shared .claude/settings.json → dead-config finding.

Per-feature gates (all must pass before that feature's commit):

  • New tests RED→GREEN; full suite N/N green.
  • node scanners/self-audit.mjs --json --check-readme → configGrade ≥ A, pluginGrade ≥ A, readmeCheck.passed, scanner count still 13.
  • Any snapshot/fixture touch → SC-5 re-seed + contamination-grep clean; gitleaks clean.
  • docs-gate (feat: commits): README and CLAUDE.md each get ≥3 substantive lines documenting the new finding — OR [skip-docs]. Prefer real docs (each finding deserves a CLAUDE.md "Scanner" note + README mention). Stage docs in a separate Bash call before commit.
  • Humanizer: each new finding needs a userImpactCategory mapping (dead config / conflict / configuration mistake) — verify --json/--raw stay byte-stable for existing findings (json-backcompat + raw-backcompat green); new findings are additive.

Scanner count stays 13. No badge/table/countScannerShape change (no new scanner file).


Session C — Release v5.4.0 (version sync + docs + tag + push)

  1. Bump .claude-plugin/plugin.json 5.3.0 → 5.4.0. (Version lives ONLY here.)
  2. README: version- badge → 5.4.0; tests- badge → exact suite count after Session B (will rise — new tests added). Replace "What's New in v5.3.0" → v5.4.0 (+ TOC anchor). Insert a new version-history row above **5.3.0**. Scanner count stays 13.
  3. CHANGELOG.md: insert ## [5.4.0] - <date> above ## [5.3.0] (Added / Test count / Verification — mirror the 5.3.0 block; this release is Added-only + Internal if any).
  4. Knowledge: apply the 3 backing entries (list below).
  5. Marketplace cross-repo (../catalog/, separate repo): bump the config-audit ref/version in marketplace.json + README after the plugin tag is pushed. Same push-window rules, separate push. Cross-repo edit requires operator GO.
  6. Gates: suite N/N; self-audit configGrade A, pluginGrade A, readmeCheck.passed, count 13; SC-5 byte-equal or re-seeded + contamination-grep clean; gitleaks clean (both repos).
  7. Commit release: v5.4.0 — <one-line>, tag v5.4.0, push (in push window).

Verifisering (testbar): git tag --list v5.4.0 returns it; README version- badge == plugin.json version; self-audit --check-readme passed + badge tests == suite count; git log -1 subject starts release: v5.4.0; marketplace entry shows 5.4.0.


Key assumptions (test, don't trust)

  • 5.4.0 is non-breaking. Three additive findings; no envelope/JSON-shape change. Verify json-backcompat + raw-backcompat + SC-5 snapshot stay green; new findings appear only in configs that trigger them. If anything changes the JSON shape or existing-finding output → it is a Breaking note, not Added.
  • settings-validator knows per-file scope. Feature 3's dead-config sub-check needs to know a settings file is the shared project one. Test this assumption first in Feature 3's chunk — if the scanner doesn't already carry scope per file, either thread it through or drop the dead-config sub-check to structure-only (still ships).
  • docs-gate blocks feat: commits lacking ≥3 substantive lines in BOTH README and CLAUDE — proven 2026-06-19. (release:/fix:/chore:/docs: pass freely.)
  • self-audit --check-readme is number-only (badge vs filesystem) — it does NOT catch prose gaps. Manually eyeball the scanner table + "What's New" (the SKL-row gap slipped exactly here).

Knowledge-touch list (Session C step 4)

Three short backing entries (mirrors v5.3.0's 3-entry pattern; grep-verify each is absent first):

  1. knowledge/claude-code-capabilities.md — plugin component-path keys shadow default folders; CC warns in /doctor & claude plugin list (~2.1.140/142). Backs CA-PLH-015.
  2. knowledge/claude-code-capabilities.mdclaude plugin validate requires skills: entries to be directories (~2.1.145). Backs CA-PLH-016.
  3. knowledge/claude-code-capabilities.md or configuration-best-practices.mdautoMode schema (environment/allow/soft_deny/hard_deny string arrays, "$defaults" literal) and the "not read from shared project settings" rule. Backs CA-SET autoMode finding.

Everything else in the matrix (env-var rows, model-lineup nuance, hook-output fields, nested .claude doc) → defer to rolling knowledge maintenance.

Out of scope (do not start without separate GO)

  • #2 acceptEdits-on-shell/build-config (deferred — needs the exact CC special-cased file-category list primary-source-confirmed before it's buildable). Candidate for a later release.
  • #6 nested-.claude closest-wins (deferred — a NEW scanner: ancestor-chain walker + grouping + badge bump 13→14. Heaviest item; natural headline for its own release, e.g. v5.5.0).
  • #3 Read-deny/Glob-Grepwontfix (premise refuted by primary source; see reconciliation).
  • Any scanner refactor not required by the 3 approved features; marketplace-wide changes beyond the config-audit catalog entry.

v5.4.0 scope decision (Session A output, 2026-06-19)

Operator GO (recorded verbatim)

"Option A" — ship #1 PLH shadow-folder + #5 PLH skills:-array + #4 autoMode structure/ dead-config. No new scanner (badge stays 13). #2 and #6 deferred, #3 wontfix.

Ship-list (3 features): CA-PLH-015 shadow-folder · CA-PLH-016 skills:-array · CA-SET-NNN autoMode structure + dead-config. All extend existing scanners.

Reconciliation result (6 candidates vs HEAD, code-state + CC-premise)

# Candidate (matrix row) Code-state @HEAD CC premise Verdict
1 PLH shadow-folder (164) OPEN — plugin-health-scanner parses only name/desc/version CONFIRMED (~2.1.140; /doctor+plugin list warning) ship-5.4
5 PLH skills:-array dirs (165) OPEN — does not read parsed.skills CONFIRMED (~2.1.145 claude plugin validate) ship-5.4
4 autoMode.hard_deny structure (179) OPEN — key in KNOWN_KEYS:21, no TYPE_CHECKS/nested val CONFIRMED (primary source: 4 string-array sub-keys; not read from shared project settings) ship-5.4
2 acceptEdits-writes shell/build (176) OPEN — 0 matches in scanners/ CONFIRMED (~2.1.160) but exact file-category list unpinned defer (needs primary-source field list)
6 nested-.claude closest-wins (139/166) OPEN — no scanner; CML walks only the CLAUDE.md cascade CONFIRMED (~2.1.178) defer (NEW scanner, badge bump)
3 Read-deny hides Glob/Grep (175) PARTIAL — hint in permission-rules.mjs:158-159, but dominates() treats Read/Glob/Grep as distinct REFUTED — docs: "Claude makes a best-effort attempt to apply Read rules to … Grep and Glob"; Read-deny already covers them wontfix

Matrix correction (Verifiseringsplikt): row 175's framing was backwards. CC's permissions doc states Read deny rules already apply (best-effort) to Glob/Grep, so a "Read-deny is bypassable" finding would be a false positive — the same failure mode as the invented MCP trust field. The only real Read-deny bypass is via Bash subprocesses (a python/node script that opens files), which is already documented behavior, not a config mistake to flag. The Windows-path half (CC normalizes C:\/c/) is real but narrow/low-value and is folded into "defer rolling maintenance".

Draft CHANGELOG narrative (finalize after Session B)

Added

  • PLH plugin-folder shadowing (CA-PLH-015) — flags a plugin.json component-path key (commands/agents/skills/hooks) that points at a custom path while the default folder of that name also exists, so one silently shadows the other. Mirrors Claude Code's /doctor & claude plugin list warning. Severity medium (dead config), category: 'plugin-hygiene'.
  • PLH skills:-array validation (CA-PLH-016) — validates each entry in a plugin.json skills: array resolves to a directory inside the plugin; flags non-string entries, missing paths, file-not-directory, and path-escape. Mirrors claude plugin validate. Severity medium, category: 'plugin-hygiene'.
  • SET autoMode structure + dead-config (CA-SET-NNN) — validates autoMode contains only the four known sub-keys (environment/allow/soft_deny/hard_deny), each a string array, and flags autoMode placed in shared project settings (.claude/settings.json), which Claude Code ignores. Structure = low/medium, dead-config = low.

Internal / Test count / Verification — fill in Session C (mirror the 5.3.0 block).

Scanner count stays 13 (all extend existing PLH/SET scanners). --json/--raw byte-stable.

What's New in v5.4.0 (draft prose)

v5.4.0 — plugin-hygiene & settings-validation hardening. Three additive findings extend the plugin and settings surfaces: PLH now flags a plugin.json component-path key that silently shadows the default folder of the same name (mirroring Claude Code's /doctor warning), and validates that skills:-array entries resolve to real directories (mirroring claude plugin validate); the settings validator now checks the structure of autoMode and flags it when placed in shared project settings, where Claude Code ignores it. Scanner count stays 13 (all extend existing scanners). --json/--raw remain byte-stable.

Version & non-breaking confirmation

  • 5.4.0 (minor). Additive findings only; no envelope/JSON-shape change. json-backcompat + raw-backcompat + SC-5 snapshot green at HEAD; new findings are additive (appear only in configs that trigger them).