The module's half of PLAN_FIXES §6 step 2 (decisions D181-D185, docs#288).
- F13/F14 (D170, D183): `world.expired`, recognisable from protocol 13 by its
`what`, is handed to core as the resource the zone step ledgered
(`world`, `<serverId>:<id>`) through ctx.events.expired, which records it
`expired`. coreApi moves to ^1.11.0 (website#209).
- F8 (D184): `plugin.loaded` / `plugin.unloaded` mark the permission sync dirty
when the plugin added or removed permissions, so an unresolved grant lands on
the next tick instead of the fifteen-minute audit.
- Catalogue: plugin.loaded/unloaded, world.expired and lease.expired are staff
kinds. The last two were never classified (default deny kept them off public
pages); the test now covers every event kind through protocol 13.
- F7: permission and title pushes hold while the stored hello says
`worldReady: false` (a human's "sync now" does not); a failed or refused
permission sync now logs at warn.
- F2 (D185): the killfeed names an NPC attacker — a family (Scientist, Bandit
guard, Bradley APC…) or the prefab without its variant digits (wolf2 → Wolf).
- F5/F6: a link code is asked of the servers that minted one in the last six
minutes first, then of the rest, each group in parallel; "unsure" only when
one of the minting servers is unreachable.
- D182: the admin server list carries the ZoneManager helper's state from the
hello, and the servers page says what a missing or failed helper costs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Four event verbs and the announce leg, per PLAN.md §29:
- rust.participation.open / .collect: the plugin counts who takes part
(seconds, kills or both, in a zone this run opened or the whole server)
and collect files them as the run's participants, keyed by Steam id.
- rust.kit.entitle: the five recipient modes (D101), rows in the new
rust_perm_run_grants (D84) unioned into the permission push, one extra
use of the kit per reward as site-held credits on perm.sync (D103),
and the rust.kit.entitled notice deferred from phase 10 (D64).
- rust.announce: one server or every server (D105).
- rust.chat announce leg, speaking only on servers whose new news switch
is on (D104) - a card on Admin -> Rust visibility (D106).
Budgets rust.grants and rust.announcements; the kit source and four
fixed-choice sources (core has no enum param type). rust_perm_run_grants
carries core's idempotency key so a revert of a lost answer can find its
rows. Protocol 10.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
The live walk rendered a generic in-app notice as "A server came online.
A server's game started..." Core's structural projection falls back to
the trigger's label and description when the payload has no title, and
on a multi-server site that never says which server. Core's rule is that
the payload wins, so every trigger now declares `title` and `intro`, and
the emitter writes the sentence ("Oxide rig is online"). An operator's
own template can still ignore it and use the parts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Registers the engagement set R7 put in v1: thirteen triggers, four push
streams, three audiences, four bodies (two triggers, email and in-app)
and thirteen disabled rules in seven groups (PLAN.md §25, D59-D68).
The raid alert goes to everyone authorised on the tool cupboard, one
emit per linked person with ownerUserId, so the owner ceiling holds per
emit. It covers doors and walls (protocol 7), never names the raider,
alerts nobody when there is no cupboard, and carries ownerOnline so
"offline only" is the seeded rule's condition rather than code.
The fan-out runs off ingest before a frame is applied, since applying a
disband deletes the roster the notice is sent to. A replayed event is
told only while it is news: 15 minutes for broadcasts, 24 hours for
personal and staff events. Dedupe keys come from the event, not the
sidecar's row id. Server online/offline and a new kills leader are
in-memory transitions, never on first sight, and a tie is not a lead.
A login with no approval within a minute becomes a staff notice via a
query, so a restart loses nothing.
Also fixes a phase-4 gap (D68): the refresh now asks /health, so a game
that hung, or whose bridge was unloaded, while the sidecar stayed up no
longer reads as online. It stops naming players as online, and a stale
board no longer moves "last seen".
engagement-triggers.json is the committed freeze of all of it, checked
in CI with line endings normalised. The check was verified by breaking
it both ways.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY