From 66330961bdc289f4cce4ecb0ee03dc0b2c905868 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 28 Oct 2026 11:41:48 +0000 Subject: [PATCH] docs(chord): slice 6b D5; Harness.open in the examples --- packages/chord/docs/next/decisions.md | 10 ++++++++++ packages/chord/docs/next/design.md | 4 ++-- packages/chord/docs/next/slice-6b.md | 22 ++++++++++++---------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/chord/docs/next/decisions.md b/packages/chord/docs/next/decisions.md index 0e929cda3..c72a6d3b0 100644 --- a/packages/chord/docs/next/decisions.md +++ b/packages/chord/docs/next/decisions.md @@ -1659,6 +1659,16 @@ validation and copies the v2 kernel added around Delta. step refused fails live (`object_failed`, `broken`: memory and disk disagree); an `int` `CHECK` failure (a plugin's value out of range) fails only that transaction (`invalid/args`). `StoreStorage` runs Durable's commit checks in the step, where `SqliteStorage` runs them: no special case. Built in 6b (the store's step and object failure). +- **Slice 6b D5: persisted run records are documents of their object; one harness per store (owner, 2026-10-27: + "sure"):** each unsettled run's record is a keyed persisted document of its object in the engine's namespace + `chord.jobs` (owner the object's owner string, kind the job name, key the run id); Chord sets that namespace, a plugin + never does, so nothing collides; the state document stays an ordinary document of the run; `finish` deletes the + record and retires the state (Q9), so only unsettled runs are stored and in memory; activation loads them through the + engine's key listing and a load per run (no new engine read). Durable's tables are named under the type name + `pi.durable` (`"session:pi.durable:entries"`; type names are unique per app). One `StoreStorage` per store: a second + one on the same store is refused (`invalid/args`), as one host per store (rule 7). Rejected on the way: a table per + job, a store-wide runs table, `$`-marked internal names (owner: "if our abstractions can't cleanly support this + without $ magic, it's a bad abstraction"). - **The performance and memory bar, and converging reviews (owner, 2026-10-27: "yes"):** supersedes "as good as HEAD in everything" for Chord v2 slices and pi changes. CPU: each hot path (calls, changes, watches, links) within about 5% of HEAD, measured in bursts with alternating processes pinned to one CPU; worse is fixed or goes to the diff --git a/packages/chord/docs/next/design.md b/packages/chord/docs/next/design.md index cef6bc3f1..0e9f439f4 100644 --- a/packages/chord/docs/next/design.md +++ b/packages/chord/docs/next/design.md @@ -1923,12 +1923,12 @@ loop. Chord has no stream type (it would need its own rules for drops, cancellat access and execution environments from registry entries, looked up at each use: ```ts - const harness = self.open(async () => Durable.open(storage, { + const harness = self.open(async (context) => Harness.open(new StoreStorage(store), { // the host's store (slice 6b) models: () => host.registry(HarnessModels).value, env: (target) => host.registry(HarnessEnvironments).value(target.cwd), settings: () => currentSettings(), registry: durableRegistry(host.registry(HarnessExtensions)), // a RegistryReader over the composed NativeWatchable - }), (h) => h.close()); + }, context), (h, _reason, context) => h.close(context)); ``` Durable's `models` and `settings` accept functions (pi `7ec1f648e`). Durable opens with scheduling paused; base code diff --git a/packages/chord/docs/next/slice-6b.md b/packages/chord/docs/next/slice-6b.md index aa874e71f..4a87d1f19 100644 --- a/packages/chord/docs/next/slice-6b.md +++ b/packages/chord/docs/next/slice-6b.md @@ -25,8 +25,8 @@ const worker = createHost

({ address: Session({ id }), principal, store, onErr // the core facet: the harness object opens Durable on the same store host.serve(Harness, (_, self) => { - const durable = self.open(async (context) => - Durable.open(storeStorage(store, { owner: "session:harness" }), { …, paused: true }, context), + const harness = self.open(async (context) => + Harness.open(new StoreStorage(store), { models, registry, … }, context), // one StoreStorage per store (h, _reason, context) => h.close(context)); const view = self.follow(...) // Decision needed (D9): name and shape return { … }; @@ -57,8 +57,9 @@ Chord imports nothing of Durable (S13); Durable's conformance suite runs in Dura Durable's four record tables (conversations, entries, tasks, submissions) and its ID namespace. **Layout (D2, owner, 2026-10-27: "the whole purpose of this slice"):** declared tables of the store, written as -ordinary `put` writes: `defineTable`s that `StoreStorage` declares for itself (SQL names `":"`, e.g. -`"session:harness:entries"`), each row the record's indexed columns (`id`, `conversation_id`, `kind`, `status`, …) plus +ordinary `put` writes: `defineTable`s that `StoreStorage` declares for itself under the type name `pi.durable` (SQL names +`":pi.durable:
"`, e.g. `"session:pi.durable:entries"`; type names are unique per app, so no plugin +table can meet them), each row the record's indexed columns (`id`, `conversation_id`, `kind`, `status`, …) plus the whole record in a `json(...)` column. The outbox, export, import and the applier handle them as every table; Durable's layout changes later through `evolveTable`. The partial head index becomes an index on `(conversation_id, has_head, id)` (index columns are non-`opt`); indexed strings that must stay lossless (`kind`, `requestId`, document @@ -154,12 +155,13 @@ abort request is persisted; a restart limit; the job fingerprint stored with eve - **Where records live:** -> **Decision needed (D5).** (a) **Documents of the object:** each run's record is a keyed persisted document of the -> object (an internal definition per job, key the run id: `{ version, fingerprint, args, key?, phase, outcome?, -> abort?, restarts, trace? }`), its state document a second keyed document; both written in the transactions that -> already change them (start, state change, outcome, finish, block, drop), loaded at the object's first activation -> (all unsettled runs: `running` needs every one). (b) A table per job. Proposal: (a): the records are few, change -> in the same transactions as today, and reuse persisted documents (versions, checkpoints, export). +**Where records live (D5, owner, 2026-10-27):** each unsettled run's record is a keyed persisted document of its +object in the engine's namespace `chord.jobs` (owner the object's owner string, kind the job name, key the run id: +`{ version, fingerprint, args, key?, phase, outcome?, abort?, restarts, trace? }`); Chord sets that namespace, a plugin +never does (its documents are always `chord`), so nothing can collide. Its state document stays an ordinary document +of the run. Both change in the transactions that change them today (start, state change, outcome, finish, block, +drop); `finish` deletes the record and retires the state in its commit (Q9), so only unsettled runs are stored. At the +object's first activation the records are loaded through the engine's key listing and a load per run (no new read). - **Resume at activation:** a run recorded `finishing` resumes `finish` only; a run recorded `running` was cut off by a crash: `rerun` invokes `run` again with the stored args and state, `abandon` records `failed { aborted, reason: