diff --git a/fia-templates/modules/session.mjs b/fia-templates/modules/session.mjs index aba2e33..e57654b 100644 --- a/fia-templates/modules/session.mjs +++ b/fia-templates/modules/session.mjs @@ -124,6 +124,12 @@ export function ensure(cfg, fdaId = null, { resume = false } = {}) { // the interrupted run's engine-failure markers count as unavailability too // (that engine already proved it cannot finish), so the resumed run keeps // moving on the fallbacks instead of dying on the same outage again. + // A typo in defaults.relay must not silently become 'auto': say so once. + const relayRaw = cfg.defaults?.relay; + if (relayRaw !== undefined && relayRaw !== relayModeOf(cfg)) { + console.log(` ⚠ unknown relay mode "${relayRaw}" in imp/fia.config.yaml — using "auto" (valid: auto | resume | off)`); + } + // relay 'off' means the student owns every engine switch — the resumed run // must not walk the chain on its own either. const runtimeFailures = {}; diff --git a/fia-templates/scripts/fia-tui.mjs b/fia-templates/scripts/fia-tui.mjs index 8545a63..ef4edfb 100644 --- a/fia-templates/scripts/fia-tui.mjs +++ b/fia-templates/scripts/fia-tui.mjs @@ -465,7 +465,7 @@ function RunDetail({ detail, now, height, width }) { // Engine switches (run-start fallback or mid-run relay) — the student must // see WHICH engine actually did the work without opening the trace db. const relays = (detail.engineEvents || []) - .filter((e) => e.type === 'engine_fallback' || e.type === 'engine_relay') + .filter((e) => (e.type === 'engine_fallback' || e.type === 'engine_relay') && e.payload?.from && e.payload?.to) .slice(-3); const phaseRoom = Math.max(3, height - 8 - relays.length); const hiddenPhases = Math.max(0, phaseRows.length - phaseRoom); @@ -497,7 +497,10 @@ function RunDetail({ detail, now, height, width }) { trunc( `⚠ ${e.name}: ${e.payload?.from?.coding_agent} (${e.payload?.from?.model}) → ` + `${e.payload?.to?.coding_agent} (${e.payload?.to?.model})` + - `${e.payload?.kind ? ` — ${e.payload.kind}` : ''}${e.type === 'engine_relay' ? ' (mid-run)' : ''}`, + // Run-start fallbacks carry `reason` (no kind unless runtime-armed); + // mid-run relays carry `kind` — show whichever explains the switch. + `${e.payload?.kind || e.payload?.reason ? ` — ${e.payload.kind || e.payload.reason}` : ''}` + + `${e.type === 'engine_relay' ? ' (mid-run)' : ''}`, width - 6, ), ), diff --git a/pi-templates/.pi/skills/fia/SKILL.md b/pi-templates/.pi/skills/fia/SKILL.md index d825307..d21685f 100644 --- a/pi-templates/.pi/skills/fia/SKILL.md +++ b/pi-templates/.pi/skills/fia/SKILL.md @@ -49,6 +49,7 @@ Short commands (prompt templates in `.pi/prompts/`) are the student entry points | `/absorb [focus]` | existing project → as-built PRD + map + conventions + stack manifest + component registry | — | | `/kit` | existing code → design-system audit: as-built registry + `/ui-components`, gap report vs the core kit, approved design-only tasks (`Kind: kit`) | components | | `/status` | progress + latest runs | observability | +| `imp handoff` (terminal, outside Pi) | Codex outage — continue the newest Pi conversation in the `claude` CLI (`--list` picks a session) | update_roster | For anything else, route by request: diff --git a/pi-templates/.pi/skills/fia/cookbooks/update_roster.md b/pi-templates/.pi/skills/fia/cookbooks/update_roster.md index aa01cf6..2bf3e81 100644 --- a/pi-templates/.pi/skills/fia/cookbooks/update_roster.md +++ b/pi-templates/.pi/skills/fia/cookbooks/update_roster.md @@ -53,6 +53,11 @@ on `--resume`), `off` (never auto-switch; deaths are still recorded). Every switch is printed and traced — never silent. Edit the chain visually in `/agents` or by hand in `imp/fia.config.yaml`. +The interactive twin: when the Codex provider is down (so Pi itself cannot +answer), `imp handoff` in a terminal — outside Pi — continues the newest Pi +conversation in the `claude` CLI with the same continuation handover +(`--list` picks a session, `--print` shows the prompt without launching). + ## Add or change an agent (FDAs) 1. Edit `imp/fia.config.yaml` — the file is protected for agents; the ENGINEER edits it (or uses `/agents`): diff --git a/src/config.js b/src/config.js index d794c83..6fffef5 100644 --- a/src/config.js +++ b/src/config.js @@ -122,6 +122,7 @@ export const FIA = { 'imp/data/fia.db-wal', 'imp/data/fia.db-shm', 'imp/data/backups/', + 'imp/data/handoff/', 'imp/.runtime-backup-*/', ], // Runtime update contract (`--update-runtime`). The stamp records a manifest diff --git a/src/steps/fia.js b/src/steps/fia.js index a264565..88618d1 100644 --- a/src/steps/fia.js +++ b/src/steps/fia.js @@ -185,6 +185,7 @@ export async function setupFia(ctx) { 'Visual timeline: npm run fda:viewer · What /map created: npm run plan', 'Terminal dashboard (tasks, specs, runs — live): npm run tui', 'Checks: npm run env:check (dev keys the stack needs) · npm run launch:check (before going live)', + 'Codex down? `imp handoff` continues your newest Pi conversation in the `claude` CLI.', '', ...(ctx.impGlobal ? [ diff --git a/src/steps/finish.js b/src/steps/finish.js index dc132aa..1ad85f9 100644 --- a/src/steps/finish.js +++ b/src/steps/finish.js @@ -140,6 +140,9 @@ export async function finish(ctx) { ? `FIA: installed — last step: run \`${agentCmd(ctx)}\` and type /login openai-codex (only that login)` : `FIA: installed — run \`${agentCmd(ctx)}\` and use /fia, /map, /task, /goal (plan: npm run plan · agents: npm run agents · viewer: npm run fda:viewer · terminal dashboard: npm run tui)` : null, + ctx.fiaInstalled + ? 'Codex outage? `imp handoff` continues your newest Pi conversation in the `claude` CLI.' + : null, ] .filter(Boolean) .join('\n'),