feat(events): a trigger can start a run
Rust PLAN_REDESIGNS §14.3.12 (D370), stage 9 of §14.3. A definition's published version may carry `starts`: a trigger, a condition and a cooldown. A matching firing creates a run of it, now. - spec: `starts` validated by the phase gate's own function (same grammar, same dormant rule; a dormant start refuses publish), a cooldown from 0m to 7d (30m default), at most ten, event.* refused. - events/starts.js: the emit path's third subscriber, beside dispatch and the gate observer. An unverified version starts nothing; one run at a time per definition and the cooldown are one conditional UPDATE on the new event_trigger_starts table; the occurrence key makes one firing reported twice one run. Ignored firings are counted by why. - event_runs.origin: the firing (trigger, label, time, subject, matched condition, declared variables), also on the run.created audit line. - Editor: a "Started by the game" panel using the gate's condition builder, with the ignored-firings line. Run console: "Started because ... fired". - No module contract change and no MODULE_API bump. Tests: eventStarts (server 21, client 8); eventRunnerSql +7 against a real MariaDB (75/75); Module-uo proof on uo.champ.boss_up against the dev database. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -159,6 +159,30 @@ const findSchedulable = async ({ listedOnly = false } = {}) => {
|
||||
return rows.map((row) => ({ ...row, version_spec: parseJson(row.version_spec, null) }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Every `ready` definition whose PUBLISHED version starts on this trigger
|
||||
* (Rust PLAN_REDESIGNS §14.3.12, D370). The emit path's question, asked on every
|
||||
* firing, so it answers in one query and the common answer is no rows.
|
||||
*
|
||||
* Read from the version, never the working copy, for the reason the schedule
|
||||
* expansion is: a start an author is midway through typing must not fire.
|
||||
* `JSON_EXTRACT` over `$.starts[*].on` is an array, or NULL for a version with
|
||||
* no starts, and `JSON_CONTAINS` of NULL is not true.
|
||||
*/
|
||||
const findStartingOn = async (triggerId) => {
|
||||
const rows = await query(
|
||||
`SELECT d.id, d.title, d.grace_seconds, d.current_version_id,
|
||||
v.spec AS version_spec, v.verified_at AS version_verified_at
|
||||
FROM event_definitions d
|
||||
JOIN event_versions v ON v.id = d.current_version_id
|
||||
WHERE d.state = 'ready'
|
||||
AND JSON_CONTAINS(JSON_EXTRACT(v.spec, '$.starts[*].on'), JSON_QUOTE(?))
|
||||
ORDER BY d.id`,
|
||||
[triggerId],
|
||||
)
|
||||
return rows.map((row) => ({ ...row, version_spec: parseJson(row.version_spec, null) }))
|
||||
}
|
||||
|
||||
/**
|
||||
* One definition by slug, for the PUBLIC event page (Phase 14a).
|
||||
*
|
||||
@@ -206,6 +230,7 @@ module.exports = {
|
||||
listPublicBySeries,
|
||||
slugTaken,
|
||||
findSchedulable,
|
||||
findStartingOn,
|
||||
insert,
|
||||
update,
|
||||
markReady,
|
||||
|
||||
Reference in New Issue
Block a user