Persisting agent work as a node graph
Linear chat lies about parallel agent work. Persist a node graph on disk; spawn children when topic forks; parent and child record each other's address.
Agent harnesses run many jobs at once, but the product still looks like one scrolling chat. When topics drift, the right move is to persist a node graph — spawn a child context on disk — not to pretend every line still belongs to one thread.
You connect every org to Cursor. The bot reports 9 of 12 running — then reports it again. And again. A Done card for Merge docs#3 slides past, duplicated. A paragraph about Bitwarden permissions you already read twice sits between two unrelated repo updates.
That is not one conversation. It is many actors pretending to be one scroll.

Linear chat is the wrong primitive
Chat products assume a single thread drifts until the user resets. Agent harnesses violate that constantly:
- A coordinator discussion spawns twelve task nodes across repos
- A task chat wanders into CLA policy — that tangent should spawn a discussion child, not pollute the merge transcript
- A discussion inside a task spawns more tasks when the user says “also do these three forks”
We need a graph, not a longer scroll.
Node = actor
A node is an actor: bounded chat context with:
- A type (
coordinator,discussion,task, …) - A disk address (
$CHAT_ROOT/nodes/{id}/) - Spawn lineage — parent lists children in
spawned[]; child recordsspawnedBy - A mailbox summary coworkers read without loading the full transcript
Same thing, two words: node for the graph; actor for the concurrency model.
Spawn on drift
When chat in node A drifts from A’s topic, the harness creates node B:
- Write B’s
meta.jsonwithspawnedBy: A - Append B to A’s
spawned[]; flush A’smeta.json - Only then accept messages on B’s
chat.jsonl - A gets a routing marker; new lines belong to B
Parent knows child’s name. Child knows parent’s address. Both on disk — not a session-only fork in RAM.
Trigger: model proposes spawn, user clicks Spawn tangent, or policy (non-task speech inside a task node).
The grid is a view, not the model
Parallel repo jobs still deserve a task grid — status dot, summary, click for fork chat. That grid is a filter over the graph (type=task), not the whole story.
Discussion nodes may never appear in the grid but still matter: they are where coordination happened before tasks existed, or where a CLA tangent became its own context instead of noise.
Epochs when coherence breaks
When the mission reframes — CLA clears, batch phase changes, user says “forget the last plan” — start a new epoch. Freeze the grid layout for that epoch. Prior epochs stay inspectable.
This replaces the earlier “chapter” language; epochs name the same immutability without implying email.
Disk is non-negotiable
Yes, every spawn hits the filesystem. graph.json, per-node meta.json, per-node chat.jsonl, mailbox summaries/latest.json. I/O cost beats losing lineage when the session ends or the model compacts context.
DevCentr’s harness documents the schema and ACTOR_AGENTIC_UI variables. HCI Nerdz owns the surface — grid, lineage strip, spawn affordances.
Grounded tokens inside nodes
Grounded tokens mark how a span was produced inside a message.
Actor-model UI marks which node owns the message across the graph.
Use both.
Where to go next
- Demo — Actor-model agentic UI desk
- Docs — Actor-model agentic UI
- Harness — DevCentr — Actor-model agentic UI
- Prior essay — Seeing parallel agents as a task grid (grid symptom; this essay names the graph)