docs(engagement): widen Phase 10 to three wire enrichments and Phase 11 to the full trigger catalogue #192

Closed
wtclaude wants to merge 0 commits from docs/engagement-phase10-11-scope into edge
Member

Two scope decisions taken by the org lead on 2026-08-31, before any Phase 10 code, plus the three factual corrections that finding them out produced. Doc-only; no code in any repo yet.

Why now rather than in Phase 11

The org lead's ask was "house.decay is only one game-powered trigger that I want — propose others". The natural home for that is Phase 11. But two of the wanted triggers turned out to need wire fields, and Phase 10 is the one phase where a missed field is expensive: a protocol bump costs a sidecar release, a republished bundle and an operator update on every shard. Leaving them out does not cost a follow-up commit — it costs a second bump with the same three-part lead time, and an operator population split across two protocol versions in the meantime. So the Phase 11 decision had to be taken before the Phase 10 PR, not after it.

Phase 10 — three enrichments, not one

Added Why it could not wait
(a) house.decay enrichment (unchanged) as planned
(b) vendor.listing gains ownerAcct, holdGold, chargePerDay, daysRemaining uo.vendor.expiring is the same "owned asset at risk, with a real deadline" shape as the flagship. Today vendor.listing carries ownerSerial/ownerName — so the trigger can neither be addressed to a person nor know that anything is expiring
(c) a new post-decision account.login.result (acct, accepted, rejectReason, ip) EventSink.AccountLogin is a pre-decision veto hook — the emitter's own comment says "Fires before the auth decision, so this is an attempt, not a result"

Explicitly declined: moving vendor.sale out of the opt-in patch tier.

Also records two things the emitter work must not get wrong: estimatedCollapse is exact only at IDOC (dynamic decay draws each stage at random on entry), while a vendor's daysRemaining is exact — the two must not be documented as carrying the same confidence. And shardVisibility.js's KIND_FEATURE must be chosen for account.login.result rather than inherited: rule 2 fails an unmapped kind closed to admin-only, which is the right answer for a frame carrying an IP, but only by accident.

Phase 11 — the full catalogue

Replaces "ships exactly one rule" with every row of §8.6, carving out uo.market.item_listed for the reason §8.6 already gave (it is a saved search; no per-user query store exists anywhere in the tree). ~23 triggers, grouped in the phase by the audience kind each family exercises rather than by subject matter — because the point is not volume of mail, it is the first real test that the ceiling lattice holds when most triggers are not public, that check:modules still finds no UO identifier in core when the module is this large, and that a player's preferences catalogue stays legible when it is real. Every rule still ships enabled = 0 per Q3.

Flagged, not decided: the phase is now the largest in the workstream and will likely want an 11a (declarations, mappings, ceilings) / 11b (seeded templates + the live walk) split on the 4a/4b and 5a/5b precedent — to confirm at its start.

Three §8.6 rows were wrong

Each was checked against the emitters rather than against the table:

  • uo.vendor.sale — real, and it does carry ownerAcct. But it lives in servuo-plugins/patches/, the opt-in tier verified only against ServUO 57.4 (a PlayerVendorSale EventSink the patches add to core, plus a subscriber), which is why it is absent from overlay/. A shard that declined the tier emits it never — dormant, not broken. The obligation is that the seeded rule and the operator docs say so.
  • uo.vendor.expiring — was marked ⚠ "needs a mapper". It had no data at all.
  • uo.account.login_attempt — was marked "already a personal stream". A rule on it would have mailed "someone tried to log into your game account" every time the player logged in successfully — the exact inversion that makes people distrust security mail. Renamed uo.account.login_failed so the id cannot be misread again.

Also amended

The status header, scope decision 3, Part 8's preamble (§8.6 is now a commitment; §8.1–8.5 and §8.7 stay a catalogue), and §6.0b's documentation assignment for rows 10 and 11 — link/v5.md is now earned (three enrichments across three subsystems is what earned v3 and v4 theirs), the patch-tier caveat becomes an operator-facing doc obligation, and runicgateway.com's capability claim changes when "one rule" becomes "the catalogue".


  • AI-assisted — drafted with Claude Code (Claude Opus 5), reviewed before opening.

