Pilot Readiness
Purpose: Systematic health check of every tier, with pass/fail criteria, current status, and next actions. This is the single document where anyone can see what’s solid, what’s broken, and what to work on next.
How to use: Work top-down. Fix all RED items in a tier before moving to the next tier. Within a tier, fix RED before YELLOW. Check off items as they’re verified.
Last assessed: 2026-03-24 Assessed by: Claude (code audit + production SQL verification via Management API)
Reading This Document
Seção intitulada “Reading This Document”Each item has a health indicator:
- PASS — Verified working correctly
- WARN — Works but has known limitations or risks
- FAIL — Broken, missing, or unverified — must fix before pilot
- N/A — Not applicable or intentionally deferred
Each tier has an overall gate:
- GATE: OPEN — Tier is healthy, safe to invest in lower tiers
- GATE: BLOCKED — Has FAIL items that must be resolved first
Tier 1 — Foundation Pillars
Seção intitulada “Tier 1 — Foundation Pillars”Does the core compliance engine work?
GATE: OPEN (no FAIL items — 1 WARN remains: item 2.5 deferred to production)
Pillar 1: Document Ingestion & Identity
Seção intitulada “Pillar 1: Document Ingestion & Identity”| # | Check | Status | Notes |
|---|---|---|---|
| 1.1 | Upload stores file scoped to correct company | PASS | Path: users/{userId}/companies/{company_id}/documents/{year}/{docId}/{file} |
| 1.2 | Document type is identified during upload | PASS | Two-step: type selection + optional OCR auto-detection |
| 1.3 | Category is derived from document type (not manual) | PASS | Read-only, fetched from document_types table |
| 1.4 | File is retrievable and viewable | PASS | DocumentViewer.tsx handles PDF + image rendering |
| 1.5 | Upload enforces file type/size restrictions | PASS | Validated in upload modal |
| 1.6 | Storage bucket policies block direct access | PASS | Migration 20250805012821 enforces get_user_company_id() + auth.uid() on INSERT/SELECT/UPDATE/DELETE |
Pillar 2: Temporal Truth (Dates & Derived Status)
Seção intitulada “Pillar 2: Temporal Truth (Dates & Derived Status)”| # | Check | Status | Notes |
|---|---|---|---|
| 2.1 | Issue date and expiry date captured (OCR or manual) | PASS | Both fields in upload and edit forms; OCR auto-fills when detected |
| 2.2 | Status derived automatically from dates | PASS | daysUntilExpiry ≤ 0 → expired; ≤ 90 → expiring; else valid |
| 2.3 | Users cannot manually override status | PASS | No status field in any edit form |
| 2.4 | Indefinite-validity types handled correctly | PASS | expiry_date set to null; no false alerts |
| 2.5 | Status recalculated on date edit | WARN | Status updates on next daily job run, not immediately on edit. Same-day gap exists |
| 2.6 | RLS policies properly restrict document access | PASS | Verified 2026-03-23 via SQL audit: no permissive policies. All tables use get_user_company_id(). All 39 public tables have RLS enabled |
Pillar 3: Proactive Compliance Visibility
Seção intitulada “Pillar 3: Proactive Compliance Visibility”| # | Check | Status | Notes |
|---|---|---|---|
| 3.1 | Alerts created at correct thresholds | PASS | 90, 60, 30, 15, 7 days before expiry |
| 3.2 | Alert Center shows pending alerts with company_id filter | PASS | useAlertCenter.ts filters by company_id + status |
| 3.3 | Dashboard shows real compliance signal | PASS | Expired/expiring/valid counts, compliance score, blocking issues |
| 3.4 | Expiring documents surfaced on dashboard | PASS | Top 5 expiring docs via useExpiringDocuments with company_id filter |
| 3.5 | Email alerts actually reach users | PASS | Verified 2026-03-24: test document (7-day expiry) triggered alert + email via SMTP2Go. emailsSent: 1 confirmed in job response. Test data cleaned up |
| 3.6 | Daily alert job runs and recalculates status | PASS | BUG-044 fixed 2026-03-23. CRON_SECRET set + pg_cron job updated with x-cron-secret header. Manual trigger returned success: true (3 docs processed, 1 status update) |
| 3.7 | Email templates support user language | PASS | Fixed 2026-03-24: All 4 templates (expiry, welcome, invite, notification) now bilingual (pt/en). Daily job reads user.language and passes to template. Edge Functions redeployed |
Tier 1 Blockers — Must Fix
Seção intitulada “Tier 1 Blockers — Must Fix”| Priority | Item | Bug | Action Required |
|---|---|---|---|
| DONE 2026-03-23 — SQL audit confirmed no permissive policies. All 39 tables have RLS enabled | |||
DONE 2026-03-23 — CRON_SECRET set + pg_cron job updated. Manual trigger confirmed success: true | |||
| — | DONE 2026-03-24 — Test document (7-day expiry) triggered alert + email. emailsSent: 1 confirmed |
Tier 2 — Operational Readiness
Seção intitulada “Tier 2 — Operational Readiness”Can a real team use this daily?
GATE: OPEN (BUG-010 fixed 2026-03-24)
Cluster A: CRUD Completeness
Seção intitulada “Cluster A: CRUD Completeness”| # | Check | Status | Notes |
|---|---|---|---|
| A.1 | Edit document metadata (dates, number, authority) | PASS | DocumentEditModal.tsx supports all key fields |
| A.2 | Delete documents (soft delete to recycle bin) | PASS | useRecycleBin.ts with deleted_at timestamp |
| A.3 | Restore deleted documents | PASS | Single and bulk restore with race condition guards |
| A.4 | Permanently delete from recycle bin | PASS | Includes storage file cleanup |
| A.5 | Document versioning/supersession | PASS | MultiSupersessionDialog + is_current flag |
| A.6 | Audit trail for changes | PASS | useActivityLog + useDeletionAuditLog |
| A.7 | Supersession enforces company_id | PASS | BUG-026 fixed 2026-03-15 |
| A.8 | Duplicate detection enforces company_id | PASS | BUG-027 fixed 2026-03-15 |
Cluster A health: PASS
Cluster B: Delivery & Communication
Seção intitulada “Cluster B: Delivery & Communication”| # | Check | Status | Notes |
|---|---|---|---|
| B.1 | Welcome email sent on registration | PASS | Fire-and-forget in Auth.tsx; failure logged, not blocking |
| B.2 | Invitation email sent with signup link | PASS | 7-day token expiry; invite created via RPC |
| B.3 | Alert thresholds configurable per user | PASS | useAlertSettings.ts stores per-type custom thresholds |
| B.4 | In-app activity feed | PASS | useRecentActivity on dashboard + Reports activity tab |
| B.5 | Email failure doesn’t break user flow | PASS | Caught with warning toast; non-blocking |
| B.6 | SMS notifications | N/A | Schema supports it; not implemented. Deferred to post-pilot |
| B.7 | Email templates bilingual | PASS | Fixed 2026-03-24: All templates bilingual (pt/en). Uses user.language from database |
| B.8 | Notification delivery audit trail | WARN | No alert_delivery_log table. Can’t confirm which emails were sent |
Cluster B health: PASS (with known language limitation)
Cluster C: Navigation & Findability
Seção intitulada “Cluster C: Navigation & Findability”| # | Check | Status | Notes |
|---|---|---|---|
| C.1 | Global search across documents/packs/persons | PASS | Accent-insensitive, debounced, grouped results |
| C.2 | Category filtering on documents page | PASS | Multi-select with status filters |
| C.3 | Dashboard shows real compliance signal (not vanity) | PASS | Score = valid/total; 0 when empty (conservative) |
| C.4 | Reports with category breakdown | PASS | Status, Activity, Deletions tabs |
| C.5 | Search input properly sanitized | PASS | BUG-040 was false alarm; prepareSearchQuery() escapes input |
Cluster C health: PASS
Cluster D: Team & Access
Seção intitulada “Cluster D: Team & Access”| # | Check | Status | Notes |
|---|---|---|---|
| D.1 | Authentication (email/password) | PASS | Supabase Auth with session management |
| D.2 | Password reset flow | PASS | Email link flow implemented |
| D.3 | Multi-tenancy (company_id isolation in queries) | PASS | All major queries include .eq('company_id', companyId) |
| D.4 | User invitation and onboarding | PASS | RPC-based invite with token, email, and accept flow |
| D.5 | Roles enforced server-side (RLS) | PASS | BUG-010 fixed 2026-03-24: has_write_access() blocks viewers from writing across 8 tables (22 policies). is_user_manager_or_admin() on applicability tables. Storage file operations also role-gated |
| D.6 | Supabase credentials secured | PASS | BUG-005 fixed 2026-03-24: old token revoked + rotated. .env gitignored. Anon key is public by design |
| D.7 | RLS policies verified in production | PASS | Verified 2026-03-23: no permissive policies. All 39 tables have RLS enabled |
| D.8 | Admin role assigned server-side | WARN | Currently assigned client-side in Auth.tsx. Could be spoofed |
| D.9 | Forced logout on user removal | WARN | User removal sets company_id = null but doesn’t revoke session |
Cluster D health: PASS — All security blockers resolved
Tier 2 Blockers — Must Fix
Seção intitulada “Tier 2 Blockers — Must Fix”| Priority | Item | Bug | Action Required |
|---|---|---|---|
| 1 | Rotate Supabase credentials | BUG-005 | Rotate management token + anon key in Supabase dashboard. Remove .env from git tracking. Scrub history if repo may become public |
| DONE 2026-03-23 — resolved by Tier 1 verification | |||
| 2 | Implement server-side role enforcement | BUG-010 | Create check_user_role() SQL function; add to RLS policies for INSERT/UPDATE/DELETE |
Tier 3 — Value Multipliers
Seção intitulada “Tier 3 — Value Multipliers”What makes the product compelling?
GATE: OPEN (no blockers, but assess after Tiers 1-2 are clean)
| # | Feature | Status | Notes |
|---|---|---|---|
| 3.1 | Smart Packs (nested hierarchy) | PASS | Fully implemented with status rollup |
| 3.2 | Smart Pack PDF export | PASS | Cover pages, document indices, ZIP bundling (BUG-020 fixed) |
| 3.3 | Compliance Resolution Flow | PASS | Guided modal for resolving pack blockers |
| 3.4 | OCR auto-extraction | WARN | 7 of 14 document types MVP-ready. Coverage expanding |
| 3.5 | Duplicate detection | PASS | SHA-256 + OCR metadata matching |
| 3.6 | Custom document types | PASS | Company-created types with validity + pack integration |
| 3.7 | Knowledge Base | PASS | Contextual tooltips + full articles per document type |
| 3.8 | Inline form validation (WCAG 3.3.1) | PASS | BUG-037 fixed 2026-03-23 |
Tier 4 — Polish & Delight
Seção intitulada “Tier 4 — Polish & Delight”What makes the product excellent?
GATE: OPEN (address after Tiers 1-3 are stable)
| # | Feature | Status | Notes |
|---|---|---|---|
| 4.1 | Favorites / recently accessed | WARN | useFavorites tracks access; no true bookmarking UI |
| 4.2 | Document timeline visualization | PASS | DocumentTimelineBar component |
| 4.3 | Advanced reporting / export | WARN | Basic reports exist; no CSV/Excel export |
| 4.4 | Mobile-optimized workflows | WARN | Capacitor build-ready; mobile UX not tested on device |
| 4.5 | Offline support | WARN | useOfflineSupport hook exists; depth of support unclear |
| 4.6 | Accessibility (WCAG 2.1 AA) | WARN | Active work. Multiple bugs open: BUG-029 through BUG-038 |
| 4.7 | i18n coverage (all strings translated) | WARN | BUG-039: 555 inline ternaries; BUG-015: duplicated language check |
Summary Dashboard
Seção intitulada “Summary Dashboard”| Tier | Gate | PASS | WARN | FAIL | Blockers |
|---|---|---|---|---|---|
| 1 — Pillars | OPEN | 18 | 1 | 0 | Email delivery verified; templates bilingual; BUG-045 deferred |
| 2 — Operational | OPEN | 26 | 3 | 0 | — |
| 3 — Multipliers | OPEN | 7 | 1 | 0 | — |
| 4 — Polish | OPEN | 1 | 6 | 0 | — |
Overall: PILOT-READY — All gates OPEN. 0 FAIL items. 11 WARN items remain (non-blocking).
Critical Path to Pilot-Ready
Seção intitulada “Critical Path to Pilot-Ready”These are the minimum actions to unblock the gate, in priority order:
| # | Action | Resolves | Type | Owner | Done? |
|---|---|---|---|---|---|
| [x] Done 2026-03-23 | |||||
| [x] Done 2026-03-23 | |||||
| [x] Done 2026-03-24 | |||||
.env from git tracking | [x] Already gitignored | ||||
has_write_access() SQL function + add to RLS policies | [x] Done 2026-03-24 | ||||
| [x] Done 2026-03-24 — test doc triggered alert + email | |||||
| [x] Done 2026-03-24 — all 4 templates bilingual | |||||
| 1 | Move admin role assignment to server-side (Edge Function or trigger) | Item D.8 | Build | [ ] |
All gates are OPEN. All critical path items resolved. 1 WARN-level improvement remains (D.8).
How to Re-Assess
Seção intitulada “How to Re-Assess”- Work through the checklist top-down
- For each item, verify in code or production (not from memory)
- Update the Status column and the “Last assessed” date at the top
- If a new issue is found, add it to
docs/BUGS.mdfirst, then reference it here - When all FAIL items in a tier are resolved, change the GATE to OPEN
This document is the handoff artifact. Anyone picking up the work reads this first.