diff --git a/CHANGELOG.md b/CHANGELOG.md index 715f389..f98af04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ All notable changes to the code-et plugin will be documented in this file. +## [4.3.0] - 2026-05-13 + +### Changed — `/code:plan` emits structured `files[]` entries; `/code:ship` consumes them + +`metadata.files` was an array of `path:line` strings — flat, lossy, and a tax the subagent paid in tokens. Plan-time LSP already resolved each acceptance criterion to a qualified symbol (per the existing "LSP for symbols" rule), but the symbol name was thrown away before `TaskCreate`, leaving the implementer subagent to rediscover it with `Grep` + `Read` on a cold start. On an 8-task `/code:ship` run that's 16-48k tokens of pure rediscovery — paid every time, for information the orchestrator already had. + +**Changed.** Each `files[]` entry is now `{path, symbol, line, op}` where `op ∈ {add, modify, replace, delete}`. `path` + `op` are required; `symbol` is required for `modify|replace|delete` and recommended for `add`; `line` is an LSP-resolved hint. The contract is `symbol` — if the line drifts between plan and ship, the implementer re-resolves via `documentSymbol`. Deletion of superseded code, previously buried in prose under `rationale`, is now an explicit `op: "delete"` entry — typed action, not a narrative aside. + +**Plan-time validation.** Before `TaskCreate`, every `path` is checked against `git ls-files` (for `modify|replace|delete`) or against `FILE-REFERENCE.md`'s documented top-level areas when that file exists (for `add`). Stale references caught at plan time are subagent dispatches not wasted. + +**Ship-side rendering.** `/code:ship`'s dispatch prompt renders each entry as `- [:] → `, so the subagent receives a typed action list rather than a bare path list. The cold-start cost drops from "find the symbol" to "open the file, jump to the symbol." + +**Token math.** ~35 extra tokens per entry × ~8 tasks = ~280 tokens at plan time, against 15-45k saved per `/code:ship` dispatch. The LSP work was already happening; the change is to *persist* it instead of recomputing it inside every subagent. + +Files touched: `commands/plan.md`, `commands/ship.md`, `CLAUDE.md`. + ## [4.2.3] - 2026-05-12 ### Fixed — task-metadata hook was wired to the wrong event diff --git a/code-et-implementer/.claude-plugin/plugin.json b/code-et-implementer/.claude-plugin/plugin.json index d3e35d6..eaaeee8 100644 --- a/code-et-implementer/.claude-plugin/plugin.json +++ b/code-et-implementer/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "code", - "version": "4.2.3", + "version": "4.3.0", "description": "Pure-Rust Clean Architecture workflow. Six commands: start, fix, plan, ship, review, install-ci. Always-latest deps, CI audit gate, anti-slop enforced.", "author": { "name": "Kennet Kusk" diff --git a/code-et-implementer/CLAUDE.md b/code-et-implementer/CLAUDE.md index c25034a..0183bbc 100644 --- a/code-et-implementer/CLAUDE.md +++ b/code-et-implementer/CLAUDE.md @@ -35,7 +35,11 @@ Tasks created with `TaskCreate` carry: ``` metadata: { verification: "cargo nextest run && cargo clippy --all-targets -- -D warnings", - files: ["crates//src/path/to/file.rs:42"], + files: [ + {"path": "crates//src/path/to/file.rs", "symbol": "Type::method", "line": 42, "op": "modify"}, + {"path": "crates//src/new.rs", "symbol": "NewType", "op": "add"}, + {"path": "crates//src/legacy.rs", "symbol": "old_fn", "line": 89, "op": "delete"} + ], expected_outcome: "what success looks like", rationale: "why this task exists — the constraint or decision driving it", user_story: "US-N" | "AC-N.M" | "chore:", @@ -43,6 +47,8 @@ metadata: { } ``` +`files[]` entries: `path` + `op` always required; `symbol` required for `modify|replace|delete`; `line` is an LSP-resolved hint (drift-tolerant — `symbol` is the contract). Full schema and validation rules in `commands/plan.md` §"TaskCreate metadata". + `rationale` is mandatory. Subagents in `/code:ship` start cold — they need the *why*, not just the *what*, to make judgment calls. `layer` is mandatory. Each *file* declares its layer; vertical slices may span layers. diff --git a/code-et-implementer/commands/plan.md b/code-et-implementer/commands/plan.md index 2f9e0ef..339cf71 100644 --- a/code-et-implementer/commands/plan.md +++ b/code-et-implementer/commands/plan.md @@ -130,7 +130,13 @@ Read the PRD (it is the authoritative spec). **Replace, don't accumulate.** When a slice supersedes existing logic, the task scope **includes deletion of the superseded code**. State the `path:line` being replaced in `metadata.rationale`. No parallel utilities, no `// TODO: remove old X`. -**LSP for symbols.** Use `documentSymbol` / `findReferences` / `definition` to anchor each US/AC to `file:line`. Grep/Glob for discovery; LSP for precision. Never use LSP to enumerate the project. For 3+ independent areas, spawn parallel `Agent(subagent_type: "Explore", model: "haiku")` queries in a single message — Haiku 4.5 is the right tier for breadth scans. +**LSP for symbols.** Use `documentSymbol` / `findReferences` / `definition` to resolve each US/AC to a `{path, symbol, line, op}` entry — persist the result in `metadata.files[]` (schema below). Do not throw away the symbol name; that's the contract the subagent edits against if `line` drifts. Grep/Glob for discovery; LSP for precision. Never use LSP to enumerate the project. For 3+ independent areas, spawn parallel `Agent(subagent_type: "Explore", model: "haiku")` queries in a single message — Haiku 4.5 is the right tier for breadth scans. + +**Path validation.** Before `TaskCreate`, validate every `files[].path`: +- `op ∈ {modify, replace, delete}` → path must appear in `git ls-files`. If not, the symbol moved or was deleted — re-resolve via LSP or drop the entry. +- `op = add` → path must either appear in `git ls-files` (append to existing file) or, if `FILE-REFERENCE.md` exists at repo root, sit under a documented top-level area there. Reject paths under undocumented top-level directories when `FILE-REFERENCE.md` is present; otherwise accept any path the workspace `Cargo.toml` covers. + +Path drift caught at plan time is one less wasted subagent dispatch. ### Anti-slop self-critique (before TaskCreate) @@ -150,15 +156,28 @@ The list lives in `code-et-implementer/docs/anti-slop.md`; the inline summary ab ```json { "verification": "", - "files": ["crates//src/path.rs:42", ...], + "files": [ + {"path": "crates//src/path.rs", "symbol": "Type::method", "line": 42, "op": "modify"}, + {"path": "crates//src/new_file.rs", "symbol": "NewType", "op": "add"}, + {"path": "crates//src/legacy.rs", "symbol": "deprecated_fn", "line": 89, "op": "delete"} + ], "expected_outcome": "", - "rationale": "<1-2 sentences: why this slice exists, the constraint driving it. If replacing existing code, name the path:line being deleted.>", + "rationale": "<1-2 sentences: why this slice exists, the constraint driving it.>", "user_story": "US-N | AC-N.M | chore:", "layer": "domain | application | infrastructure | interface | chore" } ``` -`rationale` is mandatory — the subagent starts cold and needs the *why*. `layer` is mandatory; the per-file layer also feeds the validator on every file the task touches. `verification` exercises the full slice — `cargo nextest run -p ` for unit, `cargo nextest run --workspace` for cross-layer. +**`files[]` entry shape:** + +| Field | Required | Notes | +|---|---|---| +| `path` | always | Workspace-relative. Validated against `git ls-files` (`modify\|replace\|delete`) or `FILE-REFERENCE.md` modules (`add`). | +| `op` | always | `add` (create symbol), `modify` (edit body), `replace` (full rewrite — pair with sibling `delete` if cross-file supersession), `delete` (remove symbol). | +| `symbol` | for `modify\|replace\|delete`; recommended for `add` | Qualified Rust path: `User::validate`, `db::pool`, `routes::auth::login`. Resolved via LSP `documentSymbol`. | +| `line` | optional hint | Current line at plan time. Implementer re-resolves via LSP if it drifts. Omit for `add` on a new file. | + +`rationale` is mandatory — the subagent starts cold and needs the *why*. `layer` is mandatory; the per-file layer also feeds the validator on every file the task touches. Deletion of superseded code is encoded as explicit `op: "delete"` entries in `files[]`, not prose in `rationale`. `verification` exercises the full slice — `cargo nextest run -p ` for unit, `cargo nextest run --workspace` for cross-layer. Set dependencies with `TaskUpdate(addBlockedBy)`. Independent slices stay parallel. diff --git a/code-et-implementer/commands/ship.md b/code-et-implementer/commands/ship.md index 4f8826b..7dfaba7 100644 --- a/code-et-implementer/commands/ship.md +++ b/code-et-implementer/commands/ship.md @@ -46,7 +46,13 @@ Each subagent starts cold. Send one comprehensive first turn — intent, constra ## Files to touch - + [:] → " +omitting ":" if absent and "→ " if absent. Examples: + - modify crates/domain/src/user.rs:42 → User::validate + - add crates/infrastructure/src/db/users.rs → UserRepository + - delete crates/infrastructure/src/legacy.rs:89 → old_validate_fn +Read each entry's file (sliced) before editing. `line` is a hint — if the symbol has moved, re-resolve via LSP `documentSymbol`; `symbol` is the contract. Apply each op exactly: `add` creates, `modify` edits in place, `replace` full-rewrites the symbol, `delete` removes it (plus all references).> ## Layer