Planning
Feature Definition
Section titled “Feature Definition”Planning is the contract that turns “do this change” into an explicit implementation strategy before heavy mutation starts.
In agent systems there are usually three distinct concepts that get conflated:
- Execution checklist tracking (task-progress state)
- Planning collaboration mode (an interaction phase where implementation is intentionally deferred)
- Plan artifact (a durable spec that another engineer/agent can execute)
Keeping these separate is critical. If they collapse into one concept, you get brittle behavior: progress checklists treated as design specs, or design specs accidentally triggering execution.
Why It Is Hard
Section titled “Why It Is Hard”Planning is hard because agent runtimes need to enforce semantics across model behavior, tool handlers, and UI protocols:
- The model must be able to propose strategy without prematurely mutating files.
- The runtime must encode which actions are allowed in planning context vs execution context.
- The client must render plan state clearly enough that humans can approve, reject, or refine.
- The system must avoid deadlocks where planning never exits, or exits without decision-complete detail.
This is not only prompt engineering. It is a runtime-state problem.
See Also
Section titled “See Also”- Todo Write for structured checklist mutation during execution.
- Todo Read for state re-synchronization and progress visibility.
- Agent Loop for where planning behavior sits in the full turn lifecycle.
Aider Implementation
Section titled “Aider Implementation”Aider at commit b9050e1d5faf8096eae7a46a9ecc05a86231384b does planning as a workflow pattern, not as a first-class planning tool.
No Model-Callable Planning Tool
Section titled “No Model-Callable Planning Tool”Aider does not expose a structured planning tool like update_plan, plan_enter, or plan_exit.
In aider/commands.py, planning-adjacent commands are chat-mode switches:
cmd_ask()cmd_code()cmd_architect()cmd_chat_mode()
There is no cmd_plan or model-callable planning function.
Ask Mode as Planning Workflow
Section titled “Ask Mode as Planning Workflow”/ask routes through _generic_chat_command(args, "ask") and uses AskCoder (aider/coders/ask_coder.py), which is explicitly non-editing.
Operationally this is how Aider users plan:
- switch to ask mode
- discuss approach in plain conversation
- switch back to code/architect mode to execute
The planning state is conversational context, not a typed plan object.
Architect Mode as Planning + Translation Pipeline
Section titled “Architect Mode as Planning + Translation Pipeline”ArchitectCoder (aider/coders/architect_coder.py) is Aider’s strongest planning-like mechanism:
- Architect model generates a detailed change strategy in natural language.
- Aider asks for confirmation (
confirm_ask("Edit the files?")unless auto-accept). - Editor model converts that plan text into concrete edits.
This is a planning pipeline, but still not a tool protocol with plan lifecycle events.
Planning in Legacy Function-Calling Formats
Section titled “Planning in Legacy Function-Calling Formats”Older Aider function-coder variants include an explanation field described as a step-by-step plan:
aider/coders/single_wholefile_func_coder.pyaider/coders/wholefile_func_coder.pyaider/coders/editblock_func_coder.py
That field is embedded in an edit payload and is not persisted as an independent planning object with status transitions.
Practical Consequence
Section titled “Practical Consequence”Aider has good planning behavioral ergonomics but limited planning protocol semantics. Planning is a convention layered over chat modes, not a runtime primitive with explicit lifecycle states.
Codex Implementation
Section titled “Codex Implementation”Codex at commit 4ab44e2c5cc54ed47e47a6729dfd8aa5a3dc2476 has an explicit split between planning checklist updates and true planning mode.
update_plan Is a Checklist Tool, Not Plan Mode
Section titled “update_plan Is a Checklist Tool, Not Plan Mode”core/src/tools/handlers/plan.rs defines update_plan and directly documents intent: the function output is not important; the input payload is for clients to render checklist state.
protocol/src/plan_tool.rs defines a typed schema:
StepStatus:pending | in_progress | completedPlanItemArg { step, status }UpdatePlanArgs { explanation?, plan[] }
Important semantic guard in handle_update_plan(...):
- if current mode is
ModeKind::Plan, tool returns an error - message:
update_plan is a TODO/checklist tool and is not allowed in Plan mode
This prevents mode confusion at runtime.
Tool Registration and Event Emission
Section titled “Tool Registration and Event Emission”core/src/tools/spec.rs always registers PLAN_TOOL and binds handler update_plan.
Execution path:
- model issues
update_planfunction call - handler parses
UpdatePlanArgs - handler emits
EventMsg::PlanUpdate(args) - tool returns textual acknowledgement (
Plan updated)
Notably, descriptive prompt guidance says “at most one in_progress”, but handler does not enforce cardinality; it only parses and forwards.
Client and Protocol Rendering Path
Section titled “Client and Protocol Rendering Path”EventMsg::PlanUpdate(UpdatePlanArgs) is part of the core protocol (protocol/src/protocol.rs).
Render paths:
- TUI:
tui/src/chatwidget.rs->on_plan_update()->history_cell::new_plan_update() - History cell (
tui/src/history_cell.rs) renders checklist with status styling - Human exec output (
exec/src/event_processor_with_human_output.rs) prints status markers (✓,→,•)
JSONL and SDK Todo Projection
Section titled “JSONL and SDK Todo Projection”exec/src/event_processor_with_jsonl_output.rs converts PlanUpdate events into a todo_list thread item lifecycle:
- first update in a turn ->
item.started - subsequent updates ->
item.updated - turn completion ->
item.completed
StepStatus::Completed maps to TodoItem.completed=true; all other statuses map to false.
This means Codex exposes planning checklist state to SDK consumers as a stable typed item (sdk/typescript/src/items.ts).
Plan Mode Is a Separate Collaboration Contract
Section titled “Plan Mode Is a Separate Collaboration Contract”Plan mode preset comes from core/src/models_manager/collaboration_mode_presets.rs, loading core/templates/collaboration_mode/plan.md.
Key plan-mode mechanics:
request_user_inputallowed only inModeKind::Plan(protocol/src/config_types.rs)- plan output is expected inside
<proposed_plan>...</proposed_plan>block - streaming parser (
core/src/proposed_plan_parser.rs) andcodex.rssplit normal assistant text from plan deltas - plan deltas emit
EventMsg::PlanDelta; finalized plan emits completed plan item
So Codex has two planning channels by design:
- checklist tracking (
update_plan/PlanUpdate) - plan authoring mode (
<proposed_plan>/PlanDelta)
App-Server Semantics
Section titled “App-Server Semantics”app-server/src/bespoke_event_handling.rs explicitly comments that update_plan is a todo/checklist tool and not plan-mode updates, then maps EventMsg::PlanUpdate to TurnPlanUpdatedNotification for API v2 clients.
This reinforces the split across transport boundaries.
Test Coverage
Section titled “Test Coverage”Codex has direct tests validating planning semantics:
core/tests/suite/tool_harness.rs- successful
update_planevent emission - malformed payload rejection
- successful
exec/tests/event_processor_with_json_output.rs- todo_list start/update/complete lifecycle from plan updates
OpenCode Implementation
Section titled “OpenCode Implementation”OpenCode at commit 7ed449974864361bad2c1f1405769fd2c2fcdf42 models planning as an experimental mode transition between two primary agents (build and plan) plus a plan file contract.
Plan Entry and Exit Tools
Section titled “Plan Entry and Exit Tools”packages/opencode/src/tool/plan.ts defines two tools:
plan_enterplan_exit
Both tools:
- ask the user a structured yes/no question via
Question.ask(...) - if accepted, synthesize a new user message with target agent (
planorbuild) - inject synthetic text telling the next turn what to do
This is not merely prompting; it changes session agent routing.
Plan Agent Permission Isolation
Section titled “Plan Agent Permission Isolation”packages/opencode/src/agent/agent.ts defines native plan agent permissions:
question: allowplan_exit: allow- broad edit deny, with exception for plan markdown path patterns
- external directory allowlist for plan storage path
This is a hard permission wall: plan mode is constrained to plan-artifact editing and read-only exploration.
Plan File Lifecycle
Section titled “Plan File Lifecycle”Session.plan(...) in packages/opencode/src/session/index.ts computes durable plan path:
- repo present:
<worktree>/.opencode/plans/<timestamp>-<slug>.md - no VCS:
<global data>/plans/<timestamp>-<slug>.md
This yields a concrete artifact that survives turn boundaries.
Prompt Injection and Workflow Protocol
Section titled “Prompt Injection and Workflow Protocol”packages/opencode/src/session/prompt.ts injects synthetic reminders for plan mode.
With OPENCODE_EXPERIMENTAL_PLAN_MODE enabled, injected instructions enforce a multi-phase workflow:
- explore and clarify
- design with subagents
- review
- write final plan file
- call
plan_exit
The injected reminder explicitly forbids non-readonly execution outside the plan file.
Registry and Feature Gating
Section titled “Registry and Feature Gating”packages/opencode/src/tool/registry.ts only exposes plan_enter/plan_exit when:
OPENCODE_EXPERIMENTAL_PLAN_MODEis true- client is CLI
So planning mode is feature-flagged, not universal.
Relationship to Todo and Question Tools
Section titled “Relationship to Todo and Question Tools”OpenCode planning is composed from multiple primitives:
questionfor decision collectiontodowritefor execution checklist visibilityplan_enter/plan_exitfor mode transitions- plan markdown file for durable design artifact
This compositional design is flexible but requires careful permission and UX coordination.
Pitfalls & Hard Lessons
Section titled “Pitfalls & Hard Lessons”Tool/Mode Name Collisions Cause Real Bugs
Section titled “Tool/Mode Name Collisions Cause Real Bugs”“Plan” often names both a checklist and a collaboration mode. Codex avoids this with explicit runtime guards and comments; systems without this split drift into inconsistent behavior.
Prompt Rules Without Runtime Guards Are Fragile
Section titled “Prompt Rules Without Runtime Guards Are Fragile”Instruction text like “only one in_progress” helps model behavior but is not a safety boundary. If invariants matter, enforce them in handler validation.
Plan-Only Modes Need File-System Guardrails
Section titled “Plan-Only Modes Need File-System Guardrails”If a planning agent can still mutate arbitrary files, “planning mode” becomes advisory. OpenCode’s explicit allowlist for plan files is the right direction.
Separate Streams Need Clear Rendering Semantics
Section titled “Separate Streams Need Clear Rendering Semantics”Codex’s <proposed_plan> streaming parser exists because interleaving plan text with normal assistant text causes UI ambiguity and tool-loop confusion.
Exit Criteria Must Be Explicit
Section titled “Exit Criteria Must Be Explicit”Plan loops can become endless if no concrete “done planning” transition exists. OpenCode’s plan_exit handshake and Codex’s <proposed_plan> completion convention both solve this in different ways.
OpenOxide Blueprint
Section titled “OpenOxide Blueprint”Keep the Split: Checklist vs Plan Mode vs Plan Artifact
Section titled “Keep the Split: Checklist vs Plan Mode vs Plan Artifact”Adopt three explicit primitives:
update_plan(execution checklist)- collaboration
Planmode (planning-only behavior profile) - durable plan artifact (
.openoxide/plans/*.md)
Do not merge these abstractions.
Mode Contract
Section titled “Mode Contract”Plan mode should enforce:
- read-only tool subset by default
- explicit allowlist for plan artifact path
- structured user-decision tool availability (
request_user_input)
Checklist tool should be disallowed in Plan mode unless explicitly intended.
Tool Contracts
Section titled “Tool Contracts”update_plan:
- typed enum statuses
- optional explanation
- handler-level validation for exactly one
in_progresswhen incomplete - emit typed
PlanUpdateevents
Plan finalization:
- dedicated
PlanDeltastream item for progressive rendering - finalized plan event with markdown payload
Artifact and Persistence
Section titled “Artifact and Persistence”Store plan files under:
<repo>/.openoxide/plans/for VCS-backed sessions- global app data fallback for no-repo sessions
Include session id + timestamp in filename for traceability.
UI and Protocol
Section titled “UI and Protocol”Expose two visually distinct lanes:
- checklist updates (task tracking)
- plan document stream (design artifact)
Support TUI + API consumers with consistent event vocabulary.
Crates
Section titled “Crates”serde/schemarsfor tool schemastokiochannels for mode/event flowratatuifor plan/todo cell renderingpulldown-cmarkfor proposed plan markdown visualizationsqlxorrusqlitefor optional persisted checklist history
Key Design Decisions
Section titled “Key Design Decisions”- Treat planning as runtime state, not only prompt text.
- Enforce mode invariants in code, not conventions.
- Keep checklist and design-spec streams separate end-to-end.
- Make plan exit an explicit protocol step.