From ef379bedf7bdf9af29758ad5cbde38fcf76f2261 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Sat, 9 May 2026 09:40:21 +0200 Subject: [PATCH] =?UTF-8?q?feat(voyage):=20add=205=20additive=20profile=20?= =?UTF-8?q?fields=20to=20JSONL=20stats=20=E2=80=94=20SC=20#11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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' | ) - 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 --- plugins/voyage/commands/trekresearch.md | 4 + .../tests/fixtures/stats-with-profile.jsonl | 5 + .../tests/lib/profile-stats-fields.test.mjs | 101 ++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 plugins/voyage/tests/fixtures/stats-with-profile.jsonl create mode 100644 plugins/voyage/tests/lib/profile-stats-fields.test.mjs diff --git a/plugins/voyage/commands/trekresearch.md b/plugins/voyage/commands/trekresearch.md index 1af0522..14f6166 100644 --- a/plugins/voyage/commands/trekresearch.md +++ b/plugins/voyage/commands/trekresearch.md @@ -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. diff --git a/plugins/voyage/tests/fixtures/stats-with-profile.jsonl b/plugins/voyage/tests/fixtures/stats-with-profile.jsonl new file mode 100644 index 0000000..ee1ec42 --- /dev/null +++ b/plugins/voyage/tests/fixtures/stats-with-profile.jsonl @@ -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"} diff --git a/plugins/voyage/tests/lib/profile-stats-fields.test.mjs b/plugins/voyage/tests/lib/profile-stats-fields.test.mjs new file mode 100644 index 0000000..27c33f0 --- /dev/null +++ b/plugins/voyage/tests/lib/profile-stats-fields.test.mjs @@ -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)'); +});