🤖 Generated with Claude Code

Two scope decisions taken by the org lead on **2026-08-31, before any Phase 10 code**, plus the three factual corrections that finding them out produced. Doc-only; no code in any repo yet. ## Why now rather than in Phase 11 The org lead's ask was "`house.decay` is only one game-powered trigger that I want — propose others". The natural home for that is Phase 11. **But two of the wanted triggers turned out to need wire fields**, and Phase 10 is the one phase where a missed field is expensive: a protocol bump costs a sidecar release, a republished bundle and an operator update on every shard. Leaving them out does not cost a follow-up commit — it costs a *second* bump with the same three-part lead time, and an operator population split across two protocol versions in the meantime. So the Phase 11 decision had to be taken before the Phase 10 PR, not after it. ## Phase 10 — three enrichments, not one | | Added | Why it could not wait | | --- | --- | --- | | **(a)** | `house.decay` enrichment (unchanged) | as planned | | **(b)** | `vendor.listing` gains `ownerAcct`, `holdGold`, `chargePerDay`, `daysRemaining` | `uo.vendor.expiring` is the same "owned asset at risk, with a real deadline" shape as the flagship. Today `vendor.listing` carries `ownerSerial`/`ownerName` — so the trigger can neither be **addressed to a person** nor **know that anything is expiring** | | **(c)** | a new post-decision `account.login.result` (`acct`, `accepted`, `rejectReason`, `ip`) | `EventSink.AccountLogin` is a pre-decision **veto hook** — the emitter's own comment says "Fires before the auth decision, so this is an attempt, not a result" | **Explicitly declined:** moving `vendor.sale` out of the opt-in patch tier. Also records two things the emitter work must not get wrong: `estimatedCollapse` is exact only at IDOC (dynamic decay draws each stage at random on entry), while a vendor's `daysRemaining` **is** exact — the two must not be documented as carrying the same confidence. And `shardVisibility.js`'s `KIND_FEATURE` must be *chosen* for `account.login.result` rather than inherited: rule 2 fails an unmapped kind closed to admin-only, which is the right answer for a frame carrying an IP, but only by accident. ## Phase 11 — the full ✅ catalogue Replaces "ships exactly one rule" with **every ✅ row of §8.6**, carving out `uo.market.item_listed` for the reason §8.6 already gave (it is a *saved search*; no per-user query store exists anywhere in the tree). ~23 triggers, grouped in the phase by **the audience kind each family exercises** rather than by subject matter — because the point is not volume of mail, it is the first real test that the ceiling lattice holds when most triggers are *not* public, that `check:modules` still finds no UO identifier in core when the module is this large, and that a player's preferences catalogue stays legible when it is real. Every rule still ships `enabled = 0` per Q3. Flagged, not decided: the phase is now the largest in the workstream and will likely want an **11a (declarations, mappings, ceilings) / 11b (seeded templates + the live walk)** split on the 4a/4b and 5a/5b precedent — to confirm at its start. ## Three §8.6 rows were wrong Each was checked against the emitters rather than against the table: - **`uo.vendor.sale`** — real, and it does carry `ownerAcct`. But it lives in `servuo-plugins/patches/`, the **opt-in tier verified only against ServUO 57.4** (a `PlayerVendorSale` EventSink the patches add to core, plus a subscriber), which is why it is absent from `overlay/`. A shard that declined the tier emits it *never* — dormant, not broken. The obligation is that the seeded rule and the operator docs say so. - **`uo.vendor.expiring`** — was marked ⚠ "needs a mapper". It had **no data at all**. - **`uo.account.login_attempt`** — was marked ✅ "already a personal stream". A rule on it would have mailed *"someone tried to log into your game account"* **every time the player logged in successfully** — the exact inversion that makes people distrust security mail. Renamed **`uo.account.login_failed`** so the id cannot be misread again. ## Also amended The status header, scope decision 3, Part 8's preamble (§8.6 is now a commitment; §8.1–8.5 and §8.7 stay a catalogue), and §6.0b's documentation assignment for rows 10 and 11 — `link/v5.md` is now earned (three enrichments across three subsystems is what earned v3 and v4 theirs), the patch-tier caveat becomes an operator-facing doc obligation, and `runicgateway.com`'s capability claim changes when "one rule" becomes "the catalogue". --- - [x] AI-assisted — drafted with Claude Code (Claude Opus 5), reviewed before opening. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wtclaude added 1 commit 2026-08-31 16:07:15 +00:00
Two scope decisions taken by the org lead on 2026-08-31, before any Phase 10 code,
plus the three factual corrections that finding them out produced.

