Summary
I would like to propose adding task and plan lifecycle hooks to Codex, together with a small external plan update API for harness-driven workflows.
I already have a working prototype in my fork, but I read docs/contributing.md and saw that external code contributions are by invitation only. So I am opening this issue first to check whether this direction fits the project before asking for PR review.
Fork PR:
ashione#1
Upstream compare:
main...ashione:feat/task-plan-lifecycle-hooks
Motivation
Codex has hooks for tools and session events, but there is not currently a hook surface for the higher-level lifecycle that external harnesses usually care about:
- a task/turn has started
- a task/turn has completed
- a checklist plan was created
- a checklist plan changed
- a checklist plan reached completion
For human users, the checklist is visible progress. For an external harness, it can also be a machine-readable progress contract. Without lifecycle hooks or a bounded plan update API, a harness has to keep a separate out-of-band plan, inject synthetic text, or ask the model to rewrite plan state even when the change came from deterministic external state.
The use case I am trying to support is an orchestrator around Codex that can:
- observe when a real task starts and finishes
- capture task completion metadata
- observe checklist creation/update/completion
- append or update active checklist items from outside the model loop
- avoid rewriting completed checklist history
Proposed Events
The prototype adds these command-hook events:
TaskCreated
TaskCompleted
PlanCreated
PlanUpdated
PlanCompleted
Task hook matchers use task_kind:
Plan hook matchers use plan_source:
update_plan
external
proposed_plan
TaskCompleted includes metadata such as the last agent message, completion timestamp, duration, and time to first token.
Plan hook payloads include the current plan, previous plan, explanation, and status counts. For Plan Mode proposed plans, the proposal text is exposed as plan_text, and it is intentionally not written into the checklist snapshot.
External Harness-Driven Plan Updates
The prototype adds an app-server JSON-RPC method:
The API lets an external client patch the active turn checklist in a restricted way. It requires the active turn id to match expectedTurnId, and supports two operations:
{ type: "append", step: string, status?: "pending" | "in_progress" | "completed" }
{ type: "update", index: number, step?: string, status?: "pending" | "in_progress" | "completed" }
The important constraint is that completed checklist items cannot be modified. My intent is to treat completed steps as historical facts, while still letting an external harness add new work or adjust open items.
A successful external update reuses the normal plan update notification path and emits plan lifecycle hooks with plan_source = "external".
Behavior Boundaries
The prototype keeps this intentionally narrow:
- lifecycle hooks are command hooks only
- hook failures are reported but do not block task completion or plan updates
- external plan updates only affect the current active turn
- external updates are patch-based, not full snapshot replacement
- Plan Mode proposals remain text proposals and do not become checklist state
If blocking policy is needed later, I think it should probably be a separate pre-update hook such as PrePlanUpdate, rather than overloading these lifecycle notifications.
Prototype Status
The fork branch wires this through hook config, hook discovery/listing, app-server schema generation, task/plan lifecycle emission, turn-scoped plan state, and TUI hook labels.
I also did local validation, including hook tests, core plan-update tests, schema fixture checks, a release codex-cli build, and CLI smoke tests confirming that the new command hooks fire with the expected payloads.
I did hit some local environment limitations: just was not installed on my machine, and a full core lib run hit an unrelated existing stack overflow test. The release CLI build succeeded and was used for the hook verification.
Questions
Would the Codex team be open to this lifecycle hook surface and external harness-driven plan update API?
If the direction seems reasonable, I would appreciate guidance on:
- event naming
- payload shape
- matcher semantics
- whether
turn/plan_update should stay app-server v2 only
- whether the external patch constraints are strict enough
- whether non-blocking lifecycle hooks are the right v1 behavior
Summary
I would like to propose adding task and plan lifecycle hooks to Codex, together with a small external plan update API for harness-driven workflows.
I already have a working prototype in my fork, but I read
docs/contributing.mdand saw that external code contributions are by invitation only. So I am opening this issue first to check whether this direction fits the project before asking for PR review.Fork PR:
ashione#1
Upstream compare:
main...ashione:feat/task-plan-lifecycle-hooks
Motivation
Codex has hooks for tools and session events, but there is not currently a hook surface for the higher-level lifecycle that external harnesses usually care about:
For human users, the checklist is visible progress. For an external harness, it can also be a machine-readable progress contract. Without lifecycle hooks or a bounded plan update API, a harness has to keep a separate out-of-band plan, inject synthetic text, or ask the model to rewrite plan state even when the change came from deterministic external state.
The use case I am trying to support is an orchestrator around Codex that can:
Proposed Events
The prototype adds these command-hook events:
TaskCreatedTaskCompletedPlanCreatedPlanUpdatedPlanCompletedTask hook matchers use
task_kind:regularreviewcompactPlan hook matchers use
plan_source:update_planexternalproposed_planTaskCompletedincludes metadata such as the last agent message, completion timestamp, duration, and time to first token.Plan hook payloads include the current plan, previous plan, explanation, and status counts. For Plan Mode proposed plans, the proposal text is exposed as
plan_text, and it is intentionally not written into the checklist snapshot.External Harness-Driven Plan Updates
The prototype adds an app-server JSON-RPC method:
The API lets an external client patch the active turn checklist in a restricted way. It requires the active turn id to match
expectedTurnId, and supports two operations:The important constraint is that completed checklist items cannot be modified. My intent is to treat completed steps as historical facts, while still letting an external harness add new work or adjust open items.
A successful external update reuses the normal plan update notification path and emits plan lifecycle hooks with
plan_source = "external".Behavior Boundaries
The prototype keeps this intentionally narrow:
If blocking policy is needed later, I think it should probably be a separate pre-update hook such as
PrePlanUpdate, rather than overloading these lifecycle notifications.Prototype Status
The fork branch wires this through hook config, hook discovery/listing, app-server schema generation, task/plan lifecycle emission, turn-scoped plan state, and TUI hook labels.
I also did local validation, including hook tests, core plan-update tests, schema fixture checks, a release
codex-clibuild, and CLI smoke tests confirming that the new command hooks fire with the expected payloads.I did hit some local environment limitations:
justwas not installed on my machine, and a full core lib run hit an unrelated existing stack overflow test. The release CLI build succeeded and was used for the hook verification.Questions
Would the Codex team be open to this lifecycle hook surface and external harness-driven plan update API?
If the direction seems reasonable, I would appreciate guidance on:
turn/plan_updateshould stay app-server v2 only