docs(chord): decisions: restart-only reload with old work on old code, answers follow their state, app authoring rules

This commit is contained in:
Mario Zechner 2026-11-05 10:31:22 +00:00
parent 7a52c2108d
commit 9ff0ebfd05

View file

@ -2592,6 +2592,43 @@ validation and copies the v2 kernel added around Delta.
targets for it become documented-behaviour tests. Rejected: the kit ordering steps by the direction of the changed
contracts (works only when every change points one way). (2) No grace on consumers' `loading` (a reference with
values reporting `loading` only after ~300 ms): rejected from the restart-only proposal.
- **Reload restarts every affected object; old work finishes on old code (owner, 2026-11-05: "if you think the new
model is good, just ... fix p2"; the final design round: `/tmp/reload-final-brief.md`, three reviewers, model LS3
`/tmp/reload-final-step3-restart-study.md`, accepted by the attacking reviewer `/tmp/reload-final-step3-reload-review-2.md`;
supersedes the pure swap of slice 7b §5, "Swaps: dependents first", parked swaps and the swap handover of routines
and jobs):** the problem: a restart makes new calls wait until the old attempt drained, so any wait of old work on
something that needs the new attempt closed a cycle that only `closeTimeout` broke (a provider's running call
reaching its dependent through `ctx.get` while both restart; re-entry; mutual calls; the same across hosts). The
rules: (1) a reload restarts every live object of a changed plugin (no swap in place); documents and tables stay,
F-a, F-b, F-c of the restart study apply. (2) **Old work** (the calls running at the commit and every call they cause,
found through the call chain in `ctx` on one host and an `old` flag on `call` over trusted links, never honoured from
untrusted ones) runs on the target's open old attempt; a getter of old work for a version the new code dropped binds
the open old attempt or fails at once. (3) The old attempt seals once its own calls and the old work routed to it
drained and its successor's requests (as staging recorded them, bound after every fate of the reload started) are
live or failed for a reason other than another host's version skew, bounded by the reload drain bound
`reloadDrainTimeout` (default 5 s, at most `closeTimeout`); only then the successor's factory runs, so no new
declaration meets old work. (4) **New work** waits at the gate and runs on new code. (5) A same-host consumer whose
version the open old attempt binds sees the object live meanwhile (its new code may read the provider's old state
for up to the drain). (6) A wait of old work that its own open old attempt blocks through parked dependencies or an
opener's gate waits fails at once, `unavailable/reload_cycle`, naming the chain. What remains (waits that leave
Chord: roots opened without the method's `ctx`, Durable tasks, an app's locks) ends at the reload drain bound. The
bound cuts any method still running then (`released`): long work belongs in jobs (in-memory runs end `aborted
{ reload }`, persisted ones resume per `resume`). A restart whose successor finds a failed request is reported
`parked { cause }`. Built as one change with the restart-only removal of the swap.
- **A call's answer follows its state (owner, 2026-11-05: "seems doable without any big downsides"; Petr's report
that p2's UI is not optimistic):** before a host sends a `result` (or forwards one as a relay), it sends the page's
pending `state` for that origin, past the window if the window is full (bounded by the calls in flight). So when a
call resolves on the page, every change the answering host (and a relaying host) committed before answering is
applied there. Pages show optimistic values with React's `useOptimistic` (a helper for `TuiScope`), dropped when the
call settles; Chord changes no replicated state. Not covered: changes committed later (a method that returns before
its effect) or on another host that does not relay the answer. The guide's rule: a method that should feel instant
awaits its own changes and keeps them on the host that answers.
- **App authoring rules confirmed by the p2 audit (owner, 2026-11-05):** state that lives outside Chord (a native's
store, a Durable document) is shown through `self.follow` of its synchronous observer, never copied into a Chord
document by a routine; long work is a job, never a method or a root a method awaits; a root opened inside a method
takes the method's `ctx` (without its cancellation); a session's worker owns its data and pushes what the server's
index shows (title, branches, working state) to the server inside the method that changed it, never pulling from
the server. p2 is rewritten by these rules (audit `/tmp/p2-optimistic-audit.md`).
- **Workers start from the base code the server booted with (owner, 2026-11-04: "let's do this for armin"; Armin's
incident report, Radius artifact 01m6m3xek8fveayqgstayg8w92):** the problem: a worker loads its entry module and the
base code it imports from disk when it is forked, while the server keeps what it loaded at boot. An edit of the