diff --git a/packages/chord/docs/next/dependencies/book.md b/packages/chord/docs/next/dependencies/book.md index 15665a434..1bafbc4a2 100644 --- a/packages/chord/docs/next/dependencies/book.md +++ b/packages/chord/docs/next/dependencies/book.md @@ -166,22 +166,22 @@ and a reload that removes a type sends `gone`. Both go. A request goes to one place, and the address decides which. For a `local` type, the place is this host. For a `remote` type, the consumer computes the address from the params with its own contract's -placement function (design §3): `Notes { session: "S1" }` is placed on `Session { id: "S1" }`. If that is this host's +`hostAddress` function (design §3; `placement` until 2026-10-29): `Notes { session: "S1" }` is placed on `Session { id: "S1" }`. If that is this host's address, the request goes here; otherwise over the route for that kind, or over the parent link. Today the code checks something else first: whether the type is registered here. A type placed on this host but not registered yet is sent over the parent link, which is wrong twice. Sol's example: the server has address `Server`, -the catalog type is placed on `Server`, and no facet serves it yet. Today's `Placement.reference` sends the server's +the catalog type is placed on `Server`, and no facet serves it yet. Today's `Placement.reference` (`host/placement.ts`) sends the server's own request over a parent link the server never had. And when the catalog plugin is installed later, nothing wakes that request, because it was never registered as waiting here. -:::flow wide | Placement by address. The destination follows from the address alone; only then is the provider found there checked. +:::flow wide | The destination by address. The destination follows from the address alone; only then is the provider found there checked. row "Where" accent -box addr "the request's address" "from the consumer's own placement function" accent +box addr "the request's address" "from the consumer's own `hostAddress`" accent box here "here" "a local type, or this host's address" box away "a route or the parent" "every other address" blue row "Then" muted -box check "check the provider here" "served? fitting version? authorize, own placement" +box check "check the provider here" "served? fitting version? authorize, own host address" box hold "hold over the path" "the provider host checks it the same way" blue arrow addr -> here arrow addr -> away @@ -189,13 +189,14 @@ arrow here -> check arrow away -> hold ::: -The rule, from the reviews (Sol B1, Astra F1): placement never looks at what is registered when the type names an +The rule, from the reviews (Sol B1, Astra F1): the destination never depends on what is registered when the type names an address. A request placed here whose provider is missing is **unbound here**, waiting for the name. A version check compares the consumer with the provider actually selected, never with a same-name registration on the consumer's own -host (today `#assertRoute` does that before placement, so worker W1 serving `Notes` v1 rejects a `Notes` v2 request +host (today `#assertRoute` does that before choosing the destination, so worker W1 serving `Notes` v1 rejects a `Notes` v2 request placed on W2, which serves v2). -Every `remote` type declares its placement (owner, 2026-10-29). Until then `remote()` allowed leaving it out, a +Every `remote` type declares its `hostAddress` (owner, 2026-10-29, who also renamed `placement` to it). Until then +`remote()` allowed leaving it out, a leftover of slice 3a, which built remote objects over one link before host kinds existed; such a type went "here if served, otherwise the parent", which needed its own fallback and a re-placement when a tab that loads before it connects gets its first parent link (Opus, Sol). Opus and Fable proposed making it required; the owner agreed. @@ -203,7 +204,7 @@ connects gets its first parent link (Opus, Sol). Opus and Fable proposed making ## What the check finds Once placed, the provider found there is checked: served, a fitting version (the current one or an accepted older one, -with the fingerprint computed from the provider's own history), `authorize`, and the provider's own placement check. +with the fingerprint computed from the provider's own history), `authorize`, and the provider's own host-address check. The request is then **bound** (to an object here, or to a hold over a path), or **unbound** with a reason: | The check finds | The request | Shows | @@ -211,7 +212,7 @@ The request is then **bound** (to an object here, or to a hold over a path), or | a fitting provider | bound | its object's state | | this host has not published its facets yet | unbound `unpublished` | `loading` | | nobody serves the name at its address | unbound `not_served` | `failed { waiting }` | -| no fitting version, a fingerprint that differs at the same version, a placement the provider computes elsewhere | unbound `version` | `failed { waiting }` | +| no fitting version, a fingerprint that differs at the same version, a host address the provider computes elsewhere | unbound `version` | `failed { waiting }` | | the registry is served, but its first `compose`, or the reader's version mapping, threw | unbound `compose` | `failed { waiting }` | | the provider host is installing, and the hold does not fit its current code | unbound `installing`, at the provider | `loading` | | the path has no link (a drop, a route resolving) | waits on the path | `loading` | @@ -598,9 +599,9 @@ has natives (§21), as for any reload. ## A type that moved -A plugin can move a type from the worker to the server by changing its placement. A consumer's request carries the +A plugin can move a type from the worker to the server by changing its `hostAddress`. A consumer's request carries the address its own code computed, so the provider appearing on the server cannot wake it: the request is not waiting -there. The old host answers `not_served`; the consumer's own reload, which brings the new placement, makes a new +there. The old host answers `not_served`; the consumer's own reload, which brings the new `hostAddress`, makes a new request. The model adds no redirect: moving state between hosts is a deliberate migration, not something Chord guesses. ## Rollouts across hosts @@ -1216,7 +1217,7 @@ scenarios against five drafts; the catalog in the chapters before this one is a | R18, the boundary | "live" means every dependency was live at the end of activation | the dependency check is the boundary: after it, a dependency leaving `live` is treated as after live | | `self.get` of a missing `local` type | throws in the factory: `broken` | unbound `not_served`: `failed { waiting }`, woken by the publication that serves it | | a remote hold the provider cannot serve | refused `broken`; the dependent polls by backoff | refused and kept at the provider, re-run at its publication; `failed { waiting }` | -| design §3, placement | "served here" decides "here"; `placement` optional on `remote()` | the address decides; every remote type declares its placement (owner, 2026-10-29) | +| design §3, the destination | "served here" decides "here"; `placement` optional on `remote()` | the address decides; every remote type declares its `hostAddress`, renamed from `placement` (owner, 2026-10-29) | | the start barrier, `pendingLoad` | holds wait for all of `load` | holds wait only for the publication; the barrier is (a) or (b) | | S2, a registry reader newer than its provider | `broken` | `failed { waiting, version }` | | S3, a registry without a value | `transient`, woken at the first value only | `failed { waiting }`, woken at every transition to usable | @@ -1314,14 +1315,14 @@ A needs W's B, B needs S's C; W's hold for C waits for S's whole `load`, which w code (`host.get`, `host.hold`, `host.registry`), a link's peer (`hold`). **It ends only when its holder ends it** (the attempt ends, the call settles, `release()`, a getter gives up, the peer's link or origin closes, revocation, the holding host closes), never because its provider is missing, went away or does not fit. -- **Placement by address** (round 1): a `local` type goes here; a `remote` type with a placement goes to the address - its placement (computed from the consumer's own contract) gives: here if that is this host's address, whether served +- **The destination by address** (round 1): a `local` type goes here; a `remote` type goes to the address its + `hostAddress` (computed from the consumer's own contract) gives: here if that is this host's address, whether served here or not, else over the route for that kind or the parent (design §3). Every `remote` type declares - its placement (owner, 2026-10-29: the optional placement of slice 3a is removed, and with it the fallback "here if + its `hostAddress` (owner, 2026-10-29: the optional `placement` of slice 3a is removed and the field renamed, and with it the fallback "here if served, otherwise the parent" and its re-placement at the first parent link). A hold arriving for an address that is not this host's is relayed or - refused (`invalid/placement`) as today; a relay never captures a hold because it serves the same name. + refused (`invalid/host_address`) as today; a relay never captures a hold because it serves the same name. - **Then the selected provider is checked** (served, version or accepted version and fingerprint, `authorize`, the - provider's own placement check). Version checks never look at a same-name registration elsewhere. + provider's own host-address check). Version checks never look at a same-name registration elsewhere. - **The publication gate:** a request made on this host, or arriving on its links, before the host's first publication (§6) is not placed yet; it shows `loading` and is placed by the publication. The gate lasts only for the synchronous setups, never for activation, so it cannot deadlock. @@ -1332,7 +1333,7 @@ A needs W's B, B needs S's C; W's hold for C waits for S's whole `load`, which w | a fitting provider | bound (to the object here, or to the hold over a path) | | not published yet | unbound `unpublished`: `loading` | | nobody serves the name at its address | unbound `not_served`: `failed { waiting }` | - | no fitting version, a fingerprint that differs at the same version, a placement the provider computes elsewhere (code skew) | unbound `version`: `failed { waiting }` | + | no fitting version, a fingerprint that differs at the same version, a host address the provider computes elsewhere (code skew) | unbound `version`: `failed { waiting }` | | the registry is served, but its first `compose` (or the reader's version mapping) threw | unbound `compose`: `failed { waiting }` | | the provider host is installing a revision and the hold does not fit its current code | unbound `installing` at the provider: `loading` | | the path has no link (a drop, a route resolving) | on the path: `loading` | @@ -1646,7 +1647,7 @@ ring that never existed, `/tmp/model/sol-probe4.log`). R18 (dependency failures `waiting`, no backoff, the deadline paused while parked, the dependency check as boundary, wakes on every change, an ended dependency breaks), S2 and S3 (`waiting`, woken at every usable value), slice 8's -last-good rule (dropped when a contribution leaves), §3's placement table (by address; the publication gate), §6 (the +last-good rule (dropped when a contribution leaves), §3's destination table (by address; the publication gate), §6 (the simultaneous-cycle gap: (A) one narrowed deadline or (B) the probe), §7 (`host.hold`, getters, calls), §21 (no `gone` on removal; a dropped version unbinds only its requests; installs never await activations; rollout without revision fields; the reload handover), the start barrier (R17, W6), `FailureCause` (+ `waiting`), `loading`/`failed` with the diff --git a/packages/chord/docs/next/dependencies/index.html b/packages/chord/docs/next/dependencies/index.html index 656a4f6fa..81c7ae1fe 100644 --- a/packages/chord/docs/next/dependencies/index.html +++ b/packages/chord/docs/next/dependencies/index.html @@ -314,26 +314,27 @@ and a reload that removes a type sends gone. Both go.

The address decides where

A request goes to one place, and the address decides which. For a local type, the place is this host. For a remote type, the consumer computes the address from the params with its own contract's -placement function (design §3): Notes { session: "S1" } is placed on Session { id: "S1" }. If that is this host's +hostAddress function (design §3; placement until 2026-10-29): Notes { session: "S1" } is placed on Session { id: "S1" }. If that is this host's address, the request goes here; otherwise over the route for that kind, or over the parent link.

Today the code checks something else first: whether the type is registered here. A type placed on this host but not registered yet is sent over the parent link, which is wrong twice. Sol's example: the server has address Server, -the catalog type is placed on Server, and no facet serves it yet. Today's Placement.reference sends the server's +the catalog type is placed on Server, and no facet serves it yet. Today's Placement.reference (host/placement.ts) sends the server's own request over a parent link the server never had. And when the catalog plugin is installed later, nothing wakes that request, because it was never registered as waiting here.

-
WHERETHENthe request's addressfrom the consumer's own placementfunctionherea local type, or this host's addressa route or the parentevery other addresscheck the provider hereserved? fitting version? authorize,own placementhold over the paththe provider host checks it the sameway
Figure 2. Placement by address. The destination follows from the address alone; only then is the provider found there checked.
-

The rule, from the reviews (Sol B1, Astra F1): placement never looks at what is registered when the type names an +

WHERETHENthe request's addressfrom the consumer's own `hostAddress`herea local type, or this host's addressa route or the parentevery other addresscheck the provider hereserved? fitting version? authorize,own host addresshold over the paththe provider host checks it the sameway
Figure 2. The destination by address. The destination follows from the address alone; only then is the provider found there checked.
+

The rule, from the reviews (Sol B1, Astra F1): the destination never depends on what is registered when the type names an address. A request placed here whose provider is missing is unbound here, waiting for the name. A version check compares the consumer with the provider actually selected, never with a same-name registration on the consumer's own -host (today #assertRoute does that before placement, so worker W1 serving Notes v1 rejects a Notes v2 request +host (today #assertRoute does that before choosing the destination, so worker W1 serving Notes v1 rejects a Notes v2 request placed on W2, which serves v2).

-

Every remote type declares its placement (owner, 2026-10-29). Until then remote() allowed leaving it out, a +

Every remote type declares its hostAddress (owner, 2026-10-29, who also renamed placement to it). Until then +remote() allowed leaving it out, a leftover of slice 3a, which built remote objects over one link before host kinds existed; such a type went "here if served, otherwise the parent", which needed its own fallback and a re-placement when a tab that loads before it connects gets its first parent link (Opus, Sol). Opus and Fable proposed making it required; the owner agreed.

What the check finds

Once placed, the provider found there is checked: served, a fitting version (the current one or an accepted older one, -with the fingerprint computed from the provider's own history), authorize, and the provider's own placement check. +with the fingerprint computed from the provider's own history), authorize, and the provider's own host-address check. The request is then bound (to an object here, or to a hold over a path), or unbound with a reason:

@@ -359,7 +360,7 @@ The request is then bound (to an object here, or to a hold over - + @@ -721,9 +722,9 @@ object then; Sol and Astra caught it: the object, its documents, its jobs and it fine. Only the requests on version 1 become unbound version. The object's own plugin reload still restarts it if it has natives (§21), as for any reload.

A type that moved

-

A plugin can move a type from the worker to the server by changing its placement. A consumer's request carries the +

A plugin can move a type from the worker to the server by changing its hostAddress. A consumer's request carries the address its own code computed, so the provider appearing on the server cannot wake it: the request is not waiting -there. The old host answers not_served; the consumer's own reload, which brings the new placement, makes a new +there. The old host answers not_served; the consumer's own reload, which brings the new hostAddress, makes a new request. The model adds no redirect: moving state between hosts is a deliberate migration, not something Chord guesses.

Rollouts across hosts

A rollout installs a revision on the server first, then on the workers, then on the tabs (design §21). Between those @@ -1929,9 +1930,9 @@ owner's decisions.

- + - + @@ -2099,15 +2100,15 @@ code (host.get, host.hold, host.registry) attempt ends, the call settles, release(), a getter gives up, the peer's link or origin closes, revocation, the holding host closes), never because its provider is missing, went away or does not fit.

-
  • Placement by address (round 1): a local type goes here; a remote type with a placement goes to the address -its placement (computed from the consumer's own contract) gives: here if that is this host's address, whether served +

  • The destination by address (round 1): a local type goes here; a remote type goes to the address its +hostAddress (computed from the consumer's own contract) gives: here if that is this host's address, whether served here or not, else over the route for that kind or the parent (design §3). Every remote type declares -its placement (owner, 2026-10-29: the optional placement of slice 3a is removed, and with it the fallback "here if +its hostAddress (owner, 2026-10-29: the optional placement of slice 3a is removed and the field renamed, and with it the fallback "here if served, otherwise the parent" and its re-placement at the first parent link). A hold arriving for an address that is not this host's is relayed or -refused (invalid/placement) as today; a relay never captures a hold because it serves the same name.

    +refused (invalid/host_address) as today; a relay never captures a hold because it serves the same name.

  • Then the selected provider is checked (served, version or accepted version and fingerprint, authorize, the -provider's own placement check). Version checks never look at a same-name registration elsewhere.

    +provider's own host-address check). Version checks never look at a same-name registration elsewhere.

  • The publication gate: a request made on this host, or arriving on its links, before the host's first publication (§6) is not placed yet; it shows loading and is placed by the publication. The gate lasts only for the @@ -2134,7 +2135,7 @@ synchronous setups, never for activation, so it cannot deadlock.

  • - + @@ -2565,7 +2566,7 @@ once it is live; the run count keeps the object as today.

    12. What changes in the design

    R18 (dependency failures waiting, no backoff, the deadline paused while parked, the dependency check as boundary, wakes on every change, an ended dependency breaks), S2 and S3 (waiting, woken at every usable value), slice 8's -last-good rule (dropped when a contribution leaves), §3's placement table (by address; the publication gate), §6 (the +last-good rule (dropped when a contribution leaves), §3's destination table (by address; the publication gate), §6 (the simultaneous-cycle gap: (A) one narrowed deadline or (B) the probe), §7 (host.hold, getters, calls), §21 (no gone on removal; a dropped version unbinds only its requests; installs never await activations; rollout without revision fields; the reload handover), the start barrier (R17, W6), FailureCause (+ waiting), loading/failed with the diff --git a/packages/chord/docs/next/dependencies/model.md b/packages/chord/docs/next/dependencies/model.md index 2b838eb79..bc3d606c8 100644 --- a/packages/chord/docs/next/dependencies/model.md +++ b/packages/chord/docs/next/dependencies/model.md @@ -61,14 +61,14 @@ A needs W's B, B needs S's C; W's hold for C waits for S's whole `load`, which w code (`host.get`, `host.hold`, `host.registry`), a link's peer (`hold`). **It ends only when its holder ends it** (the attempt ends, the call settles, `release()`, a getter gives up, the peer's link or origin closes, revocation, the holding host closes), never because its provider is missing, went away or does not fit. -- **Placement by address** (round 1): a `local` type goes here; a `remote` type with a placement goes to the address - its placement (computed from the consumer's own contract) gives: here if that is this host's address, whether served +- **The destination by address** (round 1): a `local` type goes here; a `remote` type goes to the address its + `hostAddress` (computed from the consumer's own contract) gives: here if that is this host's address, whether served here or not, else over the route for that kind or the parent (design §3). Every `remote` type declares - its placement (owner, 2026-10-29: the optional placement of slice 3a is removed, and with it the fallback "here if + its `hostAddress` (owner, 2026-10-29: the optional `placement` of slice 3a is removed and the field renamed, and with it the fallback "here if served, otherwise the parent" and its re-placement at the first parent link). A hold arriving for an address that is not this host's is relayed or - refused (`invalid/placement`) as today; a relay never captures a hold because it serves the same name. + refused (`invalid/host_address`) as today; a relay never captures a hold because it serves the same name. - **Then the selected provider is checked** (served, version or accepted version and fingerprint, `authorize`, the - provider's own placement check). Version checks never look at a same-name registration elsewhere. + provider's own host-address check). Version checks never look at a same-name registration elsewhere. - **The publication gate:** a request made on this host, or arriving on its links, before the host's first publication (§6) is not placed yet; it shows `loading` and is placed by the publication. The gate lasts only for the synchronous setups, never for activation, so it cannot deadlock. @@ -79,7 +79,7 @@ A needs W's B, B needs S's C; W's hold for C waits for S's whole `load`, which w | a fitting provider | bound (to the object here, or to the hold over a path) | | not published yet | unbound `unpublished`: `loading` | | nobody serves the name at its address | unbound `not_served`: `failed { waiting }` | - | no fitting version, a fingerprint that differs at the same version, a placement the provider computes elsewhere (code skew) | unbound `version`: `failed { waiting }` | + | no fitting version, a fingerprint that differs at the same version, a host address the provider computes elsewhere (code skew) | unbound `version`: `failed { waiting }` | | the registry is served, but its first `compose` (or the reader's version mapping) threw | unbound `compose`: `failed { waiting }` | | the provider host is installing a revision and the hold does not fit its current code | unbound `installing` at the provider: `loading` | | the path has no link (a drop, a route resolving) | on the path: `loading` | @@ -393,7 +393,7 @@ ring that never existed, `/tmp/model/sol-probe4.log`). R18 (dependency failures `waiting`, no backoff, the deadline paused while parked, the dependency check as boundary, wakes on every change, an ended dependency breaks), S2 and S3 (`waiting`, woken at every usable value), slice 8's -last-good rule (dropped when a contribution leaves), §3's placement table (by address; the publication gate), §6 (the +last-good rule (dropped when a contribution leaves), §3's destination table (by address; the publication gate), §6 (the simultaneous-cycle gap: (A) one narrowed deadline or (B) the probe), §7 (`host.hold`, getters, calls), §21 (no `gone` on removal; a dropped version unbinds only its requests; installs never await activations; rollout without revision fields; the reload handover), the start barrier (R17, W6), `FailureCause` (+ `waiting`), `loading`/`failed` with the

    failed { waiting }
    no fitting version, a fingerprint that differs at the same version, a placement the provider computes elsewhereno fitting version, a fingerprint that differs at the same version, a host address the provider computes elsewhere unbound version failed { waiting }
    refused and kept at the provider, re-run at its publication; failed { waiting }
    design §3, placementdesign §3, the destination "served here" decides "here"; placement optional on remote()the address decides; every remote type declares its placement (owner, 2026-10-29)the address decides; every remote type declares its hostAddress, renamed from placement (owner, 2026-10-29)
    the start barrier, pendingLoad unbound not_served: failed { waiting }
    no fitting version, a fingerprint that differs at the same version, a placement the provider computes elsewhere (code skew)no fitting version, a fingerprint that differs at the same version, a host address the provider computes elsewhere (code skew) unbound version: failed { waiting }