fix(commands): stop writing files no later step can read, and payloads nobody asked for
Dogfooding the four read commands (posture, tokens, manifest, whats-active) surfaced four defect classes, all in the seam between what a command template promises and what the scanner behind it actually does. M-BUG-40, fifth arm: posture wrote four temp files it could never read back. #49 closed the $$/cross-block class in four commands, but posture survived it — and so did the guard written to prevent exactly this. The guard compared each $$ path to the block that created it, so a path written once and then read via prose had no second occurrence to flag. Measured live: written from PID 21614, read attempted from PID 23772. The invariant is now blanket (no $$ in any temp path), which also caught fix.md and feature-gap.md. M-BUG-43: 6 of 7 scanners write their payload to stdout when --raw/--json is set even when --output-file was given, and the templates redirected only stderr. Measured: posture 255 182 B, whats-active 35 922 B, drift 28 316 B, manifest 23 825 B, tokens 8 768 B. fix and feature-gap never read the file they wrote, so both recovered one letter grade from a quarter-megabyte dump. tokens swallowed --json and --with-telemetry-recipe: documented, never threaded, so --json returned the humanized payload where the docs promise byte-stable v5.0.0 output. M-BUG-42: manifest's render contract asked for {load}; the payload carries loadPattern, so the Load column rendered blank for all 96 rows. Four new tests (1449 -> 1453), each verified red before the fix. The render-contract test checks {field} names against a live payload from a fixture, since a hardcoded key list would drift. Frozen v5.0.0 snapshots untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VGCk9o27eWo9uXLjkZTXEq
This commit is contained in:
parent
09f817977c
commit
acd1cf1248
13 changed files with 336 additions and 21 deletions
35
CHANGELOG.md
35
CHANGELOG.md
|
|
@ -8,6 +8,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- **`M-BUG-40`, fifth arm — `posture` wrote four temp files it could never read back.** #49 closed the
|
||||
`$$`/cross-block class in four commands, but `posture.md` survived it, and so did the guard written
|
||||
to prevent exactly this. The guard compared each `$$` path against the block that created it, so a
|
||||
path written **once** and then read via prose ("Read the JSON output file using the Read tool") had
|
||||
no second occurrence to flag. Measured live: the scanner wrote `/tmp/config-audit-posture-21614.json`
|
||||
from PID 21614 while the next Bash call ran as PID 23772, and the read step had no path to hand the
|
||||
Read tool at all. The invariant is now blanket — **no `$$` in any temp path in any command file** —
|
||||
which also caught `fix.md` and `feature-gap.md`. All five sites now use fixed literal paths, repeated
|
||||
literally in every step that needs them.
|
||||
- **`M-BUG-43` — commands leaked whole JSON payloads into the transcript.** Every scanner except
|
||||
`scan-orchestrator` writes its payload to stdout when `--raw`/`--json` is set, **even when
|
||||
`--output-file` was given** — and the command templates redirected only stderr. Measured on a real
|
||||
repo: `posture` 255 182 B, `whats-active` 35 922 B, `drift` 28 316 B, `manifest` 23 825 B, `tokens`
|
||||
8 768 B. `fix` and `feature-gap` were the worst case: both ran posture with `--json`, never read the
|
||||
file they wrote, and in practice recovered a single letter grade from a quarter-megabyte dump — in
|
||||
the plugin that exists to cut token cost. 13 invocations across 10 command files now redirect stdout,
|
||||
and the two commands that needed the data read it from their output file instead.
|
||||
- **`tokens` swallowed two documented flags.** `--json` and `--with-telemetry-recipe` were listed as
|
||||
recognized flags but never threaded into the CLI call, so `--json` returned the *humanized* payload
|
||||
where the docs promised byte-stable v5.0.0 output (measured: 4/4 findings carried humanizer fields,
|
||||
and the title read "Your file starts with content that changes between turns" instead of
|
||||
"Cache-breaking volatile content at top of CLAUDE.md"), while `--with-telemetry-recipe` silently
|
||||
produced no `telemetry_recipe_path` — the very flag the command's own closing tip recommends.
|
||||
- **`M-BUG-42` — `manifest` asked for a field the scanner never emits.** The render contract used
|
||||
`{load}`; the payload carries `loadPattern`. The Load column — which the command's own prose calls
|
||||
the whole point of the view — would render blank for all 96 rows. `posture`'s headline had the same
|
||||
shape (`{qualityAreaCount}`, never emitted) and now takes its count from the humanized scorecard
|
||||
rather than `areas.length`, which counts a Feature Coverage row the table below deliberately excludes.
|
||||
|
||||
### Added
|
||||
- Four command-template shape tests (1449 → 1453), each verified to fail before the fix: a blanket
|
||||
`$$` ban, stdout-redirect discipline for any scanner invoked with `--output-file` in raw/json mode,
|
||||
flag threading from prose to shell, and a render-contract test that checks every `{field}` against a
|
||||
**live payload generated from a fixture** rather than a hardcoded key list, which would drift.
|
||||
|
||||
- **`M-BUG-40` — command templates assumed shell state survives between fenced blocks.** It does not:
|
||||
every ```` ```bash ```` fence is executed as its own Bash call, in its own process. A variable
|
||||
assigned in one block is empty in the next, and `$$` (the PID) differs between calls, so a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue