Pilotfish-Codex design rationale

September 23, 2026 · View on GitHub

Pilotfish-Codex preserves Pilotfish's role routing, approval boundaries, leaf workers, and fresh-context verification while using Codex-native TOML roles and global AGENTS.md policy. Claude-specific worktrees, task dashboards, agent IDs, resume commands, and Explore shadowing are not Codex runtime claims.

Native Multi-Agent boundary

The active target is the native Codex contract, with 0.147.0 as the minimum compatibility floor; later releases are accepted after parsing and native contract validation. Child concurrency is an [agents] setting:

[agents]
max_concurrent_threads_per_session = 3

[features]
default_mode_request_user_input = true

The value is child concurrency, so three permits the root plus three children. The native decision-card feature is enabled so Default mode can expose the same bounded request_user_input interaction used by Plan mode. The root model, reasoning effort, and Plan-mode effort are user preferences: Pilotfish defaults them for a fresh install but does not claim ownership, so a user may temporarily switch the main session to another compatible model. Role TOMLs retain model and reasoning-effort precedence. The retired V2 feature table is migratable only with exact installer provenance; the active contract does not rely on an adapter namespace, metadata visibility override, or an undocumented rollout marker.

Migration provenance is an exact committed sidecar schema: config.toml, the seven canonical role paths, and the currently selected policy must be the only entries in both target maps, with matching SHA-256 fingerprints and original byte evidence. Missing, stale, extra, or malformed state, an unowned V2 key, or a conflicting [agents] concurrency value aborts before writes. Dry-run reports only primary paths and the pending/committed sidecars plus backups for replaced targets; it creates none.

An explicit Astra main session is a zero-write, session-only preference. The user starts it with gpt-6-astra, high main and Plan effort, and one optional child; the default root config is GPT-6 Luna/max with Plan xhigh. Without a root override, automatic routing uses the installed Astra executor and verifier roles for design, tool use, interpretation, and judgment, while routine and mechanical work stays on the installed Luna roles. The prompt's max_tool_calls=12 and max_wall_seconds=300 values are advisory usage guards, not provider quota enforcement. Invalid or unavailable Astra activation fails closed before task work; a separately started no-flags session returns to the normal root Luna/Sol policy. plan-verifier uses GPT-6 Sol/high; all approval, security, release, and fresh-verifier boundaries remain intact. This slice adds no main-session typed-dispatch receipt field; existing child receipt schemas remain unchanged.

The role manifest is seven recursively discovered TOMLs. Pilotfish validates a single approved staged manifest and rejects duplicate names, filename/name mismatches, extra roles, path escape, and role drift. Only release-pinned prior canonical bytes may upgrade automatically; customized same-name roles still fail closed. This local validation does not claim to duplicate Codex's layered loader, which may merge role data across layers. The native smoke instead requires a one-user-layer staged home.

Policy and evidence

Policy constructs typed named-role requests with a non-empty message, installed agent_type, lowercase schema-safe task name, and fork_turns = "none" or "1" through "3". It forbids full history for named roles, untyped retries, and child model, reasoning-effort, service-tier, and context overrides.

Plan readiness

Large work keeps shared constraints in a program envelope and splits only independent execution slices. Concrete security, irreversible or external, data, release, or cross-component acceptance risk triggers a fresh plan-verifier; file count or “non-trivial” alone does not. REVISE returns all known P0-P2 blockers in one pass; P3/P4 and adjacent hardening do not block.

After two automatic revisions for one unit, the main session stops resubmitting, dispositions every blocker as FIX, DEFER, or REJECT, and narrows, splits, or continues independent slices. User input is reserved for unresolved P0/P1, product or authority choices, or an original scope that can no longer be met.

Outcome verification

A risk-triggered fresh verifier receives the exact completed-work claim and acceptance after the primary flow has been exercised. It returns CONFIRMED, REFUTED, or INCONCLUSIVE; P3/P4 advisories do not block confirmation, while REFUTED requires a reproducible P0-P2 blocker. A known blocker takes precedence over missing evidence for another condition; otherwise any unevaluated required condition is INCONCLUSIVE. The verifier reads and runs checks but never plans, edits, fixes, delegates, or exposes raw secrets.

Role verdicts are evidence, not implementation or scope authority. The main session records FIX, DEFER, or REJECT after adjudicating reproducibility, scope, claim relevance, priority, and confidence. P0 freezes the affected slice; P1 is fixed or paused for user direction. Regressions caused by the reviewed implementation remain claim-relevant even when the brief omitted the affected flow, and an introduced P2 regression must be fixed or paused rather than hidden by a narrowed claim. Other bounded in-acceptance P2 is fixed, while lower-priority findings may be reported with a narrowed claim. An inconclusive gate is retried once only after its prerequisite materially changes.

Adaptive intent routing

The orchestration policy now selects among three initial interaction shapes: execute for clear bounded work, explore_then_plan for clear but broad or high-impact work, and co_discover for an idea without a stable product boundary. These are descriptive starting points, not a keyword classifier or an exhaustive scenario list. Route selection records intent confidence, five-band change impact, reversibility, discovery budget, blocking decisions, and the next gate.

Discovery has a grounding floor and a stopping ceiling. The default bands are none, minimum, bounded, and deep, measured in logical inspections, searches, and reversible probes. A missing floor causes the session to state uncertainty; an exhausted ceiling causes it to narrow, pause, or ask rather than silently authorize writes.

Material choices use an AskUserQuestion-style decision card. It is one concise, high-level user checkpoint containing the current interpretation, recommended default, relevant scope and exclusions, decision options, and the next reversible slice. The default remains autonomous: low-risk, reversible, scope-clear work and bounded exploratory probes continue with a reasonable local default. The card appears only when the choice can change the outcome, permission, security, or acceptance; it does not replace the internal Plan or turn ordinary ambiguity into a user approval step. Secondary details are deferred until the selected direction resumes and reaches a new material boundary.

The native card is the default interaction surface. An independently maintained MCP elicitation bridge may be installed as an optional structured transport for the same checkpoint when the Codex host exposes form elicitation. It is not a Pilotfish core dependency, does not replace request_user_input, and must preserve the card schema, affected scope, exclusions, approval boundary, and resume point. Unsupported, cancelled, timed-out, or invalid MCP responses fall back to the native card or concise text checkpoint.

Outcome-level continuation

The unit of execution is the requested user-visible outcome, not one command, tool call, or checklist item. A clear execute request defaults to execution_scope=outcome and continuation_mode=attended_until_acceptance. The main session continues through necessary commands, phases, role handoffs, and verification; a phase update is progress reporting and does not ask for approval.

Explicit “only this step”, “只檢查這個檔案”, or “只改這個 module” wording selects step or slice and stops at that named boundary. Unclear product direction uses the smallest reversible slice and stops for a decision. Acceptance stops the requested outcome. Approval, security, release, destructive, external, and irreversible gates stop at material_gate before acceptance. Work after acceptance is limited to the requested outcome and its acceptance evidence.

The internal contract records execution_scope, continuation_mode, and stop_condition. The offline evaluator checks these fields as semantic policy evidence; it does not prove that every live model or host will comply.

Direction checks reuse the existing verifier role through an explicit direction_checkpoint contract. CONTINUE preserves the path, PIVOT requires a bounded re-plan, and ROLLBACK stops new writes and identifies the latest verified good checkpoint. Missing evidence remains INCONCLUSIVE. External, destructive, release, security-sensitive, and irreversible actions retain their existing approval and containment gates. The offline route and checkpoint evaluators are semantic behavior evidence only; they do not prove live model routing or native dispatch.

Long autonomous runs

AUTO/ASK is required for likely long unattended work. An attended task with clear scope continues through its outcome without selecting a mode merely because it has many commands or phases. Absence, sleeping, or leaving the agent alone is not authority; explicit “continue while I am away” selects AUTO. AUTO covers approved, reversible scope and P2 adjudication, not new version-control, publish, install, credential, destructive, external, scope, or spending authority. ASK uses Codex request_user_input only when exposed, may use the optional MCP bridge when configured, and otherwise pauses the turn.

Normal recovery is one targeted recheck of the original reproduction plus a bounded basic regression. Five materially changed P1/P2 passes remain an emergency ceiling for high-risk, claim-critical recovery, not a quota. Verification identity includes the complete tested candidate, claim, acceptance, contract, external evidence or prerequisites, and environment; a prior verifier's own output is not a change. The candidate fingerprint covers committed head, tracked and staged diff, untracked input paths plus content, and dirty submodule content. Artifact digests complement source identity unless the artifact is the sole deliverable. A fifth failure pauses only that slice and its dependents when risk is not cross-cutting; recovery stops earlier when another pass would only search adjacent risk.

Continuation liveness

User input does not necessarily replace the task already in progress. The main-session policy therefore keeps an unfinished root objective active when a message answers a pending decision, steers or corrects the work, asks for status or explanation, or resumes a pause. Contextually clear replacement intent may supersede it without a literal cancellation phrase.

Before pausing, the main session exposes the objective, current phase or slice, pending blocker or decision, and exact resume point. A decision response binds to that point only when it resolves the decision unambiguously; otherwise the pause remains and the session asks one concise clarification. Status and explanation requests resume only work not gated by the unresolved decision; otherwise the session remains paused. An incomplete objective cannot end with a normal final: the session must continue or return PAUSED_NEEDS_USER with the blocker, question, and resume point. A user-requested pause instead records the objective, phase, and resume point without inventing a blocker or question. It remains active through status or explanation requests until the user explicitly resumes or clearly replaces the objective.

This is a prompt-level liveness contract. It neither persists task state outside the conversation nor changes Codex App, app-server, approval, security, or scope behavior. Static assertions prevent accidental policy removal; they are not behavioral proof that a live model or host always complies.

The verifier is post-hoc evidence classification, not a pre-execution cancel hook. Native proof requires one spawn_agent with exact typed arguments, call/activity correlation, and child turn_context.model and turn_context.effort matching the installed role. It records only redacted, hashed identifiers and receipt fields. A namespace and an undocumented rollout marker are neither required nor sufficient evidence.

Staging boundary

The native smoke first copies the post-install active target into a distinct, not-yet-existing staged home using canonical containment, confined reads, TOCTOU checks, cleanup on failure, and exclusive atomic no-replace publication. Only the canonical native-agent config projection, hashed policy/manifest input, and auth.json are materialized. Unrelated active config and existing runtime metadata are outside the smoke projection and are not copied or hashed. Before launch the staged home is an exact minimal allowlist; Codex creates its own runtime state there only after preflight. The verifier compares active and staged projected config, policy, and canonical role-manifest hashes before it can launch Codex. It also rejects project-local configuration and instruction discovery in the supplied smoke working directory.

NATIVE_OK is the only completed native gate. SKIPPED remains incomplete and FAILED blocks migration completion. Historical adapter behavior belongs only to explicitly labeled offline fixtures and archived evidence.