docs(admin): the engagement rules screen, and the privacy inventory an engagement mailer changes #26

Merged
whitlocktech merged 1 commits from docs/engagement-rules-and-privacy into edge 2026-09-01 13:18:57 +00:00
Member

Engagement Phase 12a — the half of Phase 12 that is green today. Its other half is #27, a draft held deliberately red until the Phase 13 cutover; the org lead's decision was to split them rather than park a failing check for the whole edge window.

Two corrections to the plan's own Phase 12 text before anything else, both found by reading the tree rather than the plan:

  • It says moduleApi1.7.0. The real value is 1.9.0 — Phase 11 moved it twice after that sentence was written. (Carried in #27, not here.)
  • It lists the capabilities.mjs "Web" claim as outstanding. Phase 7 already fixed it (#24), and grep -ri gmail src/ is already down to the migration and historical references the acceptance line allows.

The page that was missing

The site had a page for where a message goes (Notifications and email) and one for what it says (Message templates), and nothing at all for what makes one get sent — which is the entire admin surface this workstream built.

Engagement rules covers Rules, Audiences, the trigger catalog and the send log on one page, sitting between the two it joins up. Templates already has its own page and Suppressions is in Troubleshooting, so neither is repeated.

Two things it exists to state plainly:

Every rule ships disabled, including the ones a module brings. "Installed" is not "on", and the upgrade whose Team mail went quiet is the same fact wearing a different hat.

The ceiling is a tree, not a ladder. This is the one part of the system a reader will get wrong by reasoning about it, and the example is the whole argument:

The tempting reading is a ladder — that a staff-only event could obviously also go to just one person. It cannot, and "just one person" for cheat detection is the player it was detected on. The question a ceiling answers is never how many, it is which.

Troubleshooting gains the symptom that page answers — nothing is sent for one particular event — with the four causes in the order an operator should check them: the rule is off, the rule is dormant, its own cooldown held it, the audience is empty.

The sentence that had become false

/privacy's app-content row claimed "Nothing is cached for offline use and nothing is duplicated anywhere else". Phase 8 shipped a DataStore snapshot of the inbox, so it had been untrue since — and that row is an input to the generated Play Data Safety answers, which is a store-review matter rather than a doc nit.

The snapshot now has its own row and its own Play mapping (Messages / Other in-app messages; not collected by us, stored on the device), and app-content's claim is narrowed to everything else rather than quietly softened. Read out of DataStoreInboxCache.kt: at most thirty items, first page only, owner-scoped, deleted on sign-out, in the ordinary preference store rather than the encrypted one — which the row says, because these are notification bodies rather than credentials and someone with root on the device can read them.

deploy-engagement is the new deployment-scope row: an address is now used for more than getting into an account, there is a delivery log holding a one-way hash of it, and there is a suppression list holding the same hash plus a masked form.

Its retention line says what is true rather than what a reader assumes. None of these tables has a retention sweep — engagementCooldowns.db.prune() exists and has no caller, and a finished outbox row is never deleted — so the honest answer is "kept until the operator removes them; nothing here expires on its own". Flagged rather than fixed: a retention sweep is a website change and outside this phase.

PLAY_DATA_SAFETY.md is regenerated from the inventory, and legal.lastUpdated moves with the page it dates.

Verification

The whole verify chain, green:

  • checkSidebar — 5 groups, 42 pages, and plannedSidebar moved with the live tree, which is the drift it exists to catch
  • checkFacts 19/19 · checkQuickstart 59 · checkReference 22 · checkLinks 2605
  • checkA11y 52 pages · checkCsp 52 pages · playDataSafety --check · astro check 0 errors
  • 42 + 7 tests

Read in a browser against the served build as well — the ceiling and send-log tables, the sidebar entry in place, and /privacy rendering both new rows.


AI-assisted: written with Claude Code.

🤖 Generated with Claude Code

Engagement **Phase 12a** — the half of Phase 12 that is green today. Its other half is **#27, a draft held deliberately red** until the Phase 13 cutover; the org lead's decision was to split them rather than park a failing check for the whole `edge` window. Two corrections to the plan's own Phase 12 text before anything else, both found by reading the tree rather than the plan: - It says `moduleApi` → **1.7.0**. The real value is **1.9.0** — Phase 11 moved it twice after that sentence was written. (Carried in #27, not here.) - It lists the `capabilities.mjs` "Web" claim as outstanding. **Phase 7 already fixed it** (#24), and `grep -ri gmail src/` is already down to the migration and historical references the acceptance line allows. ## The page that was missing The site had a page for *where* a message goes (Notifications and email) and one for *what it says* (Message templates), and nothing at all for **what makes one get sent** — which is the entire admin surface this workstream built. `Engagement rules` covers Rules, Audiences, the trigger catalog and the send log on one page, sitting between the two it joins up. Templates already has its own page and Suppressions is in Troubleshooting, so neither is repeated. Two things it exists to state plainly: **Every rule ships disabled, including the ones a module brings.** "Installed" is not "on", and the upgrade whose Team mail went quiet is the same fact wearing a different hat. **The ceiling is a tree, not a ladder.** This is the one part of the system a reader will get wrong by reasoning about it, and the example is the whole argument: > The tempting reading is a ladder — that a staff-only event could obviously also go to just one person. It cannot, and "just one person" for cheat detection is *the player it was detected on*. The question a ceiling answers is never how many, it is **which**. Troubleshooting gains the symptom that page answers — *nothing is sent for one particular event* — with the four causes in the order an operator should check them: the rule is off, the rule is dormant, its own cooldown held it, the audience is empty. ## The sentence that had become false `/privacy`'s `app-content` row claimed **"Nothing is cached for offline use and nothing is duplicated anywhere else"**. Phase 8 shipped a DataStore snapshot of the inbox, so it had been untrue since — and that row is an input to the generated Play Data Safety answers, which is a store-review matter rather than a doc nit. The snapshot now has its **own row and its own Play mapping** (Messages / Other in-app messages; *not collected by us, stored on the device*), and `app-content`'s claim is narrowed to everything else rather than quietly softened. Read out of `DataStoreInboxCache.kt`: at most thirty items, first page only, owner-scoped, deleted on sign-out, in the ordinary preference store rather than the encrypted one — which the row says, because these are notification bodies rather than credentials and someone with root on the device can read them. `deploy-engagement` is the new deployment-scope row: an address is now used for **more than getting into an account**, there is a delivery log holding a one-way hash of it, and there is a suppression list holding the same hash plus a masked form. > **Its retention line says what is true rather than what a reader assumes.** None of these tables has a retention sweep — `engagementCooldowns.db.prune()` exists and has no caller, and a finished outbox row is never deleted — so the honest answer is "kept until the operator removes them; nothing here expires on its own". Flagged rather than fixed: a retention sweep is a website change and outside this phase. `PLAY_DATA_SAFETY.md` is regenerated from the inventory, and `legal.lastUpdated` moves with the page it dates. ## Verification The whole `verify` chain, green: - `checkSidebar` — 5 groups, 42 pages, and **`plannedSidebar` moved with the live tree**, which is the drift it exists to catch - `checkFacts` 19/19 · `checkQuickstart` 59 · `checkReference` 22 · `checkLinks` 2605 - `checkA11y` 52 pages · `checkCsp` 52 pages · `playDataSafety --check` · `astro check` 0 errors - 42 + 7 tests Read in a browser against the served build as well — the ceiling and send-log tables, the sidebar entry in place, and `/privacy` rendering both new rows. --- AI-assisted: written with Claude Code. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wtclaude added 1 commit 2026-09-01 13:14:46 +00:00
Engagement Phase 12a. The site had pages for where a message goes (Notifications and
email) and what it says (Message templates), and nothing at all for what makes one get
sent -- the four Engagement screens the workstream built.

New page: Engagement rules. Rules, Audiences, the trigger catalog and the send log on
one page, sitting between the two it joins up. Templates already has its own page and
Suppressions is in Troubleshooting, so neither is repeated here.

Two things it exists to state plainly:

  * Every rule ships disabled, including the ones a module brings. "Installed" is not
    "on", and an upgrade whose Team mail went quiet is the same fact.
  * The ceiling is a TREE, not a ladder. The tempting reading -- a staff-only event
    could obviously also go to one person -- is wrong, and the example is the argument:
    "one person" for cheat detection is the player it was detected on.

Troubleshooting gains the symptom that page answers ("nothing is sent for one
particular event"): the rule is off, the rule is dormant, its own cooldown held it, or
the audience is empty.

Privacy: two rows the engagement work makes necessary, and one sentence it made false.

  * app-content claimed "Nothing is cached for offline use". Phase 8 shipped a DataStore
    snapshot of the inbox, so it was untrue -- and that row feeds the generated Play Data
    Safety answers, which is a store-review matter rather than a doc nit. The snapshot now
    has its own row and its own Play mapping (Messages / Other in-app messages; not
    collected by us, stored on the device), and app-content's claim is narrowed to
    everything else.
  * deploy-engagement, for the deployment scope: an address is now used for more than
    getting into an account, there is a delivery log holding a one-way hash of it, and
    there is a suppression list. Its retention line says what is true rather than what a
    reader assumes -- none of these tables has a retention sweep.

PLAY_DATA_SAFETY.md regenerated from the inventory; legal.lastUpdated moved with the page
it dates.

Verified: the whole `verify` chain green -- checkSidebar (plannedSidebar moved with the
live tree), checkFacts 19/19, checkQuickstart 59, checkReference 22, checkLinks 2605,
checkA11y, checkCsp, playDataSafety --check, 42 + 7 tests. Read in a browser as well, in
the served build.

AI-assisted: written with Claude Code.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech merged commit 83c438885e into edge 2026-09-01 13:18:57 +00:00
whitlocktech deleted branch docs/engagement-rules-and-privacy 2026-09-01 13:18:58 +00:00
Sign in to join this conversation.
No description provided.