This file tracks the capabilities of @earendil-works/pi-durable (repos/pi/packages/durable) and their
Effect-native counterparts. Tick an item when it is implemented and covered by a test. Design and
milestones are in Analysis and plan.
How to read the table columns:
- Tier:
Core is the first deliverable. Parity comes after core.
- M: the milestone the item lands in (M1–M5 for core;
P for the parity track).
- pi ref: a README section or test file in
repos/pi/packages/durable.
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
Atomic commits: entries, documents, tasks and submissions in one batch |
Core |
M1 |
spec §Storage, session-* |
| [x] |
Single serialized commit line; observers see a change only after storage succeeds |
Core |
M1 |
spec inv. 1–3 |
| [x] |
Uninterruptible once storage has admitted a commit |
Core |
M1 |
spec §Commit |
| [x] |
StorageRejected rolls back; any other storage failure poisons the session |
Core |
M1 |
session-* |
| [x] |
Global ID space; IDs never reused after a committed write |
Core |
M1 |
types |
| [x] |
Storage port + storage conformance suite (testing/StorageConformance.ts) |
Core |
M1 |
/testing registerStorageConformance |
| [x] |
MemoryStorage adapter |
Core |
M1 |
memory-storage |
| [x] |
SqlStorage adapter over any SqlClient (sqlite-node first; NodeSqliteStorage.layer({ filename })) |
Core |
M4 |
sqlite-storage, sqlite-facade |
| [x] |
SQL schema migrations |
Core |
M4 |
sqlite-migrations |
| [x] |
Crash recovery on disk (reopen the same file) |
Core |
M4 |
harness-*-recovery; examples 13, 23, 24, 31 |
| [x] |
JsonlStorage adapter (sidecars, commit markers, torn-line recovery, fsync option) |
Parity |
P |
jsonl-storage |
| [ ] |
StorageLease port (single owner enforced) |
Parity |
P |
— (new) |
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
Root conversation created on first use (Conversation.root); agent choices land with pi.agent in M3 |
Core |
M1 |
Quick Start |
| [x] |
Additional ownerless conversations |
Core |
M1 |
More Conversations |
| [x] |
Custom entry kinds appended in commits |
Core |
M1 |
Concepts |
| [x] |
Built-in entries: user, assistant, system, tool-result, reset, compaction (Entry.Compaction) |
Core |
M3e |
entries.ts |
| [x] |
Context derivation: head marker, edits, dropping failed assistants, tool-result ordering and synthesis (Conversation.context) |
Core |
M3b |
harness-context |
| [x] |
Context edits (omit / replace) |
Core |
M3b |
harness-context |
| [x] |
Conversations owned by a task (subagent children): creation with owner liveness checks (M1), abort and idle semantics (M2) |
Core |
M2 |
Abort and Subagents |
| [ ] |
reset() with an optional handoff note |
Parity |
P |
Reset and Handoff |
| [x] |
Tx.forkConversation / Conversation.fork(id, at): transcript through the entry, state copied by fork policy |
Parity |
M3a |
session-forks |
| Done |
Feature |
Tier |
M |
pi ref |
| [ ] |
Document families (defineDocFamily, keyed members). Storage already supports key |
Parity |
P |
session-documents |
| [x] |
Typed state (State.session / State.conversation / State.task, pi’s defineDoc): Schema-typed, versioned, migrate |
Core |
M1 |
session-documents |
| [x] |
Scopes session / conversation |
Core |
M1 |
Your Own State |
| [x] |
Scope task (retired in the commit that makes the task terminal; kept while it is completing) |
Core |
M2 |
session-documents |
| [x] |
Storage as base + delta (JSON Patch), checkpointWhen |
Core |
M1 |
spec §Documents |
| [x] |
Read a snapshot (State.snapshot) and watch one document (State.watch, collapsing after 100 pending frames) |
Core |
M1 |
session-watches |
| [x] |
Live read-only state (State.live, pi’s Chord documentState) |
Parity |
M3a |
session-states |
| [x] |
conversationCreated hook and init / agent on create and fork |
Core |
M3b |
Your Own State |
| [x] |
history: "rewindable" + State.snapshotAsOf(ref, entryId), through fork ancestry |
Parity |
M3a |
session-checkpoints-migrations |
| [x] |
Fork policies initial / current / asOf (copies run inside storage; a copy accessed in the fork commit becomes one creation) |
Parity |
M3a |
session-forks |
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
Task.make: phases keyed by a Schema-typed checkpoint, plus an abort handler |
Core |
M2 |
harness-tasks |
| [x] |
Advancing only through TaskRuntime.commit; a phase with no durable progress faults |
Core |
M2 |
spec §Tasks |
| [x] |
Recovery: running becomes pending on open; resume dispatches |
Core |
M2 |
harness-tasks-recovery |
| [x] |
Child tasks and the ownership tree |
Core |
M2 |
Child Tasks |
| [x] |
waiting with allSettled / failFast |
Core |
M2 |
24-child-tasks |
| [x] |
completing holds until owned work drains |
Core |
M2 |
spec §Holds |
| [x] |
Bottom-up abort cascade, re-derived on open |
Core |
M2 |
harness-ownership |
| [x] |
Background boundary (background: true, abort({background: true})) |
Core |
M2 |
Abort and Subagents |
| [x] |
Versioning: a newer stored version blocks; migrate runs at reservation; a missing definition blocks |
Core |
M2 |
harness-lifecycle |
| [x] |
Task.await(id) (pi’s waitForTask) |
Core |
M2 |
Compaction example |
| [x] |
TaskDispatcher port + in-process adapter with a concurrency limit |
Core |
M2 |
— (new) |
| [x] |
TaskRegistry (installed task definitions, scoped install, change notification); extensions build on it in M5 |
Core |
M2 |
harness-registry |
| [x] |
Blocked reasons and per-task scheduling state (Scheduler.inspect); the Harness-level inspect joins with M3 |
Parity |
M2 |
harness-inspect |
| [x] |
Task graph view and watch (TaskGraph.get / TaskGraph.watch, JSON Patch frames) |
Parity |
M3e |
harness-task-graph, 24-child-tasks |
| [x] |
Invocation-bound conversation handles (TaskRuntime.conversation, ToolCall.conversation) |
Parity |
M3e |
harness-ownership |
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
Submit input, run generation, get the answer, settle the submission |
Core |
M3c |
Quick Start |
| [x] |
Streaming partials persisted to pi.live (100 ms trailing throttle) |
Core |
M3c |
harness-live-deltas |
| [x] |
Durable retry with backoff (retry{until} survives a restart) |
Core |
M3c |
harness-generation-recovery |
| [x] |
Error classification: retryable / overflow / fatal |
Core |
M3c |
harness-generation |
| [x] |
Thinking level, applied per request by the catalog model’s withThinking (the Anthropic mapping lands with anthropicProfile) |
Core |
M3c |
Per-Conversation Agent |
| [x] |
ModelCatalog: a stored ModelRef resolves to a LanguageModel layer with limits and pricing; FauxModel (a real effect/ai LanguageModel) |
Core |
M3c |
— |
| [ ] |
anthropicProfile (overflow detection, pricing, thinking) |
Core |
M4 |
— |
| [x] |
Stream timeout setting |
Core |
M3c |
Settings |
| [ ] |
Provider fallback (ExecutionPlan) |
Parity |
P |
— (new) |
| [ ] |
OpenAI / OpenRouter (just their @effect/ai-* model(...) + a profile) |
Parity |
P |
— |
| [ ] |
Deferred responses (poll, fetchDeferred, cancelDeferred) |
Parity |
P |
spec §generation |
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
Tools are the harness’s own Tool.make (Schema parameters, offered to effect/ai as Tool.dynamic declarations; see plan 4.7) |
Core |
M3c |
Tools |
| [x] |
Each call is a durable task; intent committed before execute |
Core |
M3c |
harness-tools |
| [x] |
Tool.Idempotent (pi replay: "safe") reruns after a crash; otherwise an interrupted result with partial output |
Core |
M3c |
harness-tools-recovery |
| [ ] |
Streamed output from preliminary results, committed with the adaptive throttle |
Core |
M3 |
harness-output |
| [x] |
ToolCall service (details, commit, conversation, env, taskId) as a tool dependency |
Core |
M3c |
Abort and Subagents |
| [x] |
Result truncation (50 KiB / 2000 lines) and diagnostics block |
Core |
M3c |
harness-output |
| [x] |
Thrown error becomes an error result; unknown tool / invalid args become error results |
Core |
M3c |
harness-tools |
| [x] |
Parallel vs sequential execution (setting and per-tool executionMode) |
Core |
M3c |
Settings |
| [x] |
Tool-reported usage |
Core |
M3c |
Tools |
| [x] |
ExecEnv port + NodeExecEnv (cwd per conversation; process-group kill on interrupt and timeout; output spill) |
Core |
M3d |
env-node, env-node-spill |
| [x] |
Coding tools read, write, edit, bash (CodingTools extension) |
Core |
M3d |
tools, env-truncate |
| [x] |
Per-file serialization of edit / write by env id and canonical path |
Core |
M3d |
tools |
| [x] |
control: { terminate } |
Parity |
M3c |
Tools |
| [ ] |
control: { handoff } |
Parity |
P |
Tools |
| [ ] |
addTools from a tool round |
Parity |
P |
spec §generation |
| [ ] |
Reading images |
Parity |
P |
(unsupported in pi too) |
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
requestId idempotency |
Core |
M3c |
Persist and Resume |
| [x] |
whenBusy: followUp (default), steer, reject → ConversationBusy |
Core |
M3c |
Busy Conversations |
| [x] |
write submissions (append without asking the model) |
Core |
M3c |
harness-submissions |
| [x] |
Withdrawing a queued submission (Submission.abort) |
Core |
M3c |
harness-inbox |
| [x] |
steeringMode / followUpMode "all" |
Core |
M3c |
Busy Conversations |
| [x] |
Reacquire and await a submission by ID after a restart |
Core |
M3c |
Persist and Resume |
| [x] |
Conversation.abort: withdraws queued inputs, aborts the work, resolves when idle |
Core |
M3c |
Abort and Subagents |
| [ ] |
Ingress adapters (at-least-once queue → idempotent submit) |
Parity |
P |
— (new) |
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
pi.agent state + Conversation.configure / Agent.configure (model, thinking, instructions, cwd) |
Core |
M3b |
Per-Conversation Agent |
| [x] |
Settings read at each use (Settings.layer(settings) with getters) |
Core |
M3b |
Settings |
| [x] |
Registry with install / uninstall / in-place replacement (resolution takes the current snapshot; no mid-invocation handover) |
Core |
M3b |
Reload, harness-registry |
| [ ] |
Extension.layer (install on build, uninstall on scope close) |
Core |
M5 |
— (new) |
| [x] |
Extension tools, with later same-name tools overriding earlier ones |
Core |
M3b |
30-tool-override |
| [x] |
Extension tasks |
Core |
M3b |
Extensions |
| [x] |
System prompt sections + instructions |
Core |
M3c |
System Prompt |
| [x] |
pi.system diffing for prompt-cache stability |
Core |
M3c |
harness-prompt |
| [x] |
configure of extension / tool selection (add / remove / exact lists, null resets) |
Core |
M3b |
Per-Conversation Agent |
| [x] |
Generation hooks: beforeRequest, afterResponse, onYield, afterTools |
Parity |
M3c |
Hooks |
| [x] |
Tool hooks: beforeTool (block / rewrite), afterTool |
Parity |
M3c |
Hooks |
| [x] |
Wraps (Extension.wrapTool, Extension.wrapSection) in agent resolution |
Parity |
M3b |
30-tool-override |
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
Manual Conversation.compact(id, instructions?); the task’s result carries the summary’s submission |
Parity |
M3e |
Compaction, 25-compaction |
| [x] |
Automatic blocking compaction above contextWindow - reserveTokens |
Parity |
M3e |
harness-compaction |
| [x] |
Background compaction (backgroundTokens) |
Parity |
M3e |
harness-compaction |
| [x] |
Overflow error leads to compact and retry once |
Parity |
M3e |
25-compaction |
| [x] |
Stale summaries settle as stale |
Parity |
M3e |
spec §compaction |
| [x] |
beforeCompact hook (decline or supply a summary; Hooks.compaction) |
Parity |
M3e |
Compaction |
| [ ] |
Token estimates through the effect/ai Tokenizer (heuristic layer first; M3e estimates with pi’s chars/4 heuristic behind one internal module) |
Core |
M3 |
— |
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
Conversation view stream: entries + docs, per-commit ops (Conversation.view / Conversation.watch, JSON Patch instead of Chord ops) |
Core |
M3d |
Watching a Conversation |
| [x] |
Overflow collapses undelivered view frames and event batches into one snapshot (Session.MaxPendingWatchFrames, default 100), like document watches (M1); no separate ViewBuffer port |
Core |
M3d |
spec §watch |
| [x] |
AgentEvent stream (message_*, tool_execution_*) with snapshot-on-lag (AgentEvent.watch) |
Core |
M3d |
Agent Events |
| [x] |
Late join and reconnect start from the current view |
Core |
M3d |
21-late-join |
| [x] |
Usage and cost per conversation (pi.usage) |
Core |
M3c |
Usage and Cost |
| [ ] |
Session-wide usage total (pi harness.usage()) |
Parity |
P |
Usage and Cost |
| [ ] |
Remote views and control over effect/rpc |
Parity |
P |
— (new) |
| [ ] |
Expose harness tools over MCP (McpServer.registerToolkit) |
Parity |
P |
— (new) |
| Done |
Feature |
Tier |
M |
pi ref |
| [x] |
Foreground subagent tool (replay-safe, owned child) |
Parity |
M3e |
22-subagent-foreground |
| [x] |
Background persistent subagents (spawn, steer, stop, list, report back) |
Parity |
M3e |
23-subagent-background |
| [x] |
Reviewer / plan-mode examples |
Parity |
M3d |
27-plan-mode, 28-reviewer |