test(voyage): refactor trekbrief command test to runtime SC1 (closes #1)

This commit is contained in:
Kjell Tore Guttormsen 2026-05-14 21:44:38 +02:00
commit 94c696fee6

View file

@ -1,23 +1,43 @@
// tests/commands/trekbrief.test.mjs
// v5.1 — Pattern D prose-pattern regression tests for /trekbrief Phase 3.5.
// v5.1 prose-pin tests + v5.1.1 runtime SC1 tests.
//
// Brief SC1 + SC2: end-of-brief effort dialog covering 4 downstream phases,
// with `phase_signals_partial` as the force-stop record.
// Pattern D prose-pins kept as doc-anchors for the .md file. Runtime tests
// added per finding 350853 (BLOCKER SC1) + a7f4f95a (MAJOR Plan Step 5 drift).
//
// SC1 re-interpretation (per plan Step 10 amendment): "asserts on 4
// AskUserQuestion calls" → "asserts resolvePhaseSignal returns non-null for
// all 4 entries in PHASE_SIGNAL_PHASES when applied to a brief with a
// committed phase_signals block." See brief amendment for full rationale.
import { test } from 'node:test';
import { strict as assert } from 'node:assert';
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { resolvePhaseSignal } from '../../lib/profiles/phase-signal-resolver.mjs';
import { validateBriefContent, PHASE_SIGNAL_PHASES, EFFORT_LEVELS } from '../../lib/validators/brief-validator.mjs';
import { parseDocument } from '../../lib/util/frontmatter.mjs';
const HERE = dirname(fileURLToPath(import.meta.url));
const ROOT = join(HERE, '..', '..');
const COMMAND_FILE = join(ROOT, 'commands', 'trekbrief.md');
const FIXTURE = (name) => join(ROOT, 'tests', 'fixtures', name);
function read() {
return readFileSync(COMMAND_FILE, 'utf8');
}
function readFixture(name) {
return readFileSync(FIXTURE(name), 'utf8');
}
function frontmatterOf(text) {
const doc = parseDocument(text);
return doc.parsed && doc.parsed.frontmatter;
}
// --- Pattern D prose-pins (doc-anchors) ---
test('trekbrief — Phase 3.5 heading is present', () => {
const text = read();
assert.match(text, /^## Phase 3\.5 — Per-phase effort dialog$/m,
@ -40,3 +60,71 @@ test('trekbrief — Phase 3.5 documents phase_signals_partial force-stop', () =>
assert.ok(text.includes('phase_signals_partial'),
'phase_signals_partial not mentioned in /trekbrief command prose');
});
// --- v5.1.1 runtime SC1 tests ---
test('trekbrief — SC1: resolvePhaseSignal returns non-null for all 4 phases on committed brief (brief-effort-low)', () => {
const fm = frontmatterOf(readFixture('brief-effort-low.md'));
for (const phase of PHASE_SIGNAL_PHASES) {
const r = resolvePhaseSignal(fm, phase);
assert.ok(r && typeof r === 'object',
`phase=${phase}: resolver must return non-null for committed brief; got ${JSON.stringify(r)}`);
assert.ok(typeof r.effort === 'string',
`phase=${phase}: resolver result must include effort`);
}
});
test('trekbrief — SC1: each of 4 phases has both effort AND model on full-signals fixture', () => {
const fm = frontmatterOf(readFixture('brief-with-phase-signals.md'));
for (const phase of PHASE_SIGNAL_PHASES) {
const r = resolvePhaseSignal(fm, phase);
assert.ok(r && typeof r === 'object', `phase=${phase}: must resolve`);
assert.ok(EFFORT_LEVELS.includes(r.effort),
`phase=${phase}: effort "${r.effort}" not in EFFORT_LEVELS`);
if ('model' in r) {
assert.ok(['sonnet', 'opus'].includes(r.model),
`phase=${phase}: model "${r.model}" not in [sonnet, opus]`);
}
}
});
test('trekbrief — SC1: missing phase_signals + brief_version 2.1 triggers BRIEF_V51_MISSING_SIGNALS', () => {
const r = validateBriefContent(readFixture('brief-v21-no-signals.md'), { strict: true });
assert.equal(r.valid, false);
assert.ok(
r.errors.find(e => e.code === 'BRIEF_V51_MISSING_SIGNALS'),
`gate must fire; errors=${JSON.stringify(r.errors)}`,
);
});
test('trekbrief — SC1: phase_signals_partial: true does NOT trigger the gate', () => {
const partial = `---
type: trekbrief
brief_version: "2.1"
created: 2026-05-14
task: "Partial brief"
slug: partial-brief
project_dir: .claude/projects/2026-05-14-partial-brief/
research_topics: 0
research_status: complete
auto_research: false
interview_turns: 2
source: fixture
phase_signals_partial: true
---
# Task
## Intent
Stop early.
## Goal
Test partial mode.
## Success Criteria
- gate does not fire.
`;
const r = validateBriefContent(partial, { strict: true });
assert.equal(r.valid, true, `errors=${JSON.stringify(r.errors)}`);
assert.ok(!r.errors.find(e => e.code === 'BRIEF_V51_MISSING_SIGNALS'));
});