feat(voyage): add phase-signal-resolver helper for v5.1.1 wiring
This commit is contained in:
parent
8f4b79cfc6
commit
3ed2d84caa
2 changed files with 163 additions and 0 deletions
77
plugins/voyage/tests/lib/phase-signal-resolver.test.mjs
Normal file
77
plugins/voyage/tests/lib/phase-signal-resolver.test.mjs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { test } from 'node:test';
|
||||
import { strict as assert } from 'node:assert';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { writeFileSync, unlinkSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { resolvePhaseSignal, resolvePhaseSignalFromFile } from '../../lib/profiles/phase-signal-resolver.mjs';
|
||||
|
||||
const FULL_SIGNALS_FM = {
|
||||
phase_signals: [
|
||||
{ phase: 'research', effort: 'low', model: 'sonnet' },
|
||||
{ phase: 'plan', effort: 'standard' },
|
||||
{ phase: 'execute', effort: 'high', model: 'opus' },
|
||||
{ phase: 'review', effort: 'standard', model: 'sonnet' },
|
||||
],
|
||||
};
|
||||
|
||||
test('resolvePhaseSignal — returns {effort, model} for all 4 phases on full-signals brief', () => {
|
||||
for (const phase of ['research', 'plan', 'execute', 'review']) {
|
||||
const r = resolvePhaseSignal(FULL_SIGNALS_FM, phase);
|
||||
assert.ok(r && typeof r === 'object', `phase=${phase} should resolve non-null`);
|
||||
assert.ok(typeof r.effort === 'string', `phase=${phase} should have effort`);
|
||||
}
|
||||
});
|
||||
|
||||
test('resolvePhaseSignal — returns null when brief has no phase_signals', () => {
|
||||
const r = resolvePhaseSignal({ task: 'x' }, 'plan');
|
||||
assert.equal(r, null);
|
||||
});
|
||||
|
||||
test('resolvePhaseSignal — returns partial {effort} with model undefined when signal omits model', () => {
|
||||
const r = resolvePhaseSignal(FULL_SIGNALS_FM, 'plan');
|
||||
assert.equal(r.effort, 'standard');
|
||||
assert.equal(r.model, undefined);
|
||||
assert.ok(!('model' in r), 'model key should be absent when not in signal');
|
||||
});
|
||||
|
||||
test('resolvePhaseSignal — returns null when phase is not in PHASE_SIGNAL_PHASES', () => {
|
||||
assert.equal(resolvePhaseSignal(FULL_SIGNALS_FM, 'brief'), null);
|
||||
assert.equal(resolvePhaseSignal(FULL_SIGNALS_FM, 'continue'), null);
|
||||
assert.equal(resolvePhaseSignal(FULL_SIGNALS_FM, 'nonsense'), null);
|
||||
});
|
||||
|
||||
test('resolvePhaseSignal — defensive: null/non-object input returns null', () => {
|
||||
assert.equal(resolvePhaseSignal(null, 'plan'), null);
|
||||
assert.equal(resolvePhaseSignal(undefined, 'plan'), null);
|
||||
assert.equal(resolvePhaseSignal('string', 'plan'), null);
|
||||
assert.equal(resolvePhaseSignal({ phase_signals: 'not-array' }, 'plan'), null);
|
||||
});
|
||||
|
||||
test('resolvePhaseSignalFromFile + CLI shim — writes JSON to stdout, exit 0', () => {
|
||||
const fixture = join(tmpdir(), `phase-signal-test-${process.pid}.md`);
|
||||
writeFileSync(fixture, `---
|
||||
type: trekbrief
|
||||
brief_version: "2.1"
|
||||
phase_signals:
|
||||
- phase: plan
|
||||
effort: high
|
||||
model: opus
|
||||
---
|
||||
# x
|
||||
`);
|
||||
try {
|
||||
// Programmatic invocation
|
||||
const r = resolvePhaseSignalFromFile(fixture, 'plan');
|
||||
assert.deepEqual(r, { effort: 'high', model: 'opus' });
|
||||
// CLI shim
|
||||
const helperPath = new URL('../../lib/profiles/phase-signal-resolver.mjs', import.meta.url).pathname;
|
||||
const out = execFileSync('node', [helperPath, '--brief', fixture, '--phase', 'plan', '--json'], {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
const parsed = JSON.parse(out.trim());
|
||||
assert.deepEqual(parsed, { effort: 'high', model: 'opus' });
|
||||
} finally {
|
||||
try { unlinkSync(fixture); } catch { /* swallow */ }
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue