/trekexecute no longer builds steps itself. Each plan step is built by one fresh helper (new agent step-implementer: opus, effort high, no Agent tool) and judged by one controller (a general helper on opus) that reads the diff from a recorded start_sha and gives two verdicts, spec and quality. Fix rounds go up, maximum 5: three fixes by a fresh helper, then back to the plan, then to the operator. No cheaper-model ladder. Proof is a command, never the model's word: - lib/verification/red-proof.mjs: the step's test must fail at the start point and pass at the result (exit 0/1/2; 2 is never green). Reproduces the02243c6red-first measurement (6 of 55) through the shim fixture. - lib/verification/test-integrity.mjs: the floor guard on the diff (removed asserts, skipped/deleted tests, muted checks, stubs, loosened thresholds). Over the last 150 commits: 149 clean, 1 real finding. - lib/verification/done-line.mjs: DONE step N only when the test command exits 0. Test first is required from plan_version 1.8. The test lock (K8) is used through its CLI and reported as not-locked while it is missing. A run stops only on a closed list of four classes; every other call goes into a decision log with its cost if wrong. Without a passing run-the-thing criterion the result is not verified end to end, never green. Handover 5 gains optional per-step start_sha, report_path, red_proof, test_lock, fix_rounds (0-5) and run-level decisions and end_to_end; old progress files still validate and resume. No isolation on the helper: measured, a worktree-isolated subagent branches from the default branch (02f320f), not the executor's HEAD (2c89a6b), so it would miss the earlier steps. Borrowings credited in commands/trekexecute.md ## Credits (Superpowers, ai-native-sdlc, Kiro, GitHub Spec Kit, agent-skills, Cherny). Tests: 1411 -> 1457 (1455 pass, 0 fail, 2 skipped). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 KiB
Profile system — voyage v4.1
This document describes the model profile system: built-in tiers, lookup precedence, custom-profile authoring, drift detection, and cost estimation (with disclaimer).
Built-in profiles
Four pre-defined tiers ship with the plugin (fable added in v5.9), located at
lib/profiles/{economy,balanced,premium,fable}.yaml.
| Profile | Brief | Research | Plan | Execute | Review | Continue | Use case |
|---|---|---|---|---|---|---|---|
economy |
sonnet | sonnet | sonnet | sonnet | sonnet | sonnet | ⚠ Experimental (uncalibrated Jaccard floor) — lowest cost; small-scope tasks where you have high confidence the brief is right |
balanced |
sonnet | sonnet | opus | sonnet | opus | sonnet | Mixed — opus where reasoning depth pays off (plan synthesis + adversarial review); opt-in via --profile balanced |
premium (default) |
opus | opus | opus | opus | opus | opus | Maximum quality — Opus on every phase + external research on (default since the 2026-05-13 operator decision) |
fable |
fable | fable | fable | fable | fable | fable | Max quality — Fable 5.1 (the most capable model in Claude Code) on every phase; opt-in via --profile fable; reasoning effort inherits from the session (see Model & effort axes) |
premium is the default tier — set by the 2026-05-13 operator decision and
matched by the hardcoded resolver default in lib/profiles/resolver.mjs. It
runs opus on every phase and turns external research on: maximum quality, at
roughly 5× the sub-agent cost of an all-sonnet run, accepted as a deliberate
trade-off. Drop to --profile balanced (opus only on the two phases where
quality matters most — Plan synthesis + Review — and sonnet everywhere else)
or --profile economy (sonnet everywhere) when cost or latency matters more
than depth. Step up to --profile fable (Fable 5.1 on every phase) when
maximum quality is wanted end-to-end and cost is not a constraint.
economy is strictly experimental in v4.1, and says so in the profile
data itself: lib/profiles/economy.yaml carries experimental: true. The
cross-tier Jaccard floor (0.55) is grounded in parked-synthetic fixtures, not
empirical runs (Step 17 calibration was deferred — see
tests/synthetic/profile-jaccard-calibration.md). The flag is pinned: it must
stay true while the calibration status is parked-synthetic, and must be
dropped in the same change that lands empirical calibration
(status: empirical). If you observe economy-plan quality regressions, fall
back to balanced.
Model & effort axes
opus, sonnet, and fable are model aliases, not pinned ids. On the
Anthropic API (verified 2026-10-02 against the Claude Code model-config docs)
the opus alias resolves to Opus 5.5 and sonnet to Sonnet 5.5, both
with default reasoning effort medium; fable resolves to Fable 5.1
(the most capable model in Claude Code), whose default reasoning effort is
high. Other providers resolve opus and sonnet to other versions. The
profile table above selects which alias runs each phase — it does not touch
reasoning effort.
Reasoning effort inherits from the session. Voyage effort (orchestration
shape — which agents/passes run) and model reasoning effort are different
axes. Fable 5.1's default reasoning effort is high, NOT xhigh, and Claude
Code saves effort per model, so a level saved for one model does not carry to
another. To run the fable tier at xhigh, choose it explicitly: /effort xhigh,
--effort xhigh, or CLAUDE_CODE_EFFORT_LEVEL.
Two different things share the word "effort" in Voyage. They are orthogonal axes — same name, different mechanism:
| Orchestration effort | Reasoning effort (native) | |
|---|---|---|
| Where | brief phase_signals.effort, consumed by command prose |
native Claude Code effort: in agent frontmatter |
| Values | low / standard / high |
low / medium / high / xhigh / max |
| Controls | which agents/passes/gates run (swarm cardinality, gate strictness, sequential-vs-parallel) | per-spawn reasoning-token budget the harness gives one agent |
low means |
"run fewer agents" (the --quick-equivalent code-path) |
"think with a smaller budget" |
| Applied by | Voyage command logic | the harness, at spawn time |
The phase-signal-resolver.mjs helper only reads the orchestration axis
(phase_signals.effort, gated against low/standard/high) plus the optional
per-phase model (gated against ['sonnet','opus','fable']). It never emits
native effort:.
Native effort: on agents. Voyage sets the reasoning axis statically on
selected agents, overriding the session level:
- Nine agents pin
high: the retrieval agentstask-finder,git-historian,dependency-tracer,architecture-mapper, the adversarial-reasoning agentsplan-critic,risk-assessor,contrarian-researcher,review-coordinator, and/trekexecute's step helperstep-implementer. No agent pins belowhigh(operator rule, 2026-10-05: standard is Opus 5.5 at effort High). - All other agents: unset → inherits the session's effort level (a session
that nothing sets runs at its model's default —
mediumon Opus 5.5).
Native-effort precedence (harness): env CLAUDE_CODE_EFFORT_LEVEL > frontmatter
effort: > session setting > model default, and a maxEffortLevel setting or
an organization effort cap still limits the result. MAX_THINKING_TOKENS=0 has
no effect on Opus 5.5, Sonnet 5.5 or the Fable models: thinking cannot be turned
off there. availableModels restricts which models can be selected — it does
not bound effort.
Decision tree
Are you uncertain whether the brief is correctly framed?
└── Yes → premium (opus on brief + plan + review)
└── No → continue
↓
Is the change small (≤ 5 steps in the plan)?
└── Yes → economy (sonnet everywhere)
└── No → balanced (opus on plan + review)
Special cases:
- Critical-infrastructure plan → premium
- Migration with rollback risk → premium
- Research-heavy task (≥ 4 dimensions) → balanced (research-stage benefits)
- Bug fix with clear reproducer → economy
- Documentation-only PR → economy
Lookup order
Voyage resolves the profile in this priority order:
- Explicit
--profile <name>flag — passed to the command - Plan-file frontmatter
profile:— when resuming via/trekexecute --resumeor/trekcontinue VOYAGE_PROFILEenvironment variable — useful for headless CI- Default
premium— final fallback
The resolved value is recorded in two places:
- Plan-file frontmatter
profile: <name>andphase_models: [...] - Stats stream
${CLAUDE_PLUGIN_DATA}/trek*-stats.jsonl—profile,profile_source,phase_models,model_used,phase_models_resolvedfields
profile_source distinguishes how the profile was resolved (flag /
plan_frontmatter / env / default), so dashboards can surface
unexpected env-var inheritance in CI.
Custom profiles
Drop a YAML file at lib/profiles/<name>.yaml to define a new tier.
The validator (lib/validators/profile-validator.mjs) enforces:
- Every
phase_models[].phasemust be a known phase enum:brief/research/plan/execute/review/continue - Every
phase_models[].modelmust exactly match an entry inBASE_ALLOWED_MODELS(['sonnet', 'opus', 'fable']inlib/validators/profile-validator.mjs;haikuonly withVOYAGE_ALLOW_HAIKU=1) - All six phases must be present (no partial profiles)
The four built-in names (economy, balanced, premium, fable) resolve to their
bundled yaml first — findProfilePath() returns the built-in before consulting
voyage-profiles/, so a same-named custom file is ignored and cannot shadow a
built-in. To customize, give your profile a new name and reference it via
--profile <new-name> or VOYAGE_PROFILE=<new-name>.
Example custom profile
# lib/profiles/critical.yaml — opus everywhere except continue
phase_models:
- phase: brief
model: opus
- phase: research
model: opus
- phase: plan
model: opus
- phase: execute
model: opus
- phase: review
model: opus
- phase: continue
model: sonnet
Validate with: node lib/validators/profile-validator.mjs --json lib/profiles/critical.yaml
Drift detection
In --strict mode, plan-validator.mjs emits a MANIFEST_PROFILE_DRIFT
warning when the plan-level profile: differs from any step manifest's
profile_used. The warning is a signal, not a failure — the plan
remains valid: true. This catches:
- Manual edits where an operator changed a single step's profile
- Resume from a partial run where the previous session used a different tier
- Copy-paste errors when stitching plan fragments
To suppress the warning intentionally (e.g. when a critical step
genuinely needs a higher tier), document the override in the step's
prose and re-run with --soft to validate without strict-mode warnings.
Cost estimation
Disclaimer: the table below is an anslag, not a contractual SLA. Real cost depends on context size, agent-swarm cardinality, tool-use density, and Claude Code billing schedule. Treat these as rough order-of-magnitude.
| Profile | Brief | Research | Plan | Execute | Review | Total |
|---|---|---|---|---|---|---|
economy |
$0.10–0.50 | $0.50–2.00 | $0.50–2.00 | $1.00–5.00 | $0.20–1.00 | $2–10 |
balanced |
$0.10–0.50 | $0.50–2.00 | $1.00–4.00 | $1.00–5.00 | $0.50–2.00 | $3–14 |
premium |
$0.50–2.00 | $0.50–2.00 | $1.00–4.00 | $1.00–5.00 | $0.50–2.00 | $4–15 |
Numbers are per full pipeline run (brief + research + plan + execute + review) on a moderate-complexity task. Numbers scale roughly linearly with the size of the resulting plan (10 steps ≈ baseline; 30 steps ≈ 3× the execute column).
Per-profile actuals are emitted to JSONL stats — pipe them through the
OTel export (docs/observability.md) to get real cost-attribution
graphs in Grafana. Replace the table above with your own measured
numbers after ≥ 3 runs of each profile.
Deferred to v4.2
balanced.external_research_enabledoperator-override — v4.1 omits this per scope-guardian SG2. v4.2 may add an opt-in flag to enable external research agents in the balanced tier without forcing premium.- Empirical Jaccard re-calibration — parked-synthetic fixtures in v4.1 use a 0.55 conservative starting threshold. v4.2 plans an empirical re-run with $60-120 LLM budget to derive a calibrated threshold from real economy-vs-premium plan pairs.
- ROUGE-L + char-4gram MinHash as primary/secondary cross-tier gates per research/02 Recommendation #7. Jaccard remains the gate in v4.1; v4.2 may layer ROUGE-L on top.
See also
README.md§ Profile system — top-level overviewCLAUDE.md§ Profile system — internal referencedocs/observability.md— JSONL → OTel pipelinetests/synthetic/profile-jaccard-calibration.md— calibration status and threshold rationalelib/profiles/— built-in profile YAMLslib/validators/profile-validator.mjs— schema validator with CLI shim