// tests/lib/retterunde-prose-pins.test.mjs // Prose pins for the fix round before 6.3.0 (order 10) whose fix is text a // session reads. Counted as doc pins by lib/util/test-census.mjs (the file name). // 1b A step of only tests can never be proven red: the plan text, the // template and plan-critic say the test belongs in the step that builds // the behaviour (voyage-lekerepo trial 3, 2026-10-09, finding 1). // 1c The executor and the step helper pass `--plan`/`--step` to red-proof // and send a `tests_only` `not-red-at-base` up to the plan, not into fix // rounds that cannot succeed. // 4 /trekreview names FINDING_PROSE_LINE_OUT_OF_RANGE and hands the check // the reviewer's whole answer, not only its JSON block. // 6 The resolver returns `effort` only when the brief's phase signal sets // one; no command says it always does. import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { mkdtempSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { tmpdir } from 'node:os'; import { fileURLToPath } from 'node:url'; import { spawnSync } from 'node:child_process'; import { findSection } from '../../lib/parsers/plan-schema.mjs'; const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..'); const read = (p) => readFileSync(join(ROOT, p), 'utf8'); /** The text of a heading's section (level from its #-count), up to the next one outside a fence. */ function section(text, heading) { const level = heading.match(/^#+/)[0].length; const s = findSection(text, heading.replace(/^#+ /, ''), level); assert.ok(s, `missing ${heading}`); return s.text; } const RULE = /test\s+for\s+a\s+behaviou?r\s+belongs\s+in\s+the\s+step\s+that\s+builds\s+it/i; // --- 1b -------------------------------------------------------------------------- test('1b: /trekplan says the test belongs in the step that builds the behaviour, and names the warning', () => { const plan = read('commands/trekplan.md'); assert.match(plan, RULE); // At the vertical-slice rule and at the step field list. assert.match(plan, /Steps are vertical slices[\s\S]{0,900}only tests[\s\S]{0,300}never be proven red/i); assert.match(plan, /\*\*Test first:\*\* — [^\n]*\n[^\n]*step that builds/i); assert.match(plan, /PLAN_STEP_TESTS_ONLY/); }); test('1b: the plan template carries the same rule', () => { const t = read('templates/plan-template.md'); assert.match(t, RULE); assert.match(t, /only tests[\s\S]{0,200}never be proven red/i); }); test('1b: plan-critic has it as a finding under Verification gaps, and still 12 dimensions', () => { const critic = read('agents/plan-critic.md'); const d8 = section(critic, '### 8. Verification gaps'); assert.match(d8, /PLAN_STEP_TESTS_ONLY/); assert.match(d8, /only tests[\s\S]{0,400}\*\*major\*\*/i); assert.equal((critic.match(/^### \d+\. /gm) ?? []).length, 12); }); // --- 1c -------------------------------------------------------------------------- test('1c: the executor runs red-proof with --plan/--step and sends a tests_only not-red-at-base up to the plan', () => { const ex = read('commands/trekexecute.md'); assert.match(ex, /red-proof\.mjs --base \{start_sha\} --cmd "\{Verify command\}" --plan "\{plan_path\}" --step \{N\}[^\n]*--json/); assert.match(ex, /`not-red-at-base`, `tests_only: true`[^\n]*straight up to the plan \(Sub-step F, `fix_rounds = 4`, stop class 4\)/); // The helper's gate, as the executor describes it, carries the same flags. assert.match(ex, /red-proof\.mjs --red-only --base \{start_sha\} [^\n]*--plan "\{plan_path\}" --step \{N\}/); // The other not-red-at-base keeps its fix round. assert.match(ex, /`not-red-at-base`, otherwise[^\n]*finding for Sub-step F/); }); test('1c: the step helper passes --plan/--step to its red gate and does not rewrite a tests_only test', () => { const h = read('agents/step-implementer.md'); assert.match(h, /red-proof\.mjs --red-only [^\n]*--plan "\{plan\}" --step \{step\}/); assert.match(h, /`tests_only: true`[\s\S]{0,300}do not rewrite/i); }); // --- 4 --------------------------------------------------------------------------- test('4: /trekreview names FINDING_PROSE_LINE_OUT_OF_RANGE: re-ask on the error, report the warning', () => { const r = read('commands/trekreview.md'); assert.match(r, /FINDING_PROSE_LINE_OUT_OF_RANGE/); assert.match(r, /FINDING_PROSE_LINE_OUT_OF_RANGE[\s\S]{0,600}re-ask/i); assert.match(r, /FINDING_PROSE_LINE_OUT_OF_RANGE[\s\S]{0,900}warning[\s\S]{0,300}report/i); }); test('4: /trekreview saves the reviewer\'s whole answer for the check, not only the JSON block', () => { const r = read('commands/trekreview.md'); assert.match(r, /\{project_dir\}\/\.review-out-\{reviewer\}\.md/); assert.match(r, /whole answer[\s\S]{0,300}not only the\s+(JSON\s+)?block/i); assert.match(r, /findings-schema\.mjs --json --repo-root [^\n]*"\{project_dir\}\/\.review-out-\{reviewer\}\.md"/); assert.doesNotMatch(r, /Collect each reviewer's trailing JSON block/); }); test('4: what the check is given decides: the whole answer warns on a prose citation, the block alone cannot', () => { const dir = mkdtempSync(join(tmpdir(), 'retterunde-review-')); try { mkdirSync(join(dir, 'src')); writeFileSync(join(dir, 'src', 'a.mjs'), 'export const a = 1;\n'); const block = '```json\n{"reviewer":"code-correctness-reviewer","findings":[]}\n```\n'; writeFileSync(join(dir, 'whole.md'), `The bug is at \`src/a.mjs:40\`.\n\n${block}`); writeFileSync(join(dir, 'block.md'), block); const cli = (f) => spawnSync(process.execPath, [join(ROOT, 'lib', 'review', 'findings-schema.mjs'), '--json', '--repo-root', dir, join(dir, f)], { encoding: 'utf8' }); const whole = JSON.parse(cli('whole.md').stdout); assert.deepEqual(whole.warnings.map((w) => w.code), ['FINDING_PROSE_LINE_OUT_OF_RANGE']); assert.deepEqual(JSON.parse(cli('block.md').stdout).warnings, []); } finally { rmSync(dir, { recursive: true, force: true }); } }); // --- 6 --------------------------------------------------------------------------- const RESOLVER_COMMANDS = ['trekplan', 'trekexecute', 'trekresearch', 'trekreview']; test('6: no command says the resolver always returns {effort, model, source}', () => { for (const f of readdirSync(join(ROOT, 'commands')).filter((x) => x.endsWith('.md'))) { const t = read(`commands/${f}`); assert.doesNotMatch(t, /\{effort, model, source\}/, f); } }); test('6: each resolver-using command says `effort` comes only from the brief signal, else standard', () => { for (const c of RESOLVER_COMMANDS) { const t = read(`commands/${c}.md`); const hits = t.match(/\{model, source\}[^\n]*\n?[^\n]*effort[^\n]*only when the brief's phase signal sets one/g) ?? []; assert.ok(hits.length >= 1, `${c}: no statement that effort is only present with a brief signal`); assert.match(t, /without it[^\n]*\n?[^\n]*`?standard`?/i, c); } });