diff --git a/DOCS.md b/DOCS.md index c1594de..6d0752b 100644 --- a/DOCS.md +++ b/DOCS.md @@ -1253,6 +1253,14 @@ are accepted inside an override; engine, model, prompts, permissions and fallbacks remain the validated base configuration. Any other field fails config validation instead of silently changing how or where a phase runs. +The shipped roster pre-seeds the recipes above (they used to be +documentation-only): builder `fix_*`/`fix` at low reasoning (repair rounds act +on concrete failure output), reviewer `ui_check`/`ui_verify` at low (a fixed +rubric is a checklist judgement — the functional `review` keeps full effort), +and the documenter's base `thinking` is `low` (it narrates an existing diff). +Models are never touched by these — which model each agent runs on is always +the engineer's choice (`imp llm` / the Agents tab), on their own plan. + Engines (`coding_agent`): | Engine | Binary | Model examples | Notes | diff --git a/fia-templates/fia.config.yaml b/fia-templates/fia.config.yaml index 229118b..ca97d6e 100644 --- a/fia-templates/fia.config.yaml +++ b/fia-templates/fia.config.yaml @@ -230,6 +230,12 @@ agents: thinking: medium color: "#22d3ee" purpose: Implement the plan exactly; report every changed file in the envelope. + # Repair rounds get concrete failure output to act on — deep reasoning adds + # cost, not quality, there. Only thinking/effort may vary by phase; the + # model never does (that would break the session and the cache). + phase_overrides: + "fix_*": { thinking: low, effort: low } + fix: { thinking: low, effort: low } fallbacks: - { coding_agent: claude_code, model: sonnet, effort: high } prompt_engineering: @@ -271,6 +277,12 @@ agents: thinking: high color: "#fb7185" purpose: Confirm that what was built matches what was asked; change nothing. + # The UI-conformance passes audit a fixed rubric against named files — a + # checklist judgement, not deep reasoning; the functional review keeps the + # full effort. Both fields set so the override survives an engine relay. + phase_overrides: + ui_check: { effort: low, thinking: low } + ui_verify: { effort: low, thinking: low } fallbacks: - { coding_agent: pi, model: openai-codex/gpt-5.6-sol, thinking: high } prompt_engineering: @@ -288,7 +300,9 @@ agents: - name: documenter coding_agent: pi model: openai-codex/gpt-5.6-sol - thinking: medium + # Documentation narrates a diff that already exists — low reasoning reads + # the same and costs a fraction. + thinking: low color: "#e879f9" purpose: Write up the change from the diff; documentation only. fallbacks: diff --git a/pi-templates/.pi/skills/fia/cookbooks/update_roster.md b/pi-templates/.pi/skills/fia/cookbooks/update_roster.md index b773ce2..f16cbb7 100644 --- a/pi-templates/.pi/skills/fia/cookbooks/update_roster.md +++ b/pi-templates/.pi/skills/fia/cookbooks/update_roster.md @@ -38,6 +38,16 @@ Pi API-key providers (any of them, per agent): `openrouter/…` (OPENROUTER_API_ Rule of thumb: heavy reasoning (planner, reviewer) on a frontier model; volume work (builder, scout, documenter) on a fast/cheap one — different providers in the SAME run is the point. +Cost reality check (measured on a real project ledger): the planner on a +top-tier model averaged **US$3.55 per call** and 15% of ALL tokens; the same +role on the mid-tier model of the same plan costs roughly a fifth for briefs +that are already self-contained. The model is ALWAYS the engineer's choice — +their plan, their subscription — so never switch it silently: show the ledger +(`npm run fda:cost-report`, or the Agents tab) and offer the one-liner +(`/llm set planner ` swaps it in seconds). The shipped roster already +carries `phase_overrides` that drop reasoning (never the model) on repair and +UI-verify phases — keep them when editing an agent by hand. + WARNING: Claude INSIDE Pi bills as per-token "extra usage" — to spend plan limits, always use `coding_agent: claude_code`. ## Fallbacks — per-agent `fallbacks:` chain diff --git a/src/steps/finish.js b/src/steps/finish.js index 3749044..963582c 100644 --- a/src/steps/finish.js +++ b/src/steps/finish.js @@ -221,6 +221,8 @@ export async function finish(ctx) { ? 'but Pi also accepts other providers/models — type /login inside Pi to see them.' : 'but you can also work through Cursor or add engines later at any time.', 'Re-check this roster (and the whole setup) anytime with `imp doctor`.', + 'Every model is YOUR choice, on YOUR plan: `imp llm` swaps any agent in', + 'seconds, and `npm run fda:cost-report` shows what each one actually spends.', ] .filter(Boolean) .join('\n'), diff --git a/test/fia-agent-overrides.test.js b/test/fia-agent-overrides.test.js index 18c2ccb..07daf3b 100644 --- a/test/fia-agent-overrides.test.js +++ b/test/fia-agent-overrides.test.js @@ -67,3 +67,43 @@ test('validate: thinking/effort overrides pass', () => { validate({ agents: [builder({ 'fix_*': { thinking: 'low' }, review: { effort: 'medium' } })] }, ['builder']), ); }); + +// ── the SHIPPED roster carries the recipes (they used to be docs-only) ─────── + +test('the shipped fia.config.yaml pre-seeds low-reasoning overrides on repair and UI-verify phases', async () => { + const { readFileSync } = await import('node:fs'); + const { parse } = await import('yaml'); + const template = parse(readFileSync(new URL('../fia-templates/fia.config.yaml', import.meta.url), 'utf8')); + const byName = Object.fromEntries(template.agents.map((a) => [a.name, a])); + + assert.deepEqual(byName.builder.phase_overrides['fix_*'], { thinking: 'low', effort: 'low' }); + assert.deepEqual(byName.builder.phase_overrides.fix, { thinking: 'low', effort: 'low' }); + assert.deepEqual(byName.reviewer.phase_overrides.ui_check, { effort: 'low', thinking: 'low' }); + assert.deepEqual(byName.reviewer.phase_overrides.ui_verify, { effort: 'low', thinking: 'low' }); + assert.equal(byName.documenter.thinking, 'low', 'the documenter narrates an existing diff'); + // Models stay exactly the engineer-facing defaults — overrides tune + // reasoning only, never the model (that choice belongs to the user). + for (const agent of template.agents) { + for (const override of Object.values(agent.phase_overrides || {})) { + assert.deepEqual( + Object.keys(override).filter((k) => k !== 'thinking' && k !== 'effort'), + [], + `${agent.name}: an override may tune reasoning only`, + ); + } + } + + // The shipped shapes pass the real validator (prompt paths rewritten to the + // repo copies — the template's imp/ paths only exist in a stamped project). + const cfg = { + ...template, + agents: template.agents.map((a) => ({ ...a, prompt_engineering: { system: SYSTEM, user: USER } })), + }; + assert.doesNotThrow(() => validate(cfg, ['builder', 'reviewer', 'documenter'])); + + // And resolveForPhase applies them where the runners will ask. + assert.equal(resolveForPhase(cfg, 'builder', 'fix_2').thinking, 'low'); + assert.equal(resolveForPhase(cfg, 'builder', 'build').thinking, 'medium', 'build keeps the base reasoning'); + assert.equal(resolveForPhase(cfg, 'reviewer', 'ui_verify').effort, 'low'); + assert.equal(resolveForPhase(cfg, 'reviewer', 'review').effort, 'high', 'the functional review keeps full effort'); +});