docs: pass-17 CHANGELOG / HANDOFF / schema migration table
Records the UX polish tranche and the incident-RLS diagnosis (incl. the discriminating production SQL) and adds migration 025 to the schema migration table.
This commit is contained in:
@@ -16,6 +16,12 @@ All notable changes to this project are documented here. Format follows [Keep a
|
||||
- `types/database.ts` brought in sync with the schema (8 tables, the `comment_entity_type` enum, and the new columns); docs (`DATABASE_SCHEMA.md`, `DB_SCHEMA_MAP.md`, `API_REFERENCE.md`) refreshed for migrations 016-024.
|
||||
- The member directory has a per-row "make area lead" assignment for admin/board (mirrors Customize -> Area leads).
|
||||
|
||||
### Fixed
|
||||
- Production report: a basic member filing an incident hit `new row violates row-level security policy for table "incidents"`, including the anonymous path. Not reproducible from source (the code and the 016/017 policy both permit it). Migration 025 idempotently re-asserts the hardened `incidents_insert` policy verbatim so the deployed policy converges to the known-good definition (access-neutral, no schema change). The file-incident action now maps an RLS rejection to an actionable message and logs the technical detail server-side. If the report persists after deploy, the root cause is not policy drift; see `docs/HANDOFF.md` for the discriminating queries.
|
||||
|
||||
### UX
|
||||
- Accessibility and consistency polish pass (Tier 1 systemic, in progress): distinct Ops nav icon (was a duplicate of Settings), `aria-current` on active nav, accessible mobile drawer (role/aria-modal, Escape, scroll lock, focus management), skip-to-content link, shared `BrandMark` in the sidebar, a shared `PageTitle`/`SectionTitle` primitive adopted app-wide, and a single shared `Empty` primitive replacing ~18 ad-hoc empty states (projects gains a real empty state with a CTA).
|
||||
|
||||
### Security
|
||||
- Closed two member self-escalation paths: a non-privileged member can no longer change their own `tier_id` or `onboarding_completed_at` on the `space_members` row (migration 024 extends the self-change trigger). Onboarding completion now goes through the service client after server-side validation.
|
||||
- Added in-code space scoping to forum/comment/channel mutations (defence-in-depth on top of RLS).
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
> **Source of Truth**: `scripts/schema.sql` (canonical, idempotent); numbered
|
||||
> migrations `scripts/0NN_*.sql` upgrade existing deployments. The 13-table
|
||||
> reference below is the original baseline; tables added by migrations
|
||||
> 014-024 are summarized in the "Migrations 014-024" section at the end.
|
||||
> 014-025 are summarized in the "Migrations 014-025" section at the end.
|
||||
|
||||
---
|
||||
|
||||
@@ -686,7 +686,7 @@ CREATE INDEX idx_activity_space ON activity_log(space_id, created_at);
|
||||
|
||||
---
|
||||
|
||||
## Migrations 014-024 (additions since the 13-table baseline)
|
||||
## Migrations 014-025 (additions since the 13-table baseline)
|
||||
|
||||
`scripts/schema.sql` is the canonical idempotent schema; each numbered
|
||||
migration is mirrored as a section in it. Tables/columns added:
|
||||
@@ -704,6 +704,7 @@ migration is mirrored as a section in it. Tables/columns added:
|
||||
| 022 | `space_onboarding_steps`; `space_members.onboarding_completed_at/onboarding_progress`; seed default steps |
|
||||
| 023 | `space_role_permissions`, `ops_acl`; `user_effective_roles()`, `user_has_permission()` (SECURITY DEFINER); `secrets`/`knowledge_base` SELECT rewritten as `existing-rule OR ops_acl-match` (additive) |
|
||||
| 024 | `prevent_member_self_role_change()` extended to also block self-change of `tier_id` and `onboarding_completed_at` |
|
||||
| 025 | Re-asserts the hardened `incidents_insert` policy verbatim (production convergence; access-neutral, no schema change) |
|
||||
|
||||
New enum: `comment_entity_type` = `forum_thread | proposal | incident | policy`.
|
||||
|
||||
|
||||
@@ -4,6 +4,39 @@ Append-only. Newest entries on top. Keep each entry to one screen.
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-15 (pass 17) — UX polish (Tiers 1-3) + production incident-RLS report
|
||||
|
||||
Branch: `main`. Migration: 025 (applies on next deploy). Not pushed.
|
||||
|
||||
### UX polish (in progress, Tiers 1-3 of the audit)
|
||||
|
||||
- Full UI/UX audit done (5 partitioned agents, code-level; no live browser, auth routes need seed). Findings report delivered to the user, grouped Tier 1 systemic / Tier 2 first-run / Tier 3 security-adjacent / Tier 4 brand / Tier 5 per-surface. User chose: implement Tiers 1-3 this pass, pause before 4-5; sidebar nav stays as-is (no role-filtering); comments get one-level nesting + edit (Tier 5, deferred to post-pause).
|
||||
- Committed (local, build+test green 271/271 each): (1) accessible sidebar/drawer + distinct Ops icon + skip link + BrandMark; (2) shared `PageTitle`/`SectionTitle` primitive adopted across 21 files; (3) shared `Empty` primitive replacing ~18 ad-hoc empty states (projects gains a real empty state).
|
||||
- Still open in Tier 1: Dialog/AlertDialog adoption (hand-rolled modals, `confirm()` x13), focus-visible, tap targets, label associations, silent-failure toasts. Tier 2/3 not started.
|
||||
|
||||
### Production incident-filing RLS report (priority interrupt)
|
||||
|
||||
User screenshot: a basic active member filing an incident at `/incidents/new` (anonymous checked) gets `new row violates row-level security policy for table "incidents"`. Production is on the OLD deployed commit (sidebar still `{hs}`), so this is pre-existing, not from this session.
|
||||
|
||||
- Not reproducible from source: `fileIncident` sets `reporter_id=NULL` when anonymous and `space_id=member.space_id`; `getAuthMember` resolves by `user_id=auth.uid()`; the 016/017 `incidents_insert` policy allows exactly this. So it is a production deploy/data divergence.
|
||||
- Three candidate root causes: (1) `auth.uid()` NULL inside the DB request (SSR session not reaching PostgREST); (2) deployed `incidents_insert` policy differs from 016/017; (3) acting `space_members.user_id` != JWT sub on production (data drift).
|
||||
- Defensive fixes prepared (user-approved, blind, NOT pushed): `scripts/025` idempotently re-asserts the hardened `incidents_insert` policy verbatim (access-neutral; fixes cause #2 only); `fileIncident` maps RLS rejection to an actionable message and logs detail server-side. `scripts/schema.sql` already matches (no change).
|
||||
|
||||
### Pending decision / next step
|
||||
|
||||
Run these on the production DB (service role) to disambiguate before relying on 025:
|
||||
|
||||
```sql
|
||||
SELECT polname, pg_get_expr(polwithcheck, polrelid) AS with_check
|
||||
FROM pg_policy WHERE polrelid = 'public.incidents'::regclass AND polcmd = 'a';
|
||||
SELECT id, space_id, user_id, status, role FROM public.space_members WHERE user_id = '<auth_uid>';
|
||||
SELECT public.get_user_space_ids('<auth_uid>'::uuid);
|
||||
```
|
||||
|
||||
If query 1 already shows the 016/017 expression and query 2 shows a matching `user_id` with the space present, 025 will NOT fix it (cause #1 or #3); investigate the SSR Supabase client / membership data instead.
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-15 — SESSION CLOSE: state of the app
|
||||
|
||||
One-screen status for the next session. Detailed history in the pass entries below.
|
||||
|
||||
Reference in New Issue
Block a user