docs(events): Phase 10 as built — the integrations, and a ceiling per firing
Five files. `EVENTS.md` §F gains "What Phase 10 settled" and §D/§J are corrected to what was built; `EVENTS_PLAN.md` records the phase as built with its six decisions, the live walk and the three defects it found; `MODULE_API.md` folds two envelope members into 1.10.0 in place; `ENGAGEMENT.md` §8.5's "come back for X" row is resolved; `BACKEND_DESIGN.md` gains the eleventh event table and the two columns Phase 10 put on tables that already existed. The four contract findings, three of which are rules rather than facts about events: - a firing may carry a ceiling of its own and it may only ever NARROW, applied at the send-time G24 gate as meet(declared, emitted). §I promised a rehearsal "ceilinged to staff" and nothing implemented it; - two incomparable ceilings refuse rather than resolve, which is §5.1a rule 3 reused rather than re-argued; - a module reports participants on the success envelope and there is no other door; - a trigger that is not also a STREAM has no push and says nothing about it, so every stream must be a trigger while a trigger need not be a stream. And a trigger's `description` is read by a recipient, not only by an operator: it is the structural projection's `intro` fallback, so a line naming its own audience ends up in that audience's inbox. - [x] AI-assisted: Claude Code (Opus 5). Code: RunicGateway/website#TBD. Co-Authored-By: Claude <noreply@anthropic.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
@@ -47,6 +47,13 @@ a human and publish results.
|
||||
> API for the first time. 1.10.0 has not shipped, so the whole module contract reaches an author as
|
||||
> one version they read once, which was the argument for putting the lease declaration here in the
|
||||
> first place.
|
||||
>
|
||||
> **Phase 10 amended it a second time, under the same rule**, with `participants` on the success
|
||||
> envelope and an optional narrowing `ceiling` on `ctx.events.emit`'s. `main` still declares 1.9.0,
|
||||
> so 1.10.0 remains unshipped and the whole event contract — actions, budgets, leases, option
|
||||
> sources, reconcile, participants — still reaches an author as one number. The `ceiling` member is
|
||||
> the one of the two that widens something 1.9.0 already shipped, and it is additive and optional:
|
||||
> a module that never passes it is emitting exactly what it emitted before.
|
||||
|
||||
**Only one of the four is new machinery.** The ACTION registry has staged core's `core.announce`,
|
||||
`core.wait` and `core.cue` on every boot since Events Phase 1; what it never had was a way in —
|
||||
@@ -273,9 +280,28 @@ unchanged, and so is every rule about composition narrowing rather than widening
|
||||
**1.10.0 — the event contract** (`website/EVENTS.md` §F). Four additions, no removals and no changed
|
||||
signature, so minor; `module-uo`'s `coreApi: "^1.9.0"` still resolves and it registers no actions
|
||||
until `EVENTS_PLAN.md` Phase 9. `api.registerEventActions([...])`, `api.registerEventBudgets([...])`,
|
||||
`api.registerEventLeases([...])` and `api.registerEventOptionSources([...])` (§2.4). Nothing was added
|
||||
to `ctx`: an action is called BY core, so what a module needs from this contract it is handed in the
|
||||
envelope rather than reaching for.
|
||||
`api.registerEventLeases([...])` and `api.registerEventOptionSources([...])` (§2.4). Almost nothing
|
||||
was added to `ctx`: an action is called BY core, so what a module needs from this contract it is
|
||||
handed in the envelope rather than reaching for — `ctx.events.reconcile()` (Phase 8) is the one
|
||||
exception, because only the module knows when the game it talks to has restarted.
|
||||
|
||||
Two members joined it in Phase 10, both on an envelope:
|
||||
|
||||
- **`participants` on an action's SUCCESS envelope** (`EVENTS.md` §D, §J). An action may answer
|
||||
`{ ok: true, participants: [{ memberKey, userId?, score?, meta?, joinedAt? }] }` and core records
|
||||
them against the run, on both success shapes, beside `resources`. `memberKey` is required and
|
||||
module-opaque; `userId` is optional and is the module's own answer to "is this player a website
|
||||
account", because core cannot map one and a core that guessed would be one game's identity model
|
||||
compiled into core. A bad entry is dropped and logged, never a retry: a retried step re-dispatches
|
||||
a world write that already happened.
|
||||
- **`ceiling` on `ctx.events.emit`'s envelope** (`EVENTS.md` §I). An optional audience ceiling for
|
||||
THIS firing, which may only ever narrow: the send-time G24 gate applies `meet(declared, emitted)`,
|
||||
so a rule wider than the meet is refused and one narrower is unaffected. Two incomparable ceilings
|
||||
meet to null and every rule is refused, which is §5.1a rule 3's posture rather than a guess about
|
||||
which branch was meant. The case that forced it is core's own — a rehearsal fires the same
|
||||
lifecycle triggers as a real run and must not mail every subscriber — and it is on the shared
|
||||
envelope rather than in `events/` because "this particular firing is narrower than the kind
|
||||
usually is" is a fact any emitter can have.
|
||||
|
||||
**1.7.0 — the engagement contract** (`website/ENGAGEMENT.md` Phase 2). Four additions, no removals
|
||||
and no changed signature, so minor; `module-uo`'s `coreApi: "^1.3.0"` still resolves.
|
||||
@@ -1020,6 +1046,19 @@ rather than implementation and belong here:
|
||||
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.
|
||||
- **A module reports who took part on the envelope, and there is no other door.** `participants`
|
||||
rides back from `perform()` exactly as `resources` does, on both success shapes — including
|
||||
`await: 'human'`, because a cue's confirm finishes the step without a second dispatch and that is
|
||||
therefore the only moment its participants can be recorded. There is deliberately no
|
||||
`ctx.events.participants` and no route: a second write path into a run core is mid-tick on would be
|
||||
a second thing that can race the step claim. One step may report at most 5000, the same bound the
|
||||
engagement engine puts on a list of users a caller may assert, and a member reported twice in one
|
||||
step is recorded once with the duplicate named.
|
||||
- **A declaration's ceiling bounds the kind of event; a firing's `ceiling` bounds the occasion.** An
|
||||
emitter that already knows this particular firing must not reach as far as the declaration allows
|
||||
passes one, and the gate takes the meet. It only narrows — passing a wider value changes nothing —
|
||||
and passing something incomparable with the declaration refuses every rule rather than resolving
|
||||
to either. See §1.1 under 1.10.0.
|
||||
- **`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