Four of the eight open questions in ENGAGEMENT.md §7.1 were answered on
2026-08-28. Q1's answer turned out to carry a whole phase with it.
- Q5: document all three SMTP postures, lead with a relay, name
Gmail-app-password as the migration path off OAuth2.
- Q3: rules stay operator-editable data, but `enabled` defaults to 0 and
every rule carries a hard per-hour send ceiling. Adds max_sends_per_hour
to engagement_rules — the ceiling is what makes "data" safe to choose
over "code".
- Q7: no campaigns surface at all. Lists DO exist, but only module-declared
and powered by module data, on a surface core exposes to every module.
Operators may compose them; composition must NARROW, never widen. Adds
§5.1a, api.registerAudiences and engagement_audience_segments.
- Q1: opt-in only, users.email becomes UNIQUE, and the verification gate is
an admin setting (on for fresh installs, off for upgrades).
New §0.6 records why the UNIQUE index is not a one-line ALTER, verified in
the tree rather than assumed:
- ensureSchema() runs the ALTER block on every boot, so ADD UNIQUE INDEX
against a table holding duplicates stops the site from starting.
- isDuplicateUsername() tests only ER_DUP_ENTRY/1062 and never which index
collided, so register would answer "that username is already taken" for a
duplicate email, and provisionSsoPlayer would retry usernames for an email
conflict until it exhausts PROVISION_MAX_TRIES and fails opaquely.
- SSO auto-provisioning manufactures those duplicates and marks addresses
verified merely for existing — which is also why dedupe is oldest-wins
rather than verified-wins. CLAUDE.md's "identities are never
auto-provisioned" is stale.
- No route lets a user change their own address, so a verification gate has
no flow to gate; Phase 1b builds one.
New Phase 1b sequences the fix before the index, dedupes oldest-wins with an
admin report, and keeps the collision error generic, rate-limited and out of
the bot scorer. Phase 9 loses the verification flow to it and therefore no
longer blocks Phase 11.
Also corrects §6.0a: android-app DOES have an edge (1 behind main), so
Phase -1 is six fast-forwards and two branch creations, not five and three.
Assisted-By: Claude Code (Opus 5)
Co-Authored-By: Claude <noreply@anthropic.com>