docs(engagement): Phase 12 as built — the 12a/12b split and three findings #198

Merged
whitlocktech merged 1 commits from docs/engagement-phase-12 into edge 2026-09-01 13:19:24 +00:00
Member

The design of record for Phase 12. Pairs with runicgateway.com#26 (12a, mergeable on edge today) and runicgateway.com#27 (12b, a draft held deliberately red for the Phase 13 window).

The split, and why building the phase sharpened its own timing note

§12 says the work is "written and reviewed before the cutover and merged inside the same window". Building it turned that into something more specific than a scheduling preference:

  • checkFacts.mjs fetches from main, so the moment platform.json says protocol: 5 the repo is red — and it stays red for the entire edge window, which is a long time to keep a public site's CI failing on purpose.
  • Three of its values cannot be written today at all. bundle.tag / bundle.sidecar / bundle.overlay name an artefact that does not exist until Phase 13 has landed link + servuo-plugins on main and CI has republished the bundle.

So the org lead split it. Phase 13's order gains the fill-in step, written to be mechanical rather than remembered — the check names its own expected values, so it is run checkFacts and copy the "says" column, plus the two pages that quote the bundle to re-read before merging.

Two of the phase's own statements were already stale

Both because Phase 11 moved after §12 was written:

  • moduleApi is 1.9.0, not 1.7.0 — 1.8.0 for the admin ceiling, 1.9.0 for the module seed API. Phase 13's step 3 is corrected to match.
  • The capabilities.mjs "Web" claim was already fixed in Phase 7's rg.com#24, and the Gmail sweep is already done: grep -ri gmail src/ returns only Phase 1's migration note and one historical reference in Troubleshooting, which is exactly what the acceptance line asks for.

Three findings, none of them in the phase's text

1. /privacy had been publishing a false sentence since Phase 8. The app-content row claimed "Nothing is cached for offline use and nothing is duplicated anywhere else", and Phase 8 shipped a DataStore snapshot of the inbox. That row is an input to the generated Play Data Safety answers, so it is a store-review matter rather than a doc nit — a form at Google cannot be corrected without a review round, which is the whole argument for generating it from the inventory in the first place.

2. Nothing in the engagement schema has a retention sweep. engagementCooldowns.db.prune() exists and has no caller; a finished engagement_outbox row is never deleted; the send log and the suppression list grow forever. The new privacy row says so plainly rather than implying an expiry nobody wrote. Recorded, not fixed — a sweep is a website change and outside this phase's scope.

3. The one page whose whole subject is the protocol number was the one page that hardcoded it. platform.json's own header says no version number is ever hardcoded in prose; protocol-versions.mdx wrote 4 three times. It reads the JSON now. The trap for whoever does this again is recorded with it: MDX does not evaluate an expression inside backticks, so the interpolation has to sit outside the code span or the braces reach the published page.

Files

  • website/ENGAGEMENT.md — Phase 12's As built section (the split, the page it owed, the three findings), Phase 13's runicgateway.com step rewritten as the two-merge order with the fill-in recipe, step 3's MODULE_API_VERSION corrected to 1.9.0, and §6.0b's Phase 12 row naming both PRs.

AI-assisted: written with Claude Code.

🤖 Generated with Claude Code

The design of record for Phase 12. Pairs with **runicgateway.com#26** (12a, mergeable on `edge` today) and **runicgateway.com#27** (12b, a draft held deliberately red for the Phase 13 window). ## The split, and why building the phase sharpened its own timing note §12 says the work is "written and reviewed before the cutover and merged inside the same window". Building it turned that into something more specific than a scheduling preference: - `checkFacts.mjs` fetches from **`main`**, so the moment `platform.json` says `protocol: 5` the repo is red — and it stays red for the entire `edge` window, which is a long time to keep a public site's CI failing on purpose. - **Three of its values cannot be written today at all.** `bundle.tag` / `bundle.sidecar` / `bundle.overlay` name an artefact that does not exist until Phase 13 has landed `link` + `servuo-plugins` on `main` and CI has republished the bundle. So the org lead split it. **Phase 13's order gains the fill-in step**, written to be mechanical rather than remembered — the check names its own expected values, so it is *run `checkFacts` and copy the "says" column*, plus the two pages that quote the bundle to re-read before merging. ## Two of the phase's own statements were already stale Both because Phase 11 moved after §12 was written: - **`moduleApi` is 1.9.0, not 1.7.0** — 1.8.0 for the `admin` ceiling, 1.9.0 for the module seed API. Phase 13's step 3 is corrected to match. - **The `capabilities.mjs` "Web" claim was already fixed** in Phase 7's rg.com#24, and the Gmail sweep is already done: `grep -ri gmail src/` returns only Phase 1's migration note and one historical reference in Troubleshooting, which is exactly what the acceptance line asks for. ## Three findings, none of them in the phase's text **1. `/privacy` had been publishing a false sentence since Phase 8.** The `app-content` row claimed *"Nothing is cached for offline use and nothing is duplicated anywhere else"*, and Phase 8 shipped a DataStore snapshot of the inbox. That row is an **input to the generated Play Data Safety answers**, so it is a store-review matter rather than a doc nit — a form at Google cannot be corrected without a review round, which is the whole argument for generating it from the inventory in the first place. **2. Nothing in the engagement schema has a retention sweep.** `engagementCooldowns.db.prune()` exists and **has no caller**; a finished `engagement_outbox` row is never deleted; the send log and the suppression list grow forever. The new privacy row says so plainly rather than implying an expiry nobody wrote. **Recorded, not fixed** — a sweep is a `website` change and outside this phase's scope. **3. The one page whose whole subject is the protocol number was the one page that hardcoded it.** `platform.json`'s own header says no version number is ever hardcoded in prose; `protocol-versions.mdx` wrote `4` three times. It reads the JSON now. The trap for whoever does this again is recorded with it: **MDX does not evaluate an expression inside backticks**, so the interpolation has to sit outside the code span or the braces reach the published page. ## Files - **`website/ENGAGEMENT.md`** — Phase 12's *As built* section (the split, the page it owed, the three findings), Phase 13's runicgateway.com step rewritten as the two-merge order with the fill-in recipe, step 3's `MODULE_API_VERSION` corrected to 1.9.0, and §6.0b's Phase 12 row naming both PRs. --- AI-assisted: written with Claude Code. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wtclaude added 1 commit 2026-09-01 13:17:17 +00:00
Records Phase 12 against runicgateway.com#26 (12a, mergeable) and #27 (12b, a draft held
for the cutover window).

The split is the org lead's decision, and building the phase sharpened the reason the
timing note only gestured at: checkFacts.mjs fetches from `main`, so the moment
platform.json says protocol 5 the repo is red for the whole edge window -- and three of
its values cannot be written today at all, because bundle.tag/sidecar/overlay name an
artefact that does not exist until Phase 13 republishes it.

Phase 13's order gains the fill-in step, written to be mechanical rather than remembered:
the check names its own expected values, so it is "run checkFacts and copy the says
column", plus the two pages that quote the bundle to re-read first.

Two of the phase's own statements were already stale. moduleApi is 1.9.0, not 1.7.0
(Phase 11 moved it twice after that sentence was written), and Phase 7 had already fixed
the capabilities.mjs "Web" claim. Phase 13's step 3 corrected to 1.9.0 as well.

Three findings, none of them in the phase's text:

  1. /privacy had been publishing a false sentence since Phase 8 -- app-content claimed
     nothing is cached offline, and Phase 8 shipped a DataStore snapshot of the inbox.
     That row is an INPUT to the generated Play Data Safety answers, so it is a store
     review matter rather than a doc nit.
  2. Nothing in the engagement schema has a retention sweep. cooldowns' prune() has no
     caller, finished outbox rows are never deleted, and the send log and suppression
     list grow forever. Recorded rather than fixed -- a sweep is a website change and
     outside this phase.
  3. The one page whose whole subject is the protocol number was the one page that
     hardcoded it, against platform.json's own rule. Plus the trap for whoever does this
     again: MDX does not evaluate an expression inside backticks.

AI-assisted: written with Claude Code.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech merged commit 1dfc786fe8 into edge 2026-09-01 13:19:24 +00:00
whitlocktech deleted branch docs/engagement-phase-12 2026-09-01 13:19:25 +00:00
Sign in to join this conversation.
No description provided.