// tests/commands/trekexecute.test.mjs // v5.1 prose-pin tests + v5.1.1 runtime SC4 + SC7 tests for /trekexecute. // Plan Assumption 2 locks low-effort to --gates open + sequential-only. 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 } 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', 'trekexecute.md'); const PHASE = 'execute'; function read() { return readFileSync(COMMAND_FILE, 'utf8'); } function readFixture(name) { return readFileSync(join(ROOT, 'tests', 'fixtures', name), 'utf8'); } function frontmatterOf(text) { const doc = parseDocument(text); return doc.parsed && doc.parsed.frontmatter; } // --- Pattern D prose-pins --- test('trekexecute — sequencing-gate surface mentions BRIEF_V51_MISSING_SIGNALS + phase_signals', () => { const text = read(); assert.ok(text.includes('BRIEF_V51_MISSING_SIGNALS'), '/trekexecute must surface the BRIEF_V51_MISSING_SIGNALS sequencing gate'); assert.ok(text.includes('phase_signals'), '/trekexecute must reference phase_signals (v5.1 composition rule)'); }); test('trekexecute — low-effort path references --gates open + sequential', () => { const text = read(); const compIdx = text.indexOf('## Composition rule (v5.1)'); assert.ok(compIdx >= 0, 'Composition rule (v5.1) section missing'); const section = text.slice(compIdx, compIdx + 2000); assert.match(section, /--gates open/, 'Low-effort path must mention --gates open'); assert.match(section, /sequential/, 'Low-effort path must mention sequential-only execution'); }); // --- v5.1.1 runtime SC4 + SC7 --- test('trekexecute — SC4: low-effort fixture → resolver returns {effort: low, model: sonnet}', () => { const fm = frontmatterOf(readFixture('brief-effort-low.md')); const r = resolvePhaseSignal(fm, PHASE); assert.equal(r.effort, 'low'); assert.equal(r.model, 'sonnet'); }); test('trekexecute — SC4: standard-effort fixture → resolver returns {effort: standard, model: undefined}', () => { const fm = frontmatterOf(readFixture('brief-effort-standard.md')); const r = resolvePhaseSignal(fm, PHASE); assert.equal(r.effort, 'standard'); assert.equal(r.model, undefined); }); test('trekexecute — SC4: high-effort fixture → resolver returns {effort: high, model: opus}', () => { const fm = frontmatterOf(readFixture('brief-effort-high.md')); const r = resolvePhaseSignal(fm, PHASE); assert.equal(r.effort, 'high'); assert.equal(r.model, 'opus'); }); test('trekexecute — SC7: brief_version 2.1 + no phase_signals + no partial → 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'), `sequencing gate must fire; errors=${JSON.stringify(r.errors)}`, ); }); // --- S38 loop-discipline hardening: completion gate (Step 2) --- test('trekexecute — machine-verifiable completion gate + stop-signal contract literals present (SC a)', () => { const text = read(); assert.ok(text.includes('machine-verifiable completion gate'), 'completion-gate literal must be grep-able'); assert.ok(text.includes('stop-signal contract'), 'stop-signal contract literal must be grep-able'); }); test('trekexecute — completion gate anchors result:completed to Phase 7.5 audit + DONE-after-check (SC a)', () => { const text = read(); const gateIdx = text.indexOf('machine-verifiable completion gate'); assert.ok(gateIdx >= 0, 'gate literal missing'); const section = text.slice(gateIdx, gateIdx + 1400); assert.match(section, /Phase 7\.5/, 'gate must name Phase 7.5 as the objective predicate'); assert.match(section, /DONE/, 'gate must reference the DONE stop-signal token'); assert.match(section, /emitted AFTER/, 'gate must require DONE emitted AFTER the audit ran'); }); // --- S38 loop-discipline hardening: caps + global recovery budget (Step 3) --- test('trekexecute — global recovery/retry budget TREKEXECUTE_MAX_RECOVERY_ITERATIONS default 25 (SC b)', () => { const text = read(); assert.ok(text.includes('TREKEXECUTE_MAX_RECOVERY_ITERATIONS'), 'global recovery/retry budget constant must be documented'); assert.match( text, /TREKEXECUTE_MAX_RECOVERY_ITERATIONS[^\n]{0,40}\b25\b|default[^\n]{0,20}\b25\b[^\n]{0,40}aggregate/i, 'global budget must document numeric default 25 adjacent to the constant', ); }); test('trekexecute — every recovery/retry loop bounded + 3-axis cap hierarchy (SC b)', () => { const text = read(); assert.match(text, /maximum 2 retries|Retry cap = 3 attempts/, 'per-step retry cap must be explicit'); assert.match(text, /recovery_depth < 2/, 'recovery-dispatch cap must be explicit'); const capIdx = text.indexOf('Iteration caps'); assert.ok(capIdx >= 0, 'cap-hierarchy section ("Iteration caps") must exist'); const section = text.slice(capIdx, capIdx + 1500); assert.match(section, /attempts/, 'hierarchy must name per-step attempts axis'); assert.match(section, /recovery_depth/, 'hierarchy must name per-session recovery_depth axis'); assert.match(section, /TREKEXECUTE_MAX_RECOVERY_ITERATIONS/, 'hierarchy must name the global budget axis'); }); // --- S38 loop-discipline hardening: iterations_remaining budget signal (Step 4) --- test('trekexecute — iterations_remaining surfaced in progress schema + summary JSON, no (if wired) (SC c)', () => { const text = read(); assert.ok(text.includes('iterations_remaining'), 'iterations_remaining must be present (SC c)'); assert.ok(!/iterations_remaining[^\n]*\(if wired\)/.test(text), 'no "(if wired)" conditional — field present in the spec at minimum'); const schemaIdx = text.indexOf('### Progress file schema'); assert.ok(schemaIdx >= 0, 'progress schema section missing'); assert.match(text.slice(schemaIdx, schemaIdx + 1400), /iterations_remaining/, 'progress schema must include iterations_remaining'); const sumIdx = text.indexOf('"trekexecute_summary": {'); assert.ok(sumIdx >= 0, 'summary JSON block missing'); assert.match(text.slice(sumIdx, sumIdx + 900), /iterations_remaining/, 'summary JSON must include iterations_remaining (observable)'); }); test('trekexecute — iterations_remaining decrement + backfill + gate cross-check documented (SC c)', () => { const text = read(); assert.match(text, /decrement[^.\n]*iteration|every recovery\/retry iteration/i, 'decrement-per-iteration rule must be documented'); assert.match(text, /backfill|seed (it )?to (the )?cap/i, 'backfill-on-absent (legacy resume) rule must be documented'); const gateIdx = text.indexOf('machine-verifiable completion gate'); const gate = text.slice(gateIdx, gateIdx + 2200); assert.match(gate, /iterations_remaining/, 'gate must cross-check iterations_remaining'); assert.match(gate, /recovery_depth/, 'gate cross-check must reconcile against recovery_depth'); assert.match(gate, /attempts/, 'gate cross-check must reconcile against attempts'); });