Commit Graph
68 Commits
Author SHA1 Message Date
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
Moheeb Zara a364d152ca docs: HANDOFF pass-31 deploy-state (deployed, run 25982959375) 2026-05-16 23:02:19 -07:00
Moheeb Zara d0aef9a6aa docs: correct ARCHITECTURE layout/§7, note intentional settings + single-space
Project tree + section 7 module paths now reflect the per-domain
lib/actions/* reality (no monolithic lib/actions.ts); explicit note that
/settings admin secret loading is by-design same-space management;
getAuthMember single-space invariant documented in code. HANDOFF pass-31.
2026-05-16 22:44:21 -07:00
Moheeb Zara 0bb934d5bd docs: HANDOFF pass-30 deploy-state (deployed, run 25982428014) 2026-05-16 22:32:30 -07:00
Moheeb Zara c12a103e11 docs: HANDOFF pass-30 (presence module + full audit pass + deferred items) 2026-05-16 22:30:43 -07:00
Moheeb Zara e7475d954c docs: HANDOFF pass-29 deploy-state (deployed, run 25981712253) 2026-05-16 21:53:30 -07:00
Moheeb Zara 46b294286a docs: API_REFERENCE + ARCHITECTURE + HANDOFF pass-29 (class form gate, rosters, /doors) 2026-05-16 21:48:46 -07:00
Moheeb Zara 268d78dd6d docs: HANDOFF pass-28 deploy-state (self-entry deployed, run 25981195445) 2026-05-16 21:23:50 -07:00
Moheeb Zara 6d1c8536cd feat(door): member self-entry on the dashboard (locked design)
selfEntry({connectionId}): any active member with >=1 active card on file
may trigger a momentary open on a connection that is enabled and has
allow_member_self_entry on (locked rule: any active card, no slot row
required). Membership/cards resolved server-side so a member can only open
for themselves; never unlock/lock/grant/revoke; never anonymous. Strict
5/min per-member rate limit, one redacted self_entry audit row (denials
audited too), through the existing hardened executor. listSelfEntryDoors
returns the enabled self-entry connections only when the caller has an
active card, so the dashboard 'Door access' panel is hidden for
ineligible members. API_REFERENCE + ARCHITECTURE + HANDOFF pass-28.
2026-05-16 21:21:09 -07:00
Moheeb Zara 14a2554d06 docs: HANDOFF pass-27 deploy-state (Door P3 deployed, run 25981082371) 2026-05-16 21:17:18 -07:00
Moheeb Zara d0478202b6 docs(door): P3 ARCHITECTURE + HANDOFF pass-27 (slot allocator, live actions, self-entry checkpoint) 2026-05-16 21:15:08 -07:00
Moheeb Zara 7ea3dea697 docs: HANDOFF pass-26 — clean checkpoint, Door P3-P5 plan 2026-05-16 20:42:57 -07:00
Moheeb Zara 5e40b91550 docs: HANDOFF pass-25 (Door P1 deployed, P2 local) 2026-05-16 20:32:21 -07:00
Moheeb Zara 1cbea138ba docs: HANDOFF pass-24 (door design approved, P1 + nav local, adapter facts re-verified) 2026-05-16 20:17:30 -07:00
Moheeb Zara f32743d8bd feat(equipment): My reservations on /me + docs
/me gains a "My equipment reservations" section (the member's own
reservations with status and window). ARCHITECTURE documents the
equipment module; HANDOFF pass-23 records Classes deployed and the
code-complete-but-local equipment module.
2026-05-16 19:55:49 -07:00
Moheeb Zara 209622867f feat(classes): My classes on /me + docs
/me gains a "My classes" section (the member's own signups with status
and attendance). ARCHITECTURE documents the classes module; HANDOFF
pass-22 records the code-complete-but-local module and the reaffirmed
roadmap.
2026-05-16 19:38:37 -07:00
Moheeb Zara f783541a87 docs: mark production incident-INSERT RLS bug resolved (user-confirmed) 2026-05-16 19:18:24 -07:00
Moheeb Zara 70da0c3752 feat(markdown): KB/process render + working in-document anchor links
KB/process content was shown via a bare unsanitized ReactMarkdown in the
ops view dialog (the /ops/[id] route is the editor, not a view). Route
it through the shared SafeMarkdown instead. New dependency-free
lib/markdown-anchors.ts (heading-text flatten, GitHub-ish slug,
per-render dedupe, in-doc href test; unit-tested) is wired into both
SafeMarkdown and MarkdownBody so headings get stable ids and a
[link](#heading) resolves. In-document links no longer force a new tab,
which previously broke the anchor jump for policies/proposals too.
rehype-slug was not added (no offline install); the helper is equivalent
and tested.
2026-05-16 19:11:49 -07:00
Moheeb Zara 2687c1578d fix(secrets): honor ops.secrets.read permission for reveal/list
A regular member granted reveal access either via the ops.secrets.read
role permission or a per-secret ops_acl entry could not actually see or
reveal secrets: revealSecret hard-gated on built-in role admin/board
(ignoring both the permission and the ACL), and secrets_select RLS never
consulted the permission, and the Ops page short-circuited the secrets
fetch by built-in role.

Migration 031 additively adds an OR user_has_permission(...,
'ops.secrets.read') branch to secrets_select; the admin/board and
per-secret ops_acl branches are unchanged so deployment is access-neutral
unless an admin has granted the permission. revealSecret now resolves
the member and lets RLS be the boundary (a denied row simply does not
return). The Ops page always queries secrets (RLS filters the set) and
shows the section to admin/board, a permission holder, or anyone with an
ACL-granted secret. Write paths (create/update/delete) are unchanged.
Mirrored in schema.sql; migration docs and HANDOFF updated.
2026-05-16 19:07:06 -07:00
Moheeb Zara b2ac0e0271 feat(certifications): member-facing /me view + docs
/me shows the signed-in member their own certifications (status badges
from the shared logic) and a read-only view of their effective
permissions. Sidebar gets a "My access" link. ARCHITECTURE documents the
module; HANDOFF pass-21 records the UX fixes (deployed) and the
code-complete-but-local certifications module.
2026-05-16 19:01:57 -07:00
Moheeb Zara ad477252c8 feat(incidents): anonymous report tracking by token + public /track
Gap: fileIncident minted a reporter_token and the filing UI showed it
("the only way to look it up later") but there was no lookup UI.

- lib/incident-logic.ts (pure, tested): publicIncidentView redacts —
  board_only updates, subjects, decision-maker ids dropped; disposition
  only surfaced once decided/closed; status label mapping
- trackIncident: public service-client action (token is a 192-bit
  bearer credential on a UNIQUE column; anon has no RLS path so the
  service client is used AFTER token validation; generic not-found is
  enumeration-safe; board_only filtered at the query AND in logic)
- public /track page + client (also accepts ?token= deep link);
  /track added to middleware PUBLIC_ROUTES
- filing UI now deep-links /track?token=<code>
- trackIncidentSchema; __tests__/incident-tracking.test.ts (6)
- docs: API_REFERENCE + HANDOFF pass-20 (session arc, web-verified
  integration facts, locked forward roadmap, open items incl. the
  still-open production incident-INSERT RLS bug)

343 tests; build verified (Compiled successfully, exit 0).
2026-05-16 18:13:09 -07:00
Moheeb Zara 73eef7293a feat(forms): obvious publish flow, member Forms surface, templates (P8)
- builder: draft/published/closed status banners; creating a form now
  lands on its edit page so Publish is immediately visible; a
  members-only form shows a "no public link" note instead of a dead URL
- member-facing forms: /my-forms list + /my-forms/[id] fill
  (MemberFillClient), "Forms" sidebar item for all members. Fixes the
  long-standing gap where members-only forms had no fill surface
- starter templates: lib/form-templates.ts + a picker on /forms/new
  (Liability waiver, Membership application, Event RSVP, Photo release,
  Blank) that seeds builder state (no table)
- forms-client relabels members-only forms (was showing a misleading
  /f/<space>/<slug> that always 404s for members-only)
- docs corrected per the audit pass: DATABASE_SCHEMA / DB_SCHEMA_MAP /
  ARCHITECTURE / API_REFERENCE now reflect migrations 014-029, per-space
  slug, /f/[space]/[slug], getPublicForm({space,slug}), invite roles;
  HANDOFF pass-19 added with residual gaps

337 tests; build verified (Compiled successfully, exit 0). No deploy.
2026-05-16 17:41:16 -07:00
Moheeb Zara 61403ad29f docs: HANDOFF — test-coverage hardening (forms-logic extraction, 333 tests) 2026-05-16 16:40:32 -07:00
Moheeb Zara 40d4cdad98 feat(forms): verified-email retro-link of anonymous submissions (Phase 5)
- claimMyAnonymousSubmissions(): exported but safe — no trusted params,
  resolves member + email from the caller's session, gated on
  email_confirmed_at, idempotent. Links the caller's own prior
  anonymous submissions to their member row
- hooked best-effort from joinSpace (member insert now returns id;
  links prior anon submissions for the verified email) and
  finishOnboarding
- addMember/importMembers deliberately NOT auto-linked: those emails
  are admin-asserted, not verified; the locked decision forbids
  auto-link there, so the forms.manage linkSubmissionsForMember stays
  the admin manual-link path
- also repairs a build break committed in 295d220: finishOnboarding
  declared `const admin` twice (Phase 4 added a second). The probe and
  the completion update now share one admin client
- docs: API_REFERENCE / ARCHITECTURE / HANDOFF

287/287 tests, build compiles clean (verified Compiled successfully). No deploy.
2026-05-16 16:29:32 -07:00
Moheeb Zara 295d220849 feat(forms): onboarding form/waiver step with required enforcement (Phase 4)
- scripts/027 (idempotent CHECK replace) + schema.sql Section 14: allow
  space_onboarding_steps.step_type = 'form'; referenced form id lives in
  the step's existing config.form_id (no new column)
- finishOnboarding: a required form step is satisfied when a submission
  for config.form_id by the member exists (any version — re-sign is
  non-blocking / auto-satisfy); fails open if the form is missing or
  unpublished so a misconfig cannot trap a member. Probe uses the
  service client (submissions are forms.manage-only under RLS)
- onboarding page enriches form steps with the published form def +
  prior-submission flag; onboarding-flow renders FormRenderer + waiver
  legal text/consent and submits via submitForm then marks the step done
- customize: forms fetched and threaded to the onboarding panel; new
  "+ Form step" button + form picker writing config.form_id
- validations/types/casts include 'form'; forms.test.ts +2
- docs: DATABASE_SCHEMA/DB_SCHEMA_MAP/API_REFERENCE/ARCHITECTURE/HANDOFF

287/287 tests, build exit 0. No deploy.
2026-05-16 15:15:17 -07:00
Moheeb Zara 03bb3a7466 feat(forms): builder UI, management pages, and public fill page (Phase 3)
- components/forms/form-renderer.tsx: one controlled renderer for all 8
  field types, shared by the builder preview and the public page
- components/forms/form-builder.tsx: easy builder — label-only field
  creation (keys auto-derived and de-duped at save), one-click type
  buttons, up/down reordering, inline options editor, live preview;
  edit mode adds publish/close/draft/delete and copy-public-link
- /forms directory, /forms/new, /forms/[id]/edit, /forms/[id]/results
  (CSV download). Server-gated by lib/forms-guard.ts (forms.manage via
  the same RPC the RLS uses; RLS remains the real boundary)
- public app/f/[slug] (outside the app group): getPublicForm serves
  only published public forms — members-only forms are never exposed
  there; waiver consent, anon email capture, signed-in gate for
  public_auth, thank-you state
- sidebar "Forms & waivers" link; sonner toasts (codebase convention)
- ARCHITECTURE + HANDOFF updated

285/285 tests, build clean, all four routes compiled. No deploy.
2026-05-16 15:03:48 -07:00
Moheeb Zara 897fa30ec1 feat(forms): server actions, Zod schemas, and dynamic answer validation (Phase 2)
- lib/validations.ts: form field/array schemas (8 field types,
  duplicate-key and select-options refinements) and create/update/
  status/submit/link envelope schemas; slug immutable on update
- lib/forms-schema.ts: pure parseFormSchema + validateAnswers; unknown
  answer keys are discarded so a caller cannot inject arbitrary jsonb
- lib/actions/forms.ts: create/update/setStatus/delete/list/results/
  CSV export (forms.manage via user_has_permission + RLS), submitForm
  (service-client read+write, visibility + waiver consent enforced,
  schema/legal/version snapshot, IP/UA capture), and
  linkSubmissionsForMember (manual admin link; auto path is Phase 5)
- updateForm bumps a published waiver's version on legal/schema change
  (non-blocking; existing submissions stay valid against their snapshot)
- deleteForm refuses when submissions exist (immutable records)
- types/database.ts: type user_has_permission/user_effective_roles
  (stale since migration 023)
- __tests__/forms.test.ts (14); API_REFERENCE + HANDOFF updated

285/285 tests, build clean. No deploy.
2026-05-16 14:51:20 -07:00