From 9909ce10665cb1615258694ded03c710ec3428de Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Sun, 10 May 2026 21:21:02 +0200 Subject: [PATCH] fix(voyage): dashboard reads fm.plan_critic + orchestrator doc-consistency (906f155d, bee33a69) --- plugins/voyage/agents/planning-orchestrator.md | 11 +++++++++++ .../voyage/playground/voyage-playground.html | 2 +- .../playground/voyage-playground.test.mjs | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/plugins/voyage/agents/planning-orchestrator.md b/plugins/voyage/agents/planning-orchestrator.md index d7f6abb..a2bf7af 100644 --- a/plugins/voyage/agents/planning-orchestrator.md +++ b/plugins/voyage/agents/planning-orchestrator.md @@ -431,6 +431,17 @@ After both complete: machine-checkable — a plan without verifiable manifests cannot drive deterministic execution. - Add a "Revisions" note at the bottom documenting changes +- **Inject `plan_critic` verdict into plan frontmatter** (after dedup, after + any blocker/major revisions). Read the verdict from + `/tmp/plan-critic-out.json` and atomically update plan.md via + `readAndUpdate` from `lib/util/markdown-write.mjs`: + `frontmatter.plan_critic = criticVerdict`. Field semantics: one of + `APPROVE`, `APPROVE_WITH_NOTES`, `REVISE`, or `BLOCK`. The + `plan_critic` field is additive + optional — plans without it remain + valid. Surfaces (notably `playground/voyage-playground.html` + `buildArtifactKeyStat`) read this field directly without re-parsing + the review JSON. Detailed contract documented in + `commands/trekplan.md` Phase 9 (906f155d, bee33a69). ### Phase 7 — Completion diff --git a/plugins/voyage/playground/voyage-playground.html b/plugins/voyage/playground/voyage-playground.html index a90130a..a908825 100644 --- a/plugins/voyage/playground/voyage-playground.html +++ b/plugins/voyage/playground/voyage-playground.html @@ -1763,7 +1763,7 @@ playground first-run shows a complete round-trip-able artifact. if (!entry) return '—'; var fm = entry.frontmatter || {}; if (key === 'brief') return 'Quality: ' + (fm.brief_quality || 'complete'); - if (key === 'plan') return 'Profile: ' + (fm.profile || '—'); + if (key === 'plan') return 'Plan-critic: ' + (fm.plan_critic || '—'); if (key === 'review') return 'Verdict: ' + (fm.verdict || '—'); if (key === 'progress') { var s = ''; diff --git a/plugins/voyage/tests/playground/voyage-playground.test.mjs b/plugins/voyage/tests/playground/voyage-playground.test.mjs index a9e85e7..cdc7798 100644 --- a/plugins/voyage/tests/playground/voyage-playground.test.mjs +++ b/plugins/voyage/tests/playground/voyage-playground.test.mjs @@ -483,6 +483,24 @@ test('voyage-playground.html renderArtifact strips comments before md.render (v4 assert.ok(stripIdx > 0 && stripIdx < renderIdx, 'stripUnsafeComments must run before md.render'); }); +// v4.3 Step 7 — buildArtifactKeyStat reads fm.plan_critic (not fm.profile) +// for the 'plan' key. Regression guard for finding bee33a69. +test('voyage-playground.html buildArtifactKeyStat reads fm.plan_critic for plan key (v4.3 Step 7, finding bee33a69)', () => { + const text = readFileSync(HTML, 'utf-8'); + // Locate the buildArtifactKeyStat function body. + const fnStart = text.indexOf('function buildArtifactKeyStat'); + assert.ok(fnStart > 0, 'buildArtifactKeyStat() must exist'); + // Slice the function body: up to the next top-level closing brace at the same indent. + // Use a generous end marker — next `function` declaration that starts a new function. + const nextFn = text.indexOf('\n function ', fnStart + 1); + const fnEnd = nextFn > 0 ? nextFn : fnStart + 800; + const body = text.slice(fnStart, fnEnd); + // Positive: fm.plan_critic literal present + assert.match(body, /fm\.plan_critic/, 'buildArtifactKeyStat must read fm.plan_critic for the plan key'); + // Regression: fm.profile must NOT appear inside this function body (was the old field) + assert.doesNotMatch(body, /\bfm\.profile\b/, 'fm.profile must not appear inside buildArtifactKeyStat (use fm.plan_critic instead)'); +}); + // v4.3 Step 3 — sidebar-toggle button must be a sibling of