docs: fix doc drift from D/F/G work — migration 046, harness, subsystem-level facts
DATABASE_SCHEMA + DB_SCHEMA_MAP: 046 migration row (was missing — added last turn but not table-rowed; CLAUDE.md doc-with-change rule). README Features: Stripe recurring dues, transactional notifications, member self-serve portal (Phase 1/2/3 — months of shipped product spine were absent from the first thing a reader sees). ARCHITECTURE: Known Limitations #1 now describes the DB-backed integration harness (the previous 'no harness' claim was misleading); Classes section notes the advisory-lock RPCs (045); Equipment notes the GiST exclusion constraint + btree_gist (042); Door notes the DNS-rebind defense (resolve-once, pin validated IP) + no-raw-reason client policy + auth-param-aware redaction; Stripe webhook notes the out-of-order monotonic period guard (D5), zero-decimal currency handling (D7), and generic error responses (E3); Notifications notes the per-space fair drain + per-attempt Idempotency-Key + .eq('status', 'pending') re-entrancy guard; Authorization adds the RLS-layer privilege-eligible status gate (046).
This commit is contained in:
@@ -726,6 +726,7 @@ migration is mirrored as a section in it. Tables/columns added:
|
||||
| 043 | `members_update` RLS policy gains a `WITH CHECK` mirroring its `USING` (`user_id = auth.uid()` OR admin/board/treasurer in `space_id`). Defense-in-depth: the post-image is re-validated so a privileged caller cannot move a member row into a space they do not administer. No new table/column |
|
||||
| 044 | `prevent_member_self_role_change` extended to also block a non-privileged member changing their own `payment_status`, `payment_note`, `dues_paid_until`, `last_paid_at`, `last_payment_at`, `stripe_customer_id`, `joined_at` (RLS lets a member UPDATE their own row, so these were self-settable to forge a dues-good signal). Service-client (`auth.uid()` NULL) and privileged-on-another-member writes still bypass. No new table/column |
|
||||
| 045 | `class_signup_tx(p_session_id,p_space_id,p_member_id)` + `class_cancel_tx(...)` SECURITY DEFINER, `search_path=public`. Each takes `pg_advisory_xact_lock(hashtext(session_id))` then does the capacity-decision+dup-check+insert (signup) or cancel+conditional waitlist-promote (cancel) atomically. Closes the concurrent over-enroll / double-promote races (dynamic capacity can't be a constraint). Mirrors `computeSignupStatus`/`pickPromotion`; the SQL is the runtime authority. No new table/column/policy |
|
||||
| 046 | `user_has_role_in_space` and `user_has_permission` extended with `status IN ('current','late')` (mirrors `lib/permissions` `PRIVILEGE_STATUSES`). RLS-layer defense-in-depth for D2 (the require_approval / unverified-admin escalation): a non-privilege-eligible member who somehow bypassed the app guards still cannot exercise a role or permission. `get_user_space_ids` and `user_effective_roles` are deliberately NOT gated so unverified members keep their own /me + onboarding reads (the legitimate require_approval flow). No new table/column/policy |
|
||||
|
||||
New enum: `comment_entity_type` = `forum_thread | proposal | incident | policy`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user