fix(commands): wire profile phase_models into spawn-site model resolution

This commit is contained in:
Kjell Tore Guttormsen 2026-07-02 17:08:09 +02:00
commit 77ccf6ba06
5 changed files with 105 additions and 54 deletions

View file

@ -55,15 +55,18 @@ Supported flags:
Create `{dir}/research/` if it does not already exist.
When `{dir}/brief.md` exists, ALWAYS run the brief-validator (soft mode)
AND the phase-signal-resolver for this command's phase before continuing.
The resolver's JSON output is captured as `phase_signal_result` and used
at Agent-spawn sites in Phase 4 to inject the brief-resolved model:
AND the composed phase-model resolver for this command's phase before
continuing. The resolver's JSON output `{effort, model, source}`
(brief signal > profile > default) is captured as `phase_signal_result`
and used at Agent-spawn sites in Phase 4 to inject the resolved model:
```bash
# When --min-brief-version was passed, append --min-version {min_brief_version}
# so an older brief raises BRIEF_VERSION_BELOW_MINIMUM (warn, never block).
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/brief-validator.mjs --soft --json [--min-version {min_brief_version}] "{dir}/brief.md"
node ${CLAUDE_PLUGIN_ROOT}/lib/profiles/phase-signal-resolver.mjs --brief "{dir}/brief.md" --phase research --json
# v5.9 — composed resolver: ONE call returns {effort, model, source}.
# Append --profile {profile} when the operator passed --profile.
node ${CLAUDE_PLUGIN_ROOT}/lib/profiles/resolver.mjs --resolve-phase-model --phase research --brief-path "{dir}/brief.md" [--profile {profile}] --json
```
6. `--gates` — autonomy control. When present, set `gates_mode = true`. The
@ -546,7 +549,7 @@ If `${CLAUDE_PLUGIN_DATA}` is not set or not writable, skip tracking silently.
## Profile (v4.1)
Accepts `--profile <name>` where `<name>` is `economy`, `balanced`, `premium`,
Accepts `--profile <name>` where `<name>` is `economy`, `balanced`, `premium`, `fable`,
or a custom profile under `voyage-profiles/`. Default: `premium`.
Resolution order (per `lib/profiles/resolver.mjs`):
@ -581,13 +584,15 @@ model_for_phase = brief.phase_signals[<phase>]?.model ?? profile.phase_models[
```
The brief signal wins per-phase when present; the profile fills any
gaps. Composition is mechanically resolved via
`node ${CLAUDE_PLUGIN_ROOT}/lib/profiles/phase-signal-resolver.mjs`
invoked in Phase 1; the resolved JSON is captured as `phase_signal_result`
and passed to `Agent` tool calls explicitly. The resolver controls only
the orchestrator and the model parameter at Agent-spawn sites — sub-agents
otherwise read `model:` from their own `agents/*.md` frontmatter (still
pinned to `opus`).
gaps. Both fields are mechanically resolved by the single composed CLI
`node ${CLAUDE_PLUGIN_ROOT}/lib/profiles/resolver.mjs --resolve-phase-model`
invoked in Phase 1; the resolved JSON `{effort, model, source}` is captured
as `phase_signal_result` and passed to `Agent` tool calls explicitly. The
resolver controls the `model` parameter at Agent-spawn sites only — the
orchestrator's own model is fixed at invocation time (command frontmatter
omits `model:`, so it follows the session model) and cannot be switched
mid-turn. Sub-agents fall back to `model:` in their own `agents/*.md`
frontmatter when no spawn-site injection happens.
For `/trekresearch` specifically: `effort == 'low'` activates the
existing `--quick`-equivalent code-path (inline research, no agent swarm).
@ -632,10 +637,11 @@ Low effort: inline research only, no agent swarm (existing
Triangulate AFTER independent research.
- **Graceful degradation:** If MCP tools are unavailable (Tavily, Gemini, MS Learn),
proceed with available tools and note limitations in brief metadata.
- **Cost:** Sub-agents use their pinned `model:` frontmatter (currently `opus`).
When `phase_signals[<phase>].model` is set, the orchestrator AND Agent-spawn
sites use the resolved model (`phase_signal_result.model`) for that phase.
Frontmatter is the default; brief signal is the per-phase override.
- **Cost:** Model resolution at Agent-spawn sites is a three-layer fallback:
brief `phase_signals[<phase>].model` > `profile.phase_models[<phase>]` >
agent frontmatter `model:`. The composed resolver returns the first two
layers as `phase_signal_result.model`; spawn sites inject it, and agent
frontmatter is the fallback when no injection happens.
- **Privacy:** Never log secrets, tokens, or credentials.
- **Honesty:** If the question is trivially answerable, say so. Don't inflate research.
- **Scope of codebase:** Only analyze the current working directory for local research.