docs(website): record the org lead's answers to Q1/Q3/Q5/Q7, and add Phase 1b #177

Merged
whitlocktech merged 2 commits from docs/engagement-decisions into edge 2026-08-29 00:48:04 +00:00

2 Commits

Author SHA1 Message Date
a9ba4707c8 docs(website): revert the §6.0a "correction" — android-app really had no edge
Phase -1 is now executed, and running it disproved the correction made in the
previous commit. android-app's `edge` did not exist: the push reported
`[new branch]`, and `git ls-remote --heads origin edge` confirms it. §6.0a was
right the first time — three repos needed a branch cut, not two.

The cause is worth keeping, because it read as a real measurement. `git fetch
origin` does not prune, so android-app's `refs/remotes/origin/edge` survived the
server-side deletion after the M12 cutover. rev-parse resolved it and
`rev-list --left-right --count` returned "1 behind" — a plausible number for a
stale branch, which is exactly what it looked like. A remote-tracking ref is a
cache, not an answer; ls-remote is.

Records Phase -1 as complete: five fast-forwards (module-uo 9, installer 7,
servuo-plugins 7, website 5, link 3) plus three branches cut from main
(android-app, runicgateway.com, Integration-kit).

Assisted-By: Claude Code (Opus 5)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-28 19:44:46 -05:00
3d93b3865c docs(website): record the org lead's answers to Q1/Q3/Q5/Q7, and add Phase 1b
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>
2026-08-28 19:40:49 -05:00