docs(events): Phase 1 as built — schema, CRUD and the core action registry

The docs half of RunicGateway/website#<n>. Three files.

**BACKEND_DESIGN.md** gains the six event tables, column by column, and the
eleven admin routes. Written where the other table groups are, in the same
shape, because the argument for a column belongs beside the column.

**EVENTS.md** records four things the build settled that §D and §F had left
open:

  - `event_definitions.spec`, the working copy. §D's column list does not name
    one because §D describes what a PUBLISHED event is made of — but "editing a
    draft is free; no version exists yet" means the draft has to live somewhere,
    and it cannot be an `event_versions` row: that table is immutable and a run
    pins one, so a mutable unpublished row in it would be exactly what
    versioning exists to prevent.
  - A param's `example` is REQUIRED, on optional params too, the same rule
    `registerEventTriggers` makes of a variable's example and for the same
    reason: it is the authoring form's placeholder, one word at declaration
    time and unreconstructable afterwards.
  - The authoring side of dormancy. §F said what happens at DISPATCH; the save
    path draws the same line one step earlier, in the shape `engagement_rules`
    established — a saved step may keep an unregistered action, a new step may
    not add one, and a dormant step blocks the publish rather than the save.
  - Publish re-validates against the registries as they stand at that moment,
    not from the save that wrote the spec.

Plus two routes the § API surface table did not name — `GET /admin/events/:id`
(the list serves a summary; the editor needs the tree) and `GET
/admin/events/series` (a form cannot offer a value it cannot enumerate) — and a
note stating which of that table's rows Phase 1 deliberately did not build.

**EVENTS_PLAN.md** marks Phase 1 complete, names those four settlements, and
states the two deliberate absences so a reviewer does not read them as gaps:
the live run controls are not stubbed, and core's three `perform()` bodies
answer `{ ok: false }` rather than `{ ok: true }` — `ok: true` on an action
that did nothing is a recorded world change that did not occur.

`api-route-inventory.json` is NOT resynced here. It has been stale since
engagement Phase 2 and is 47 routes behind; catching it up in this PR would
bury a 13-route change under an unrelated 47.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-09-01 23:28:26 -05:00
parent 83309e9d09
commit 9ba119dc5a
3 changed files with 252 additions and 5 deletions

View File

@@ -137,6 +137,38 @@ document and should be written where they will be found:
### Phase 1 — Schema, CRUD and the core action registry (`website` + `docs`)
> **Complete.** `edge` in `website` and `docs`. Six tables, thirteen routes, the action registry with
> core as its first registrant, and 44 tests. **Nothing dispatches** — a run row is created and stays
> `scheduled`, which is this phase's correct answer and is rendered as such.
>
> **Four things the build settled that the plan had left open, each recorded in `EVENTS.md`:**
>
> - **`event_definitions` gained a `spec` column.** §D's column list does not name one, because §D
> describes what a published event is made of. But "editing a draft is free; no version exists yet"
> means the working copy has to live somewhere, and it cannot be an `event_versions` row: that table
> is immutable and a run pins one. Publishing copies the column into a version and leaves it as the
> next draft.
> - **The spec validator must accept its own output**, and a test found it did not. `validate()` adds
> `actionVersion` and `dormant`, then refused them as unknown keys on the next call — which would
> have made the *second* save of any definition, and publish's own re-validation, impossible. Both
> are now accepted and recomputed rather than trusted.
> - **A param's `example` is required**, on optional params too, matching `registerEventTriggers`. It
> is the authoring form's placeholder and there is no other source for one.
> - **Two routes the §API-surface table did not name**: `GET /admin/events/:id` (the list serves a
> summary; the editor needs the tree) and `GET /admin/events/series` (a form cannot offer a value it
> cannot enumerate). Both are staff reads over data the list already exposes.
>
> **Two deliberate absences, both stated so a reviewer does not read them as gaps.** The live run
> controls and `verify` are not stubbed — nothing is in flight until P2, and a control that answers
> `200` and does nothing is worse than one that is not there. And core's three `perform()` bodies
> answer `{ ok: false, retry: false }` rather than `{ ok: true }`: `ok: true` on an action that did
> nothing is a recorded world change that did not occur, which is the exact mistake §F's failure
> default exists to prevent.
>
> `registerEventActions` is on the staging area and reachable **only** by `registerCore()` — the
> loader builds its own `api` facade and has no method that delegates to it, so no module can call it
> yet and `MODULE_API_VERSION` is untouched. P7 adds that facade and makes the bump.
The six tables that do not depend on the module contract: `event_definitions`, `event_series`,
`event_versions`, `event_runs`, `event_run_steps`, `event_run_log`. Admin CRUD, publish (which
snapshots a version), archive. `router/v1/admin/events.router.js` + `events.controller.js`, models as