docs(chord): slice 8 D2, D3; spec decided

This commit is contained in:
Mario Zechner 2026-10-28 17:20:43 +00:00
parent 9cef0db2d6
commit 07a76f1ef0
3 changed files with 22 additions and 16 deletions

View file

@ -1747,6 +1747,12 @@ validation and copies the v2 kernel added around Delta.
a throwing `compose` (the owner's bug) is reported (`registry_failed`), its watches get a `failed` call, and `.value`
keeps the last good composition (before any, it throws). Derives keep `.value` throwing while failed: state must match
its sources, an extension point must stay available.
- **Slice 8 D2 and D3 (owner, 2026-10-27: "yes"):** D2: a `retryOn` source retries a failed object only on a `value`
call; a `failed` call does nothing. D3: `host.root<T>(fn, context, { principal? })` gives `fn` a handler's call
context (the host's principal or the given one, this host's incarnation, `context`'s telemetry and signal linked
until `fn` settles, `ctx.get`); at settle or abort the context is sealed (`ctx.get` rejects `released`, its references
released, their calls reject), so an uncooperative `fn` fails at its next use of Chord; before `load` its gets park;
after `host.close` it rejects `released`. `chord(fn, context)` is p3's helper. Closes design §26 #25.
- **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

View file

@ -1494,13 +1494,13 @@ export default defineFacet((host, self) => { // `self` (contributions) a
## 17. Registries
```ts
export const Http = defineRegistry<HttpContribution, Router>("chord.http", {
version: 2,
export const Http = defineRegistry<HttpContribution, Router>("chord.http", { version: 2 }); // shared: identity only
host.serve(Http, { // the defining plugin's facet: the one provider (slice 8 D1)
compose: (table, { report }) => buildRouter(table, report), // pure; always delivers a value; reports conflicts
accepts: { 1: (old: HttpContributionV1): HttpContribution => upgrade(old) },
compose: (table, { report }) => buildRouter(table, report), // pure; always delivers a value; reports conflicts
});
self.contribute(Http, { routes: { … }, wraps: [ … ] });
const router = host.registry(Http).value; // live
const router = self.registry(Http).value; // live (host.registry(Http) outside objects)
```
- Each contributor (a facet or an object) contributes **at most one value per registry**; the table is an immutable,
@ -2019,7 +2019,6 @@ this document):
| 14 | project plugins | 21 | own pass over `plugin-loading.md` |
| 16 | a Durable Object profile | 24 | when a DO target becomes concrete |
| 24 | the rollout install timeout (60 s) and a per-type drain override: host options or fixed (the routine handover uses `closeTimeout`; a failing job `finish` backs off 1-60 s and blocks after five failures, decided) | 3 | with slice 7 |
| 25 | `host.root` signature (`root(fn, context, { principal? })` proposed) and what an aborted, uncooperative `fn` means for its references (match the handler and `CallContext` rules) | 3, 24 | slice 8 |
| 26 | how a migration commit reaches consumers (`[["r", value]]` per changed member, or a fresh `bound`; adapted by `accepts` for older consumers) | 21, 22 | slice 7 |
| 28 | Astra's circuit breaker (a hard transport-buffer cap plus a stall deadline); the heartbeat is built, the cap was never decided | 3 | if a real case needs it |
| 29 | round-3 cut C11 (`ctx.trace`); C3 and C4 are decided (§14, §15) | 8 | slice 9 |

View file

@ -1,6 +1,6 @@
# Slice 8: registries and `host.root` (spec)
Status: **draft, open points marked "Decision needed"** (2026-10-27), written by the redesign session. Built in
Status: **decided, ready for the spec reviews** (2026-10-27; D1-D3 and the registry failures decided by the owner), written by the redesign session. Built in
parallel with 6b (mostly new files). The single source for building 8, together with `design.md` (§7, §8, §11, §16,
§17, §18, §24 read in full), the decision log `decisions.md` (R21, R23, R25, Q8, D1 of "Review round 1", "Registries
work like Durable's", "Changing contributions, `self.writable`", W4, "Failures are watch calls", "Slice 8 is
@ -96,19 +96,20 @@ has them; a call of any source while the object is failed (`transient` or `broke
independently of the attempt: a registry's composed value, another object's `writable` reached through a registry,
`constant`; a reference's member is a type error (it would fail with the dependency, R18 handles that).
> **Decision needed (D2): which calls retry.** A registry value's `failed` call (compose threw) is no reason to retry
> the dependent. Proposal: only `value` calls retry; `failed` calls are ignored.
**Which calls retry (D2, owner, 2026-10-27):** only `value` calls: a new value of a source wakes a failed object; a
`failed` call (a throwing `compose`) does nothing, its next good composition retries.
## 4. `host.root(fn, context, options?)`
> **Decision needed (D3): the signature and its guarantees** (design §26 #25). Proposal: `host.root<T>(fn: (ctx: Ctx)
> => Promise<T>, context: Context, options?: { principal?: P }): Promise<T>`: `fn` gets a call context like a
> handler's: the host's principal (or `options.principal`, a trusted same-host caller's choice, as base code acting for
> an authenticated user), this host's incarnation, `context`'s telemetry and abort signal linked until `fn` settles
> (`withLinkedAbortSignal`), and `ctx.get`. When `fn` settles, every reference it took is released and a later
> `ctx.get` throws `released`; an aborted `fn` that does not stop keeps running, but its references are already
> released (as a handler after its caller's cancellation). `host.root` before `load` resolved parks its `ctx.get`s
> like any reference; after `host.close` it rejects `released`. No span of its own until slice 9.
**Signature and guarantees (D3, owner, 2026-10-27; design §26 #25):** `host.root<T>(fn: (ctx: Ctx) => Promise<T>,
context: Context, options?: { principal?: P }): Promise<T>`. `fn` gets a call context like a handler's: the host's
principal (or `options.principal`, a trusted same-host caller's choice, as base code acting for an authenticated
user), this host's incarnation, `context`'s telemetry and abort signal linked until `fn` settles
(`withLinkedAbortSignal`), and `ctx.get`. When `fn` settles or `context` aborts, the call context is sealed:
`ctx.abortSignal` aborted, `ctx.get` rejects `released`, every reference it took released (their calls reject
`released`, their watches end), so an uncooperative `fn` fails at its next use of Chord, as a handler after its caller
cancelled; work outside Chord runs on until then. `ctx.get` before `load` resolved parks like any reference;
`host.root` after `host.close` rejects `released`. No span of its own until slice 9.
- `chord(fn, context)` is p3's helper over `host.root` (design §24); Chord ships none.