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
258 lines
18 KiB
Markdown
258 lines
18 KiB
Markdown
# 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 validation** — `autoMode`, 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-config** — `autoMode` 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.md` — `claude 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.md` — `autoMode`
|
|
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-Grep** — **wontfix** (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).
|