diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e923db..b29789f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,3 +18,6 @@ | 2026-02-24 | 0.1.13 | Added verification architecture contract with endpoint design and workflow split for attire, government ID, and certification. | | 2026-02-24 | 0.1.14 | Implemented core verification endpoints in dev and updated frontend/API docs with live verification route contracts. | | 2026-02-24 | 0.1.15 | Added live Vertex Flash Lite attire verification path and third-party adapter scaffolding for government ID and certification checks. | +| 2026-02-24 | 0.1.16 | Added M4 target schema blueprint doc with first-principles modular model, constraints, and migration phases. | +| 2026-02-24 | 0.1.17 | Added full current-schema mermaid model relationship map to the M4 target schema blueprint. | +| 2026-02-24 | 0.1.18 | Updated schema blueprint with explicit multi-tenant stakeholder model and phased RBAC rollout with shadow mode before enforcement. | diff --git a/docs/MILESTONES/M4/planning/m4-target-schema-blueprint.md b/docs/MILESTONES/M4/planning/m4-target-schema-blueprint.md new file mode 100644 index 00000000..5a709ad9 --- /dev/null +++ b/docs/MILESTONES/M4/planning/m4-target-schema-blueprint.md @@ -0,0 +1,390 @@ +# M4 Target Schema Blueprint (Command-Ready) + +Status: Draft for team alignment +Date: 2026-02-24 +Owner: Technical Lead + +## 1) Goal +Define the target database shape we want **before** command-backend implementation, so critical flows are atomic, secure, and scalable. + +## 1.1 Stakeholder and tenancy model +This product should be designed as a **multi-tenant platform**. + +1. Tenant: +- One staffing company account (example: Legendary Event Staffing and Entertainment). +2. Business: +- A customer/client account owned by a tenant. +3. User: +- A human identity (auth account) that can belong to one or more tenants. +4. Staff: +- A workforce profile linked to a user identity and tenant-scoped operations. + +Practical meaning: +1. The same platform can serve multiple staffing companies safely. +2. Data isolation is by `tenant_id`, not only by business/vendor IDs. +3. Not every record starts as a full active user: +- invite-first or pending onboarding records are valid, +- then bound to `user_id` when activation is completed. + +```mermaid +flowchart LR + U["User identity"] --> M["Tenant membership"] + M --> T["Tenant staffing company"] + T --> B["Business client"] + T --> V["Vendor partner"] + B --> O["Orders and shifts"] + V --> O +``` + +## 2) First-principles rules +1. Every critical write must be server-mediated and transactional. +2. Tenant boundaries must be explicit in data and queries. +3. Money and rates must use exact numeric types, not floating point. +4. Data needed for constraints should be relational, not hidden in JSON blobs. +5. Every high-risk state transition must be auditable and replayable. + +## 3) Current anti-patterns we are removing +1. Direct client mutation of core entities. +2. Broad `USER`-auth CRUD without strict tenant scoping. +3. Financial values as `Float`. +4. Core workflow state embedded in generic `Any/jsonb` fields. +5. Missing uniqueness/index constraints on high-traffic paths. + +## 4) Target modular schema + +## 4.1 Identity and Access +Tables: +1. `users` (source identity, profile, auth linkage) +2. `tenant_memberships` (new; membership + base access per tenant) +3. `team_members` (membership + scope per team) +4. `roles` (new) +5. `permissions` (new) +6. `role_bindings` (new; who has which role in which scope) + +Rules: +1. Unique tenant membership: `(tenant_id, user_id)`. +2. Unique team membership: `(team_id, user_id)`. +3. Access checks resolve through tenant membership first, then optional team/hub scope. + +## 4.2 Organization and Tenant +Tables: +1. `tenants` (new canonical boundary: business/vendor ownership root) +2. `businesses` +3. `vendors` +4. `teams` +5. `team_hubs` +6. `hubs` + +Rules: +1. Every command-critical row references `tenant_id`. +2. All list queries must include tenant predicate. + +## 4.8 RBAC rollout strategy (deferred enforcement) +RBAC should be introduced in phases and **not enforced everywhere immediately**. + +Phase A: Auth-first (now) +1. Require valid auth token. +2. Resolve tenant context. +3. Allow current work to continue while logging actor + tenant + action. + +Phase B: Shadow RBAC +1. Evaluate permissions (`allow`/`deny`) in backend. +2. Log decisions but do not block most requests yet. +3. Start with warnings and dashboards for denied actions. + +Phase C: Enforced RBAC on command writes +1. Enforce RBAC on `/commands/*` only. +2. Keep low-risk read flows in transition mode. + +Phase D: Enforced RBAC on high-risk reads +1. Enforce tenant and role checks on sensitive read connectors. +2. Remove remaining broad user-level access. + +```mermaid +flowchart LR + A["Auth only"] --> B["Shadow RBAC logging"] + B --> C["Enforce RBAC on command writes"] + C --> D["Enforce RBAC on sensitive reads"] +``` + +## 4.3 Scheduling and Orders +Tables: +1. `orders` +2. `order_schedule_rules` (new; replaces schedule JSON fields) +3. `shifts` +4. `shift_roles` +5. `shift_role_requirements` (optional extension for policy rules) +6. `shift_managers` (new; replaces `managers: [Any!]`) + +Rules: +1. No denormalized `assignedStaff` or `shifts` JSON in `orders`. +2. Time constraints: `start_time < end_time`. +3. Capacity constraints: `assigned <= count`, `filled <= workers_needed`. +4. Canonical status names (single spelling across schema). + +## 4.4 Staffing and Matching +Tables: +1. `staffs` +2. `staff_roles` +3. `workforce` +4. `applications` +5. `assignments` + +Rules: +1. One active workforce relation per `(vendor_id, staff_id)`. +2. One application per `(shift_id, role_id, staff_id)` unless versioned intentionally. +3. Assignment state transitions only through command APIs. + +## 4.5 Compliance and Verification +Tables: +1. `documents` +2. `staff_documents` +3. `certificates` +4. `verification_jobs` +5. `verification_reviews` +6. `verification_events` + +Rules: +1. Verification is asynchronous and append-only for events. +2. Manual review is explicit and tracked. +3. Government ID and certification provider references are persisted. + +## 4.6 Financial and Payout +Tables: +1. `invoices` +2. `invoice_templates` +3. `recent_payments` +4. `accounts` (refactor to tokenized provider references) + +Rules: +1. Replace monetary `Float` with exact numeric (`DECIMAL(12,2)` or integer cents). +2. Do not expose raw account/routing values in query connectors. +3. Add one-primary-account constraint per owner. + +## 4.7 Audit and Reliability +Tables: +1. `domain_events` (new) +2. `idempotency_keys` (already started in command API SQL) +3. `activity_logs` + +Rules: +1. Every command write emits a domain event. +2. Idempotency scope: `(actor_uid, route, idempotency_key)`. + +## 5) Target core model (conceptual) + +```mermaid +erDiagram + TENANT ||--o{ BUSINESS : owns + TENANT ||--o{ VENDOR : owns + TENANT ||--o{ TEAM : owns + TEAM ||--o{ TEAM_MEMBER : has + USER ||--o{ TEAM_MEMBER : belongs_to + + BUSINESS ||--o{ ORDER : requests + VENDOR ||--o{ ORDER : fulfills + ORDER ||--o{ ORDER_SCHEDULE_RULE : has + ORDER ||--o{ SHIFT : expands_to + SHIFT ||--o{ SHIFT_ROLE : requires + SHIFT ||--o{ SHIFT_MANAGER : has + + USER ||--o{ STAFF : identity + STAFF ||--o{ STAFF_ROLE : skills + VENDOR ||--o{ WORKFORCE : contracts + STAFF ||--o{ WORKFORCE : linked + SHIFT_ROLE ||--o{ APPLICATION : receives + STAFF ||--o{ APPLICATION : applies + SHIFT_ROLE ||--o{ ASSIGNMENT : allocates + WORKFORCE ||--o{ ASSIGNMENT : executes + + STAFF ||--o{ CERTIFICATE : has + STAFF ||--o{ STAFF_DOCUMENT : uploads + DOCUMENT ||--o{ STAFF_DOCUMENT : references + STAFF ||--o{ VERIFICATION_JOB : subject + VERIFICATION_JOB ||--o{ VERIFICATION_REVIEW : reviewed_by + VERIFICATION_JOB ||--o{ VERIFICATION_EVENT : logs + + ORDER ||--o{ INVOICE : billed_by + INVOICE ||--o{ RECENT_PAYMENT : settles + TENANT ||--o{ ACCOUNT_TOKEN_REF : payout_method + + ORDER ||--o{ DOMAIN_EVENT : emits + SHIFT ||--o{ DOMAIN_EVENT : emits + ASSIGNMENT ||--o{ DOMAIN_EVENT : emits +``` + +## 6) Command write boundary on this schema + +```mermaid +flowchart LR + A["Frontend app"] --> B["Command API"] + B --> C["Policy + validation"] + C --> D["Single database transaction"] + D --> E["orders, shifts, shift_roles, applications, assignments"] + D --> F["domain_events + idempotency_keys"] + E --> G["Read models and reports"] +``` + +## 7) Minimum constraints and indexes to add before command build + +## 7.1 Constraints +1. `shift_roles`: check `assigned >= 0 AND assigned <= count`. +2. `shifts`: check `start_time < end_time`. +3. `applications`: unique `(shift_id, role_id, staff_id)`. +4. `workforce`: unique active `(vendor_id, staff_id)`. +5. `team_members`: unique `(team_id, user_id)`. +6. `accounts` (or token ref table): unique primary per owner. + +## 7.2 Indexes +1. `orders (tenant_id, status, date)`. +2. `shifts (order_id, date, status)`. +3. `shift_roles (shift_id, role_id, start_time)`. +4. `applications (shift_id, role_id, status, created_at)`. +5. `assignments (workforce_id, shift_id, role_id, status)`. +6. `verification_jobs (subject_id, type, status, created_at)`. +7. `invoices (business_id, vendor_id, status, due_date)`. + +## 8) Data type normalization +1. Monetary: `Float -> DECIMAL(12,2)` (or integer cents). +2. Generic JSON fields in core scheduling: split into relational tables. +3. Timestamps: store UTC and enforce server-generated creation/update fields. + +## 9) Security boundary in schema/connectors +1. Remove broad list queries for sensitive entities unless tenant-scoped. +2. Strip sensitive fields from connector query payloads (bank/routing). +3. Keep high-risk mutations behind command API; Data Connect remains read-first for client. + +## 10) Migration phases (schema-first) + +```mermaid +flowchart TD + P0["Phase 0: Safety patch + - lock sensitive fields + - enforce tenant-scoped queries + - freeze new direct write connectors"] --> P1["Phase 1: Core constraints + - add unique/check constraints + - add indexes + - normalize money types"] + P1 --> P2["Phase 2: Tenant and RBAC base tables + - add tenants and tenant_memberships + - add roles permissions role_bindings + - run RBAC in shadow mode"] + P2 --> P3["Phase 3: Scheduling normalization + - remove order JSON workflow fields + - add order_schedule_rules and shift_managers"] + P3 --> P4["Phase 4: Command rollout + - command writes on hardened schema + - emit domain events + idempotency + - enforce RBAC for command routes"] + P4 --> P5["Phase 5: Read migration + cleanup + - migrate frontend reads as needed + - enforce RBAC for sensitive reads + - retire deprecated connectors"] +``` + +## 11) Definition of ready for command backend +1. P0 and P1 complete in `dev`. +2. Tenant scoping verified in connector tests. +3. Sensitive field exposure removed. +4. Core transaction invariants enforced by schema constraints. +5. Command API contracts mapped to new normalized tables. +6. RBAC is in shadow mode with decision logs in place (not hard-blocking yet). + +## 12) Full current model relationship map (all models) + +```mermaid +flowchart LR + Account["Account"] + ActivityLog["ActivityLog"] + Application["Application"] + Assignment["Assignment"] + AttireOption["AttireOption"] + BenefitsData["BenefitsData"] + Business["Business"] + Category["Category"] + Certificate["Certificate"] + ClientFeedback["ClientFeedback"] + Conversation["Conversation"] + Course["Course"] + CustomRateCard["CustomRateCard"] + Document["Document"] + EmergencyContact["EmergencyContact"] + FaqData["FaqData"] + Hub["Hub"] + Invoice["Invoice"] + InvoiceTemplate["InvoiceTemplate"] + Level["Level"] + MemberTask["MemberTask"] + Message["Message"] + Order["Order"] + RecentPayment["RecentPayment"] + Role["Role"] + RoleCategory["RoleCategory"] + Shift["Shift"] + ShiftRole["ShiftRole"] + Staff["Staff"] + StaffAvailability["StaffAvailability"] + StaffAvailabilityStats["StaffAvailabilityStats"] + StaffCourse["StaffCourse"] + StaffDocument["StaffDocument"] + StaffRole["StaffRole"] + Task["Task"] + TaskComment["TaskComment"] + TaxForm["TaxForm"] + Team["Team"] + TeamHub["TeamHub"] + TeamHudDepartment["TeamHudDepartment"] + TeamMember["TeamMember"] + User["User"] + UserConversation["UserConversation"] + Vendor["Vendor"] + VendorBenefitPlan["VendorBenefitPlan"] + VendorRate["VendorRate"] + Workforce["Workforce"] + + Application --> Shift + Application --> ShiftRole + Application --> Staff + Assignment --> ShiftRole + Assignment --> Workforce + BenefitsData --> Staff + BenefitsData --> VendorBenefitPlan + Certificate --> Staff + ClientFeedback --> Business + ClientFeedback --> Vendor + Course --> Category + Invoice --> Business + Invoice --> Order + Invoice --> Vendor + InvoiceTemplate --> Business + InvoiceTemplate --> Order + InvoiceTemplate --> Vendor + MemberTask --> Task + MemberTask --> TeamMember + Message --> User + Order --> Business + Order --> TeamHub + Order --> Vendor + RecentPayment --> Application + RecentPayment --> Invoice + Shift --> Order + ShiftRole --> Role + ShiftRole --> Shift + StaffAvailability --> Staff + StaffAvailabilityStats --> Staff + StaffDocument --> Document + StaffRole --> Role + StaffRole --> Staff + TaskComment --> TeamMember + TeamHub --> Team + TeamHudDepartment --> TeamHub + TeamMember --> Team + TeamMember --> TeamHub + TeamMember --> User + UserConversation --> Conversation + UserConversation --> User + VendorBenefitPlan --> Vendor + VendorRate --> Vendor + Workforce --> Staff + Workforce --> Vendor +```