fix(voyage): inject plan_critic via Phase 9 readAndUpdate (906f155d)

This commit is contained in:
Kjell Tore Guttormsen 2026-05-10 21:03:54 +02:00
commit 5f26de2f0d
2 changed files with 62 additions and 0 deletions

View file

@ -726,6 +726,47 @@ After both complete:
- If only **minor** issues or clean: proceed without changes. Note the
review result in the plan.
### Inject plan_critic verdict into plan frontmatter (post-dedup, post-revise)
After the dedup pass completes and any blocker/major revisions are folded
in, atomically update the plan's frontmatter with the plan-critic verdict
so downstream surfaces (notably `playground/voyage-playground.html`
`buildArtifactKeyStat`) can read it without re-parsing the review JSON.
Phase 8 (write plan) precedes Phase 9 (adversarial review), so the
verdict cannot be in Phase 8's frontmatter template — it must be
injected here, after the verdict exists.
Read the verdict from `/tmp/plan-critic-out.json` and apply it via
`lib/util/markdown-write.mjs` `readAndUpdate`:
```js
import { readFileSync } from 'node:fs';
import { readAndUpdate } from `${CLAUDE_PLUGIN_ROOT}/lib/util/markdown-write.mjs`;
const criticVerdict = JSON.parse(readFileSync('/tmp/plan-critic-out.json', 'utf-8')).verdict;
const result = readAndUpdate(planPath, ({ frontmatter, body }) => {
frontmatter.plan_critic = criticVerdict;
return { frontmatter, body };
});
if (!result.valid) {
// Non-fatal: log warning. plan.md already exists from Phase 8;
// missing plan_critic is degraded UX, not blocking.
console.warn('plan_critic injection failed:', result.errors);
}
```
Field semantics:
- `plan_critic` — string. One of `APPROVE`, `APPROVE_WITH_NOTES`,
`REVISE`, or `BLOCK`. Matches the rubric in `plan-critic` agent
output. Omitted when the inject failed (Phase 9 surfaces a warning
in that case but does not abort).
Schema compatibility: `plan_critic` is **additive and optional**. The
plan validator (`lib/validators/plan-validator.mjs`) tolerates unknown
frontmatter keys, so this addition does NOT require a `plan_version`
bump. Plans written before this field was added validate identically.
## Phase 10 — Present and refine
Present a summary to the user:

View file

@ -625,3 +625,24 @@ test('docs/annotation-quickstart.md exists with ≤7 numbered steps and example-
`${path} must reference the canonical example fixture for hands-on verification`,
);
});
test('commands/trekplan.md Phase 9 documents plan_critic injection via readAndUpdate (906f155d)', () => {
// Phase 9 (adversarial review) writes the plan-critic verdict back into
// plan.md frontmatter AFTER plan-review-dedup completes. The inject must
// happen post-Phase-8 (write) because Phase 8 precedes Phase 9 in the
// pipeline — the value cannot be in Phase 8's frontmatter template.
// Both the field name (plan_critic) and the inject mechanism
// (readAndUpdate from lib/util/markdown-write.mjs) must be documented
// so future maintainers can trace the contract.
const text = read('commands/trekplan.md');
assert.match(
text,
/plan_critic/,
'commands/trekplan.md must document plan_critic frontmatter field (906f155d)',
);
assert.match(
text,
/readAndUpdate/,
'commands/trekplan.md must reference readAndUpdate from lib/util/markdown-write.mjs (906f155d)',
);
});