Runtime Task Model
When This Page Helps
The most common Stage 3 confusion: two meanings of 'task'. Read between s12 and s13.
Deep Dive -- Best read between s12 and s13. It prevents the most common confusion in Stage 3.
When to Read This
Right after s12 (Task System), before you start s13 (Background Tasks). This note separates two meanings of "task" that beginners frequently collapse into one.
This bridge note resolves one confusion that becomes expensive very quickly:
the task in the work graph is not the same thing as the task that is currently running
How to Read This with the Mainline
This note works best between these documents:
- read
s12-task-system.mdfirst to lock in the durable work graph - then read
s13-background-tasks.mdto see background execution - if the terms begin to blur, you might find it helpful to revisit
glossary.md - if you want the fields to line up exactly, you might find it helpful to revisit
data-structures.mdandentity-map.md
Why This Deserves Its Own Bridge Note
The mainline is still correct:
s12teaches the task systems13teaches background tasks
But without one more bridge layer, you can easily start collapsing two different meanings of "task" into one bucket.
For example:
- a work-graph task such as "implement auth module"
- a background execution such as "run pytest"
- a teammate execution such as "alice is editing files"
All three can be casually called tasks, but they do not live on the same layer.
Two Very Different Kinds of Task
1. Work-graph task
This is the durable node introduced in s12.
It answers:
- what should be done
- which work depends on which other work
- who owns it
- what the progress status is
It is best understood as:
a durable unit of planned work
2. Runtime task
This layer answers:
- what execution unit is alive right now
- what kind of execution it is
- whether it is running, completed, failed, or killed
- where its output lives
It is best understood as:
a live execution slot inside the runtime
The Minimum Mental Model
Treat these as two separate tables:
work-graph task
- durable
- goal and dependency oriented
- longer lifecycle
runtime task
- execution oriented
- output and status oriented
- shorter lifecycle
Their relationship is not "pick one."
It is:
one work-graph task
can spawn
one or more runtime tasks
For example:
work-graph task:
"Implement auth module"
runtime tasks:
1. run tests in the background
2. launch a coder teammate
3. monitor an external service
Why the Distinction Matters
If you do not keep these layers separate, the later chapters start tangling together:
s13background execution blurs into thes12task boards15-s17teammate work has nowhere clean to attachs18worktrees become unclear because you no longer know what layer they belong to
The shortest correct summary is:
work-graph tasks manage goals; runtime tasks manage execution
Core Records
1. WorkGraphTaskRecord
This is the durable task from s12.
task = {
"id": 12,
"subject": "Implement auth module",
"status": "in_progress",
"blockedBy": [],
"blocks": [13],
"owner": "alice",
"worktree": "auth-refactor",
}
2. RuntimeTaskState
A minimal teaching shape can look like this:
runtime_task = {
"id": "b8k2m1qz",
"type": "local_bash",
"status": "running",
"description": "Run pytest",
"start_time": 1710000000.0,
"end_time": None,
"output_file": ".task_outputs/b8k2m1qz.txt",
"notified": False,
}
The key fields are:
type: what execution unit this isstatus: whether it is active or terminaloutput_file: where the result is storednotified: whether the system already surfaced the result
3. RuntimeTaskType
You do not need to implement every type in the teaching repo immediately.
But you should still know that runtime task is a family, not just one shell command type.
A minimal table:
local_bash
local_agent
remote_agent
in_process_teammate
monitor
workflow
Minimum Implementation Steps
Step 1: keep the s12 task board intact
Do not overload it.
Step 2: add a separate runtime task manager
class RuntimeTaskManager:
def __init__(self):
self.tasks = {}
Step 3: create runtime tasks when background work starts
def spawn_bash_task(command: str):
task_id = new_runtime_id()
runtime_tasks[task_id] = {
"id": task_id,
"type": "local_bash",
"status": "running",
"description": command,
}
Step 4: optionally link runtime execution back to the work graph
runtime_tasks[task_id]["work_graph_task_id"] = 12
You do not need that field on day one, but it becomes increasingly important once the system reaches teams and worktrees.
The Picture You Should Hold
Work Graph
task #12: Implement auth module
|
+-- runtime task A: local_bash (pytest)
+-- runtime task B: local_agent (coder worker)
+-- runtime task C: monitor (watch service status)
Runtime Task Layer
A/B/C each have:
- their own runtime ID
- their own status
- their own output
- their own lifecycle
How This Connects to Later Chapters
Once this layer is clear, the rest of the runtime and platform chapters become much easier:
s13background commands are runtime taskss15-s17teammates can also be understood as runtime task variantss18worktrees mostly bind to durable work, but still affect runtime executions19some monitoring or async external work can also land in the runtime layer
Whenever you see "something is alive in the background and advancing work," ask two questions:
- is this a durable goal from the work graph?
- or is this a live execution slot in the runtime?
Common Beginner Mistakes
1. Putting background shell state directly into the task board
That mixes durable task state and runtime execution state.
2. Assuming one work-graph task can only have one runtime task
In real systems, one goal often spawns multiple execution units.
3. Reusing the same status vocabulary for both layers
For example:
- durable tasks:
pending / in_progress / completed - runtime tasks:
running / completed / failed / killed
Those should stay distinct when possible.
4. Ignoring runtime-only fields such as output_file and notified
The durable task board does not care much about them. The runtime layer cares a lot.
Key Takeaway
"Task" means two different things: a durable goal in the work graph (what should be done) and a live execution slot in the runtime (what is running right now). Keep them on separate layers.