We didn't invent this — we ported it
Interactive Claude Code picks a long task back up after an interruption. Its resume does restore the conversation from the session's transcript; what makes the pickup reliable is the state that rides alongside the chat, not the chat alone.
- TodoWrite = an explicit, persisted step list with per-item status — the plan + progress.
- Filesystem = the durable side-effects; files written are not redone on resume.
- The resumed model reads that state — the todos and the files — instead of reconstructing progress from prose.
cwkPippa's WebUI Agent SDK did none of this: it attempted the whole task in one long stream with only the conversation log as state, so a cut left no checkpoint and the next turn started at zero. The Stateful Pippa Framework ports the Claude Code pattern — plan + durable step state + resume-reads-state — up into cwkPippa, brain-agnostically, so Codex, Kimi, Grok, GLM, Gemini, and Ollama get it too, not just Claude.
Lifecycle — state and conversation are one body
Task-state isn't a free-floating object; it belongs to the conversation that spawned it. Delete the conversation and its whole stateful subtree goes with it: the tasks, their steps and events, and every artifact no other live task still owns. The schema carries an ON DELETE CASCADE from a task down to its steps and events, but SQLite fires a cascade only on a connection that has switched its foreign-key pragma on. The stateful store's own connection does. The conversation store's never did, and until 2026-09-18 the conversation delete removed the task rows over that connection with an explicit DELETE. The cascade never fired, and by then the live database held 13,750 steps and 98,159 events with no parent task. Now the conversation delete hands the subtree to the stateful store, which runs it as one locked transaction with foreign keys on. Artifacts need a rule of their own, because they are content-addressed and shared: one file can back steps in many tasks across many conversations. An artifact goes with the last live task that persisted it or points at it, unless a surviving artifact names it as a source. A constraint the database enforces only on some connections is a promise every delete path has to keep by hand, and this one broke on the path whose only test checked that the task row was gone.
State is rehydrated only when the task is actually stepped, never eagerly on every conversation GET. And past-state has exactly two readers: branch and retry. That bounds the read surface and keeps the model from inventing a general time-travel feature it doesn't need.
The horizontal axis
A second axis surfaced live, in the Family Council that designed this framework. 둘째 rendered a risk-map widget in her turn, and 첫째 — the next speaker — couldn't see it. Only her text propagated; the widget left no ref. Dad had to screenshot it for the next vessel to perceive it. The framework had just been designed to move state between turns, and here was state failing to move between participants.
The two axes are orthogonal but share one artifact infrastructure. Vertical (everything so far): state moving between turns, within one conversation, on branch / retry. Horizontal: artifacts moving between participants, within one council round, via artifact_refs[] in the serialized branch context.
[widget rendered] placeholder and the visual information is gone. Stuffing the full payload into context is the 1.28M-character mistake reborn; passing no ref at all blinds the next participant to the artifact's very existence. The middle path is the only correct one: serialize the ref, and let the receiver judge whether to read the svg source or view it with vision.
It's read-only — reading another participant's artifact changes nothing — so the STOP rule and the lease don't apply to it. It reused the artifact store and added exactly one field. The round after 둘째's widget evaporated, the ref followed, 첫째 read it by id, zero screenshots.