Branch main at /Users/avi/code/whocards/app, clean at 0ce688e. Done & merged: the
unified-play + Answer-record work (0003), the monorepo DB-migration takeover (0005 baseline), the
@whocards/logger package (0004 — shared + web + mobile-console-swap), and the mobile
native-feel pair (0007 interactive Reanimated swipe + haptics + press-springs + Reduce-Motion;
0008 Android predictive back + per-surface status bar + sheet/splash polish). All shipped via
the coder → reviewer → orchestrator-merge workflow. Remaining open work now needs a user
decision or a device — see Next Steps. 0007/0008 still need an on-device pass
(haptics, 60/120fps swipe feel, predictive-back peek, status-bar contrast, splash fade).
One unified play experience across web (apps/website, Astro) + mobile (apps/mobile, Expo) on
the shared engine, with a permanent Answer record, observable failures, and a de-risked cutover
from the currently-deployed site to this repo.
- All coding via the
codersubagent (.claude/agents/coder.md), then review via thereviewersubagent (.claude/agents/reviewer.md); merge only if review passes. Do not write feature code yourself — orchestrate the agents. - These custom agents load only at session start — if they aren't available as
subagent_type: "coder"/"reviewer", restart before starting ticket work. - Remote:
whocards/whocards-platform(GitHub, PUBLIC). Tickets were migrated to GitHub Issues on 2026-06-21 — issue#N== old ticket000N(1:1), labelled by area/type;docs/tickets/was removed. The coder/reviewer flow stays branch-local: coder works on a local branch and commits (no push/PR from the subagent); reviewer reviewsgit diff main...<branch>; the orchestrator merges tomainafter review passes +pnpm checkis green-modulo-the-known-website-type-debt. Pushing to the public remote / opening PRs: only with explicit user OK. Repo is public — never commit the gitignored root.env(prodDB_URL). Tell each subagent the branch-local flow in its prompt (their defs assume agh/PR flow). SendMessageIS available now (this was false in earlier sessions) — you can resume a finished subagent's context viaSendMessageinstead of always cold-spawning a freshcoder.
- Mobile native-feel (0007 + 0008) — researched current RN/Expo native-feel best practices, then
split into two disjoint-file tickets and shipped both. 0007 (
2597ce1+ review-fixd667531): the play screen's swipe is now an interactive, finger-trackingGesture.Pan()worklet on the UI thread (wasrunOnJS(true)+ legacyAnimated) — velocity/threshold commit, rubber-band at the first card, all motion on Reanimated; new web-safe never-throwingsrc/lib/haptics.ts(selection on card change/lang-select, light impact on buttons, medium on swipe-commit); newsrc/components/pressable-scale.tsx(press-spring to 0.96);useReducedMotion()gate. 0008 (0ce688e):predictiveBackGestureEnabled: true(flag-only — SDK 56 + expo-router needs no native-stack v5), per-surface status bar (StatusBar style="dark"mounted inside the white languageModal, light elsewhere), language-modal close✕→Ionicons name="close", and a 300 msFadeInsplash→landing transition (gated afterhideAsync(), fonts-hold preserved). Both reviewer-APPROVED;pnpm checkgreen modulo the 12 known website errors. Engine untouched (ADR-0003). iOS sheet grabber/detents deferred (pageSheetalready gives native swipe-to-dismiss). On-device pass still owed — see Next Steps. - Logger (0004) — merged
f3ec037.@whocards/logger(TS-source package, no build):logWarn/logError, dev→console/ prod→injectedLogSink,try/catchso it never throws, PII-safe (Record<string, unknown>— ids + messages only). Web wires a sink towindow.posthog?.captureException(with Error) /capture('app_log', …)(no Error) atPostHog.astroinit, and swapped the twooffline-queue.tsconsole.warns tologError/logWarn. Mobile swappedanswer-queue.tsconsole.warn→logWarn(dropped theeslint-disable no-console) and callsconfigureLogger({dev: __DEV__})at app root (console-in-dev / no-op-in-prod). Deferred remainder: the mobile PostHog transport (posthog-react-native+ provider + real sink) — needs a dev-client rebuild = user/device. NB: the event name was$exception_login the original brief; the user choseapp_log(avoid PostHog's reserved$prefix). - Answer record (0003) — merged.
answertable;answers.recordtRPC mutation via a port/adapter; per-Device anonymous id; offline queues (webapps/website/src/lib/offline-queue.ts, mobileapps/mobile/src/lib/answer-queue.ts); record-on-serve in both clients. Engine pure. - DB baseline / takeover (0005) — merged, applied to Supabase prod. 16 prod tables modeled;
idempotent
0000_full_baseline.sql; verified 16→17 tables, nothing dropped. (The old bigintmaxValue/ts80008trap is resolved — there is nomaxValueleft inschema.ts.) - Project agents in
.claude/agents/(architect, coder, researcher, reviewer). - Tickets → GitHub Issues (
whocards/whocards-platform,#N== old000N);docs/tickets/removed in the 2026-06-21 migration. Closed: #3, #4 (mobile PostHog transport — merged via PR #31), #7, #8, #9, #10, #13, #14, #18, #20, #21, #22, #23. Open: #1 CJK fonts, #2 Convex (backlog/parked), #5 DB reconcile/drops, #16/#17 release + store listing, #19 web print, #24 Hajnalig review (~2026-07-22), #27 Android store account. - Website is LIVE on Netlify (2026-06-21, #20 closed):
whocards.ccserves the monorepo build, prod APIhttps://whocards.cc/api/trpcis live (decks.manifest/pool.languages→200), OG social cards render via a deploy-persistent cache (0cad542). Netlify Base directory must beapps/website(package_path empty) — see the comment inapps/website/netlify.tomland #20.
- Mobile PostHog transport (0004 remainder) — DONE (merged PR #31):
posthog-react-nativeprovider + sink wired inapps/mobile/src/lib/observability.ts. Still owes an on-device pass (a dev-client rebuild) to confirm real events land in PostHog. - Auth decision → cleanup migrations (0005). Blocked: needs the user's auth choice
(
auth_*vsaccount_*vs drop) and explicit authorization to touch prod. - CJK question fonts (0001) — mobile; needs the user's subset/bundle/system choice.
- Pool-data dedup — surface the type-widening tradeoff (below) to the user first.
- Mobile device verification — run Answer-record + offline flush on a rebuilt dev client, and do the 0007/0008 on-device pass: haptics firing at the right moments, the finger-tracking swipe at 60/120fps + rubber-band feel, Android predictive-back peek, status-bar contrast over the white language sheet, and the 300 ms splash fade. (None of these are statically verifiable.)
- Pool-data dedup (point website at
@whocards/decksinstead of localapps/website/src/data/{questions,languages}.json): the JSON pairs are byte-identical (verified by sha) so the runtime is zero-risk, BUT it is not purely mechanical — the website derives literal-union types (keyof typeof questions,keyof typeof languagesinapps/website/src/types/index.ts, used byWhoCard.astro,card-image.ts,language.ts,gameplay.ts,pages/.../images.astro), while@whocards/deckstypes broadly (LanguageCode = string,QuestionId = string). Consuming decks' value exports would widen the website's types and weaken its compile-time safety — a design decision (accept widening vs. preserve literals via a JSON re-export), so flag it for the user rather than doing it as "no-input" work.
- Prod DB = Supabase (eu-central-1 pooler, 6543). Connecting needs EXPLICIT user authorization
naming the target (safety classifier). Creds in
website-next-15/.env.prodasDATABASE_URL;apps/websitereadsDB_URL. - Lint/format = oxlint + oxfmt (never eslint/prettier) for packages + mobile; website excluded
from both (prettier +
astro check; oxfmt still can't format.astroas of 2026-06).no-consoleand the newnode/no-process-enverrors only bite packages/mobile (website is oxlint-excluded). - Env convention:
process.envis read ONLY in anenv.ts(enforced bynode/no-process-env, with**/env.ts+**/env.test.tsallow-listed). Website:apps/website/src/env.ts(t3-env, wasenv-secrets.ts). Mobile:apps/mobile/src/env.ts(zod,z.stringboolfor booleans). Mobile's genericEXPO_PUBLIC_DEBUG(replacesEXPO_PUBLIC_POSTHOG_DEBUG) forces transports on in dev AND tees to console. Mobilelintscript is nowoxlint(was theexpo lintfootgun). pnpm check=oxfmt --check && oxlint --deny-warnings && turbo typecheck test. It exits non-zero only onwebsite#typecheck, which carries 12 PRE-EXISTINGastro checkerrors (measured onmain: missingwawoff2/bidi-jsdecls,Icontypes, etc.) — website type debt unrelated to recent work. Capture the baseline and prove zero new errors; everything else (9/10 turbo tasks) is green.window.posthog?: import('posthog-js').PostHogis declared inapps/website/src/env.d.ts;Play.tsxand nowsrc/lib/logger.tsusewindow.posthog?.capture(...). PostHog inits inapps/website/src/components/PostHog.astro(mounted inapps/website/src/layouts/Layout.astro).- Packages are consumed as TS source via
exports(no build) — copypackages/decks/packages/loggershape. Catalog deps:"typescript": "catalog:","vitest": "catalog:". - Never commit
.env*, real order data, or secrets. Mobile native deps need a dev-client rebuild.
- Scoping a ticket to an exact, file-by-file brief (conventions + verification commands) before any
agent touches code — the cold-start
codershipped 0004 in essentially one pass. - The review gate earned its keep: the
reviewerflagged the$exception_logreserved-$prefix (→ user choseapp_log) and two nits (noisyconsoleundefineds; a missing dev+error test) — all fixed in a small follow-up pass and amended into a single clean commit. - Surfacing the one outward-facing, brief-contradicting decision (the PostHog event name) to the user via a quick question rather than silently overriding the written brief.
- (Historical — no longer true) Earlier sessions had no
SendMessage, so a finishedcodercould not be resumed and review-fixes needed a fresh cold-spawnedcoder.SendMessageworks now. pnpm checkis never fully green because of the ~13 pre-existing websiteastro checkerrors. Don't chase them as part of an unrelated ticket; gate on "zero NEW errors" instead.- Custom
.claude/agents/*.mdregister only at session start — restart ifcoder/revieweraren't selectable. - Git remote NOW EXISTS —
origin=https://github.com/whocards/whocards-platform.git(public), andghworks (issues/PRs). Earlier sessions had no remote. The user's flow still stands: coder works branch-local, push/PR only with explicit user OK (public repo — never commit.env). - A
codersubagent can die mid-run — a previous coder crashed after ~13 min (API Error: ConnectionRefused; safety classifier also down) with no report, leaving uncommitted files on its branch. Recovery: inspectgit status+ the branch, then run the verification the coder skipped and commit the output yourself — don't assume a silent/dead agent finished cleanly. expo lint/pnpm -F mobile lintis a footgun here — it auto-installseslint+eslint-config-expoand rewritespackage.json/pnpm-lock.yaml(+~1300 lines) /pnpm-workspace.yaml(anunrs-resolver: set this to true or falseplaceholder). This repo lints mobile with oxlint, not eslint, andexpo lintis not part ofpnpm check. The 0007 reviewer ran it and left that churn in the worktree; I had togit checkout --those three files before merging. Tell mobile coders/reviewers to verify withoxlint/oxfmtdirectly and never runexpo lint.
- Web tokens: regenerate
base.css @themefrom@whocards/tokens. Website type debt (12 pre-existingastro checkerrors; fold the site into oxlint/oxfmt after). tRPC ETag.website-next-15retirement (parked — holds prod creds + dump).
pnpm install
pnpm -F website dev # needs apps/website/.env for full run (placeholders ok for build)
pnpm -F mobile start # Expo
pnpm check # oxfmt --check && oxlint --deny-warnings && turbo typecheck test
pnpm -F @whocards/observability test # observability events + logger behavior