fix(voyage): S23 — make /trekplan Phase 9 dedup executable (defect #1)

Phase 9's dedup hand-off was broken on two layers, both surfaced by the S22
dogfood: (a) plan-critic + scope-guardian (Read/Glob/Grep, no Write) were told
to write /tmp/*-out.json the dedup helper reads — they cannot; (b) even with
Write, their Output format emits markdown, not the helper's
{agent,findings:[{file,line,rule_key,text}]} schema. readJsonOrNull then
swallowed the absent files -> a silent empty merge that discarded every finding.

Fix (operator-chosen A'): keep the reviewers read-only; make the hand-off run.
- plan-review-dedup.mjs gains a --stdin mode reading {plan_critic,scope_guardian};
  malformed stdin exits non-zero so a broken hand-off surfaces loudly instead of
  collapsing into a silent empty merge. File mode + its tests are untouched.
- plan-critic.md + scope-guardian.md now emit a trailing machine-readable `json`
  findings block (the inline hand-off; no Write tool needed).
- trekplan.md + planning-orchestrator.md Phase 9 rewritten in lockstep: extract
  both blocks, pipe via heredoc into --stdin. No temp files, portable, no
  pathguard dependency.

TDD: malformed-stdin test failed first (CLI ignored stdin -> exit 0 = the bug),
green after impl. New S23 doc-pin asserts both docs use --stdin (not the dead
/tmp paths) and both agents declare the json block. Suite 724 (722/2/0); live
HEAD baseline was 720, not the stale 705 STATE carried forward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB
This commit is contained in:
Kjell Tore Guttormsen 2026-06-19 21:12:36 +02:00
commit b4edc12bec
7 changed files with 196 additions and 30 deletions

View file

@ -712,8 +712,9 @@ Prompt: "Review this implementation plan for the task: {task}.
Plan file: {plan path}. Read it and find every problem — missing steps,
wrong ordering, fragile assumptions, missing error handling, scope creep,
underspecified steps. Rate each finding as blocker, major, or minor.
Write the structured JSON output to `/tmp/plan-critic-out.json` so the
dedup helper can merge with scope-guardian's findings."
End your response with the REQUIRED machine-readable `json` findings block
(schema in `agents/plan-critic.md`) so the orchestrator can pipe it to the
dedup helper. You have no Write tool — return the block inline, do not write a file."
**scope-guardian** — scope alignment check.
Prompt: "Check this implementation plan against the brief.
@ -721,23 +722,28 @@ Task: {task}. Brief file: {brief_path}. Plan file: {plan path}.
Find scope creep (plan does more than the brief requires) and scope gaps
(plan misses brief requirements). Check that referenced files and functions
exist. Verify that every Success Criterion in the brief is covered by the
plan's Verification section. Write structured JSON output to
`/tmp/scope-guardian-out.json`."
plan's Verification section. End your response with the REQUIRED machine-readable
`json` findings block (schema in `agents/scope-guardian.md`). You have no Write
tool — return the block inline, do not write a file."
After both complete, run an inline dedup pass:
After both complete, **extract each reviewer's trailing `json` findings block**
and pipe both into the dedup helper via **stdin** — the reviewers are read-only
(`Read/Glob/Grep`, no `Write`), so they cannot persist temp files; the
orchestrator does the persistence by piping their inline blocks:
```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/review/plan-review-dedup.mjs \
--plan-critic /tmp/plan-critic-out.json \
--scope-guardian /tmp/scope-guardian-out.json \
> /tmp/plan-review-merged.json
node ${CLAUDE_PLUGIN_ROOT}/lib/review/plan-review-dedup.mjs --stdin <<'JSON'
{ "plan_critic": <plan-critic's json block>, "scope_guardian": <scope-guardian's json block> }
JSON
```
The merged array attributes each finding to `[plan-critic, scope-guardian]`
when both reviewers raised the same issue (exact match on
`file:line:rule_key`, or Jaccard ≥ 0.7 on text tokens). Revise the plan
once for the merged set, not twice for the duplicates. Source: research/05
R1 + R2.
The helper **exits non-zero on malformed stdin** — a broken hand-off surfaces
loudly instead of collapsing into a silently-empty merge (the historical Phase-9
defect, where the read-only reviewers never wrote the files and the dedup ran on
nothing). The merged array attributes each finding to `[plan-critic, scope-guardian]`
when both reviewers raised the same issue (exact match on `file:line:rule_key`,
or Jaccard ≥ 0.7 on text tokens). Revise the plan once for the merged set, not
twice for the duplicates. Source: research/05 R1 + R2.
After both complete:
- If **blockers** are found: revise the plan to address them. Add a "Revisions"