chore(cleanup): R-b R3 — profiles, phase signals and the effort dialog removed, one fixed path per command

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>
This commit is contained in:
Kjell Tore Guttormsen 2026-11-07 21:12:57 +01:00
commit 2a35d067d9
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
87 changed files with 225 additions and 4967 deletions

View file

@ -9,7 +9,6 @@
import { readFileSync, existsSync } from 'node:fs';
import { parseDocument } from '../util/frontmatter.mjs';
import { issue, ok, fail } from '../util/result.mjs';
import { BASE_ALLOWED_MODELS } from './profile-validator.mjs';
import { parseSuccessCriteria, runnableCheck, firstCommand } from '../verification/criteria-runner.mjs';
import { isMain } from '../util/is-main.mjs';
@ -18,8 +17,6 @@ export const REVIEW_AS_BRIEF_REQUIRED_FRONTMATTER = ['type', 'task', 'slug', 'pr
export const BRIEF_TYPE_VALUES = Object.freeze(['trekbrief', 'trekreview']);
export const BRIEF_RESEARCH_STATUS_VALUES = ['pending', 'in_progress', 'complete', 'skipped'];
export const BRIEF_BODY_SECTIONS = ['Intent', 'Goal', 'Success Criteria'];
export const PHASE_SIGNAL_PHASES = Object.freeze(['research', 'plan', 'execute', 'review']);
export const EFFORT_LEVELS = Object.freeze(['low', 'standard', 'high']);
// v5.5 — framing: how this brief relates to prior operator intent (the first layer
// of the framing-alignment defense). Required at brief_version ≥ 2.2.
export const BRIEF_FRAMING_VALUES = Object.freeze(['preserve', 'refine', 'replace', 'new-direction']);
@ -77,55 +74,6 @@ export function validateBriefContent(text, opts = {}) {
}
}
// v5.1 — phase_signals (additive optional field) + version-conditional sequencing gate.
// Composition rule documented in each downstream command's "Composition rule (v5.1)" section.
const hasSignals = 'phase_signals' in fm;
const hasPartial = 'phase_signals_partial' in fm;
if (hasSignals && hasPartial) {
errors.push(issue(
'BRIEF_SIGNALS_MUTUALLY_EXCLUSIVE',
'phase_signals and phase_signals_partial are mutually exclusive — set exactly one',
'Either commit per-phase signals OR record phase_signals_partial: true (force-stop).',
));
}
if (hasSignals) {
if (!Array.isArray(fm.phase_signals)) {
errors.push(issue(
'BRIEF_INVALID_PHASE_SIGNALS',
'phase_signals must be a list of {phase, effort?, model?} entries',
));
} else {
for (const entry of fm.phase_signals) {
if (!entry || typeof entry !== 'object' || !('phase' in entry)) {
errors.push(issue('BRIEF_INVALID_PHASE_SIGNALS', `phase_signals entry must include a "phase" key`));
continue;
}
if (!PHASE_SIGNAL_PHASES.includes(entry.phase)) {
errors.push(issue(
'BRIEF_INVALID_PHASE_SIGNAL_PHASE',
`phase_signals.phase "${entry.phase}" not in [${PHASE_SIGNAL_PHASES.join(', ')}]`,
));
}
if ('effort' in entry && !EFFORT_LEVELS.includes(entry.effort)) {
errors.push(issue(
'BRIEF_INVALID_EFFORT',
`phase_signals.effort "${entry.effort}" not in [${EFFORT_LEVELS.join(', ')}]`,
));
}
if ('model' in entry && !BASE_ALLOWED_MODELS.includes(entry.model)) {
errors.push(issue(
'BRIEF_INVALID_MODEL',
`phase_signals.model "${entry.model}" not in [${BASE_ALLOWED_MODELS.join(', ')}]`,
));
}
}
}
}
// Sequencing gate: brief_version ≥ 2.1 requires phase_signals OR phase_signals_partial.
// Coerce to String so the gate fires regardless of whether YAML parsed the value as
// a string ("2.1") or a number (2.1). v5.1.0 shipped with an unquoted-2.1 template
// that silently bypassed this gate — fix locked in by quoting the template AND
// accepting both shapes here as defense-in-depth (v5.1.1, finding 3c834097/df1435a2).
// v5.5 — framing enum check fires on ANY version when the field is present but
// malformed. The missing-framing BLOCKER below is version-gated (≥ 2.2).
if ('framing' in fm && !BRIEF_FRAMING_VALUES.includes(fm.framing)) {
@ -156,15 +104,7 @@ export function validateBriefContent(text, opts = {}) {
if (vm) {
const major = Number(vm[1]);
const minor = Number(vm[2]);
const atLeast21 = major > 2 || (major === 2 && minor >= 1);
const atLeast22 = major > 2 || (major === 2 && minor >= 2);
if (atLeast21 && !hasSignals && !hasPartial && fm.type !== 'trekreview') {
errors.push(issue(
'BRIEF_V51_MISSING_SIGNALS',
'brief_version ≥ 2.1 requires phase_signals (or phase_signals_partial: true)',
'Re-run /trekbrief — Phase 3.5 collects per-phase effort + model signals.',
));
}
// v5.5 framing enforcement — gated at ≥ 2.2 (trekreview briefs are exempt).
if (atLeast22 && fm.type !== 'trekreview') {
if (!('framing' in fm)) {