feat(engagement): sixteen in-universe bodies, 25 seeded rules, the governor's letter (Phase 11b) #23
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/engagement-uo-templates"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The module's half of Phase 11b — the content behind 11a's declarations. Needs website#178 (
MODULE_API1.9.0); decisions in docs#.32 templates and 25 rules, shipped through core's new
api.registerEngagementSeeds. Every ruleenabled = 0— the registry enforces it rather than trusting it.The voice (decision 8)
The game-powered families read from inside Britannia, with a per-family in-fiction sender rather than one voice across all sixteen. That was the decision rather than the obvious answer: a shard where Lord Blackthorn writes to you personally about a champion spawn is a shard where the letter about your governorship means nothing.
Each ships an
emailbody and aninappbody in the same voice — one rule fires on both at once, and a player who reads the inbox item and then the mail must not meet two different narrators. The digest stays core's genericnotify.digest: a day of events rolled into a list is not a letter from anybody.The governor's letter, rendered
What stays plain (decision 9)
Nine of the 25 point at core's
notify.event/inapp.eventand author nothing — §4.6.1 property 1 exercised at scale. The line is drawn where fiction costs something real: a failed-login notice written as "a stranger sought entry to thy account" is indistinguishable in register from the phishing mail it warns about, and a moderator readinguo.cheat.detectedat two in the morning wants a name, a rule and a timestamp rather than a scroll.Both account-security triggers,
uo.server.up/down, and the five staff- and admin-ceilinged ones.The governor's letter (decision 10) —
uo.governor.appointed, the 25th trigger§8.6 records that
uo.points.rank_changedcannot address a person becausetop[]names a mobile serial — and the same reasoning was silently assumed to cover the governor. It does not.city.update'sgovernoris written byBridgeJson.Actor(), which emitsserial,name,acctand evenwebId. The winner is addressable today, with no protocol change.It fires from the same frame, the same transition and the same never-on-first-sight guard as
uo.governor.elected, which is unchanged. Two triggers rather than two audiences on one, because one trigger means one rule means one template — and the town's bulletin and the governor's letter are not the same text. An operator can run either alone.Presentational fragments
A template has no conditionals by design, and an unset optional interpolates to the empty string — right for a structural body, wrong for a sentence: "Be it known that , recorded to thy name, is this day found ." So the ternary stays in the mapper and its result arrives as a declared optional (Phase 5a's
forWhomprecedent). Two shapes:houseLabelfalls back to a seal number, because a warning has to name something the owner can act on;{{slainBy}}.closes as "has fallen." either way.All additive optionals, so no trigger version bump.
What a render sweep found
Every body rendered twice — once with each declared
example, once with required variables only — then scanned for the punctuation a vanished token leaves behind. Three real defects, none visible in the source and none catchable by any existing check:A notice concerning thy house at {{region}}→ "…thy house at ". A subject is the one place a hole is unmissable and the one place no block validator looks.place()already makes for coordinates; a frame with nothing to report produces no line rather than a punctuated empty one.The labels stay
required: falsedeliberately — a required variable missing refuses the emit, and a dropped notification is worse than a cosmetic hole. That leaves nothing at runtime to notice a mapper that forgot one, soengagementSeeds.test.jsasserts every label is supplied on every path that emits its trigger.One more trap worth naming
uo.champ.boss_upis a legal trigger id (§7.2 relaxed the grammar to admit_souo.house.idoc_warningcould exist) and an illegal template key — core's key pattern admits.and-, not_. Its body is keyeduo.champ.boss-up. Caught at registration as a boot failure, and asserted here so the next person meets it as a test name.Verification
check:importsandcheck:bundleclean.check:swaggerreports STALE from CRLF alone and regenerates byte-identical — no route changed.module.json→coreApi: "^1.9.0", version 0.5.0.AI-assisted: written with Claude Code.
🤖 Generated with Claude Code
11a declared the triggers; this is the content behind them. Ships through core's new api.registerEngagementSeeds (MODULE_API 1.9.0): 32 templates and 25 rules, every rule enabled = 0. THE VOICE (decision 8). The game-powered families read from inside Britannia, with a per-family in-fiction sender rather than one voice across all sixteen — Lord Blackthorn's court writes about the crown's business (the seat, the ballot) and nothing else, because a shard where Blackthorn writes to you personally about a champion spawn is a shard where the letter about your governorship means nothing. The Office of Deeds has houses, the Merchants' Guild vendors, a herald guilds, the town crier champion spawns, a guildmaster skills and quests, the Chronicler deaths, the keeper of the rolls leaderboards. WHAT STAYS PLAIN (decision 9). Nine of the 25 point at core's notify.event / inapp.event and author nothing, and the line is drawn where fiction costs something real: a failed-login notice written as "a stranger sought entry to thy account" is indistinguishable in register from the phishing mail it warns about, and a moderator reading uo.cheat.detected at 2am wants a name, a rule and a timestamp rather than a scroll. Both account-security triggers, server up/down, and the five staff/admin-ceiling ones. THE GOVERNOR'S LETTER (decision 10) — uo.governor.appointed, the 25th trigger. §8.6 records that uo.points.rank_changed cannot address a person because top[] names a mobile serial, and the same reasoning was silently assumed to cover the governor. It does not: city.update's `governor` is written by BridgeJson.Actor(), which emits serial, name, acct AND webId. The winner is addressable today with no protocol change. It fires from the same frame, the same transition and the same never-on-first-sight guard as uo.governor.elected, which stays exactly as declared — the town's bulletin and the governor's letter are two triggers because one trigger means one rule means one template, and they are not the same text. An operator can run either alone. PRESENTATIONAL FRAGMENTS, because a template has no conditionals by design and an unset optional interpolates to the empty string. Phase 5a's `forWhom` precedent: the ternary stays in the mapper and its result arrives as a declared optional. Two shapes — a LABEL always has a value and carries a sentence's spine (houseLabel falls back to a seal number); a TRAILING FRAGMENT may be empty and leads with its own space, so `{{slainBy}}.` closes as "has fallen." either way. Additive, so no version bump. A render sweep over all 32 bodies, twice — once with every declared example and once with required variables only — is what found these. Three defects it caught: an optional `{{region}}` in a subject line ("A notice concerning thy house at "); multi-optional ledger lines rendering "On hand: gold. Charged each period: gold." on a pre-v5 frame, now assembled in the mapper from the parts actually present, the same argument place() already makes; and a leading trailing-fragment opening a body with a stray space. The labels stay `required: false` deliberately — a missing one must never REFUSE an emit, since a dropped notification is worse than a cosmetic hole — so nothing at runtime would notice a mapper that forgot one. engagementSeeds.test.js is what notices. 524 module tests green; check:imports and check:bundle clean. check:swagger reports STALE from CRLF alone and regenerates byte-identical — no route changed. Refs docs ENGAGEMENT.md Phase 11b, decisions 8, 9, 10. Co-Authored-By: Claude <noreply@anthropic.com>