Repository navigation
Open
1 / 11 of 1 issue completedLabels
Description
State of Play
- Stage: every increment is merged; what is left is David's UAT. Increment 7c merged as PR Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled #847 — Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled (
e779e9b, 2026-11-03 05:08 UTC), after David's 2026-11-03 publish of1231526. The Repl is synced ate779e9band clean. Dev ran 0122: it reported 3captcha_verifiedand 1nsfw_mode_enabledrows true before the drop, neither column remains, and healthz is ok. Production still holds both columns until the next publish. Increment 7b merged as PR Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) #845 — Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) (1231526), resequenced in review because the Drizzle model declaration counts as a use. Plan approved 2026-10-31 ~00:03 UTC. Merged: Increment 1 as PR Launch Phase 5, Increment 1: Cloudflare Turnstile replaces hCaptcha #797 (e90cc20); Increment 2 as PR Launch Phase 5, Increment 2: single-use reset links, password changes end other sessions, provider-verified email #798 (3928c87); Increment 3 as PR Launch Phase 5, Increment 3: verify, then onboard, then act #801 (fabb03b); the Bug: a Google sign-in refused for an unconfirmed email points at password reset, which never sends a link to an account with no password #800 fix as PR Fix #800: password reset reaches an account that has no password yet #802 (3f7bdc0); Increment 3b as PR Launch Phase 5, Increment 3b: Google and Apple sign-up journeys in a browser #803 (37cbf39); the Increment 4 inventory's bugs, each its own PR: Bug: saving a meme accepts another member's private photo, and the meme image then shows it publicly #804 as PR Fix #804: a meme can only be saved from the saver's own photo #807 (69a991b), Bug: the admin population lock and the media backup lock share one number, so admin changes can wait behind a backup #805 as PR Fix #805: the admin population lock and the media backup lock get their own keys #809 (81f82e1), Bug: AI-generated images never get their upload_image_metadata row; a CHECK rejects the transform value and the error is swallowed #806 as PR Fix #806: a generated fact background is never recorded as someone's upload #811 (553cf14), Bug: setting a profile photo accepts another member's private photo and makes it public #810 as PR Fix #810: a profile photo can only be the account's own upload #812 (c8482fa), the CI flake Bug: CI's Test job sometimes fails after every test passes, because the admin-settings boot test leaves a database connection to be killed #808 as PR Fix #808: a pool on a database a test drops can no longer fail the whole shard #813 (2cd0b77); Increment 4a as PR Launch Phase 5, Increment 4a: a meme is a file from the moment it is saved #814 (0cc7744); Increment 4b as PR Launch Phase 5, Increment 4b: everything in My content is undoable for 30 days #815 (c36d821); Increment 5a as PR Launch Phase 5, Increment 5a: an account can leave cleanly — the deletion engine #820 (ca521d8); Increment 5b as PR Launch Phase 5, Increment 5b: leaving and staying from the screens, and what deletion keeps #833 (a9d58bb); its follow-up as PR Launch Phase 5, Increment 5b follow-up: an admin's removal overrides a member's own deletion request #834 (f40ad18); Increment 6a as PR Launch Phase 5, Increment 6a: account suspension #835 (a3aefb9); Increment 6b as PR Launch Phase 5, Increment 6b: NSFW readiness, and the NSFW opt-in withdrawn #836 (6c11857); Increment 7a as PR Launch Phase 5, Increment 7a: retire the admin soft delete and set-inactive edit #837 (54f5b2f); the docs sweep as PR Launch Phase 5 docs sweep: accounts, suspension, deletion, and the retired rules #841 (61a2d8b); Increment 7b as PR Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) #845 (1231526); Increment 7c as PR Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled #847 (e779e9b). The Repl was synced after each, and a build report is posted on each issue. - Plan:
docs/plans/PLAN_PHASE5_ACCOUNT_IDENTITY.mdv7, onmainsince PR Launch Phase 5, Increment 1: Cloudflare Turnstile replaces hCaptcha #797, approved at sha256ff0b124e8391917c642dd7c15b9a6a14dc88da45c444cca6ec92619ced34b442; amended in PR Launch Phase 5, Increment 5b: leaving and staying from the screens, and what deletion keeps #833 with David's 2026-11-02 answers on what deletion keeps (sha25652a059b6fccc0836d2c5293fdf0680e0376524bcc4d293113bffb73cdfa844af). Note: S23's "the captcha column dies in two releases" became three in practice, because the model declaration counts as a use (now a rule inmigrations-and-backfills.md, Expand, then contract). The plan file is not amended; the requirement stands. - Outcome: an account is a verified, consenting, age-known identity that can leave cleanly; the data model is ready for beta and adult content without shipping either.
- David's decisions (2026-10-30/31): facts persist forever; date of birth required, no fallback; 14-day cooling-off on account deletion; everything in My content is undoable for 30 days (photos included: a webcam photo has no copy on the device); marketing consent pre-checked for US visitors only; deletion re-authenticates with the password or a fresh provider sign-in ("it should be HARD to do without really meaning to"); no Terms text in Phase 5, Phase 14 does it once (an override of the recommendation, recorded with the dissent); subscription cancelled at the deletion request, remaining entitlements survive a cancel; the one-day Cloudflare cache residue accepted and stated; under-13 refused without destroying anything, never-onboarded new registrations cleaned up after 30 days; a "Keep my account" button cancels a deletion; memes are baked at save and are independent of their source photo. 2026-11-01 20:31: Increment 4b continues in this session rather than a fresh one. 2026-11-02, what account deletion keeps: a member who leaves keeps their votes and hearts in the totals without their name, and their comments and submissions credited to a former member (Phase 9's member comment-delete runs before launch); an admin's removal deletes them. 2026-11-03: published production (
61a2d8b), with the Turnstile secrets set; Turnstile passes live. Published again the same day (1231526), which let 7c go. - Decisions taken on my recommendation: Increment 1 — the Turnstile widget written in-house; CI stubs Cloudflare, with a live check post-merge (passed); a 10-second siteverify timeout failing closed; increments tracked as PRs here rather than sub-issues; the challenge mounts only when someone starts a comment or enters a share address. Increment 2 — the reset token is read before hashing, then claimed atomically; admin create-with-password revokes nothing; the unverified-provider refusal points at password sign-in or reset; only the password change moved to the session that actually signed in (the rest filed as Signed-in actions that act on the wrong session when an old sign-in token is left in the browser #799); the scanner's CSRF alert cleared by removing an unused cookie parser from a test. Increment 3 — the Google/Apple browser journey split out as 3b; the OAuth new-user redirect to onboarding stays; the admin-permissions e2e watches the comment challenge; seeded and fixture accounts created set up with a synthetic adult date of birth; the production-mode test server takes only Cloudflare's test Turnstile secrets; the consent wording version is a server constant; a custom pronoun set is capped at the column's 80 characters. Bug: a Google sign-in refused for an unconfirmed email points at password reset, which never sends a link to an account with no password #800 fix — following a reset link does not also verify the email. Increment 3b — a refused provider sign-in shows its message in the sign-in popup, as every callback error does today (a gap); the fake sign-in takes whatever email the test types. Bug fixes — a photo with no owner is refused like another member's (Bug: saving a meme accepts another member's private photo, and the meme image then shows it publicly #804, Bug: setting a profile photo accepts another member's private photo and makes it public #810); the backup lock's number moved, not the admin lock's, with no handoff code for the publish window, settled over Astra's dissent (Bug: the admin population lock and the media backup lock share one number, so admin changes can wait behind a backup #805); the dead lineage write removed rather than repaired (Bug: AI-generated images never get their upload_image_metadata row; a CHECK rejects the transform value and the error is swallowed #806); test pools on a dropped database get an error listener (Bug: CI's Test job sometimes fails after every test passes, because the admin-settings boot test leaves a database connection to be killed #808). Increment 4a — Increment 4 split into 4a and 4b with all of 0115 in 4a; the backfill of older memes runs as a boot job; an unbaked meme, or one whose file is missing, falls back to the live render; the caption is baked with the identity the save used; the baked image gets no NSFW scoring (deferred "until Increment 6", now carried to Phase 9); Codex's two round-1 findings deferred, settled over Astra's dissent by Fable's tie-break (the duplicate-save key to 4b, baked-file cleanup at account purge to Increment 5). Increment 4b — a deleted item stays visible, uncached, to its owner only until purged; Recently Deleted is its own Library tab; the current profile photo cannot be deleted; a photo's derivatives go and return with it; a generated image is the account's only when no fact gallery serves it; 0116 seeds the restore window (later plan migrations each move up one) and indexes
user_ai_images.storage_path; Recently Deleted shows the 200 most recent; the purge runs hourly from five minutes after boot; a deleted meme's page keeps its "removed" 410. Increment 5a — Increment 5 split into 5a (server) and 5b (screens, admin panel, never-onboarded cleanup); a Google/Apple-only account re-authenticates with a sign-in from the last ten minutes; the request records who asked (requested_by); an admin's purge-now sends no "scheduled" email; subscriptions cancelled at once, every billable state included; the last-administrator check applies when the account being deleted is an admin; the purge deletes the person's own uploads, profile photo included; Stripe faked through a small test seam; the soft "deactivate" path unchanged until Increment 6; the worker runs hourly; from the review rounds — stage changes fenced to the request generation, a refresh that did not apply is retried, billing settled after "Keep my account" (refresh-only, versioned, crash-safe with a 10-minute bound), acceptance takes the owner content lock, the purge recounts comment counts; the crash window Astra framed as David's call was closed rather than asked. Increment 5b — an unfinished sign-up is recognised by a column only the two sign-up paths set (0118, no default; admin-made accounts never selected); an account that kept itself once is never auto-scheduled again; at most 100 scheduled per hourly run; an unfinished sign-up gets its own email; comments, submissions and links deleted on an admin removal (the votes split extended); a kept comment shows "Former member"; the admin panel lists kept-but-unsettled billing and stuck purges, "Purge now" retries a stuck purge, an admin's keep emails the member; the admin panel's endpoints beside the member's; the name sheet never shows on/account/*; one shared full-page navigation seam; plan migrations move up two more; from round 1 — account deletion forgets the member in the browser as Sign out does, and a kept submission carries a former-member marker onto the fact a later approval creates (both in 0119, which was unmerged). 5b follow-up (PR Launch Phase 5, Increment 5b follow-up: an admin's removal overrides a member's own deletion request #834) — the user editor's "Delete User" takes over a member's own pending request (the admin rule applies; undo:takeOver: false), while the panel's "Purge now" keeps who asked; the panel shows at most 200 rows; from round 1 — the purge reads who asked under its lock. Increment 6 — split into 6a (suspension, 0120) and 6b (NSFW readiness and the beta-overlay row, 0121); the contract moves to 0122. Increment 6a (PR Launch Phase 5, Increment 6a: account suspension #835) — dedicated suspend/unsuspend routes rather than PATCH fields; suspension does not cancel billing; dev-admin-login gets no refusal of its own (Must not change), the gate still blocks its writes; the suspended copy says only "This account is suspended."; the editor's delete confirmation now says it overrides a member's own request; from round 1 — Google/Apple sign-in decides suspension before the upsert writes anything, forgot-password issues nothing for a suspended account (same reply as any address), reset-password refuses a suspended owner's link before claiming it; from round 2 — every sign-in mints its session under a share lock on the account row (createSignInSession), so a sign-in and a suspension that overlap leave no session; from round 3 — a stored-field write landing in the instant a suspension commits (a reset's password, a verification, a provider link) recorded as a gap, not fixed: its end state is the one a moment-earlier action would leave, no session, no capability. Increment 6b (PR Launch Phase 5, Increment 6b: NSFW readiness, and the NSFW opt-in withdrawn #836) —beta_overlaya declared exception to "every grid key is consulted" rather than aFEATURE_KEYSentry (editable at /admin/features, read by nothing); the classifier keeps its opt-in parameter with every caller passing false; the withdrawn field also leaves the API and the auth middleware; 0121's checks written in Postgres's dump-and-restore form so production's adoption proof compares equal; the legacy fixture leavesaudienceto its default. Increment 7 — split into 7a (no publish needed) and 7b (0122, after the next publish, also droppingnsfw_mode_enabled). Increment 7a (PR Launch Phase 5, Increment 7a: retire the admin soft delete and set-inactive edit #837) — a soft DELETE is refused 400 rather than treated as a hard delete; PATCH ignoresisActive; the unreachable reinstatement path andreserveAccountForDeletiondeleted rather than kept dormant; the docs sweep its own Documentation PR;/simplifynot run on a 525-line removal. Docs sweep (PR Launch Phase 5 docs sweep: accounts, suspension, deletion, and the retired rules #841) — tracked as its own sub-issue (Launch Phase 5 docs sweep — accounts, suspension, deletion, and the retired rules #840); Manual durations stated as intent, with the figures left to the spec (the Manual's tuning guard); fleet-level gotchas left for KITT, whose files these are; the retired glossary entries (Deactivate, Reinstate) keep their anchors. Increment 7b (PR Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) #845) — resequenced in review into 7b (the fields leave the model) and 7c (0122 drops the columns after the next publish), as a technical fork settled in the loop; a temporary two-column exemption (UNDECLARED_PENDING_DROP) in the parity test, removed with the drop;/simplifynot run. Increment 7c (PR Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled #847) — 0122 reports the count oftruerows it discards rather than copying them; Replit's empty "Published your App" commit (0fd06116) kept as a local Repl branch and the Repl'smainrealigned, rather than opening a PR with an empty diff;/simplifynot run. - Carried forward: Next to plan — linked sign-in methods and the "Last used" badge, now its own workstream: Issue Linked sign-in methods and the "Last used" badge #846 — Linked sign-in methods and the "Last used" badge (scope agreed 2026-11-03; planning handed to a fresh session). Later — remove the unused
external_linkstable (nothing writes to it; 0 rows on dev); the deletion panel could point at the user editor for an abuse removal; one shared "survives if a reachable admin" lockout helper for suspension and deletion; if the round-3 gap of PR Launch Phase 5, Increment 6a: account suspension #835 is ever closed, the reset's lock must be a write lock taken before the claim, neverFOR SHARE; a member who asked to delete and is then suspended cannot keep the account (by the plan; from PR Launch Phase 5, Increment 6a: account suspension #835's build report); a stale comment inartifacts/api-server/src/lib/membershipRefresh.tsstill names "admin soft-delete, reinstatement" as callers (from PR Launch Phase 5 docs sweep: accounts, suspension, deletion, and the retired rules #841); since reinstatement went, the admin surface has no manual way to force a Stripe re-check (the accepted Stripe↔local reconciliation gap indeferred-work.md); the admin users screen's "inactive users" filter now lists a state nothing writes (from PR Launch Phase 5 docs sweep: accounts, suspension, deletion, and the retired rules #841's build report); three fleet-level lessons are owed to KITT, whose files they belong in (a field made mutable exposes every early read; chain a commit after tests with&&;check-uat-formatneeds both Setup and Regression); the Manual's account-deletion help names no figure for the cooling-off (by the tuning guard), so the deletion screen is where a member reads the date (from PR Launch Phase 5 docs sweep: accounts, suspension, deletion, and the retired rules #841's build report). Needs David confirmation (compliance phase) — whether any local retention of payment records is required beyond Stripe's own (the retention matrix's open question, from PR Launch Phase 5 docs sweep: accounts, suspension, deletion, and the retired rules #841). Phase 9 — a member's own comment-delete button (before launch);DELETE /admin/comments/:idadjusts no comment count (pre-existing; unreachable for a counted comment today); NSFW scoring of a baked meme as a whole (Increment 4a's deferral, which no Increment 6 picked up; PR Launch Phase 5, Increment 6b: NSFW readiness, and the NSFW opt-in withdrawn #836's build report). - Waiting on: David's UAT (below). Nothing of Claude's.
- What's blocking: nothing.
- At the next publish (David): Replit's schema step should drop exactly
users.captcha_verifiedandusers.nsfw_mode_enabledand nothing else; if it offers any other change, cancel and tell Claude. - UAT owed: PR Launch Phase 5, Increment 1: Cloudflare Turnstile replaces hCaptcha #797's two judgement steps; PR Launch Phase 5, Increment 2: single-use reset links, password changes end other sessions, provider-verified email #798's one; PR Launch Phase 5, Increment 3: verify, then onboard, then act #801's three (new-account path, your one-time ask, the consent sentence); PR Fix #800: password reset reaches an account that has no password yet #802's one (how "reset your password" reads to someone who never had one); PR Launch Phase 5, Increment 4a: a meme is a file from the moment it is saved #814's one (a saved meme looks the way it did in the builder); PR Launch Phase 5, Increment 4b: everything in My content is undoable for 30 days #815's one (deleting and restoring reads clearly); PR Launch Phase 5, Increment 5a: an account can leave cleanly — the deletion engine #820's one (the admin hard delete reads clearly); PR Launch Phase 5, Increment 5b: leaving and staying from the screens, and what deletion keeps #833's three (deleting, keeping, the admin panel); PR Launch Phase 5, Increment 6a: account suspension #835's one (the suspended screen reads right). PR Launch Phase 5, Increment 5b follow-up: an admin's removal overrides a member's own deletion request #834, PR Launch Phase 5, Increment 6b: NSFW readiness, and the NSFW opt-in withdrawn #836, PR Launch Phase 5, Increment 7a: retire the admin soft delete and set-inactive edit #837, PR Launch Phase 5 docs sweep: accounts, suspension, deletion, and the retired rules #841, PR Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) #845, PR Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled #847 and the bug-fix PRs Fix #804: a meme can only be saved from the saver's own photo #807, Fix #805: the admin population lock and the media backup lock get their own keys #809, Fix #806: a generated fact background is never recorded as someone's upload #811, Fix #810: a profile photo can only be the account's own upload #812 and Fix #808: a pool on a database a test drops can no longer fail the whole shard #813 owe none. On dev, all 26 accounts will be asked to onboard once; 12 must verify first; the dev admin is verified, not onboarded.
- Watch: if CI's Test job again fails with every shard at
# fail 0, PR Fix #808: a pool on a database a test drops can no longer fail the whole shard #813's fix for Bug: CI's Test job sometimes fails after every test passes, because the admin-settings boot test leaves a database connection to be killed #808 missed; reopen Bug: CI's Test job sometimes fails after every test passes, because the admin-settings boot test leaves a database connection to be killed #808. - Last movement: 2026-11-03 05:12 UTC. PR Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled #847 merged and the Repl synced.
- Spend so far: about $19.50 for planning (Claude about $7.50, Astra about $12.00) plus Increment 1's build-report role about $2.28 (152,273 tokens, Fable 5.1) and its quality reviewer about $0.36; Increment 2's build-report role 163,817 tokens and its Astra round-1 assessment and Fable assessor unmeasured; Increment 3's Sonnet inventory scout 182,144 tokens, four Sonnet quality reviewers (counts not read), its round-1 Fable assessor 170,619 tokens, one Astra run (count not read) and its build-report role 270,107 tokens; the Bug: a Google sign-in refused for an unconfirmed email points at password reset, which never sends a link to an account with no password #800 fix's build-report role 116,476 tokens; Increment 3b's build-report role 142,587 tokens; the five bug fixes' build-report roles about 547,000 tokens together (PR Fix #804: a meme can only be saved from the saver's own photo #807 105,902; Fix #805: the admin population lock and the media backup lock get their own keys #809 101,305; Fix #806: a generated fact background is never recorded as someone's upload #811 116,212; Fix #810: a profile photo can only be the account's own upload #812 113,777; Fix #808: a pool on a database a test drops can no longer fail the whole shard #813 102,012) and PR Fix #805: the admin population lock and the media backup lock get their own keys #809's round-1 Fable assessor 116,501 tokens plus one Astra run (count not read); Increment 4a's four Sonnet quality reviewers about 530,000 tokens together, its round-1 Fable assessor 179,954 tokens, one Astra run (count not read) and its build-report role 192,301 tokens; Increment 4b's four Sonnet quality reviewers 691,891 tokens together (156,374 + 178,433 + 186,637 + 170,447), no review round (Codex clean), and its build-report role 231,985 tokens; Increment 5a's Sonnet research scout 196,929 tokens, two Sonnet quality reviewers 348,132 together (171,298 + 176,834), three Astra assessments (round 1 in 627,213 / cached 544,256 / out 5,069; round 2 in 542,024 / cached 461,568 / out 3,937; round 3 in 762,308 / cached 673,024 / out 9,547), three Fable assessors 497,186 together (192,357 + 161,454 + 143,375) and its build-report role 296,859 tokens; Increment 5b's four Sonnet quality reviewers 670,228 together (162,031 + 168,206 + 164,577 + 175,414), one Astra assessment (round 1 in 676,634 / cached 589,440 / out 5,362), one Fable assessor 144,913 tokens and its build-report role 267,408 tokens; the 5b follow-up's (PR Launch Phase 5, Increment 5b follow-up: an admin's removal overrides a member's own deletion request #834) Astra assessment (round 1 in 355,813 / cached 300,928 / out 2,188), Fable assessor 120,924 tokens and build-report role 210,461 tokens; Increment 6a's (PR Launch Phase 5, Increment 6a: account suspension #835) four Sonnet quality reviewers 525,449 together (122,808 + 124,141 + 136,156 + 142,344), three Astra assessments (round 1 in 313,879 / cached 247,424 / out 3,027; round 2 in 465,320 / cached 390,912 / out 4,444; round 3 in 367,426 / cached 306,944 / out 4,024), three Fable assessors 406,642 together (136,340 + 149,091 + 121,211) and its build-report role 308,675 tokens; Increment 6b's (PR Launch Phase 5, Increment 6b: NSFW readiness, and the NSFW opt-in withdrawn #836) four Sonnet quality reviewers 516,076 together (117,682 + 129,911 + 131,462 + 137,021), no review round (Codex clean), and its build-report role 169,771 tokens; Increment 7a (PR Launch Phase 5, Increment 7a: retire the admin soft delete and set-inactive edit #837) dispatched nothing before its build report (Codex clean); the docs sweep (PR Launch Phase 5 docs sweep: accounts, suspension, deletion, and the retired rules #841, details on Launch Phase 5 docs sweep — accounts, suspension, deletion, and the retired rules #840): nine Sonnet sweep readers 1,594,789 tokens together (an earlier figure of 1,985,169 was a summing error, caught by the build report), one Fable assessor 231,298 tokens, one Astra Documentation pass (count not read) and its build-report role 209,600 tokens; Increment 7b (PR Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) #845): one round-1 Astra assessment (count not read), one Fable assessor 142,749 tokens and its build-report role 232,360 tokens; Increment 7c (PR Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled #847) dispatched nothing (Codex clean); the main loop's build turns unmeasured per turn — the harness reports this session's cumulative cost at $89.62 as of 2026-11-03 05:05 UTC.
- To resume: read this block. The plan is built; the issue closes once David's UAT is through.
Phases
- Increment 1 — Turnstile for hCaptcha (four sites swapped, share-by-email given its widget) → Launch Phase 5, Increment 1: Cloudflare Turnstile replaces hCaptcha #797
- Increment 2 — Cheap hardening (atomic reset token, every password writer ends other sessions, provider verified-claim carry) → Launch Phase 5, Increment 2: single-use reset links, password changes end other sessions, provider-verified email #798
- Increment 3 — Eligibility, one onboarding, the gate (0114) → Launch Phase 5, Increment 3: verify, then onboard, then act #801
- Fix — password reset reaches an account with no password yet → Fix #800: password reset reaches an account that has no password yet #802
- Increment 3b — The Google and Apple browser journey through a fake OAuth issuer in the isolated launcher (plan T6) → Launch Phase 5, Increment 3b: Google and Apple sign-up journeys in a browser #803
- Fixes from the Increment 4 inventory — Bug: saving a meme accepts another member's private photo, and the meme image then shows it publicly #804, Bug: the admin population lock and the media backup lock share one number, so admin changes can wait behind a backup #805, Bug: AI-generated images never get their upload_image_metadata row; a CHECK rejects the transform value and the error is swallowed #806, Bug: setting a profile photo accepts another member's private photo and makes it public #810, Bug: CI's Test job sometimes fails after every test passes, because the admin-settings boot test leaves a database connection to be killed #808 → Fix #804: a meme can only be saved from the saver's own photo #807, Fix #805: the admin population lock and the media backup lock get their own keys #809, Fix #806: a generated fact background is never recorded as someone's upload #811, Fix #810: a profile photo can only be the account's own upload #812, Fix #808: a pool on a database a test drops can no longer fail the whole shard #813
- Increment 4a — Memes baked at save, the stored-file image, the boot backfill (0115) → Launch Phase 5, Increment 4a: a meme is a file from the moment it is saved #814
- Increment 4b — The live-content rule, My content's Recently deleted and restore, the purge job (0116) → Launch Phase 5, Increment 4b: everything in My content is undoable for 30 days #815
- Increment 5a — Account deletion engine, its routes, the worker, the admin hard delete on it (0117) → Launch Phase 5, Increment 5a: an account can leave cleanly — the deletion engine #820
- Increment 5b — The delete-account and "Keep my account" screens, the admin deletion panel, the never-onboarded cleanup, what deletion keeps (0118, 0119) → Launch Phase 5, Increment 5b: leaving and staying from the screens, and what deletion keeps #833
- 5b follow-up — an admin's removal overrides a member's own deletion request → Launch Phase 5, Increment 5b follow-up: an admin's removal overrides a member's own deletion request #834
- Increment 6a — Suspension (0120) → Launch Phase 5, Increment 6a: account suspension #835
- Increment 6b — NSFW readiness, the beta-overlay grid row (0121) → Launch Phase 5, Increment 6b: NSFW readiness, and the NSFW opt-in withdrawn #836
- Increment 7a — Retire the admin soft delete and set-inactive edit → Launch Phase 5, Increment 7a: retire the admin soft delete and set-inactive edit #837
- Increment 7a docs — the docs sweep (Documentation PR, sub-issue Launch Phase 5 docs sweep — accounts, suspension, deletion, and the retired rules #840) → Launch Phase 5 docs sweep: accounts, suspension, deletion, and the retired rules #841
- Increment 7b — Remove
captcha_verifiedandnsfw_mode_enabledfrom the model (columns kept) → Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) #845 - Increment 7c — Contract: drop both columns (0122), after David publishes a release containing
1231526→ Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled #847
Parent: #628
Activity
TheAnswerManIsHere commentedon Oct 30, 2026
Handoff — 2026-10-30
Handing off because: Phase 3 (#770) is closed, and Phase 5 is a different workstream. The Phase 3 session's context would be dead weight here.
What just finished: Launch Phase 3 — Data and environments (#770, closed 2026-10-30).
overhype.meon a database proven identical to what the code builds: adopted with proof, PRs Launch Phase 3, amendment 2: adopt production's first database with proof #790, Launch Phase 3, amendment 3: adoption creates the ledger's table and tolerates Replit's record #791 and Launch Phase 3, amendment 4: adoption creates the functions and triggers Replit's copy dropped #793.RESET_PRODUCTION_DATABASE_URLis deleted, and the two spare dev databases are dropped.Settled this session — do not re-open:
overhypeme+admin@gmail.com, which grants admin by email (lib/auth.ts,BOOTSTRAP_ADMIN_EMAIL), not through a storedis_adminflag. Every pre-reset account, session and fact is gone, by design.heliumdb.tier_feature_permissionstable edited at/admin/features.decisions.md).Ruled out:
launch-feature-inventory.md).Live state that does not cross: none.
mainis clean at743558a.Still open (not Phase 5's to settle, but in its neighbourhood):
Durable learnings: the Replit gotchas are in #770's harvest-notes comment, for the next
/documentor/maintenanceharvest.Generated by Claude Code
TheAnswerManIsHere commentedon Oct 31, 2026
Planning record — plan approved 2026-10-31
Exchanges run: 3 with Astra (
gpt-6-astra, xhigh, read-only): scope (round 0), assessment rounds 1 and 2. A third round was not run: the last changes were Astra's own suggested passages, then David's answers, which removed mechanism rather than adding it.Ledger at approval: 12 concerns; 10 addressed, 2 accepted by David (paid access after a cancelled deletion; under-13 handling). None settled over dissent.
What Astra changed in the plan, in one line each: verify before onboard before act; 18+ enforced at photo upload, not just computed; a mutating-GET inventory beside the method rule; one deletion engine for admin and self-service, with the admin population lock at accept and suspend; owner-hiding derived from the deletion request so in-flight uploads stay hidden; destructive claims serialized per owner; pre-existing retired tombstones stay permanent; the captcha column retired over two releases; the share-by-email widget that sends no token today; tests that drive real paths instead of counting rows.
David's overrides and design calls: no Terms text in Phase 5 (over Astra's and Claude's recommendation; Phase 14 does it once); everything in My content undoable, photos included; memes baked at save after research (Imgflip stores files; Cloudinary persists derived images; design tools export flattened files for sharing; the existing renderer-version ETag shows recipe memes change look when the renderer changes).
👀 FYI — the container's scratch area was reclaimed during planning, taking the Astra sign-in with it; nothing in the plan was affected, and a further Astra exchange would start with a fresh sign-in code.
Generated by Claude Code
Launch Phase 5, Increment 1: Cloudflare Turnstile replaces hCaptcha
97 remaining items
TheAnswerManIsHere commentedon Nov 2, 2026
👀 FYI — Retiring reinstatement in PR #837 also removed the only manual way to correct a Stripe cancellation or refund that never reached us. Before, an admin could deactivate and then reinstate the account to force a re-check against Stripe. Now nothing on the admin surface can do that, and suspension touches no billing. The gap was already recorded as accepted ("Stripe↔local membership reconciliation",
docs/engineering/deferred-work.md). The docs sweep now states that the workaround is gone (membership-entitlements.md, Manual chapter 10). No code change has been made. Separately, a source comment inmembershipRefresh.tsstill names "admin soft-delete, reinstatement" as callers. That is product code, so it is out of scope for this Documentation PR and is recorded as next.Generated by Claude Code
TheAnswerManIsHere commentedon Nov 2, 2026
David, 2026-11-02 ~23:45 UTC: not yet published. Increment 7b (0122) stays held until he publishes. Before that publish:
TURNSTILE_SECRET_KEYandVITE_TURNSTILE_SITE_KEYgo into Replit's secrets. After it: his own set-up on the live site, passing one real challenge. Then 7b starts from this issue's To resume line.Generated by Claude Code
TheAnswerManIsHere commentedon Nov 3, 2026
Published. David published production on 2026-11-03 ~00:20 UTC, from
mainat61a2d8bwith the Turnstile secrets set (both confirmed present in the Repl by name).get_publish_statusreadssuccess, andhttps://overhype.me/api/healthzanswers 200{"status":"ok"}. Next: David's own set-up on the live site, passing a real challenge, plus one Google and one Apple sign-in. Then Increment 7b (0122) starts.Generated by Claude Code
TheAnswerManIsHere commentedon Nov 3, 2026
Live check (David, 2026-11-03): the Turnstile challenge passes on overhype.me ("Success!"). Google and Apple sign-in both work, but each created a new account, because they send
david@davidcarlos.netand the admin account isoverhypeme+admin@gmail.com. Accounts are matched by email only: no provider subject is stored, and linking refuses a mismatched email (link_error=email_mismatch). Now, no code: David makes the david@ account his everyday account and grants it admin from the bootstrap admin account; the bootstrap account stays as break-glass. Next to plan, with the "Last used" badge: linked sign-in methods. That means a provider identity stored by the provider's permanent ID; connecting from Profile whatever the provider's email, after a fresh password check; and a prompt to "sign in and connect" before a provider sign-in silently creates a second account. This also covers Apple's Hide My Email. The 7b precondition is met (the publish is live), so Increment 7b starts now.Generated by Claude Code
TheAnswerManIsHere commentedon Nov 3, 2026
👀 FYI — Increment 7b is open as PR #845 — Launch Phase 5, Increment 7b: drop captcha_verified and nsfw_mode_enabled (0122). Decision taken on my recommendation: the migration reports the count of
truerows it discards rather than copying them anywhere. Neither value is needed: onboarding replaced the captcha flag, and the NSFW opt-in was withdrawn. Undo: a forward migration re-adding the columns. Production loses the columns only at David's next publish, when he approves Replit's twoDROP COLUMNstatements.Generated by Claude Code
TheAnswerManIsHere commentedon Nov 3, 2026
👀 FYI — Increment 7b is resequenced: the two columns leave the code's database model now, and the drop (0122) waits for one more publish. Codex found, and both assessors confirmed, that the published release still declares the columns in its model, so dropping them now would break sign-in while it serves. PR #845 — Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) now holds only the model change. A follow-up PR carries 0122 after David's next publish. The rule that caught it is now in
migrations-and-backfills.md: the ORM declaration is a use. Undo: put 0122 back into PR #845 (beeb034).Generated by Claude Code
TheAnswerManIsHere commentedon Nov 3, 2026
Harvest notes — PR #845 — Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) (squash
1231526; round 1 had one Codex finding, both assessors wrote for it; round 2 was clean)Decisions and why
select(), aninsertand areturning(). Fable measured this with.toSQL()on drizzle-orm 0.45.2. So a release whoseusersTablestill declares a column reads it on every sign-in, sign-up and Stripe webhook, even though no code mentions the field. 7b removes the fields from the model; 0122 drops the columns only once a release without them is the published one. Recorded inmigrations-and-backfills.md, Expand, then contract.UNDECLARED_PENDING_DROPinlib/db/src/baseline.test.tsexempts exactlyusers.captcha_verifiedandusers.nsfw_mode_enabledfrom "built, not declared". The drop PR removes them from that set.Alternatives rejected
Gotcha candidates
&at the end of an&&chain backgrounds the whole chain in a subshell. Variables assigned inside it don't exist in the next commands of the same shell line, so a later$S/...path expanded to/.... Detach only the one command (setsid … &on its own line).run-test.sh --setup(which re-clones), run again, then restore them.The follow-up: a new PR carries 0122, its journal entry, the snapshot exemption, the
UNDECLARED_PENDING_DROPremoval and the past-tense doc, after David publishes a release containing1231526. Its migration comment names the published SHA, confirmed through the connector first.Generated by Claude Code
TheAnswerManIsHere commentedon Nov 3, 2026
Build report — PR #845 — Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) · differs on 2 points; 3 decisions questioned; partial — something could not be assessed
The first half of Increment 7b of Launch Phase 5: the code's description of the account table stops listing the two retired columns (the old 'passed a captcha' flag and the withdrawn per-account NSFW switch) so a later release can drop them without breaking sign-in; the drop itself was pulled out of this PR after review and now waits for your next publish.
Evidence: workstream Issue #795 — Launch Phase 5 — Account and identity · PR #845 — Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish)
Needs you: Nothing now; at your next publish, if Replit's schema step offers to drop captcha_verified or nsfw_mode_enabled from users, cancel and tell the builder (the PR's own post-merge instruction, and the one check only you can make).
What was built
Read from the twelve commits and the merged six-file diff (47 lines added, 16 removed, no migration). The code's model of the account table no longer declares two columns: captcha_verified, unused since Increment 3, and nsfw_mode_enabled, the opt-in Increment 6b withdrew. That matters because the database library writes every declared column by name into every 'load this account', 'create this account' and 'update and return' query, so the published site (61a2d8b) still asks for both columns on every password, Google or Apple sign-in, sign-up and Stripe event even though no code mentions them; only a release whose model omits them can safely survive their removal. Around that one change: the test that keeps the code's table model and the real database in step is told to tolerate exactly these two columns being in the database but not in the code, with a comment naming the migration that ends the exemption; the old-data test fixture stops carrying them; one test reads the NSFW column by raw SQL while it still exists and returns nothing once it is gone; the accounts spec describes the interim state; and the migrations guide gains the rule that caught this ('the ORM declaration is a use'). The database is untouched everywhere: dev, test and production keep both columns with their defaults. The PR opened with the drop migration (0122), its journal entry and a snapshot-check exemption; the second commit removed all three after Codex's finding.
Decisions taken on my recommendation
Ramifications
For you: one more publish stands between here and the end of Phase 5. The drop needs a release containing this change (squash 1231526) to be the published one, and at that publish Replit should show no change to the users table; if it offers to drop either column, that is the outage this PR was reshaped to avoid, and cancelling is the remedy. After this publish nothing visible changes. For users: nothing, now or after the publish; sign-in, sign-up and every screen behave the same. For the next unit: a follow-up PR carries 0122, its journal entry, the snapshot exemption, the removal of the parity exemption and the past-tense docs, and must confirm through the connector which release is live before it merges. More durably, every future column removal in this product now takes two releases by written rule (remove the field, then drop the column), a small standing cost on each contract step and the right one. The 'two releases' count the plan's S23 gives is now three in practice.
Shipping unfixed
Where it disagrees with the builder (2)
Spend
Where we are
Does not do: It drops nothing: both columns are still in every database, production included, with their values, and the live site is unchanged until your next publish and behaves the same after it. It does not finish Increment 7b or Phase 5; the drop needs another publish and another PR. It does not change any screen, any sign-in path or any other account column, and it does not update the plan file's 'two releases' wording.
You are now trusting: That Replit's publish-time schema step copies dev's tables, where both columns remain, rather than deriving from the code's model (the Phase 3 record says it copies dev's tables; if it offered the drop anyway, the PR asks you to cancel). That nothing in the code reads the two columns by name: the builder's and Fable's searches, and the sign-in suites passing with the columns present and then dropped, are the evidence. And that whoever merges the drop PR first confirms the live release no longer declares the columns, since that is a procedure rather than a check that fails.
Session (the builder's call): continue — the 0122 follow-up is the last Phase 5 step and waits only on David's next publish; this session holds its context
Could not assess: The fleet's paid-run ledger in TheAnswerManIsHere/KITT is not reachable from this session (access denied), the plan file is beyond my reach so its text is known only through the PR body's quotations, and the Repl sync and post-merge dev check were not reported by the cutoff, 45 seconds after the merge.
Generated by Claude Code
TheAnswerManIsHere commentedon Nov 3, 2026
👀 FYI: David's 2026-11-03 publish shipped
1231526(Increment 7b). Increment 7c is open as PR #847 — Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled. The Repl'smainis one commit ahead: Replit's empty "Published your App" deployment commit0fd06116, which has no file changes. On my recommendation it will be realigned at #847's close-out sync rather than moved to a branch.Generated by Claude Code
TheAnswerManIsHere commentedon Nov 3, 2026
Harvest notes: PR #847 — Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled
truerows it discards and does not copy them. Neither value is needed by any later release. Dev reported 3 and 1.migrations-and-backfills.md): dropping a column takes three releases, not two, when the ORM declares it. First stop the reads, then remove the declaration and publish, then drop.main, which puts it ahead of GitHub'smain. The Repl already holds threereplit-publish-marker-*branches from earlier publishes. Close-out should expect this commit and realign past it, keeping a local branch, without treating it as David's work. This belongs inreplit-environment.mdat the next harvest.Generated by Claude Code
TheAnswerManIsHere commentedon Nov 3, 2026
Build report — PR #847 — Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled · differs on 2 points; 3 decisions questioned; partial — something could not be assessed
The final step of Launch Phase 5: a database migration (0122) that removes the two retired account columns — the old 'passed a captcha' flag and the withdrawn per-account NSFW switch — now that the release which stopped using them is the published one.
Evidence: workstream Issue #795 — Launch Phase 5 — Account and identity · PR #847 — Launch Phase 5, Increment 7c: migration 0122 drops captcha_verified and nsfw_mode_enabled
Needs you: Nothing now; at your next publish, Replit's schema step should offer to drop exactly users.captcha_verified and users.nsfw_mode_enabled — if it offers anything else, cancel and tell the builder (the one check only you can make); the Phase 5 UAT list on Issue #795 — Launch Phase 5 — Account and identity stays yours whenever convenient.
What was built
Read from the one commit (seven files, 77 lines added, 33 removed; the merged squash e779e9b on main carries the same seven files). A new migration, 0122, checks whether each of the two retired account columns still exists; for each that does, it counts how many accounts had it switched on, writes that count to the server's boot log, then removes the column. It is written so that running it twice, or running it after Replit's publish has already removed the columns, does nothing. It is registered in the migration journal and added to the snapshot checker's short list of hand-written migrations, beside 0115 and 0121. The test that keeps the code's table model and the real database in step loses the temporary two-column exemption that PR #845 — Launch Phase 5, Increment 7b: remove captcha_verified and nsfw_mode_enabled from the model (the drop follows the next publish) had added, so model and database must now match with no exceptions. The NSFW-withdrawal test stops reading the column's value and instead asserts the column is gone (it still checks that the profile and admin edits answer without a server error, which is what would fail if either tried to write the field). The accounts spec and the decisions log move from 'will be dropped' to 'was dropped'. No screen, route or behaviour changes. The builder reports dev's run discarded 3 accounts' captcha flags and 1 account's NSFW switch; production keeps both columns until the next publish.
Decisions taken on my recommendation
Ramifications
For you: this is the end of Phase 5's building. One publish stands between here and production matching dev, and at that publish Replit should show exactly two column drops and nothing else; after it, nothing visible changes anywhere. For users: nothing, before or after. For the next unit: the model-versus-database test is back to zero exemptions, so any future drift between the two fails CI rather than waiting to be noticed; removing a column the code's model declares is now a recorded three-release practice (stop the reads, remove the declaration and publish, then drop); every Replit publish leaves an empty marker commit on the Repl's main that each close-out has to step past; and Phase 5's issue stays open only for your UAT, while the next workstream — Issue #846 — Linked sign-in methods and the "Last used" badge — is handed to a fresh session for planning.
Shipping unfixed
Where it disagrees with the builder (2)
Spend
Where we are
Does not do: It does not change production yet: both columns remain there until your next publish. It changes no screen, route or behaviour. It keeps no copy of the old values anywhere — the only record is a count in dev's boot log (3 and 1), and production's will most likely never be counted. It does not amend the plan file's 'two releases' wording. It does not close this issue; your UAT remains.
You are now trusting: That nothing in the published code (1231526 and later) names either column. That Replit's schema step at publish will offer exactly the two drops, with you as the check that cancels anything more. That the migration's 'only if the column still exists' guards make the order of Replit's step and the app's own migration irrelevant. And that the builder's Repl-side report (the sync, dev's counts, the health check) is accurate.
Session (the builder's call): archive — Phase 5 is fully built and merged, the Repl is synced, no PR is watched, no check-in is armed, and the harvest notes are on Issue #795; nothing dies with this session
Could not assess: The fleet's paid-run ledger in TheAnswerManIsHere/KITT is not reachable from this session (access denied); the Repl, the published site and the plan file are beyond the role's reach, so David's publish, the Repl sync, dev's run of 0122 and the plan's wording rest on the builder's report.
(Spend section condensed from the role's verbatim quotation of the Spend so far line, which appears in full in the State of Play above; everything else verbatim.)
Generated by Claude Code