docs(events): Phase 9 as built — UO wave 1, and a budgetMs rule the phase found
`EVENTS_PLAN.md` Phase 9 as built, plus the two findings that are contract rather than facts about UO. - **MODULE_API.md §2.4: `budgetMs` must exceed the timeout of whatever the action talks to.** The dispatcher classifies a budget timeout as `retry` unconditionally and cannot ask the action, so an action whose own client gives up after core's deadline never classifies its own failure and `retry: false` is unreachable. The default (10s) is shorter than module-uo's sidecar client (12s), the wrong way round. - **EVENTS.md §F gains "What Phase 9 settled, as the contract's first module registrant"**: the rule above; that `on_failure` is not a retry policy; that `reconcile()` can answer from a stamp rather than a query; and that a module's reconcile trigger must fire on a live signal, never a replay. The plan block records the four org-lead decisions, the live walk, the three defects it found — two of them data loss — and the Phase 8 defect in core it reproduced and deliberately did not fix here. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -114,7 +114,10 @@ api.registerEventLeases([{
|
||||
field:
|
||||
|
||||
- **No shape a failure can take reads as success.** A rejected promise, a throw, a `budgetMs`
|
||||
timeout, a non-object and a missing `ok` are all `{ ok: false, retry: true }`. That is
|
||||
timeout, a non-object and a missing `ok` are all `{ ok: false, retry: true }`. **A module that
|
||||
needs the `retry: false` half of that to be reachable must declare a `budgetMs` longer than its
|
||||
own transport's timeout** — see §2.4's rule, which exists because the first module to register an
|
||||
action did not, and its one non-retryable verb was retried anyway. That is
|
||||
`registerTeamProvider`'s default *inverted*, deliberately: a team provider that refuses leaves core
|
||||
showing what it had, because staleness is cheap, whereas an action that half-ran and was recorded
|
||||
as done is a world change nothing will ever come back for. `retry` is opted OUT of — a module that
|
||||
@@ -1008,6 +1011,15 @@ rather than implementation and belong here:
|
||||
dispatcher: without it a `perform()` awaiting a socket that never answers holds a step's claim
|
||||
until its lease expires, and the reclaim then re-dispatches it, which is how one wedged sidecar
|
||||
becomes an infinite loop rather than a failed step.
|
||||
- **`budgetMs` must EXCEED the timeout of whatever the action talks to** (Events Phase 9). The
|
||||
dispatcher classifies a budget timeout as `retry` unconditionally and does not ask the action —
|
||||
it cannot, the action is still awaiting a socket. So an action whose own client gives up *after*
|
||||
core's deadline never gets to classify its own failure, and `retry: false` in its envelope is
|
||||
unreachable code. The default `budgetMs` is 10s and `module-uo`'s sidecar client waits 12s, which
|
||||
is the wrong way round: every slow shard produced a retry the module had explicitly refused. The
|
||||
rule generalises past that one pairing — an action is the near end of a call with a far end, and
|
||||
the near end has to outlive it. This is why `uo.broadcast`, whose whole safety property is that it
|
||||
is attempted once, declares 15000.
|
||||
- **`once`, on all four.** A batch is a module's complete statement about what it declares; a second
|
||||
call is a module changing its mind halfway through `register()` rather than adding to it. And they
|
||||
STAGE, like every registration above: a module that registers two budgets and then throws has left
|
||||
|
||||
Reference in New Issue
Block a user