feat(scanners): the recovery path is code you can run, not prose you can read
R1+R2 as one chunk — both KRITISK rows of the Q3 severity table sit on the restore path, and neither closes alone. R1: rollback-engine.mjs verified every checksum before AND after each write, resolved the legacy backup root and reported createdNotRemoved — and none of it was reachable. Measured: 16 files under scanners/ carry a process.argv entry; the engine was not one of them. commands/rollback.md drove the restore as model prose: an ESM import block a template cannot execute, ad-hoc `cp` offered underneath as the runnable path, and "(checksum verified)" pre-rendered three times in the success output. `cp` establishes no checksum, so the verification was a property of the template rather than of the run — on the one surface that runs when the user is already in trouble. R2: implement.md Step 3 hand-built its backup (mkdir, cp, a date-derived id, a manifest typed out in the template) while parseManifest knew one frozen sample of that format, pinned by a HAND-WRITTEN fixture instead of by the template's own text. Rename a key and parseManifest returns zero files while rollback reports success. Fixing only R1 leaves the new CLI parsing a prose format; fixing only R2 leaves a clean format with no runnable entry. - scanners/rollback-cli.mjs — --list / --create / --restore / --delete over the existing engine, on the shared requireValidArgs gate. Exit 0 done, 1 outstanding (gate refusal with nothing written, or a backup that covered fewer targets than given), 2 a file failed, 3 could not do the job. A gated restore is 1, not 3: "this write leaves your project" is a verdict about a write that WAS examined, and it rides in the payload where a command under 2>/dev/null can act on it. - createBackup gains `created` (recorded, never copied — no backup can hold a file that does not exist) and `skipped`, so a backup covering fewer files than asked is no longer indistinguishable from a clean one. - implement.md Step 3 and rollback.md now call the CLI. parseManifest's implement-format branch stays: nothing writes that shape now, but every backup made before this chunk is on disk in it. - backup-restore-contract.test.mjs checks every field rollback.md renders against a payload produced by RUNNING the CLI. That is what replaced "(checksum verified)". 20 guards seen red against the original state before any production code, then each against its own defect. Two holes that surfaced there were mine: the implement assertion matched `--create` as a substring of `--created` and stayed green when the call was removed; and mutating the argv gate showed requireValidArgs sets exit 3 by itself, so a CLI can report that it could not parse its arguments and still run the restore underneath — that case is now asserted on the bytes. Suite 1752 -> 1777, 0 fail. Frozen tests/snapshots/v5.0.0 untouched. Dogfooded through the templates' own command lines against a sandboxed HOME, including the machine-wide arm: refused with the file unchanged, then restored under --approve-scope. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Logq8GGWKhtyDem63FTEnG
This commit is contained in:
parent
b35ff449e8
commit
44b222859e
13 changed files with 1083 additions and 86 deletions
|
|
@ -304,7 +304,10 @@ every project on every turn.
|
|||
|
||||
That gate now runs inside the engines, not only in the command prose that wraps them. It covers
|
||||
`/config-audit fix`, `/config-audit rollback`, `campaign export`, `--save-baseline`, and
|
||||
`optimize --subtract --apply`. When a run is withheld, nothing has been written: you get the
|
||||
`optimize --subtract --apply`. For rollback that is now literal: the restore runs through
|
||||
`scanners/rollback-cli.mjs`, which verifies each file's checksum before and after writing it, so
|
||||
"restored and verified" is something the run reports rather than something the output template
|
||||
says. A restore that would land outside your project is refused with nothing written. When a run is withheld, nothing has been written: you get the
|
||||
reason and the affected paths, and you re-run with your approval (`--approve-scope` on the CLIs).
|
||||
Approval is always a separate act — classifying a target is not approving it. The plugin's own
|
||||
bookkeeping (backups, session state, ledgers, and the report file you named with `--output-file`)
|
||||
|
|
@ -447,6 +450,7 @@ All tools work standalone — no Claude Code session needed:
|
|||
| **Tokens** | `node scanners/token-hotspots-cli.mjs <path> [--json] [--global] [--no-exclude-cache] [--output-file path] [--accurate-tokens] [--with-telemetry-recipe]` |
|
||||
| **Manifest** | `node scanners/manifest.mjs <path> [--json]` — ranked component-level source table with per-source load pattern + always-loaded subtotal |
|
||||
| **What's active** | `node scanners/whats-active.mjs <path> [--json] [--verbose] [--suggest-disables]` |
|
||||
| **Backup / restore** | `node scanners/rollback-cli.mjs [--list] [--restore <id>] [--delete <id>] [--create --target <path> …] [--created <path>] [--dry-run] [--approve-scope] [--repo <root>] [--json] [--output-file path]` |
|
||||
| **Self-audit** | `node scanners/self-audit.mjs [--json] [--fix] [--check-readme]` |
|
||||
| **Full scan** | `node scanners/scan-orchestrator.mjs <path> [--global] [--full-machine] [--no-suppress]` |
|
||||
|
||||
|
|
@ -606,6 +610,7 @@ Shared modules used by all scanners — useful if you're reading the source or e
|
|||
| `whats-active.mjs` | CLI: read-only active-config inventory (v3.1.0+) |
|
||||
| `token-hotspots-cli.mjs` | CLI: token hotspots ranking with optional `--accurate-tokens` |
|
||||
| `write-scope-cli.mjs` | CLI: classify write targets before an approval surface (`--target`, repeatable) |
|
||||
| `rollback-cli.mjs` | CLI: the runnable entry to backup and restore (`--list` / `--create` / `--restore` / `--delete`). Both pipelines back up through this one code path, so the manifest format is never written or read by hand |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue