feat(voyage): add 5 additive profile fields to JSONL stats — SC #11
Step 8 av v4.1-execute (Wave 3, Session 4).
5 nye additive felter er nå dokumentert i hver kommandos prose-stats-blokk
(via Profile-seksjonen fra Step 7 — felles overflate per kommando):
- profile — string ('economy' | 'balanced' | 'premium' | <custom>)
- phase_models — object form {brief: 'sonnet', ..., continue: 'opus'}
- parallel_agents — number (snapshot av maksverdi som faktisk ble brukt)
- external_research_enabled — boolean
- profile_source — 'flag' | 'env' | 'default' | 'inheritance'
Patcher trekresearch.md med eksplisitt profile_source-mention + alle 5 felter
(de andre 5 commands hadde dette allerede via Step 7 Profile-seksjon).
SC #11 contract-test design (per brief):
(a) Fixture-records valideres som JSONL-contracts → tests/fixtures/stats-with-profile.jsonl
(5 simulerte stats-rader, én per kommando-overflate)
(b) Command-prose contains field-names → kompenserer for plan-critic Major 4
false-confidence (faktisk runtime-emission er LLM-prose-driven, ikke
testbart i node:test alene).
Tester (12 nye, baseline 445 → 457):
- Fixture parses som JSONL (5 records)
- Hver record har profile + profile_source
- profile_source-verdier i {flag, env, default, inheritance}
- Fikstur dekker alle 4 profile_source-verdier
- 6 commands × prose contains profile + profile_source
- trekplan.md prose contains phase_models + parallel_agents
- trekresearch.md prose contains external_research_enabled
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
71fcf6065a
commit
ef379bedf7
3 changed files with 110 additions and 0 deletions
|
|
@ -431,6 +431,10 @@ Examples:
|
|||
VOYAGE_PROFILE=balanced /trekresearch
|
||||
```
|
||||
|
||||
Stats records emit `profile`, `phase_models`, `parallel_agents`,
|
||||
`external_research_enabled`, and `profile_source` so operators can audit
|
||||
which profile drove which session.
|
||||
|
||||
## Hard rules
|
||||
|
||||
- **No planning:** This command produces research briefs, not implementation plans.
|
||||
|
|
|
|||
5
plugins/voyage/tests/fixtures/stats-with-profile.jsonl
vendored
Normal file
5
plugins/voyage/tests/fixtures/stats-with-profile.jsonl
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{"ts":"2026-05-09T07:00:00.000Z","slug":"add-auth","mode":"default","interview_turns":7,"review_iterations":2,"brief_quality":"complete","research_topics":3,"profile":"economy","phase_models":{"brief":"sonnet","research":"sonnet","plan":"sonnet","execute":"sonnet","review":"sonnet","continue":"sonnet"},"profile_source":"flag"}
|
||||
{"ts":"2026-05-09T07:30:00.000Z","slug":"add-auth","mode":"default","scope":"local","dimensions":4,"agents_local":3,"agents_external":0,"profile":"economy","phase_models":{"brief":"sonnet","research":"sonnet","plan":"sonnet","execute":"sonnet","review":"sonnet","continue":"sonnet"},"parallel_agents":3,"external_research_enabled":false,"profile_source":"env"}
|
||||
{"ts":"2026-05-09T08:00:00.000Z","slug":"add-auth","mode":"default","codebase_files":156,"agents_deployed":6,"deep_dives":2,"critic_verdict":"PASS","guardian_verdict":"ALIGNED","outcome":"execute","profile":"balanced","phase_models":{"brief":"sonnet","research":"sonnet","plan":"opus","execute":"sonnet","review":"opus","continue":"sonnet"},"parallel_agents":6,"profile_source":"default"}
|
||||
{"ts":"2026-05-09T08:30:00.000Z","plan":"trekplan-add-auth.md","plan_type":"plan","mode":"execute","result":"completed","steps_total":12,"steps_passed":12,"steps_failed":0,"steps_skipped":0,"profile":"balanced","phase_models":{"brief":"sonnet","research":"sonnet","plan":"opus","execute":"sonnet","review":"opus","continue":"sonnet"},"profile_source":"inheritance"}
|
||||
{"ts":"2026-05-09T09:00:00.000Z","slug":"add-auth","verdict":"ALLOW","reviewed_files_count":18,"mode":"default","duration_ms":4521,"profile":"premium","phase_models":{"brief":"opus","research":"opus","plan":"opus","execute":"opus","review":"opus","continue":"opus"},"profile_source":"flag"}
|
||||
101
plugins/voyage/tests/lib/profile-stats-fields.test.mjs
Normal file
101
plugins/voyage/tests/lib/profile-stats-fields.test.mjs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// tests/lib/profile-stats-fields.test.mjs
|
||||
// SC #11 contract-test per brief design — kombinasjonen av:
|
||||
// (a) fixture-records valideres som JSONL-contracts AND
|
||||
// (b) command-prose contains field-names
|
||||
// er den brief-designede gating-mekanismen. Faktisk runtime-emission av
|
||||
// feltene er LLM-prose-driven og ikke testbart i node:test alene.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import { strict as assert } from 'node:assert';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const REPO_ROOT = join(__dirname, '..', '..');
|
||||
|
||||
const PROFILE_FIELDS = [
|
||||
'profile',
|
||||
'phase_models',
|
||||
'parallel_agents',
|
||||
'external_research_enabled',
|
||||
'profile_source',
|
||||
];
|
||||
|
||||
const VALID_PROFILE_SOURCES = new Set(['flag', 'env', 'default', 'inheritance']);
|
||||
|
||||
const COMMAND_FILES = [
|
||||
'trekbrief.md',
|
||||
'trekresearch.md',
|
||||
'trekplan.md',
|
||||
'trekexecute.md',
|
||||
'trekreview.md',
|
||||
'trekcontinue.md',
|
||||
];
|
||||
|
||||
// (a) Fixture validates as JSONL contracts
|
||||
|
||||
test('SC #11(a): tests/fixtures/stats-with-profile.jsonl parses as JSONL', () => {
|
||||
const text = readFileSync(join(REPO_ROOT, 'tests', 'fixtures', 'stats-with-profile.jsonl'), 'utf-8');
|
||||
const lines = text.trim().split('\n').filter(Boolean);
|
||||
assert.equal(lines.length, 5, `expected 5 simulated stats records, got ${lines.length}`);
|
||||
for (const line of lines) {
|
||||
const record = JSON.parse(line); // throws if malformed
|
||||
assert.equal(typeof record, 'object');
|
||||
assert.ok(record.ts, 'record missing ts');
|
||||
}
|
||||
});
|
||||
|
||||
test('SC #11(a): every fixture record contains profile + profile_source', () => {
|
||||
const text = readFileSync(join(REPO_ROOT, 'tests', 'fixtures', 'stats-with-profile.jsonl'), 'utf-8');
|
||||
const records = text.trim().split('\n').filter(Boolean).map(l => JSON.parse(l));
|
||||
for (const r of records) {
|
||||
assert.ok('profile' in r, `record missing profile: ${JSON.stringify(r)}`);
|
||||
assert.ok('profile_source' in r, `record missing profile_source: ${JSON.stringify(r)}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('SC #11(a): profile_source values are in {flag, env, default, inheritance}', () => {
|
||||
const text = readFileSync(join(REPO_ROOT, 'tests', 'fixtures', 'stats-with-profile.jsonl'), 'utf-8');
|
||||
const records = text.trim().split('\n').filter(Boolean).map(l => JSON.parse(l));
|
||||
for (const r of records) {
|
||||
assert.ok(VALID_PROFILE_SOURCES.has(r.profile_source),
|
||||
`profile_source "${r.profile_source}" not in valid set`);
|
||||
}
|
||||
});
|
||||
|
||||
test('SC #11(a): fixture coverage — all 4 profile_source values represented', () => {
|
||||
const text = readFileSync(join(REPO_ROOT, 'tests', 'fixtures', 'stats-with-profile.jsonl'), 'utf-8');
|
||||
const records = text.trim().split('\n').filter(Boolean).map(l => JSON.parse(l));
|
||||
const seen = new Set(records.map(r => r.profile_source));
|
||||
for (const expected of VALID_PROFILE_SOURCES) {
|
||||
assert.ok(seen.has(expected),
|
||||
`fixture missing profile_source value: ${expected}; seen: ${[...seen].join(', ')}`);
|
||||
}
|
||||
});
|
||||
|
||||
// (b) Command prose contains field-names (false-confidence kompensasjon per plan-critic Major 4)
|
||||
|
||||
for (const filename of COMMAND_FILES) {
|
||||
test(`SC #11(b): commands/${filename} prose mentions profile + profile_source`, () => {
|
||||
const content = readFileSync(join(REPO_ROOT, 'commands', filename), 'utf-8');
|
||||
assert.match(content, /profile_source/,
|
||||
`${filename} prose missing profile_source — Step 8 stats schema additive must be documented`);
|
||||
assert.match(content, /profile/,
|
||||
`${filename} prose missing profile — Step 8 stats schema additive must be documented`);
|
||||
});
|
||||
}
|
||||
|
||||
test('SC #11(b): commands/trekplan.md prose mentions phase_models + parallel_agents', () => {
|
||||
const content = readFileSync(join(REPO_ROOT, 'commands', 'trekplan.md'), 'utf-8');
|
||||
assert.match(content, /phase_models/,
|
||||
'trekplan.md prose must mention phase_models (additive stats field)');
|
||||
assert.match(content, /parallel_agents/,
|
||||
'trekplan.md prose must mention parallel_agents (additive stats field)');
|
||||
});
|
||||
|
||||
test('SC #11(b): commands/trekresearch.md prose mentions external_research_enabled', () => {
|
||||
const content = readFileSync(join(REPO_ROOT, 'commands', 'trekresearch.md'), 'utf-8');
|
||||
assert.match(content, /external_research_enabled/,
|
||||
'trekresearch.md prose must mention external_research_enabled (additive stats field)');
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue