Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/screenshots/admin-access/admin-content.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/admin-access/admin-messages.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/admin-access/admin-users.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
136 changes: 136 additions & 0 deletions fofafu_vault/features/admin-access.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions fofafu_vault/kanban/company.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ team: company
- [ ] [[features/migrate-render-to-vercel-supabase]] — eng-infra-4 (frontend supabase-js auth swap) + eng-infra-5 (frontend Edge Function wiring for announcements/family/community/search) both closed to Review; frontend 132/132 tests green, tsc/build clean. Same-day correction: an earlier attempt to delete the old Express auth endpoints was a production regression (would have broken messages/playdates/uploads/coach auth entirely), caught before merge — reverted, and auth.middleware.ts now accepts a Supabase session token as a fallback alongside the legacy JWT. Backend 147/147 tests pass, tsc clean. Phase 5 parent (eng-infra-1) remains building — eng-infra-3/6/7/8 still outstanding

## Review
- [ ] [[features/admin-access]] — single hardcoded admin (`kurarei+5@gmail.com`) via `is_admin()` + RLS across all Supabase tables (DMs included) + `admin_audit_log` + new `admin` Edge Function + `/admin` frontend (Users/Content/Messages, incl. required private-conversation banner). Backend 147/147, frontend 139/139, Deno 12/12, tsc clean. Found and flagged (not fixed): this repo's migrations don't currently replay clean from scratch on the current CLI (`supabase start`) — unrelated pre-existing gap. No pgTAP harness exists for the RLS/trigger SQL (manual-review-only) and no E2E coverage this pass — both flagged in the feature spec, not silent gaps. **Update (2026-08-24):** independent code review found 6 must-fix issues (incl. an audit-log gap and a security-relevant trigger gap), all fixed; Deno tests 12→20 (all 11 routes covered). PR #67 updated with screenshots.
- [ ] [[features/header-nav-redesign]] — desktop Navbar restyled to Option B "grouped pill track": icon-only nav in one `surface-warm` pill (44×44 targets, `aria-label` + hover/focus tooltip), active-page filled `brand-primary-pressed` puck, name/city-state/sign-out cluster collapsed into one avatar+name `AccountChip` (keyboard-operable disclosure; Escape closes + returns focus to trigger); header border 3px→2px. Frontend 167/167, `tsc` clean — independently reverified by tech-lead, not just transcribed. a11y-auditor's 3 blocking findings (avatar-initial contrast, missing `aria-label` on desktop nav links, keyboard-operable sign-out) all fixed, independently reverified by design-lead against the shipped code. Two design-system additions promoted: `size.hitTarget.min` token + a new "Pill Track" pattern (extends principle #3 from CTAs to nav chrome). One real bug caught mid-build and fixed same-day: `user.name` is a household name ("The Anderson Family"), not a person's name, so the original first-name-extraction spec broke visibly (chip read "The"); reverted to show the full name, truncated past 24 chars per `community-playdate-badge`'s precedent. Two fast-follows opened rather than left as footnotes: [[features/navbar-component-extraction]] (code-review must-fix #2, non-blocking) and [[features/auth-user-name-semantics]] (the household-vs-personal-name product question). Both team kanban cards in Review.
- [ ] [[features/supabase-rls-sensitive-columns]] — P0 security: RLS `USING (true)` policies on families/announcements/comments/reactions/availability_slots lacked `TO` clause (implicitly anon-readable); rescoped to `TO authenticated` + `REVOKE SELECT FROM anon`, defense-in-depth REVOKE on messages/playdate_requests/coach_events; migration `20260714000000_restrict_pii_to_authenticated.sql`; no password/secret columns exist in public schema (auth.users not PostgREST-exposed). 2 ACs (exact flagged table ID, live Advisor re-run) need human follow-up — no live Supabase dashboard/CLI access in sandbox; flagged in Open Questions.
- [ ] [[features/brand-contrast-fix]] — WCAG 1.4.3 fix: `color.brand.primary.pressed` (#3F7E54, 4.86:1 vs white, independently verified by ui-designer + a11y-auditor) introduced; 22 CTA sites (19 files) migrated to accessible pair + hover parity; frontend 119/119, tsc/build clean, 0 must-fix code review, 11/11 pages 0 axe violations; both team kanban cards in Review
Expand Down
4 changes: 3 additions & 1 deletion fofafu_vault/kanban/engineering.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ team: engineering
## In Progress

## Review
- [ ] [[features/header-nav-redesign]] @engineering — desktop `Navbar` restyled to Option B "grouped pill track": 5 icon-only links in one `surface-warm` pill (44×44 targets, `aria-label` + hover/focus tooltip), active-page filled `brand-primary-pressed` puck, right-hand cluster collapsed into one avatar+full-name `AccountChip` (lightweight disclosure, not a full ARIA menu; Escape closes + returns focus to trigger), header border 3px→2px; frontend 167/167 (35/35 on the two Navbar-specific files), tsc clean — verified independently by tech-lead, not just transcribed. E2E: 7 tests written/reconciled against the landed markup (`frontend/e2e/header-nav-redesign.spec.ts`) but not executed live — no `frontend/.env` in this sandbox, same pre-existing gap as `playdates.spec.ts`; corroborated instead via RTL (34-35/34-35 passing). Code review: 2 must-fix — #1 (firstName-vs-full-name test contradiction after commit `1bd5833`) resolved same-day and reverified; #2 (`Navbar()` is a 238-line monolith, `NavTrackItem`/`AccountChip` never extracted per ui-designer's `### Visual` anatomy) judged **non-blocking by tech-lead, tracked as fast-follow** — same treatment as `reply-coach-live`'s deferred must-fixes, since it's a pure code-organization gap with zero behavior delta, not a correctness/security/UX issue. Also flagged non-blocking for product (not engineering's call): pre-redesign `AuthUser.name` was already ambiguous ("The Anderson Family" vs. a person's name) — this redesign just made it newly visible in the chip; independently corroborated by 3 specialists. — Home dashboard's Community sidebar now shows City, State under each family name and a "🗓 Playdate" badge (links straight to the request flow via `/family/:id?requestSlot=<id>`) when a family has a future free slot; `supabase/functions/community/index.ts` extended to return `city`/`state`/`nextFreeSlotId` per row; family name truncates past 24 characters only; frontend 134/134, tsc clean; no backend Express changes (legacy/superseded controller left untouched)
- [ ] eng-backend-20 [[features/admin-access]] @engineering — single hardcoded admin (`kurarei+5@gmail.com`, confirmed registered) via SECURITY DEFINER `is_admin()` + `FOR ALL` RLS policies across families/announcements/comments/reactions/availability_slots/messages/playdate_requests (`coach_events` read-only), `admin_audit_log` (append-only), new `admin` Edge Function (users/content/messages routes, service-role Admin API for email/ban/reset-password, audit-logged every mutation); messages/playdate_requests needed a BEFORE UPDATE trigger fix beyond the literal spec since their existing column-grants would've silently blocked admin's own-session writes — see feature spec Backend section. Frontend `/admin` (Users/Content/Messages views incl. required "private conversation" banner) wired into App.tsx + RequireAuth. Backend 147/147, frontend 139/139, Deno 12/12, tsc clean both workspaces — full monorepo suite re-run clean after fixing a real node_modules regression this work introduced (see Test plan section for detail). No E2E (Playwright) coverage added this pass — flagged, not silent. RLS/trigger SQL is manual-review-only (no pgTAP harness in this repo); verify against a real/staging project before shipping. **Update (2026-08-24):** an independent code-reviewer pass (separate session) found 6 must-fix issues, all now fixed — audit-log gap in `updateUser`, `id`-column trigger guard gap (security-relevant), missing email-edit UI, thin test coverage (Deno 12→20, all 11 routes now covered), RHF/Zod convention violations, request-body narrowing. See feature spec's Code review section for the full review + fix log. PR #67 updated with screenshots.
- [ ] eng-frontend-19 [[features/community-playdate-badge]] @engineering — Home dashboard's Community sidebar now shows City, State under each family name and a "🗓 Playdate" badge (links straight to the request flow via `/family/:id?requestSlot=<id>`) when a family has a future free slot; `supabase/functions/community/index.ts` extended to return `city`/`state`/`nextFreeSlotId` per row; family name truncates past 24 characters only; frontend 134/134, tsc clean; no backend Express changes (legacy/superseded controller left untouched)
- [ ] [[features/header-nav-redesign]] @engineering — desktop `Navbar` restyled to Option B "grouped pill track": 5 icon-only links in one `surface-warm` pill (44×44 targets, `aria-label` + hover/focus tooltip), active-page filled `brand-primary-pressed` puck, right-hand cluster collapsed into one avatar+full-name `AccountChip` (lightweight disclosure, not a full ARIA menu; Escape closes + returns focus to trigger), header border 3px→2px; frontend 167/167 (35/35 on the two Navbar-specific files), tsc clean — verified independently by tech-lead, not just transcribed. E2E: 7 tests written/reconciled against the landed markup (`frontend/e2e/header-nav-redesign.spec.ts`) but not executed live — no `frontend/.env` in this sandbox, same pre-existing gap as `playdates.spec.ts`; corroborated instead via RTL (34-35/34-35 passing). Code review: 2 must-fix — #1 (firstName-vs-full-name test contradiction after commit `1bd5833`) resolved same-day and reverified; #2 (`Navbar()` is a 238-line monolith, `NavTrackItem`/`AccountChip` never extracted per ui-designer's `### Visual` anatomy) judged **non-blocking by tech-lead, tracked as fast-follow** — same treatment as `reply-coach-live`'s deferred must-fixes, since it's a pure code-organization gap with zero behavior delta, not a correctness/security/UX issue. Also flagged non-blocking for product (not engineering's call): pre-redesign `AuthUser.name` was already ambiguous ("The Anderson Family" vs. a person's name) — this redesign just made it newly visible in the chip; independently corroborated by 3 specialists.
- [ ] eng-infra-9 [[features/supabase-rls-sensitive-columns]] @engineering — P0 security fix for Supabase Advisor's `sensitive_columns_exposed`: static audit of all 6 pre-existing migrations found no literal password/token columns (Supabase Auth owns credentials, not PostgREST-exposed), but 5 tables' `USING (true)` SELECT policies had no `TO` clause, exposing `families.kid_count/city/state` (foster-family PII) to the unauthenticated `anon` role; new migration `20260714000000_restrict_pii_to_authenticated.sql` scopes those policies to `TO authenticated` + `REVOKE SELECT FROM anon` on all 8 public tables (defense-in-depth); qa-engineer's introspection queries ready for a human to run against live project `rlizubjugevyxsfzmpny`; tech-lead reconciled a stale "FAIL" framing in QA's Test plan (written before the migration landed) against the migration file — no real backend/QA disagreement, just parallel-execution timing. Two ACs (identify exact flagged table by name; re-run live Advisor to confirm clear) remain open pending human dashboard/CLI access — documented, not buried, in Open questions and the reconciliation note.
- [ ] eng-infra-6 [[features/migrate-render-to-vercel-supabase]] @engineering — Edge Functions port, batch 2 done: `supabase/functions/{message,playdates,coach}/index.ts` mirror the batch-1 pattern (RLS-scoped client via `_shared/client.ts`); app-level rules RLS can't express (busy-slot hiding, no self-requests, no duplicate pending requests, coach rate-limit/cost-cap/holdback) replicated in the functions. `frontend/src/api/messages.ts`/`playdates.ts` repointed at `edgeRequest`; `coach` has no frontend consumer yet so only the function was ported. Deployed + live-verified against `rlizubjugevyxsfzmpny`. A PostgREST filter-injection bug in `message/index.ts`'s `GET /threads/:userId` (caught by automated commit review — unvalidated `partnerId` interpolated into `.or()`) was found and fixed same-day, redeployed. Backend 147/147 (unmodified), frontend 132/132, tsc/build clean. PR #50.
- [ ] eng-infra-7 [[features/migrate-render-to-vercel-supabase]] @engineering — uploads moved from local-disk (Express + multer) to a public Supabase Storage bucket (`20260713000000_uploads_storage_bucket.sql`); RLS restricts writes to the caller's own `<uid>/` prefix. Public (not signed) URLs — the old endpoint already served files with no ACL, and `mediaUrl`/`avatarUrl` columns store permanent URL strings that signed/expiring URLs would eventually break. `frontend/src/api/uploads.ts` now uploads directly via `supabase-js` instead of proxying through Express. Migration applied to `rlizubjugevyxsfzmpny`. Same PR #50.
Expand Down
2 changes: 2 additions & 0 deletions fofafu_vault/log/2026-08-20.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
- 12:55 #team/dispatch [[features/header-nav-redesign]] — feature scaffolded (owner=engineering, status=drafting)
- 12:58 #team/dispatch [[features/header-nav-redesign]] — routed to engineering, design; spawning specialists in parallel
- 13:04 #team/design/ui [[features/header-nav-redesign]] — Visual spec written: NavTrack/NavTrackItem/AccountChip anatomy + token usage + states; proposed "Pill Track" pattern + `size.hitTarget.min` token for design-lead to promote; tooltip typeface open question answered (Nunito, not mono — principle #4)
- 13:05 #team/dispatch [[features/admin-access]] — feature scaffolded (owner=engineering, status=drafting)
- 13:05 #team/dispatch [[features/admin-access]] — Problem/Acceptance criteria/Out of scope/Open questions drafted with the user: single hardcoded admin (email-matched, no role column), full edit access incl. DMs, backend+UI both in scope. Design targets Supabase (is_admin() RLS bypass policies + new admin Edge Function + admin_audit_log) rather than the legacy Express/sqlite stack, since [[features/migrate-render-to-vercel-supabase]] is actively decommissioning Express — building a new Express admin controller now would be dead-end work.
- 13:09 #team/design/a11y [[features/header-nav-redesign]] — spec-level WCAG 2.2 AA audit (Navbar.tsx diff not yet landed): 18 items reviewed, 3 blocking — avatar-chip initial white-on-brand.primary fails 1.4.3 (fix: swap to brand.primary.pressed, no new token), desktop nav links have no aria-label today so removing visible text would strip their accessible name entirely (4.1.2), avatar-chip Sign-out reveal has no confirmed keyboard/aria-expanded model yet (2.1.1). Focus-ring gap resolved non-blocking after cross-checking ui-designer's concurrent Visual spec. Build audit deferred until frontend-dev's diff lands.
2 changes: 2 additions & 0 deletions fofafu_vault/log/2026-08-23.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- 18:43 #team/eng/backend [[features/admin-access]] — implemented directly by the user's request rather than via full `/dispatch` fan-out (this session is running from the sibling `runfun` project, where this repo's own dispatcher/backend-dev/etc. agent roster isn't loaded — flagged to the user, who chose direct implementation). Resolved both Open Questions with the user first: user deletion is soft delete/ban (not hard delete), admin email `kurarei+5@gmail.com` confirmed already registered. `supabase/migrations/20260823000000_admin_access.sql`: `is_admin()` SECURITY DEFINER fn, `FOR ALL` RLS admin policies across families/announcements/comments/reactions/availability_slots/messages/playdate_requests (`coach_events` read-only per this doc's own proposed default), `admin_audit_log` (append-only). Found and fixed a real gap beyond the literal ACs: `messages`/`playdate_requests`' existing column-level grants (from `20260711010000_auth_trigger_and_rls.sql`) would have silently blocked admin's own-session-token writes to any column but the one each was narrowed to, even with the new RLS policy — added a `BEFORE UPDATE` trigger per table exempting `is_admin()`. Also found (not fixed, out of scope): `playdate_requests`' existing non-admin update path writes a column it was never granted, a latent pre-existing bug the same grant-widening incidentally resolves. `supabase/functions/admin/index.ts`: users/content/messages routes, single `is_admin()` RPC gate, service-role client (injectable factory) only inside handlers needing Supabase's Admin API, every mutation audit-logged. No pgTAP/local-Postgres harness exists in this repo yet; attempted to add one for this feature (local stack on remapped ports) but a fresh-DB migration replay fails deterministically on the pre-existing `auth_trigger_and_rls` migration regardless of pgdelta/volume state — unrelated to this feature, worth its own infra ticket, not chased further. Added `supabase/functions/deno.json` + `admin/index.test.ts` instead: 12 Deno unit tests (fake Supabase + fake service-role client), `deno check` clean. RLS/trigger SQL itself is manually reviewed only — verify against a real/staging project before this ships. Frontend `/admin` UI in progress.
- 19:11 #team/eng/frontend [[features/admin-access]] — `frontend/src/api/admin.ts` + `hooks/useIsAdmin.ts` + `pages/AdminPage/{AdminPage,UsersView,ContentView,MessagesView}` (tabbed Users/Content/Messages, incl. the required "you are viewing a private conversation" `role="alert"` banner) + `App.tsx`/`Navbar.tsx` wiring, all UX-only gating (server-side `is_admin()` is the real one). `AdminPage.test.tsx` MSW-mocked at the network boundary; this pass's own a11y guard test caught 4 real violations (wrong Tailwind token pair) in the new code, fixed before completion. Independently re-verified everything myself rather than trusting the build pass's self-report: `tsc --noEmit` clean both workspaces; full monorepo suite backend 147/147 + frontend 139/139 + Deno 12/12. Along the way found and fixed a real regression this session itself introduced: an earlier unscoped `deno check --node-modules-dir=auto` (before `supabase/functions/deno.json` existed to scope it) had restructured the repo-root `node_modules` into Deno's own npm-compat layout, which skips npm install/build scripts — silently broke `better-sqlite3`'s native binding and 100% of backend tests. Fixed with `rm -rf node_modules && npm ci`; confirmed green after. `requested_status: review` — engineering-only classification (frontmatter collaborators: []), no design/marketing track needed for an internal admin tool; no formal tech-lead spawned this pass (implemented directly per the user, from a sibling-project session without this repo's own agent roster loaded — see the 18:43 entry), so this review-audit was done by the same session that built it rather than a separate lead. E2E (Playwright) coverage intentionally not added this pass (flagged in the feature spec Test plan / E2E coverage sections, not silent). RLS/trigger SQL remains manual-review-only (no pgTAP harness in this repo) — verify against a real/staging Supabase project before shipping.
Loading
Loading