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:
@@ -848,6 +848,37 @@ api.registerEventLeases([{
|
||||
in them, so `core.options.legs` costs nothing new, and it means the seam's first exercise is not a
|
||||
module's.
|
||||
|
||||
### What Phase 9 settled, as the contract's first module registrant
|
||||
|
||||
Wave 1 — `uo.broadcast`, `uo.towncrier.post`, `uo.news.post` — is the first time anything but core
|
||||
registered an action, and three of the four things it found are rules rather than facts about UO.
|
||||
|
||||
- **`budgetMs` must EXCEED the timeout of whatever the action talks to.** 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. The default
|
||||
`budgetMs` is 10s and `module-uo`'s sidecar client waits 12s, the wrong way round, and every slow
|
||||
shard produced a retry the module had explicitly refused. Now a rule in `MODULE_API.md` §2.4.
|
||||
- **There is no per-action "do not retry me", and `on_failure` is not one.** `on_failure` is what
|
||||
happens AFTER `EVENT_STEP_MAX_ATTEMPTS`, so `skip` — already the `notify` default — does not stop a
|
||||
broadcast going out three times. The lever is the failure envelope, and an action that must be
|
||||
attempted once answers `retry: false` to everything. That is a heavier thing to ask of a
|
||||
declaration than the plan assumed, and it is the reason the rule above is load-bearing rather than
|
||||
a tuning note.
|
||||
- **`reconcile()` need not be a query.** A module that knows *when* its resources are lost can answer
|
||||
from a stamp instead of a round trip: wave 1 records the shard's `bootId` in the resource payload
|
||||
and reports in force exactly the rows whose stamp still matches. That is correct for the module's
|
||||
own trigger AND for core's boot sweep — where the game may not have restarted at all, and where
|
||||
answering "all gone" would abandon live rows. A row with no stamp is reported IN FORCE, because
|
||||
"I do not know" must never read as "it is gone", which is `reconcileModule`'s own posture stated
|
||||
from the module's side.
|
||||
- **A module's `reconcile()` trigger must fire on a LIVE signal, never a replay.** §L says the module
|
||||
says when; it did not say that a module's event feed may replay history. `module-uo`'s sidecar
|
||||
reconnect backfills the last several `server.hello` frames, each with a different `bootId`, so every
|
||||
replayed frame reads as a restart — and an intermediate one compares a resource stamped with the
|
||||
current boot against a boot that ended hours ago and orphans it. A live resource core will never
|
||||
take back, lost to a reconnect. Any module wiring this seam owes the same gate.
|
||||
|
||||
### What is contract rather than implementation
|
||||
|
||||
**Two members of the success envelope mean "succeeded, but not finished"** (org lead, 2026-09-02).
|
||||
|
||||
Reference in New Issue
Block a user