plan data types: the TODO data model
The plan module’s types fall into three groups: the state itself
(TodoList → TodoItem), the request/response types for the tool, and the
error type. All state types implement Serialize, so the harness can render
the list to JSON (and mirror it into the context block); the input types
additionally derive Deserialize + JsonSchema and double as the tool
argument schema.
The state model
TodoStatus
#[serde(rename_all = "snake_case")]
pub enum TodoStatus {
Pending,
InProgress,
Completed,
Cancelled,
}
The four states a task can be in, serialized as pending, in_progress,
completed, cancelled. The first two are active, the last two are
terminal. Statuses move exclusively through full-state writes: the model
resends the list with the updated status.
TodoItem
pub struct TodoItem {
pub id: String, // "task-1", "task-2", … assigned sequentially
pub description: String, // free text, never empty
pub status: TodoStatus,
pub depends_on: Vec<String>, // ids of tasks this one depends on
}
IDs are assigned by the tool, not the caller: a write assigns task-1..N in
listed order. depends_on stores the ids of prerequisite tasks.
TodoList
#[derive(Default)]
pub struct TodoList {
pub items: Vec<TodoItem>,
}
The whole state: a flat, ordered list of tasks. This is what Plan owns,
what the free function produces, and what every output embeds.
Request types
The tool’s input is TodoWriteInput:
pub struct TodoWriteInput {
pub todos: Vec<TodoItemInput>,
}
pub struct TodoItemInput {
pub key: Option<String>, // sibling alias, resolved to task-N
pub description: String,
pub status: TodoStatus, // defaults to `pending` when omitted
pub depends_on: Option<Vec<String>>,
}
It exists so the harness can deserialize the tool call arguments straight into typed values. The submitted list is the FULL desired state — see full-state writes.
Output types
TodoWriteOutput
pub struct TodoWriteOutput {
pub list: TodoList, // the FULL new state after the write
pub nags: Vec<Nag>,
}
The whole new list plus any advisory nags. The Plan wrapper replaces its
internal state with output.list automatically; callers of the free
function must do that themselves.
Nag
pub struct Nag {
pub message: String,
}
An advisory message the caller should read and act on. Nags never prevent an operation from succeeding — see nags are advice, errors are rejections on the module page.
PlanError
#[derive(thiserror::Error)]
#[error("{0}")]
pub struct PlanError(pub String);
The single error type for the whole module. It wraps a human-readable
message that doubles as a correction prompt for the model. An Err means
the write was rejected and the list was left unchanged.