docs(events): Phase 7 as built — the module contract at MODULE_API 1.10.0 #215

Merged
whitlocktech merged 1 commits from docs/events-phase-7 into edge 2026-09-03 19:37:08 +00:00
Member

The docs half of Event System Phase 7. Code: RunicGateway/website#189.

MODULE_API.md

1.10.0 arrives in three places — the number itself, the Part 1 entry, and §2.4.

The Part 1 entry carries the four call shapes and, more usefully, the six rules that come with them, because each is a rule rather than a field: no shape a failure can take reads as success (registerTeamProvider's default inverted, and why); a module cannot spend a budget it did not declare; verify: true must change nothing and rides the same dispatcher a real run uses; example is required on every param including the optional ones; the four id spaces are four; and a lease is declared at 1.10.0 and acquired by nothing — with the reason it is in this version rather than the next.

It also says the thing that makes the bump smaller than it looks: only one of the four names is new machinery. The action registry has staged core's three actions on every boot since Events Phase 1 and simply had no way in. That is registerCore()'s argument from the module system's Phase 3, and this is where it pays.

§2.4 gains the four names in the call list, and four notes that are contract rather than implementation: an action is core calling the module, from further away than any other member (hence budgetMs per action, and what happens without it); once on all four and why a second call is a module changing its mind; everything is optional, stated once because it governs every member; and an option source that refuses degrades its field rather than blocking the form. Plus the dormancy rule an action inherits from a trigger — a saved step keeps an unregistered action, a new step may not add one, and a dormant step blocks the publish.

EVENTS.md

§F is marked built, with a note that the seam is narrower than the four names suggest, and a new subsection recording what the build settled:

  • A budget's unit is required and its vocabulary is open — core renders it, never reads it.
  • An undeclared dimension is refused at save, at the dry run and at dispatch, with its own code. The separate code is not tidiness: an operator told "the cap is spent" raises a cap and nothing changes, because the fix is a module's declaration.
  • Example-pricing survives the arrival of a budget registry. A registry can say what a dimension is called; it cannot say which action spends it, because cost is a function of params and calling it is the only honest way to ask.
  • restore is required even though read could stand in for it — they answer different questions, and the drift check is the one thing a module must not be allowed to skip.
  • An option source that refuses degrades its field and its route answers 200. Both halves are the contract; a status code is how a client tells "broken" from "nothing to offer".
  • Core registers core.options.legs, so the seam's first exercise is not a module's — and so the leg typo Phase 6's walk caught mid-run is caught in the form.

§K's Phase 7 note becomes what shipped rather than what was promised: half of it was replaced by registerEventBudgets, half of it deliberately survives, and the paragraph now says which half is which.

§ API surface: the option-source row is filled in with what it actually does and why a refusal is a 200; the catalog row gains budgets, leases and optionSources with the reason they are served beside the actions (the step editor needs all four to draw one step); and the absent-routes list is down to cleanup. The phase table: P7 ticked.

EVENTS_PLAN.md

Phase 7 marked complete, in the shape Phases 0–6 use: the four org-lead decisions, the three things the build settled, the throwaway-module proof and why it uses the real loader, the verified numbers, and the live walk.

Including the two findings worth reading before the diff:

  • The React defect only the browser could find. The option-source loader wrote its "have I already asked for this?" guard inside a setSources updater and read the flag on the next line. setSources queues its updater, so the flag was always false and the request was never made — the field sat on "Reading the list…" for ever, with every server test green and the route answering correctly under curl.
  • The route this file caught the code building wrong. § API surface has always named it GET /admin/events/catalog/options/:sourceId; it was built one segment shallower, and the divergence surfaced only when this half was written.

Checked by hand

docs has no CI, so: every anchor introduced here resolves (#f--the-module-contract and #k--security-model were already in use; no new anchors were added), and every route, identifier, env var and file path named here exists on website#189 — registerEventBudgets, registerEventLeases, registerEventOptionSources, core.options.legs, budgetsOf, resolveOptionSource, and the route in both routes.manifest.json and the OpenAPI spec. Diff is 86/11, 110/0 and 123/1 across the three files with no CRLF artefact, verified by comparing --numstat against the real content diff rather than trusting it.


  • AI-assisted: authored with Claude Code (Claude Opus).
The docs half of Event System **Phase 7**. Code: **RunicGateway/website#189**. ## `MODULE_API.md` **1.10.0 arrives in three places** — the number itself, the Part 1 entry, and §2.4. The Part 1 entry carries the four call shapes and, more usefully, the **six rules that come with them**, because each is a rule rather than a field: no shape a failure can take reads as success (`registerTeamProvider`'s default *inverted*, and why); a module cannot spend a budget it did not declare; `verify: true` must change nothing and rides the same dispatcher a real run uses; `example` is required on every param including the optional ones; the four id spaces are four; and **a lease is declared at 1.10.0 and acquired by nothing** — with the reason it is in this version rather than the next. It also says the thing that makes the bump smaller than it looks: **only one of the four names is new machinery.** The action registry has staged core's three actions on every boot since Events Phase 1 and simply had no way in. That is `registerCore()`'s argument from the module system's Phase 3, and this is where it pays. **§2.4** gains the four names in the call list, and four notes that are contract rather than implementation: an action is core *calling the module*, from further away than any other member (hence `budgetMs` per action, and what happens without it); `once` on all four and why a second call is a module changing its mind; everything is optional, stated once because it governs every member; and an option source that refuses degrades its field rather than blocking the form. Plus the dormancy rule an action inherits from a trigger — a saved step keeps an unregistered action, a new step may not add one, and a dormant step blocks the *publish*. ## `EVENTS.md` **§F is marked built**, with a note that the seam is narrower than the four names suggest, and a **new subsection recording what the build settled**: - A budget's `unit` is required and its vocabulary is **open** — core renders it, never reads it. - An undeclared dimension is refused at save, at the dry run and at dispatch, with **its own code**. The separate code is not tidiness: an operator told *"the cap is spent"* raises a cap and nothing changes, because the fix is a module's declaration. - **Example-pricing survives the arrival of a budget registry.** A registry can say what a dimension is *called*; it cannot say which action spends it, because `cost` is a function of params and calling it is the only honest way to ask. - `restore` is required even though `read` could stand in for it — they answer different questions, and the drift check is the one thing a module must not be allowed to skip. - An option source that refuses degrades its field **and** its route answers `200`. Both halves are the contract; a status code is how a client tells "broken" from "nothing to offer". - Core registers `core.options.legs`, so the seam's first exercise is not a module's — and so the `leg` typo Phase 6's walk caught *mid-run* is caught in the form. **§K's Phase 7 note becomes what shipped** rather than what was promised: half of it was replaced by `registerEventBudgets`, half of it deliberately survives, and the paragraph now says which half is which. **§ API surface**: the option-source row is filled in with what it actually does and why a refusal is a `200`; the catalog row gains `budgets`, `leases` and `optionSources` with the reason they are served *beside* the actions (the step editor needs all four to draw one step); and the absent-routes list is down to `cleanup`. **The phase table**: P7 ticked. ## `EVENTS_PLAN.md` Phase 7 marked **complete**, in the shape Phases 0–6 use: the four org-lead decisions, the three things the build settled, the throwaway-module proof and why it uses the *real* loader, the verified numbers, and the live walk. Including the two findings worth reading before the diff: - **The React defect only the browser could find.** The option-source loader wrote its *"have I already asked for this?"* guard inside a `setSources` updater and read the flag on the next line. `setSources` **queues** its updater, so the flag was always false and the request was never made — the field sat on *"Reading the list…"* for ever, with every server test green and the route answering correctly under `curl`. - **The route this file caught the code building wrong.** § API surface has always named it `GET /admin/events/catalog/options/:sourceId`; it was built one segment shallower, and the divergence surfaced only when this half was written. ## Checked by hand `docs` has no CI, so: every anchor introduced here resolves (`#f--the-module-contract` and `#k--security-model` were already in use; no new anchors were added), and every route, identifier, env var and file path named here exists on website#189 — `registerEventBudgets`, `registerEventLeases`, `registerEventOptionSources`, `core.options.legs`, `budgetsOf`, `resolveOptionSource`, and the route in both `routes.manifest.json` and the OpenAPI spec. Diff is 86/11, 110/0 and 123/1 across the three files with **no CRLF artefact**, verified by comparing `--numstat` against the real content diff rather than trusting it. --- - [x] AI-assisted: authored with Claude Code (Claude Opus).
wtclaude added 1 commit 2026-09-03 19:16:54 +00:00
Code: RunicGateway/website#189.

MODULE_API.md gains 1.10.0 in three places: the number itself, the entry in
Part 1 with the four call shapes and the six rules that come with them, and
the four names in 2.4's call list with the contract-rather-than-
implementation notes beside them.

EVENTS.md F is marked built, with a new subsection recording what the build
settled -- the open unit vocabulary, the undeclared-dimension refusal and
why it has its own code, why example-pricing survives the arrival of a
budget registry, why restore cannot be read, and why an option source that
refuses answers 200. K's Phase 7 note becomes what shipped. The API surface
table's option-source row is filled in, and the absent-routes list is down
to cleanup.

EVENTS_PLAN.md: Phase 7 complete, in the shape phases 0-6 use -- the four
org-lead decisions, the three things the build settled, the throwaway-module
proof, the React defect only the browser could find, and the route the docs
caught the code building one segment shallower than this file specifies.

Checked by hand (docs has no CI): every anchor resolves, and every route,
identifier and file path named here exists on website#189.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T6t8mrAWhZU5vnyYgZTMtL
whitlocktech merged commit 3f5aa3b074 into edge 2026-09-03 19:37:08 +00:00
whitlocktech deleted branch docs/events-phase-7 2026-09-03 19:37:09 +00:00
Sign in to join this conversation.
No description provided.