feat(events): an expired ledger status and ctx.events.expired (MODULE_API 1.11.0)
A game that ends a resource at its own deadline — a Rust zone erased when its
time is up — could reach core only through ctx.events.reconcile(), which files
it `orphaned`: amber, "it vanished", and still claimable for a revert. The new
call files it as the plan working (Rust PLAN_FIXES F14, D170, D183):
- event_run_resources.status gains `expired`, terminal like `reverted`: the
sweep never takes it back, live_marker releases the target, and it joins
neither HELD nor UNRESOLVED. The ENUM ALTER re-runs as a no-op on every boot
(checked on MariaDB 11.8 with the stored generated column depending on it).
- ctx.events.expired({ kind, ref }) marks the calling module's own pending,
confirmed or orphaned rows for that target expired and logs
`resource.expired`; a revert in flight is left to finish. The owner is bound
by the loader, like reconcile. A finished run whose last unresolved row this
was goes to cleanup `complete`, even from `incomplete`.
- The run console shows it green, "ended by the game on time".
Additions only, so minor. Module-uo (coreApi ^1.10.0) calls none of it and
reads no ledger status; the contract test now asserts the range still holds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -261,6 +261,27 @@ function buildCtx(id, moduleRoot) {
|
||||
(err) => { log.error('ctx.events.reconcile failed', { module: id, message: err.message }) },
|
||||
)
|
||||
},
|
||||
// MODULE_API 1.11.0 (Rust PLAN_FIXES D183). The game ended one of this
|
||||
// module's ledgered resources at its own deadline — a Rust zone erased when
|
||||
// its time was up. Recorded as `expired`: terminal, never given back, and a
|
||||
// different sentence on the console from `orphaned`, which is reconcile's
|
||||
// word for a thing that vanished with nobody asking.
|
||||
//
|
||||
// `id` bound, fire-and-forget and returns undefined, all three for
|
||||
// `reconcile`'s reasons directly above. A `{ kind, ref }` that names no
|
||||
// live row is not an error: a module hears expiries of things no run ever
|
||||
// ledgered, and telling it so would be noise it can do nothing with.
|
||||
expired: (resource) => {
|
||||
// eslint-disable-next-line global-require
|
||||
require('../events/cleanup')
|
||||
.expireResource(id, resource || {})
|
||||
.then(
|
||||
(summary) => {
|
||||
if (summary && summary.expired) log.info('event resource expired', { module: id, ...summary })
|
||||
},
|
||||
(err) => { log.error('ctx.events.expired failed', { module: id, message: err.message }) },
|
||||
)
|
||||
},
|
||||
},
|
||||
// The in-app sink (§5.1) — a module writing the inbox directly, without a
|
||||
// rule. Live from Phase 7; it threw until the `user_notifications` table
|
||||
|
||||
Reference in New Issue
Block a user