Source: ViewEdit

Actor-model agentic UI

Actor-model agentic UI replaces the linear chat assumption with a persisted graph of nodes — each node is an actor with its own chat log, type, disk address, and spawn lineage.

Node and actor mean the same thing here; node is the general graph term, actor matches the concurrency model (mailbox summaries, message routing, no shared mutable transcript).

Problem

Chat UIs assume one thread drifts until the user resets. Agent harnesses break that daily:

  • A coordinator discussion spawns many task nodes (multi-repo runs)

  • A task chat tangents into permissions or CLA policy — that tangent should spawn a discussion child, not pollute the merge log

  • Parallel jobs need a grid; tangents need lineage — same store, different views

Serializing everything produces the wall of repeated 9 of 12 running headers (see screenshot below).

Serialized multi-repo agent updates in one chat scroll

Core claim

Persist the graph on disk. RAM-only actor state loses spawn lineage when the session ends or the model compacts context.

Each node writes:

  • meta.json — type, status, spawnedBy, spawned[], related[], mailbox summary, disk address

  • chat.jsonl — authoritative messages for this node only

Parent and child both record each other’s id before the child accepts traffic. Runtime structs mirror disk 1:1 — no shadow transcript cache.

Node types

Type Role

coordinator

Root orchestrator; continuous top-level voice with summarization

discussion

Exploratory chat; may spawn tasks, discussions, or disambiguation when topic forks

task

Bounded job (repo PR, merge, sync); appears in parallel grid with status dot

disambiguation

Short-lived resolver when parent/child topic boundary is unclear

More subtypes extend the same struct — type is a routing hint, not a separate storage silo.

Spawn on drift

When chat in node A drifts from A’s declared topic:

  1. Harness creates node B with spawnedBy: A

  2. A appends B to spawned[]; both meta.json files flush to disk

  3. A gets a routing marker in its chat; new lines go to B’s chat.jsonl

  4. UI may show B in the grid, graph strip, or fork panel depending on type

Primary trigger: model-proposed spawn — when the user mixes continuation of the current subject with new ideas that deserve their own context.

Secondary: user Spawn tangent, policy (non-task utterance in task node), or disambiguation child when the model cannot resolve the boundary yet.

Metathread

Cross-node audit lives in orchestrator/meta.jsonl:

  • Coordinator utterances and spawn events

  • Line refs into child logs ({ node, line, summary })

  • Agent-generated timeline bookmarks as chat progresses

Interactive typing happens in nodes/{id}/chat.jsonl; the metathread is the spine, not a filtered copy of one mega-scroll.

Graph + views

Node graph with spawn edges, mailboxes, and grid projection

One graph; multiple projections over the same disk store:

  • Cadence separation — main and discussion chat carry coordinator voice and planning; worker agents do not flood the scroll. Workers appear as tiles and status chips; fork a node for the full chat.jsonl transcript.

  • Linear — discussion and coordinator nodes in a sidebar; main thread is discussion only; workers as compact PM status, not transcript lines.

  • Grid — type=task nodes as parallel tiles with job status + summary; separate scroll from main thread; archive bin for completed work.

  • Tree — left-to-right spawn lineage (spawnedBy / spawned); spawn-on-drift without polluting the parent log.

  • Fork panel — selected node’s chat.jsonl; title-history index when the topic renames.

  • Lineage strip — spawnedBy / spawned for the active node.

  • Global coordinator thread — continuous chat with summarization; user can back out to global context from any node.

  • Temporal layout — scrubber spans linear, grid, and tree to rewind activity across parallel nodes (see below).

Hierarchical coordinator mode and global personal-context mode are different views, not different stores. Reset = scope filter (archive / trim to bookmark), not delete disk.

Rebase ownership (canvas → demo / Piles pattern / Hornet runtime): agent-harness-canvas rebase plan.

Wait graph

Default: warn. Subagents declare subscriptions:

  • warn — mailbox may be stale; keep working on independent lines

  • enforce — block until terminal status (merges, releases)

Temporal layout

Parallel nodes do not naturally chapter together. Temporal layout is a view engine — same component, different scope; the live demo exposes a scrubber control across linear, grid, and tree modes:

  • Global timeline — all activity under the chat root

  • Sub-timeline — one node subtree (widget inside node detail)

Features (phased): fade idle nodes (not delete; respect awaitingUser), horizontal scrubber, token-volume strip, heatmap overlay, zoom-dependent keyword bookmarks in a sidebar. Bookmarks are agent-generated as chat progresses.

Optional epoch snapshots (views/epoch-N.json) when the coordinator explicitly reframes a batch — supplements bookmarks; not the primary nav model.

Relationship to Dev-Centr harness

ACTOR_AGENTIC_UI = graph-grid | serialized | off
ACTOR_GRAPH_EPOCHS = on | off
ACTOR_WAIT_GRAPH = warn | enforce | off
ACTOR_NODE_STORE = jsonl-per-node | off

Defaults when graph-grid: ACTOR_NODE_STORE=jsonl-per-node, ACTOR_WAIT_GRAPH=warn.

Reference runtime: dev-centr/hornet (v0 disk store, v1 Mixr + desk, v2 temporal layout).

DevCentr — Actor-model agentic UI owns disk schema and routing.

Demo

Live demo — linear (discussion sidebar + worker status), grid (parallel task tiles), tree (spawn lineage with + child), fork + title index, epochs, temporal scrubber; serialized wall shown as the problem in the lede only.

Renamed from project inbox

Project inbox named only the task-grid layout. This topic names the underlying node graph; the grid is one view. Project inbox (renamed) redirects here.