docs(events): the Event System record — the cutover step 16b missed (edge → main)
#232
@@ -1110,6 +1110,46 @@ a second module's author will get wrong are the envelope's failure default, the
|
||||
passthrough, recording a resource *before* confirming it, and under-declaring `cost`. All four are
|
||||
one paragraph each and all four are invisible until an outage.
|
||||
|
||||
> **Built in Phase 15** (`Integration-kit#10`), held unmerged until the cutover by the same mechanism
|
||||
> Teams phase 11 was: `checkCoreApi` asserts EQUALITY between `template/module.json`'s `coreApi` and
|
||||
> the `MODULE_API_VERSION` of the core `ci/core-ref.json` pins, so a template declaring `^1.10.0`
|
||||
> against a `main` still on 1.9.0 is red on purpose from the day the branch opens. The pin move is
|
||||
> the kit's leg of P16.
|
||||
>
|
||||
> **It is three chapters, not one.** The book as it stood taught a read-only data path end to end —
|
||||
> a sidecar that listens and stores, a plugin that never blocks the game thread, a module that reads
|
||||
> its own tables. Nothing in it told anyone to build a **command** path, so a chapter 5 teaching a
|
||||
> module to send an idempotency key would have been addressing it to a sidecar with nowhere to put
|
||||
> it. Chapter 3 gains §2a (request/reply correlation, the at-most-once store belonging where the
|
||||
> state is, and a deadline sent as a duration rather than an absolute time) and chapter 4 gains *"A
|
||||
> command that changes the world runs at most once"* (the key store persisted in the world save, the
|
||||
> ownership registry, and the expiry this side arms and re-arms at load). Both open by saying they
|
||||
> are skippable until you want chapter 5.
|
||||
>
|
||||
> **The template ships one of each of the four declarations**, and the phase's value came from
|
||||
> running them through core's real registry and real `events/dispatch.js` classifier at `edge` rather
|
||||
> than from the prose. That found two things:
|
||||
>
|
||||
> - **An idempotency key belongs on a command and never on a question.** A read that carries one is
|
||||
> answered by an at-most-once store with the FIRST read's reply, forever — so a lease applied
|
||||
> correctly, the game changed correctly, and the module could no longer see either: `read()`
|
||||
> reported the pre-run baseline and `inForce()` said nothing was held. The narrower rule that falls
|
||||
> out is worth stating with the others: a key is for a write whose repetition would be a second
|
||||
> EFFECT, and a write that merely SETS a value to X is idempotent by its own nature.
|
||||
> - **§H names a `detail` member on an envelope and core has never read one** — see the correction
|
||||
> below.
|
||||
|
||||
> **`detail` is not an envelope member.** The Rust wipes row in [§H](#h--rust--oxide-compatibility)
|
||||
> says the revert contract must accept `{ ok: true, detail: 'resource no longer exists' }`, and
|
||||
> `events/dispatch.js`'s `classify()` reads `ok`, `retry`, `error`, `await`, `holdFor`, `resources`
|
||||
> and `participants` — nothing else. A reason under any other name is dropped in silence and the
|
||||
> operator sees `"<action id> refused"`. The sentence §H was making is right and its example is
|
||||
> wrong: a revert of something that no longer exists is a success, and it is spelled `{ ok: true }`.
|
||||
> **`module-uo` took §H at its word twice** — `uo.item.grant` answers
|
||||
> `detail: { granted, missed, why }` and `uo.world.save` answers `detail: { started: true }`, and
|
||||
> neither reaches a screen or the ledger. The grant one is the one that matters: which recipients did
|
||||
> not receive the item is reported nowhere else.
|
||||
|
||||
---
|
||||
|
||||
## G — UO implementation plan: the gap list
|
||||
@@ -1183,7 +1223,7 @@ more than one. Nothing here contradicts it.
|
||||
| Participation | The hard part | Substantially easier — hooks carry attacker and victim. |
|
||||
| **Rewards** | An item into a backpack. `reversible: 'none'` — once given it is gone. | A kit, a permission group, currency via an economics plugin, a cosmetic. **Several of those are revocable**, so a Rust reward may be `reversible: 'override'` — a weekend VIP group is a lease with a deadline, not a gift. Core sees the difference as one enum value it never interprets. |
|
||||
| Several servers | One shard | `run.scope` is in the run's unique key, so one definition fans out to six servers without colliding with itself. Caps are per-run, so a fan-out to six servers is six separate budgets rather than one shared pool. |
|
||||
| Wipes | Never | Monthly, and a wipe invalidates every ledgered resource for that server at once. The revert contract must accept `{ ok: true, detail: 'resource no longer exists' }` — "gone, and that is fine" is a successful revert. A wipe also resets leased values to their defaults, a second reason restore must be idempotent. |
|
||||
| Wipes | Never | Monthly, and a wipe invalidates every ledgered resource for that server at once. The revert contract must accept "gone, and that is fine" as a successful revert, spelled `{ ok: true }` — **not** `{ ok: true, detail: ... }`, which this row said until Phase 15 and which `classify()` has never read (see [§F](#f--the-module-contract)). A wipe also resets leased values to their defaults, a second reason restore must be idempotent. |
|
||||
| Identity | In-game `[link` code | Steam — still `rust-dryrun` finding 1's open gap. Events neither closes it nor depends on it: `event_run_participants` carries a module-opaque `member_key`. |
|
||||
|
||||
**The agnosticism is provable, not merely asserted.** Make `event_definitions.owner_module` nullable
|
||||
|
||||
@@ -1796,6 +1796,16 @@ confirming it, and under-declaring `cost`.
|
||||
**Like Teams Phase 11, this cannot merge until the cutover exists** — the kit is pinned to a `main`
|
||||
sha, and the contract it teaches is not on `main` until then.
|
||||
|
||||
> **Built** as `Integration-kit#10` + `docs#226`, red on `checkCoreApi` by design. **It is three
|
||||
> chapters, not one, and the template gains code.** The book taught a read-only data path end to end
|
||||
> and never told anyone to build a command path, so chapters 3 and 4 gain one section each (§2a; *"A
|
||||
> command that changes the world runs at most once"*), both skippable until you want chapter 5. The
|
||||
> template ships one budget, one option source, one lease and one ledgering action plus
|
||||
> `server/sidecarClient.js` — named for the filename `noGameConnection.test.js` already anticipated,
|
||||
> with a real timeout, a real key passthrough and a simulated transport in one replaceable function.
|
||||
> Running those through core's REAL registry and dispatcher at `edge` is what found the two defects
|
||||
> §F now records; the prose found neither.
|
||||
|
||||
---
|
||||
|
||||
### Phase 16 — Acceptance walk and cutover
|
||||
|
||||
Reference in New Issue
Block a user