docs(events): the step editor and event templates — MODULE_API 1.13.0

The contract half of website feat/event-step-editor (Rust PLAN_REDESIGNS §2):
MODULE_API.md §1.1/§2.4 gain registerEventTemplates, a param's duration, a
lease's example and an option's note; EVENTS.md §F (the note), §I (what the
1.13.0 editor settled) and the API surface (the templates route, draftAhead).
PLAN_REDESIGNS §2.8 records the core half as built and the Module-uo proof.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-12 02:09:55 -05:00
parent 1a3411b19e
commit a2dbce1f89
3 changed files with 107 additions and 5 deletions

View File

@@ -26,13 +26,41 @@ here extends the contract first, in this file, before the module is written agai
Core exports a single integer-major semver string from `server/src/modules/version.js`:
```js
const MODULE_API_VERSION = '1.12.0'
const MODULE_API_VERSION = '1.13.0'
```
The client half carries the same number (`client/src/modules/version.js`) and a test asserts the two
agree. Duplicated rather than fetched because the value has to be on `window.__rg` before the first
module chunk evaluates, which is earlier than any network round trip could answer.
**1.13.0 — the event step editor** (`website/EVENTS.md` §F, §I; Rust `modules/rust/PLAN_REDESIGNS.md`
§2, D192). Four additions and no removal, so minor. Each is optional, and each is a declaration the
editor, core's own page, renders.
- **`api.registerEventTemplates([{ id, label, description, inputs, build }])`** — a named starting
point for a new event. `inputs` are params (§2.4), at most 8, drawn by the same field a step's
params are, sources and all. On **New event** the author picks a template and fills its inputs;
core checks them, calls `build({ inputs })` (async allowed, 10 s) and validates what comes back —
`{ title, summary?, spec: { phases } }` — as a spec. The editor opens it as an ordinary unsaved
draft. **Nothing about the template is stored**: no column, no link, and the runner never sees
one. A refusal the author should read is `{ ok: false, error }`; a throw, a hang or an invalid
spec is logged and answered with core's own sentence, never the module's stack.
- **A param's `duration: 'minutes'`** — on an `int` or `float` param that is a length of time, so
the editor can see a timed step. A held thing is given back when the run ends, so the editor gives
a phase with no advance rule `after: <the longest hold>` when a timed step is added, and says so
under any phase whose run can end sooner. `core.lease`'s `minutes` declares it.
- **A lease's `example`** for its value: one scalar, shown as the placeholder when an author picks
that lease. The editor draws `core.lease` as the chosen lease's own form — its type's control, its
range, its `values`, its `target.source` as the target's picker and its `maxDurationMs` beside the
minutes — so a lease step no longer shows core's one example for every lease.
- **An option row's `note`** — a sentence, at most 300 characters, the editor shows under the field
while that option is the chosen one. It is advice about the value, never a rule: the save path
does not read it.
**Module-uo is unaffected.** It declares `^1.10.0` and uses none of the four. Its actions, leases and
sources come out of the registry exactly as they went in; its steps render with the new controls and
nothing else. The proof website#209 ran for 1.11.0 was run again against this core (website PR body).
**1.12.0 — an event action's optional `progress(envelope)`** (`website/EVENTS.md` §I; RunicNPC
`runicnpc/PLAN.md` stage 8, D304, D305). One addition and no removal, so minor.
@@ -391,6 +419,10 @@ branches lacks — so it is the only place `permits` is true between two values
between `admin` and `owner`, `members` or `subscribers`. `permits`, `meet` and `meetAll` are otherwise
unchanged, and so is every rule about composition narrowing rather than widening.
**1.13.0 — the event step editor** (`website/EVENTS.md` §F, §I; Rust PLAN_REDESIGNS §2).
`registerEventTemplates`, a param's `duration`, a lease's `example` and an option's `note`; four
optional additions, so minor; see §1.1. Module-uo's `^1.10.0` still resolves.
**1.12.0 — an action's `progress()`** (`website/EVENTS.md` §I, RunicNPC D304). One optional member
on an action, so minor; see §1.1. Core asks it while a run is live and publishes the answer, one of
two shapes, on the public event. Module-uo's `^1.10.0` still resolves.
@@ -806,10 +838,11 @@ api.registerSlashCommands([{ name, description, options, access, handler }]) //
api.registerEventTriggers([{ id, label, kind, subjectKey, audience, ceiling, version, variables }]) // 1.7.0
api.registerAudiences([{ id, label, params, ceiling, resolve }]) // 1.7.0
api.registerEngagementSeeds({ templates, ruleGroups }) // 1.9.0
api.registerEventActions([{ id, label, risk, reversible, cost, params, perform, revert, reconcile, progress }]) // 1.10.0; progress 1.12.0
api.registerEventActions([{ id, label, risk, reversible, cost, params, perform, revert, reconcile, progress }]) // 1.10.0; progress 1.12.0; a param's duration 1.13.0
api.registerEventBudgets([{ id, label, unit }]) // 1.10.0
api.registerEventLeases([{ id, label, type, min, max, values, target, maxDurationMs, read, apply, restore, inForce }]) // 1.10.0
api.registerEventOptionSources([{ id, label, searchable, resolve }]) // 1.10.0
api.registerEventLeases([{ id, label, type, min, max, values, example, target, maxDurationMs, read, apply, restore, inForce }]) // 1.10.0; example 1.13.0
api.registerEventOptionSources([{ id, label, searchable, resolve }]) // 1.10.0; a row's note 1.13.0
api.registerEventTemplates([{ id, label, description, inputs, build }]) // 1.13.0
api.onBoot(async (ctx) => {})
api.onShutdown(async () => {})
```
@@ -1216,6 +1249,13 @@ rather than implementation and belong here:
would hide what the line describes answers `null` for that reader; a public page must never say
what the module's own pages would hide. Core gives it 2 s and drops a late answer. It is a read: it
must change nothing, and it may be called every few seconds for as long as a run is live.
- **A template is a shortcut past the editor, never a second way to make an event** (1.13.0). What
`build()` returns is validated as a spec and opened as an unsaved draft; the author saves it
through the same route, with the same checks, as one they typed. So a template cannot make a
definition the save path would refuse, and changing or removing one touches no definition it
ever made. `build()` may read the module's own data — the Rust kit weekend looks up a kit's
permission — and answers `{ ok: false, error }` with the author's sentence when the inputs make
no sense; anything it throws stays in the log.
- **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