Phase 10 — the protocol bump now carries three enrichments, not one. The argument is
specific to this phase: a bump costs a sidecar release, a republished bundle and an
operator update on every shard, so a field left out does not cost a follow-up commit,
it costs a second bump with the same lead time and a split operator population. The
two additions:

  * player-vendor fee state on vendor.listing (ownerAcct, holdGold, chargePerDay,
    daysRemaining), because uo.vendor.expiring is the same "owned asset at risk with
    a deadline" shape as the flagship and today has neither an address nor a deadline;
  * a post-decision account.login.result, because EventSink.AccountLogin is a veto
    hook that fires BEFORE the auth decision.

Moving vendor.sale out of the opt-in patch tier was explicitly declined.

Phase 11 — ships every checked row of 8.6 rather than a single rule, carving out
uo.market.item_listed (a saved search; no per-user query store exists). ~23 triggers
grouped by the audience kind each family exercises, since exercising the ceiling
lattice at scale is the point rather than volume of mail. Every rule still ships
enabled = 0 per Q3. Notes that the phase will likely want an 11a/11b split on the
4a/4b precedent, to confirm at its start.

Three corrections to 8.6, each verified against the emitters rather than the table:

  * uo.vendor.sale is real and does carry ownerAcct, but lives in servuo-plugins/
    patches/ (opt-in, verified only against ServUO 57.4) — dormant, not broken, on a
    shard that declined the tier;
  * uo.vendor.expiring had no data at all, not merely no mapper — vendor.listing
    carries ownerSerial/ownerName and nothing carries held gold or daily charge;
  * uo.account.login_attempt could not have been built as described — it would have
    mailed "someone tried to log into your account" on every successful login.
    Renamed uo.account.login_failed so the id cannot be misread again.

Also amended: the status header, scope decision 3, and 6.0b's documentation
assignment for rows 10 and 11 (v5.md now earned; the patch-tier caveat is an
operator-facing doc obligation; runicgateway.com's capability claim changes when
"one rule" becomes "the catalogue").

Co-Authored-By: Claude <noreply@anthropic.com>
Author
Member

Closing as already merged — nothing here was lost.

This PR's branch was the base for #193 rather than being independent of it, so its commit (efce1d8) rode into edge when #193 merged. edge now carries the whole scope amendment: the widened Phase 10, Phase 11's full catalogue, and the three corrected §8.6 rows. Verified against origin/edge.

That stacking was a mistake in how I opened these — #193 should have been rebased onto edge so the decision record and the as-built could be merged, or rejected, separately. The content is fine; only the reviewability suffered.

Closing as already merged — **nothing here was lost.** This PR's branch was the base for #193 rather than being independent of it, so its commit (`efce1d8`) rode into `edge` when #193 merged. `edge` now carries the whole scope amendment: the widened Phase 10, Phase 11's full ✅ catalogue, and the three corrected §8.6 rows. Verified against `origin/edge`. That stacking was a mistake in how I opened these — #193 should have been rebased onto `edge` so the decision record and the as-built could be merged, or rejected, separately. The content is fine; only the reviewability suffered.
wtclaude closed this pull request 2026-09-01 00:29:20 +00:00

Pull request closed

Sign in to join this conversation.
No description provided.