Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
16 changes: 15 additions & 1 deletion fia-templates/fia.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down
10 changes: 10 additions & 0 deletions pi-templates/.pi/skills/fia/cookbooks/update_roster.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <model>` 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
Expand Down
2 changes: 2 additions & 0 deletions src/steps/finish.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
40 changes: 40 additions & 0 deletions test/fia-agent-overrides.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
Loading