pi-mono/apps/p2/docs/durable-request.md
Mario Zechner 05dc8d239d feat: add apps/p2 (imported from pi-private at 7dadff697)
p2 (Pi Mobile on Chord v2) moves from pi-private into pi as one commit. Root scripts that loop all workspaces
use --if-present; npm test, ./test.sh and the release script cover packages/* only (p2: cd apps/p2 && npm test);
sync-versions also updates apps' workspace pins.
2026-11-03 13:39:06 +00:00

3.7 KiB

Request to Durable: share the document transaction core

Superseded: p2 builds its own document store in Chord with a storage interface and adapts Durable's Storage in p2 (target-design.md §9). Only moving OutputBuffer/Progress to Chord is still under consideration.

From the Chord/p2 redesign (target-design.md). To be implemented on pi main (packages/durable, plus packages/chord where noted), then merged into private.

Why

p2 moves object state into transactional documents: drafts that record Chord ops, version plus migrate on read, checkpointWhen, create and retire, one atomic storage batch per commit, adopt and publish afterwards. Durable's Transaction already does exactly this and is battle-tested. p2 must reuse it instead of writing a second implementation. Chord itself must stay free of a Durable dependency; p2 already depends on Durable and builds its object store on top of it.

What we need

  1. A document-only transaction core, exported from Durable (for example @earendil-works/pi-durable/documents):

    • acquire a draft for a document address (kind, scope, optional key), creating it on first write (initial()), loading and migrating it through the host otherwise;
    • retire a document;
    • prepare every draft at settlement (revoking the drafts), plan the storage writes (document.create / document.change / document.retire, base or delta per checkpointWhen), discard on failure or abort;
    • adopt after the storage commit and return the publications (record, version, ops) so the caller can notify;
    • the existing rules: sealing (Transaction has settled), read-after-write, version mismatch handling (a token whose version differs from the cached value reloads; a stored version newer than the token is refused).

    Durable's Transaction then builds on this core and adds tasks, conversations, submissions and forks, so the core is covered by Durable's existing tests. The seam already exists: TransactionHost (session/transaction.ts:99); conversationCreated is its only Durable-specific member.

  2. Definitions usable outside a conversation: defineDoc with scope: "session" already exists; the core needs to accept a session-scoped address with an arbitrary kind and key chosen by p2 (p2 maps an object's identity hash, the state name and the key into them). Confirm whether kind must be globally registered or may be dynamic.

  3. Move OutputBuffer and Progress (harness/output.ts) to Chord (for example @earendil-works/chord/progress); Durable imports them from there and re-exports them under their current names, so nothing breaks.

  4. Review of p2's use of Storage: p2's store calls the core and Storage directly (memory and SQLite backends), without a Session. It must uphold what Session guarantees to Storage: serialized commits (p2 serializes per host), create before change, no change after retire, ids from mintId, deltas against the stored version. We would like the Durable owners to confirm the list is complete.

What p2 does with it (for context)

  • One store per host. Documents are owned by one object identity; the mutation line is per object; storage commits are serialized per host. In-memory documents use the memory backend semantics without persistence; persist: true documents go to the host's SQLite storage.
  • Commits notify local subscribers once and send one atomic state batch per hold on the wire.
  • No eviction: a document's memory copy lives as long as its object; persisted documents stay on disk until retired.

Out of scope

Tasks, conversations, submissions, forks, the harness and the scheduler stay as they are.