Mission
Implement MindRail's first durable persistence boundary and Cloudflare reference mapping on branch feature/persistence-v0-1.
Base SHA: 87fbfcf2ed738071f648a3b94bfff1df1f235dba.
Before editing read AGENTS.md, ADR-0001/0003/0004/0005, docs/architecture/02_CLOUDFLARE_RUNTIME_PERSISTENCE.md, Domain Contracts, current runtime, roadmap, security docs, and docs/CURRENT_STATE.md. Current repository state wins over this issue.
Goal
Move toward a runtime that can survive process restart without making Cloudflare part of canonical domain/protocol semantics.
Implement vendor-neutral persistence ports first, then a Cloudflare D1 + Workspace Durable Object reference adapter consistent with the accepted persistence design.
Do not replace the in-memory control plane wholesale or duplicate lifecycle authority. Persistence must implement/serve the accepted runtime semantics, not define new ones.
Required storage model
Durable truth must cover at least the currently implemented/accepted concepts needed by v0.1:
- Workspace
- Goal
- Task
- Agent
- Session
- Lease
- Checkpoint
- PermissionRequest / PermissionDecision storage hooks (even if policy PR lands later, design ports so these records fit without redesign)
- AuditEvent
- per-Task fencing counter / allocation state
- idempotency command receipt
Command receipt invariant
The unique logical key is exactly:
(workspaceId, commandId)
Persist atomically with an admitted mutation:
- command discriminator;
- canonical semantic request fingerprint;
- immutable bounded terminal result/error snapshot.
Do not key by operation + idempotency key. Do not persist only a reference to mutable current state for replay.
Pre-admission structural/auth failures must not reserve a receipt.
Correctness requirements
Preserve ADR-0004/0005 semantics:
- optimistic revisions;
- exactly one effective active Lease per Task;
- monotonic fencing on every new ownership grant;
- stale fence cannot mutate authoritative state;
- running Task can outlive Session/Lease and be recovered;
- Goal-level ordering prevents
CreateTask racing automatic Goal success/cancel into inconsistency;
- same-Session duplicate claim returns current Lease without new fence;
- session/lease expiry uses authoritative server time;
- canonical domain admission occurs before durable commit;
- append-only records remain append-only;
- no full event sourcing.
Ports
Create small explicit persistence interfaces under a new owned module (src/persistence/* or equivalent). Prefer transaction-oriented semantic methods over generic put(table, json) / arbitrary CRUD abstractions.
The core layer must not import Cloudflare D1/DO types.
Where an atomic operation spans multiple records, expose a transaction boundary that can correctly implement the semantic unit rather than pretending independent writes are safe.
D1 / Durable Objects reference
Follow the accepted architecture: Workspace Durable Object coordinates Workspace-scoped mutation ordering; D1 is durable canonical operational storage. Durable Object memory is never the only source of truth and restart must not lose authority state.
Implement the smallest reference adapter that proves:
- schema/migrations and required indexes;
- command receipt uniqueness;
- atomic revision update guards;
- atomic Lease/fence allocation;
- Workspace/Goal serialization boundary needed for Goal-level operations;
- restart/read-back reconstruction;
- lease/session expiry recovery queries;
- pending human permission query support;
- append-only checkpoint/audit writes.
Avoid queues/sharding/custom consensus unless an executed correctness test proves they are required.
SQL/index expectations
At minimum support efficient reads for:
- Tasks by Goal;
- claimable/recoverable Tasks;
- active/effective Lease by Task;
- Leases by expiry;
- Sessions by liveness/lastSeenAt where required;
- permission requests awaiting human decision;
- audit events by Workspace/time;
- command receipts by
(workspace_id, command_id).
Use bounded serialized snapshots only where required by protocol replay; do not add unrestricted metadata JSON bags.
Testing
Mandatory TDD. Prefer deterministic adapter/contract tests that can run in repository CI. If real Cloudflare runtime tests cannot execute in available CI, label them explicitly unexecuted and provide runnable test harness/config; never call them PASS.
At minimum prove with executed tests:
- state survives adapter re-instantiation/restart simulation;
- duplicate admitted command returns stored immutable response and does not duplicate mutation;
- same commandId/different fingerprint conflicts;
- two competing claims cannot both obtain effective ownership;
- fencing strictly increases after release/expiry/session-loss recovery;
- stale expected revision update loses deterministically;
- stale fence cannot checkpoint/complete;
- Task/Lease/receipt commit is atomic under injected failure where applicable;
- Goal completion and concurrent Task creation serialize consistently;
- cross-workspace references fail;
- canonical-invalid records are never durably written;
- checkpoint/audit append order and immutability are preserved.
Run fresh:
pnpm install --frozen-lockfile
pnpm check
pnpm test:coverage
- any additional persistence integration tests that actually execute;
- permanent
Quality on final PR head.
Scope boundaries
Do NOT:
- change canonical JSON schemas to fit D1;
- leak Cloudflare types into domain/protocol packages;
- introduce event sourcing;
- introduce a generic repository/ORM abstraction that erases semantic concurrency boundaries;
- implement HTTP/MCP;
- implement Permission policy evaluation;
- implement GitHub adapter;
- add auth/billing/account/team concepts;
- claim production Cloudflare verification unless it actually ran against the relevant runtime.
Ownership / parallel safety
Primary ownership:
- new
src/persistence/* / Cloudflare adapter modules;
- migrations/SQL/config specific to persistence;
- persistence tests;
- narrowly related docs.
Avoid large edits to src/runtime/in-memory-control-plane.ts and src/runtime/protocol*.ts; Runtime Surface is changing them in parallel. If a small integration seam is required, isolate it and document expected integration conflict rather than refactoring the runtime.
Definition of done
Open a non-draft PR from feature/persistence-v0-1 to main with:
- exact storage/transaction model;
- migrations;
- executed adapter/contract tests;
- explicit Cloudflare verification status;
- final Quality run;
- failure/restart limitations;
- no temporary workflow files.
Do not merge it yourself unless acting as integration owner.
Mission
Implement MindRail's first durable persistence boundary and Cloudflare reference mapping on branch
feature/persistence-v0-1.Base SHA:
87fbfcf2ed738071f648a3b94bfff1df1f235dba.Before editing read
AGENTS.md, ADR-0001/0003/0004/0005,docs/architecture/02_CLOUDFLARE_RUNTIME_PERSISTENCE.md, Domain Contracts, current runtime, roadmap, security docs, anddocs/CURRENT_STATE.md. Current repository state wins over this issue.Goal
Move toward a runtime that can survive process restart without making Cloudflare part of canonical domain/protocol semantics.
Implement vendor-neutral persistence ports first, then a Cloudflare D1 + Workspace Durable Object reference adapter consistent with the accepted persistence design.
Do not replace the in-memory control plane wholesale or duplicate lifecycle authority. Persistence must implement/serve the accepted runtime semantics, not define new ones.
Required storage model
Durable truth must cover at least the currently implemented/accepted concepts needed by v0.1:
Command receipt invariant
The unique logical key is exactly:
(workspaceId, commandId)Persist atomically with an admitted mutation:
Do not key by operation + idempotency key. Do not persist only a reference to mutable current state for replay.
Pre-admission structural/auth failures must not reserve a receipt.
Correctness requirements
Preserve ADR-0004/0005 semantics:
CreateTaskracing automatic Goal success/cancel into inconsistency;Ports
Create small explicit persistence interfaces under a new owned module (
src/persistence/*or equivalent). Prefer transaction-oriented semantic methods over genericput(table, json)/ arbitrary CRUD abstractions.The core layer must not import Cloudflare D1/DO types.
Where an atomic operation spans multiple records, expose a transaction boundary that can correctly implement the semantic unit rather than pretending independent writes are safe.
D1 / Durable Objects reference
Follow the accepted architecture: Workspace Durable Object coordinates Workspace-scoped mutation ordering; D1 is durable canonical operational storage. Durable Object memory is never the only source of truth and restart must not lose authority state.
Implement the smallest reference adapter that proves:
Avoid queues/sharding/custom consensus unless an executed correctness test proves they are required.
SQL/index expectations
At minimum support efficient reads for:
(workspace_id, command_id).Use bounded serialized snapshots only where required by protocol replay; do not add unrestricted metadata JSON bags.
Testing
Mandatory TDD. Prefer deterministic adapter/contract tests that can run in repository CI. If real Cloudflare runtime tests cannot execute in available CI, label them explicitly unexecuted and provide runnable test harness/config; never call them PASS.
At minimum prove with executed tests:
Run fresh:
pnpm install --frozen-lockfilepnpm checkpnpm test:coverageQualityon final PR head.Scope boundaries
Do NOT:
Ownership / parallel safety
Primary ownership:
src/persistence/*/ Cloudflare adapter modules;Avoid large edits to
src/runtime/in-memory-control-plane.tsandsrc/runtime/protocol*.ts; Runtime Surface is changing them in parallel. If a small integration seam is required, isolate it and document expected integration conflict rather than refactoring the runtime.Definition of done
Open a non-draft PR from
feature/persistence-v0-1tomainwith:Do not merge it yourself unless acting as integration owner.