The four profiles, the resolver, the profile validator and docs/profiles.md go; /trekbrief Phase 3.5 and the brief's phase_signals checks go (an older brief's key is ignored); MANIFEST_PROFILE_DRIFT and profile_used go. Every Profile, Composition rule, Sequencing gate and High-effort section leaves the commands. Decision 209: /trekresearch runs the full swarm with contrarian-researcher as wave 2 every run; /trekreview and the coordinator run all four passes with no high-effort normalization. Voyage agents are spawned with no model (their own model: opus applies); the two built-in spawns pass model "opus". --profile stays in the parser as a retired flag that swallows its value. §Model & effort moves to docs/architecture.md; the sequencing-gate eval case goes. Files removed: 44 (43 from the R3 list + tests/commands/trekreview.test.mjs, whose every test pinned removed text). Files edited: 43. Tests: 2467 (2466 pass / 0 fail / 1 skipped) before, 2309 (2309 pass / 0 fail / 0 skipped) after. Durable check: all three groups green, none pending. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
2.8 KiB
JavaScript
68 lines
2.8 KiB
JavaScript
// tests/commands/trekresearch.test.mjs
|
|
// Prose-pin tests for /trekresearch.
|
|
|
|
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';
|
|
|
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
const ROOT = join(HERE, '..', '..');
|
|
const COMMAND_FILE = join(ROOT, 'commands', 'trekresearch.md');
|
|
|
|
function read() { return readFileSync(COMMAND_FILE, 'utf8'); }
|
|
|
|
// --- Phase 5 slice (heading-bounded) ---
|
|
|
|
// Slice from the phase heading to the NEXT phase heading, never a fixed character window.
|
|
function phaseSlice(doc, startHeading, endHeading) {
|
|
const start = doc.indexOf(startHeading);
|
|
assert.ok(start >= 0, `${startHeading} missing`);
|
|
const end = doc.indexOf(endHeading, start);
|
|
assert.ok(end > start, `${endHeading} missing — could not bound ${startHeading}`);
|
|
return doc.slice(start, end);
|
|
}
|
|
|
|
function phase5(doc) {
|
|
return phaseSlice(doc, '## Phase 5 —', '## Phase 6 —');
|
|
}
|
|
|
|
test('trekresearch — Phase 4 runs the full swarm with contrarian-researcher as wave 2 every time (decision 209)', () => {
|
|
const flatText = read().replace(/\s+/g, ' ');
|
|
assert.ok(
|
|
flatText.includes('Every run launches the FULL swarm for its scope: all 5 local agents and all 4 external agents, and `contrarian-researcher` runs every time, as wave 2 (see Launch rules)'),
|
|
'Phase 4 must state the one fixed path: the full swarm, contrarian-researcher as wave 2 every run',
|
|
);
|
|
assert.ok(
|
|
!flatText.includes('use the existing conditional triggers'),
|
|
'no effort must point at a conditional trigger that nothing defines',
|
|
);
|
|
});
|
|
|
|
// --- Spawn model, turn budget and stats writer ---
|
|
|
|
/** Whitespace (line breaks too) collapsed to one space. */
|
|
const flat = (s) => s.replace(/\s+/g, ' ').trim();
|
|
|
|
/** From the line holding `marker` to the first blank line, flattened. */
|
|
function paragraphAt(text, marker) {
|
|
const lines = text.split('\n');
|
|
const start = lines.findIndex((l) => l.includes(marker));
|
|
assert.ok(start !== -1, `commands/trekresearch.md must hold "${marker}"`);
|
|
let end = lines.length;
|
|
for (let i = start + 1; i < lines.length; i++) if (lines[i].trim() === '') { end = i; break; }
|
|
return flat(lines.slice(start, end).join(' '));
|
|
}
|
|
|
|
test('trekresearch — Phase 5 follow-ups: a built-in agent is spawned with model opus, at most 2', () => {
|
|
const text = read();
|
|
const part = paragraphAt(phase5(text), 'launch a targeted follow-up agent');
|
|
assert.match(part, /Maximum 2 follow-ups\./);
|
|
assert.ok(part.includes("A follow-up may be one of Claude Code's built-in agents: Spawn it with `model: \"opus\"`: a built-in agent has no Voyage pin."), part);
|
|
});
|
|
|
|
test('trekresearch — promises no maxTurns budget', () => {
|
|
const text = read();
|
|
assert.doesNotMatch(text, /maxTurns/);
|
|
});
|