feat(events): send the idempotency key, and declare champ.boss.killed (Phase 11a)
The website's half of protocol 6. Every event-driven write now carries the step's idempotency key, and `uo.broadcast` stops being un-retryable. Phase 9 shipped it answering `retry: false` to everything including a 503 from a shard that was merely restarting, with a comment naming the line that would change when the wire could refuse a repeat. This is that line: it defers to `sidecarFailure`, the same helper its two siblings already used, so the hand-rolled variant that forced every outcome terminal is gone rather than re-tuned. One verb was less idempotent than its own id made it look. Both keyed verbs post under a run-scoped id and a repeat replaces — but `news.add` with `announce: true` makes the criers proclaim the title on every post, so a retry replaced the article silently and proclaimed it again. The key stops the second proclamation. `champ.boss.killed` is mapped to the `champs` feature (rule 2 would otherwise fail it closed to admin), with `damagers` a nested `staff` field rule: the kill is public because a champion falling is what the board is for, the ranked roll of who was strong enough to fell it is not. `uo.champ.boss_killed` is declared as a trigger — which is what makes it usable as an event PHASE CONDITION, since a condition is written over a trigger firing — and it carries `damagerCount`, never a damager name, because a trigger variable reaches mail an operator may address to every subscriber. Its seeded rule is its own group, `champ-boss-killed-v1`: `triggers-v1` is stamped once under a settings guard, so appending a 27th entry would have reached fresh installs and nothing else. It also ships email+inapp and NOT push, and the comment says why — no trigger in this module is also a registered stream, so no engagement rule here can push. That is pre-existing in twenty rules and flagged rather than fixed; this one declines to be the twenty-first. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -83,7 +83,27 @@ const FEATURES = {
|
||||
// ── Shipped before v3. Defaults reproduce the previous hardcoded behavior. ──
|
||||
status: { audience: 'anonymous', fields: {} },
|
||||
activity: { audience: 'anonymous', fields: {} },
|
||||
champs: { audience: 'anonymous', fields: {} },
|
||||
// Protocol 6 adds `champ.boss.killed` to this feature, and with it the first
|
||||
// field on a champs frame that is about PEOPLE rather than about an altar.
|
||||
//
|
||||
// `damagers` is the ranked table of who fought the boss and for how much. It is
|
||||
// the honest basis for "who slew the champion" and it is also a performance
|
||||
// record of named players that nobody consented to publish, which is precisely
|
||||
// the tension the ladder exists to let a shard resolve for itself. It defaults
|
||||
// to `staff`: the kill is public (a champion falling is announced in-world and
|
||||
// is the content the board is for), the roll of who did the damage is not. A
|
||||
// shard that wants a public board lowers one rule.
|
||||
//
|
||||
// Nested for the same reason `market.fees` and `houses.schedule` are: one rule
|
||||
// covers the whole table rather than a rule per column, and the columns here
|
||||
// are actor objects whose `acct`/`webId` remain admin-only by the locked-field
|
||||
// rule regardless of what this is set to.
|
||||
//
|
||||
// `killer` is deliberately NOT listed. It is the single actor whose blow landed
|
||||
// last, it is announced in-game to everyone present, and it is the same shape
|
||||
// and the same disclosure `mob.killed` has published on the public activity
|
||||
// feed since before this framework existed.
|
||||
champs: { audience: 'anonymous', fields: { damagers: 'staff' } },
|
||||
guilds: { audience: 'anonymous', fields: {} },
|
||||
governors: { audience: 'anonymous', fields: {} },
|
||||
// The public Houses page showed IDOC location only; owner/price were staff.
|
||||
@@ -189,6 +209,10 @@ const KIND_FEATURE = new Map(
|
||||
// boards
|
||||
'champ.update': 'champs',
|
||||
'champ.remove': 'champs',
|
||||
// Protocol 6. Without this line rule 2 would fail the new kind closed to
|
||||
// admin-only — correct as a default, and wrong as an outcome: a champion
|
||||
// falling is exactly what the public board is for.
|
||||
'champ.boss.killed': 'champs',
|
||||
'guild.update': 'guilds',
|
||||
'guild.remove': 'guilds',
|
||||
'guild.join': 'guilds',
|
||||
|
||||
Reference in New Issue
Block a user