fix(voyage): dashboard reads fm.plan_critic + orchestrator doc-consistency (906f155d, bee33a69)

This commit is contained in:
Kjell Tore Guttormsen 2026-05-10 21:21:02 +02:00
commit 9909ce1066
3 changed files with 30 additions and 1 deletions

View file

@ -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

View file

@ -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 = '';

View file

@ -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 <aside aria-hidden="true">,
// not a descendant (finding 09132940 a11y). Hidden-state must not occlude the toggle.
test('voyage-playground.html sidebar-toggle is outside aria-hidden region (v4.3 Step 3, finding 09132940)', () => {