docs(events): a trigger can start a run (Rust §14.3 stage 9, D370)

- EVENTS.md §E "Starting a run from a trigger": `starts`, the third
  emit subscriber, the four rules (verified, one at a time, cooldown,
  no event.*), the run's origin, and what is deliberately not in it.
  §D: event_runs.origin and a twelfth table, event_trigger_starts.
  API surface: triggerStarts on GET /admin/events/:id, origin on the
  run console.
- BACKEND_DESIGN.md: event_runs.origin and event_trigger_starts.
- PLAN_REDESIGNS §14.3.21: stage 9 as built, eight settled-in-code
  points, tests and the Module-uo proof; pointers in §9, §14.3.11 and
  §14.3.12. This ends the §14.3 build.

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-16 01:52:16 -05:00
parent 5f769c6e05
commit b846c0c6b4
3 changed files with 133 additions and 7 deletions

View File

@@ -931,7 +931,8 @@ pulls) is research, not a walk, so it still comes first:
build, in §14.3.11's nine stages (two of them core), §13 first. **Stage 1, §13, was built on 2026-10-15
(§14.3.14)**, with D378 and D379. **Stage 2, core's `retryAfterMs` (MODULE_API 1.15.0), was built on
2026-10-15 (§14.3.15).** **Stage 3, TruePVE in the zone step, was built on 2026-10-15 (§14.3.16)**, and **stage 4, Dynamic PVP, the same day (§14.3.17)**, and **stage 5, Dangerous Treasures, the same day (§14.3.18)**, and **stage 6, Guarded Crate, the same day (§14.3.19)**, and **stage 7, ZLevels, the same day (§14.3.20).** **Stage 8, Server Rewards, was taken off the plan on
2026-10-16 (D381).**
2026-10-16 (D381).** **Stage 9, core's trigger starts, was built on 2026-10-16 (§14.3.21)**, which ends the
build.
**Then one walk, on Oxide and Carbon, of everything still owed (D331):** §2.7; §11's rows; §12.9; §13's and
§14's rows; the in-game rows
@@ -2755,7 +2756,8 @@ until the end (D331, D359). The org lead is asked before each stage starts.
8. ~~**Server Rewards**~~: dropped on 2026-10-16 (D381), not built.
9. **Core: starting a run from a trigger** (§14.3.12, D370), a website PR of its own with the Module-uo proof,
as §2 was. It can be built alongside any stage after 1; it is last here only because nothing else waits on it.
Module-Rust needs no code for it, because the world events are already triggers.
Module-Rust needs no code for it, because the world events are already triggers. **Built 2026-10-16
(§14.3.21).**
#### 14.3.12 Core: starting a run from a trigger (D370)
@@ -2791,6 +2793,9 @@ nothing and are counted; two instances emitting the same firing make one run; an
`starts`; a dormant trigger; the run's origin on its page and in the audit. **Module-uo proof:** one definition
starting on `uo.champ.boss_up`.
> **Built on 2026-10-16** (§14.3.21), as planned here; `started_by` stayed the user column (NULL) and the origin
> went into a column of its own, `event_runs.origin`, because `started_by` is a foreign key to `users`.
**Walk rows.** On a Rust rig: *"start when Bradley is destroyed"* with a short cooldown. Destroy two in a minute and
see one run and one ignored firing; destroy one after the cooldown and see a second run.
@@ -3184,6 +3189,59 @@ were then cleaned of ZLevels and its files, and put back on the protocol-13 brid
**Walk rows** are §14.3.6's, with one more: Server settings' line for a server with and without ZLevels.
#### 14.3.21 Stage 9, built: starting a run from a trigger (2026-10-16)
Built in `website` as §14.3.12 planned it (D370), and specified in
[EVENTS.md](../../website/EVENTS.md) §E *Starting a run from a trigger*, with the schema in §D and
[BACKEND_DESIGN.md](../../website/BACKEND_DESIGN.md). **No module contract changed and no MODULE_API version
moved**: a definition starts on the triggers modules already register, so Module-Rust and Module-uo need no
code, and the Integration Kit's pin is untouched.
**What was built:** a spec's `starts` (`{ on, where, cooldown }`, at most ten, validated by the phase gate's
own function, a dormant start refusing publish, `event.*` refused); `events/starts.js`, the emit path's third
subscriber; a table, `event_trigger_starts`, whose conditional `UPDATE` is the claim; `event_runs.origin`;
the editor's *Started by the game* panel (the gate's condition builder, a cooldown, and the line *"Last started
run #12; since then 2 firings ignored while a run was going"*); and the run console's *"Started because …
fired"* line, also on the `run.created` audit line.
**Settled in the code, and overrulable:**
1. **An unverified version is not started by a trigger**, and the firing is counted. A trigger start is
unattended, as a scheduled one is, so §K's gate applies to it; §14.3.12 said "verify before publish", and
this is that rule where it actually bites.
2. **"A run is going"** is any run of the definition that has started and not ended, **or is `scheduled` and
already due**, whoever started it. A trigger-started run is `scheduled` for up to one tick, and a scheduled
occurrence in its grace window is about to start. A future occurrence does not block.
3. **The cooldown belongs to each start but is measured from the definition's last trigger-started run.**
Two starts on one definition share that clock; each keeps its own length.
4. **The counts are since the last trigger-started run**, which zeroes them, so the editor answers "is it
ignoring things now". A repeat of the firing that started the last run (same second) is not counted.
5. **The run's scope is `''`**, as a scheduled occurrence's is: there is no scope registry, and the condition
is what narrows a start to one server.
6. **A firing older than the definition's grace window starts nothing** (the missed sweep would take it next
tick), and is logged; one stamped in the future starts now.
7. **The origin keeps the trigger's declared variables only**, bounded at 4 KB (past that, only the variables
the condition named, marked `_truncated`). The run row is admin-only; nothing public reads it.
8. **A step cannot read the firing's variables.** That would put `origin` into `perform()`'s envelope, a
MODULE_API minor, and nothing asks for it yet. Noted in EVENTS.md.
**Tests.** Server: `eventStarts.test.js`, 21 (the spec rules; one run for a matching firing and its origin; a
non-match; running and cooldown counted; a second run after the cooldown; one firing reported twice; unverified;
stale; future-stamped; `event.*` never looked up; a dormant start; two definitions on one trigger; a create
that throws; the 4 KB bound; the state line). `eventRunnerSql.test.js` gained 7 run against a real MariaDB
(75/75): `findStartingOn` reads only ready definitions' published versions and every entry of the list; five
claims at once have one winner; the cooldown boundary; going runs block and a future occurrence does not; a
claim older than a minute is taken; the counts and their reset; `origin` through the insert. Client:
`eventStarts.test.js`, 8. **The Module-uo proof**, against the real core, the real `uo` module and the dev
MariaDB: a definition starting on `uo.champ.boss_up` where `spawnName` is Abyss, cooldown one minute. Firing
before the dry run: held, counted as unverified. After it: Destard started nothing; Abyss started one run with
its origin (*"A champion boss is up"*, spawnName Abyss, location Felucca 5187, 570); the same firing again
started nothing and counted nothing; a firing while it was going and one inside the cooldown were counted; a
firing after the minute started a second run. Then deleted.
**Walk rows** are §14.3.12's: on a Rust rig, *"start when Bradley is destroyed"* with a short cooldown; destroy
two in a minute and see one run and one ignored firing; destroy one after the cooldown and see a second run.
### 14.4 Questions for the org lead
**Answered on 2026-10-14:** Q17 as proposed, out (D358). **Q18 against the proposal: the end walk waits for