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
+`"
"`, 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: