Commit Graph
94 Commits
Author SHA1 Message Date
Moheeb Zara be756035d5 docs: HANDOFF deploy state for pass-59 (run 26144230714) 2026-05-19 22:56:12 -07:00
Moheeb Zara 5d153b4d68 In-app notification inbox, consolidate /profile into /me, theme realign
In-app notification center (Phase 5, migration 051):
- notifications.read_at + partial unread index. /me Notifications is now
  an inbox: unread styling, click to expand the message + mark read,
  "mark all as read", unread count. markNotificationsRead is a service-
  client write scoped to the caller's own member_id. Always-on channel:
  email-muted ('skipped') notifications still appear in-app.

Consolidate /profile into /me:
- Delete app/(app)/profile/*; /me is the single member page. Port the
  conflict-of-interest privileged-role nudge + last-disclosed date into
  /me's profile editor. Remove the "My profile" sidebar + command-palette
  entries; rename the /me nav label to "My membership". Repoint the
  recruitment empty-state link to /me.

Theme realign:
- Revert the rounded-lg cards introduced in the prior /me visual pass to
  rounded (the app norm, matching the former /profile). Route /me section
  headers through the SectionTitle primitive.

Regression guard:
- integration/auth-embed.test.ts runs the auth layout's
  space_members.select('*, spaces(*)') embed through PostgREST and fails
  on PGRST201, so a future junction table cannot silently break auth again.
2026-05-19 22:51:44 -07:00
Moheeb Zara bcbe86424d docs: HANDOFF deploy state for the audit follow-up (run 26141570221) 2026-05-19 21:37:42 -07:00
Moheeb Zara f2534323da security: enforce https on dues_payment_methods.url at the data layer
The url renders to members as a clickable href. https was enforced only
in the admin Zod action, but RLS lets an admin/board write the row
directly via PostgREST, bypassing Zod, with no DB constraint -- a
malicious admin could store a javascript: URL and XSS members who click.

- Migration 050: CHECK (url ~* '^https://') so the DB is the source of
  truth; integration-tested that the service path cannot store
  javascript:/http:.
- Member-read action filters with isSafeDuesUrl (defense-in-depth).
2026-05-19 21:35:49 -07:00
Moheeb Zara 5ce6b29d4a test: integration coverage for dues_payment_methods RLS and dispatcher skip
- dues_payment_methods RLS: member read, admin-only write, cross-space
  denial, 046 unverified-admin status gate (4 cases).
- notification dispatcher: a muted category becomes 'skipped' while
  billing always sends; no pref row sends (default on). First real
  exercise of the skip path (cron is gated off in prod).
- Fix integration/db.ts rowsAsUser: the SELECT must be the last psql -c
  statement or its output is swallowed (it was never used before).
- Dues card: hide "No active dues subscription" for spaces without
  Stripe, where it read as confusing.
2026-05-19 20:44:08 -07:00
Moheeb Zara 897ff99726 docs: HANDOFF deploy state for pass-57/58 (run 26139360768, ab8c22a) 2026-05-19 20:28:48 -07:00
Moheeb Zara ab8c22ae1d Add notification preferences and alternate dues payment options
Notification preferences (migration 048):
- Per-member opt-out of muteable categories (bookings, classes, forms,
  admin alerts); billing is always-on. Opt-out model, default on.
- Dispatcher reads prefs and marks muted outbox rows 'skipped' instead
  of sending; pure isMuted logic, fail-open on a prefs lookup error.
- notification_preferences table (default-deny RLS); /me toggles and
  self-view via validated service-client actions.

Alternate dues payment methods (migration 049):
- Admin sets external pay-here URLs per platform (PayPal/Zeffy/Venmo)
  in /settings; members see active ones on /me and pay off-platform;
  the treasurer reconciles manually (platform pre-tagged). https-only.
- dues_payment_methods table (SELECT space member, write admin/board).

/me visual pass:
- Restore the chip/preset profile editor (skills/interests/willing-to/
  affiliations); promote ChipInput and presets to shared modules.
- Gate the Stripe pay-dues button on whether the space has Stripe
  configured; show external links or a contact-admin note otherwise.
2026-05-19 20:26:32 -07:00
Moheeb Zara addedfdb0f docs: HANDOFF pass-56 closeout, recalibrated backlog for the next session
Adds a session-closeout subsection capturing the three-audit-pass
discipline that this phase set as a new high-bar (each repeat audit
on the "clean" change set found a real, deploy-blocking issue: the
RPC enumeration vector, the best-effort gap in helpers, the phishing
vector in user-controlled fields exposed by form_submission_admin).

Refreshes the remaining-work list to the post-Phase 4 priority. The
owner-gated end-to-end validation is still the single biggest gap.
Member preferences + in-app inbox are now the natural next product
work (Phase 5; preferences also doubles as the volume governor for
Phase 4 fan-outs). user_has_permission EXECUTE-TO-PUBLIC pre-existing
exposure recorded as a separate triage item with the constraint
written down (RLS policies depend on anon/authenticated EXECUTE, so
the lockdown can't just be copied from migration 047).
2026-05-19 18:33:15 -07:00
Moheeb Zara febd0a8c11 docs: HANDOFF pass-56 deploy state, run 26135623172 success
Marks the Phase 4 entry DEPLOYED. Deploy log confirms migration 047
applied (applying 047_members_with_permission.sql, REVOKE, GRANT).
Smoke clean: public 200, gated 307, Stripe webhook 400, cron 503 (no
regressions; cron-503 is the expected fails-safe with CRON_SECRET
unset). The new event types are wired but inert at the transport
layer until owner provisions Resend and the cron secret + crontab.
2026-05-19 18:30:15 -07:00
Moheeb Zara a61e34d98d docs: HANDOFF pass-56, record P4g phishing-defense audit fix
Renames the P4f entry to "first-audit fixes", adds a P4g entry for the
third-audit URL-strip defense, and bumps the test-count line to 572
(40 new cases). The commit list at the top of the section now ends at
09f293e.
2026-05-19 17:24:42 -07:00
Moheeb Zara 47e2b4614d docs: pass-56 second-audit touchups, lockdown note + style nit
DATABASE_SCHEMA: the migration 047 row now records the REVOKE/GRANT
lockdown, with one-line rationale for why it differs from
user_has_permission (RLS policies depend on anon/authenticated being
able to call user_has_permission, so locking it would break policy
evaluation; the new function has no such constraint).

HANDOFF: replaces "Next-most-leverage" with "Highest-priority" in my
pass-56 backlog header. "Leverage" is on the CLAUDE.md style-bans list.
2026-05-19 16:18:47 -07:00
Moheeb Zara 3dead56e5c docs: HANDOFF pass-56 addendum, audit findings + P4f hardening commit
Records the ultrathink audit and the P4f fixes (RPC lockdown + best-
effort enqueue wraps). Also flags pre-existing user_has_permission
EXECUTE TO PUBLIC for separate triage: narrower than the 047 case
(per-uid boolean, only useful within spaces the caller is in), and
locking it down requires careful work because RLS policies depend on
anon/authenticated being able to call it. Out of Phase 4 scope.

Updates the test-count line to 567 (35 new cases) covering the new
helper best-effort no-throw paths.
2026-05-19 16:13:04 -07:00
Moheeb Zara 6abac53380 docs: HANDOFF pass-56, Phase 4 notification breadth + migration 047
Adds an ARCHITECTURE section "Notification breadth: bookings, classes,
forms" describing each new event type, the triggering action, the
recipient rule, and the dedupe-key scheme. Updates Known Limitations #5
to reflect that booking + class + form notifications are no longer "not
wired yet."

API_REFERENCE: extends the Transactional notifications section with the
shared enqueue helper, every new event type, its trigger and recipient,
and the new members_with_permission RPC used for the forms.manage
fan-out.

DATABASE_SCHEMA + DB_SCHEMA_MAP: adds the migration 047 row and the new
helper function to the helper-functions list.

HANDOFF: pass-56 entry covers the four locked design forks, the four
sub-phase commits, test growth (532 -> 565, +33 cases), state (local,
inert post-deploy until owner provisions Resend + CRON_SECRET), the
known fan-out volume tradeoff, and the remaining backlog.
2026-05-19 15:32:39 -07:00
Moheeb Zara 792534ee7b docs: HANDOFF pass-55 — session closeout + prioritized backlog for the next session 2026-05-19 14:39:10 -07:00
Moheeb Zara 3703c2a76c docs: HANDOFF pass-54 — 046 deployed (run 26125372468), smoke clean 2026-05-19 14:08:56 -07:00
Moheeb Zara c00c82d796 docs: HANDOFF pass-54 — deferred RLS status-gate lifted (046), harness-proven, needs deploy 2026-05-19 14:04:55 -07:00
Moheeb Zara ee7f59f1f4 docs: fix ARCHITECTURE limitation #2 (Stripe IS integrated) + HANDOFF pass-53 (F4, 26 integration tests) 2026-05-19 13:26:46 -07:00
Moheeb Zara b0267bbfb6 docs: HANDOFF pass-53 state — harness pushed (run 26122719148) + F3 extension (21 integration tests) 2026-05-19 13:20:01 -07:00
Moheeb Zara 6403220c92 test(integration): DB-backed harness validating shipped SQL (045/042/044/043)
Root-cause fix for 'correctness depended on post-deploy manual audits'.
Separate psql-driven suite (pnpm test:integration, own vitest config,
integration/ outside __tests__ so the hermetic pnpm test gate is
untouched; self-skips without a DB). 18 tests run against real Postgres:
- 045 class_signup_tx/class_cancel_tx: RETURNS TABLE->array shape the
  actions destructure, capacity/waitlist/dedupe, and the per-session
  advisory lock under TWO concurrent connections (D11 proven, never
  over-enrolls).
- 042 equipment exclusion constraint (overlap 23P01; cancelled/adjacent
  ok). 044 self-change trigger (role + payment cols blocked; service
  bypass). 043 members_update WITH CHECK (cross-space move blocked).
Disproves the pass-52 flagged risk (signup rpc-shape). Non-runtime;
default pnpm test still 532/26 hermetic; build clean.
2026-05-19 13:01:16 -07:00
Moheeb Zara c2f5f2a726 docs: HANDOFF pass-52 — E1-E3 deployed (run 26088940576), smoke clean 2026-05-19 02:38:28 -07:00
Moheeb Zara 099a103ed5 docs: HANDOFF pass-52 — E1-E3 hardening + evidence-based SECURITY DEFINER deferral 2026-05-19 02:14:39 -07:00
Moheeb Zara b90debbd37 docs: HANDOFF pass-51 — D11/D12 deployed (run 26086312156), smoke clean 2026-05-19 01:45:01 -07:00
Moheeb Zara d5a282b642 docs: HANDOFF pass-51 — D1-D10 verified (D6 regression fixed), D11/D12 deferred P1s cleared 2026-05-19 01:33:32 -07:00
Moheeb Zara 6568bc9c10 docs: HANDOFF pass-50 — D1-D10 deployed (run 26071186611), smoke clean 2026-05-18 18:49:39 -07:00
Moheeb Zara a7c3a08db9 docs: HANDOFF pass-50 — deeper audit + D1-D10 remediation 2026-05-18 18:45:43 -07:00
Moheeb Zara 247e966446 docs: HANDOFF pass-49 — Phase 3 P0 fixes deployed (run 26068281306) 2026-05-18 17:21:48 -07:00
Moheeb Zara 997e2e1366 docs: HANDOFF pass-49 — Phase 3 post-deploy audit, 2 P0 fixed (deploy ASAP) 2026-05-18 17:01:47 -07:00
Moheeb Zara c8842c3f75 docs: HANDOFF pass-48 — Phase 3 deployed (run 26066604148), smoke clean 2026-05-18 16:34:28 -07:00
Moheeb Zara 83ff54b17c docs: Phase 3 portal — API_REFERENCE, ARCHITECTURE, DEPLOYMENT (email-change config), HANDOFF pass-48 (P3f) 2026-05-18 15:55:20 -07:00
Moheeb Zara 0e54a0c7cd docs: HANDOFF pass-47 — full-codebase audit + P1 remediation (R1-R4) 2026-05-18 13:15:00 -07:00
Moheeb Zara 7844c262f8 docs: HANDOFF pass-46 — Phase 2 deployed (run 25990457904), smoke clean 2026-05-17 05:12:09 -07:00
Moheeb Zara f1bae44afd fix(notifications): P2 extreme-audit P1 fixes before deploy
Independent audit verdict was NOT SAFE; all findings fixed:
- dispatcher Idempotency-Key is now per-attempt (${id}:${attempts}),
  not row id alone, so a transient-failure response cached by Resend
  cannot permanently suppress every retry and silently lose an email.
- enqueueDues is fully try/catch wrapped so a notifications-table or
  render failure can never throw into / wedge the Stripe money path.
- dues_lapsed dedupe falls back to the subscription id when periodEnd
  is null (canceled/deleted subs), so future lapses no longer collapse
  to one key and get suppressed. New test; suite 499.
- documented the dispatcher's once/min concurrency assumption.
2026-05-17 05:06:02 -07:00
Moheeb Zara 7eb83676e7 feat(notifications): P2e member self-view on /me + docs
getMyNotifications (service-client self-view, mirrors getMyBilling) +
a read-only Notifications section on /me. Docs updated in the same
change: ARCHITECTURE (Phase 2 subsystem + limitation #5), API_REFERENCE
(notifications section, fixed the stale no-auth-middleware claim),
DATABASE_SCHEMA + DB_SCHEMA_MAP (migration 041 + table), DEPLOYMENT
(env vars + dispatcher crontab), HANDOFF pass-45.
2026-05-17 04:58:46 -07:00
Moheeb Zara dc76a86949 docs: HANDOFF pass-44 — Stripe Phase 1 deployed; webhook routing P0 fixed + redeployed 2026-05-17 03:59:21 -07:00
Moheeb Zara 64ce21e9a0 docs: HANDOFF pass-43 (Stripe Phase 1 deep-audited, P0/P1 fixed, deploy-ready) 2026-05-17 03:46:46 -07:00
Moheeb Zara ef9b0dc64f docs: P1g Stripe dues — API_REFERENCE + ARCHITECTURE + HANDOFF pass-42 (Phase 1 complete) 2026-05-17 03:39:51 -07:00
Moheeb Zara 0a11d36de1 docs: HANDOFF pass-41 (Stripe P1c-P1e built, review gate before deploy) 2026-05-17 03:11:35 -07:00
Moheeb Zara 289fa2d991 docs: HANDOFF pass-40 (Stripe dues P1a+P1b done; P1c-P1g plan) 2026-05-17 03:00:37 -07:00
Moheeb Zara fe8696bbc3 docs: HANDOFF pass-39 deploy-state (backlog complete, deployed run 25987172786) 2026-05-17 02:33:23 -07:00
Moheeb Zara 41193d3f68 docs: HANDOFF pass-39 (backlog complete; product spine is the next thread) 2026-05-17 02:03:01 -07:00
Moheeb Zara d3481129a8 docs: HANDOFF pass-38 (mobile card tables; bulk/a11y remaining) 2026-05-17 01:54:55 -07:00
Moheeb Zara 51f733414b docs: HANDOFF pass-37 (dashboard attention + cached perms; remaining queued) 2026-05-17 01:37:35 -07:00
Moheeb Zara e7463c0b95 docs: HANDOFF pass-36 (F1/F2 + UX pack part 1) 2026-05-17 01:24:48 -07:00
Moheeb Zara 363e2a667f docs: HANDOFF pass-35 clean checkpoint + next-session kickoff prompt 2026-05-17 00:59:56 -07:00
Moheeb Zara 2427b6b822 docs: HANDOFF pass-34 deploy-state (deployed, run 25985079088) 2026-05-17 00:52:01 -07:00
Moheeb Zara 8befee903c docs: HANDOFF pass-34 (email-link bugfix root cause + fix) 2026-05-17 00:32:03 -07:00
Moheeb Zara c2f8368634 docs: HANDOFF pass-33 deploy-state (deployed, run 25984491717) 2026-05-17 00:22:14 -07:00
Moheeb Zara cec2d69097 docs: deleteForm cascade + deleteSubmission/relinkAll; HANDOFF pass-33
API_REFERENCE + ARCHITECTURE updated (deleteForm now permanently
cascades incl. signed waivers; immutability caveat; new deleteSubmission
+ relinkAllSubmissions). Corrected pass-32 header to DEPLOYED.
2026-05-17 00:12:43 -07:00
Moheeb Zara bff8deac55 docs: HANDOFF pass-32 deploy-state (deployed, run 25984078567) 2026-05-17 00:01:31 -07:00
Moheeb Zara 61c97e3b8d docs+landing: forms email-link decision documented; landing mobile polish
Docs: API_REFERENCE + ARCHITECTURE updated to the owner-chosen
email-match association model (incl. anonymous, tradeoff documented) and
listMemberSubmissions; HANDOFF pass-32; corrected pass-31 header.
Landing: tightened the <=600px breakpoint (nav/section/card padding) and
added a <=400px block so the nav fits ~320px without overflow. CSS-only
inside media queries; desktop unchanged.
2026-05-16 23:54:46 -07:00