seed data
This commit is contained in:
232
docs/MILESTONES/M4/planning/m4-api-catalog.md
Normal file
232
docs/MILESTONES/M4/planning/m4-api-catalog.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# M4 API Catalog (Core Only)
|
||||
|
||||
Status: Active
|
||||
Date: 2026-02-24
|
||||
Owner: Technical Lead
|
||||
Environment: dev
|
||||
|
||||
## Frontend source of truth
|
||||
Use this file and `docs/MILESTONES/M4/planning/m4-core-api-frontend-guide.md` for core endpoint consumption.
|
||||
|
||||
## Related next-slice contract
|
||||
Verification pipeline design (attire, government ID, certification):
|
||||
- `docs/MILESTONES/M4/planning/m4-verification-architecture-contract.md`
|
||||
|
||||
## 1) Scope and purpose
|
||||
This catalog defines the currently implemented core backend contract for M4.
|
||||
|
||||
## 2) Global API rules
|
||||
1. Route group in scope: `/core/*`.
|
||||
2. Compatibility aliases in scope:
|
||||
- `POST /uploadFile` -> `POST /core/upload-file`
|
||||
- `POST /createSignedUrl` -> `POST /core/create-signed-url`
|
||||
- `POST /invokeLLM` -> `POST /core/invoke-llm`
|
||||
3. Auth model:
|
||||
- `GET /health` is public in dev
|
||||
- all other routes require `Authorization: Bearer <firebase-id-token>`
|
||||
4. Standard error envelope:
|
||||
```json
|
||||
{
|
||||
"code": "STRING_CODE",
|
||||
"message": "Human readable message",
|
||||
"details": {},
|
||||
"requestId": "optional-request-id"
|
||||
}
|
||||
```
|
||||
5. Response header:
|
||||
- `X-Request-Id`
|
||||
|
||||
## 3) Core routes
|
||||
|
||||
## 3.1 Upload file
|
||||
1. Method and route: `POST /core/upload-file`
|
||||
2. Request format: `multipart/form-data`
|
||||
3. Fields:
|
||||
- `file` (required)
|
||||
- `visibility` (`public` or `private`, optional)
|
||||
- `category` (optional)
|
||||
4. Accepted types:
|
||||
- `application/pdf`
|
||||
- `image/jpeg`
|
||||
- `image/jpg`
|
||||
- `image/png`
|
||||
5. Max size: `10 MB` (default)
|
||||
6. Behavior: real upload to Cloud Storage.
|
||||
7. Success `200`:
|
||||
```json
|
||||
{
|
||||
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/...",
|
||||
"contentType": "application/pdf",
|
||||
"size": 12345,
|
||||
"bucket": "krow-workforce-dev-private",
|
||||
"path": "uploads/<uid>/...",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
8. Errors:
|
||||
- `UNAUTHENTICATED`
|
||||
- `INVALID_FILE_TYPE`
|
||||
- `FILE_TOO_LARGE`
|
||||
|
||||
## 3.2 Create signed URL
|
||||
1. Method and route: `POST /core/create-signed-url`
|
||||
2. Request:
|
||||
```json
|
||||
{
|
||||
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/file.pdf",
|
||||
"expiresInSeconds": 300
|
||||
}
|
||||
```
|
||||
3. Security checks:
|
||||
- bucket must be allowed
|
||||
- path must be owned by caller (`uploads/<caller_uid>/...`)
|
||||
- object must exist
|
||||
- `expiresInSeconds <= 900`
|
||||
4. Success `200`:
|
||||
```json
|
||||
{
|
||||
"signedUrl": "https://storage.googleapis.com/...",
|
||||
"expiresAt": "2026-02-24T15:22:28.105Z",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
5. Errors:
|
||||
- `VALIDATION_ERROR`
|
||||
- `FORBIDDEN`
|
||||
- `NOT_FOUND`
|
||||
|
||||
## 3.3 Invoke model
|
||||
1. Method and route: `POST /core/invoke-llm`
|
||||
2. Request:
|
||||
```json
|
||||
{
|
||||
"prompt": "...",
|
||||
"responseJsonSchema": {},
|
||||
"fileUrls": []
|
||||
}
|
||||
```
|
||||
3. Behavior:
|
||||
- real Vertex AI call
|
||||
- model default: `gemini-2.0-flash-001`
|
||||
- timeout default: `20 seconds`
|
||||
4. Rate limit:
|
||||
- `20 requests/minute` per user (default)
|
||||
- when exceeded: `429 RATE_LIMITED` and `Retry-After` header
|
||||
5. Success `200`:
|
||||
```json
|
||||
{
|
||||
"result": {},
|
||||
"model": "gemini-2.0-flash-001",
|
||||
"latencyMs": 367,
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
6. Errors:
|
||||
- `UNAUTHENTICATED`
|
||||
- `VALIDATION_ERROR`
|
||||
- `MODEL_TIMEOUT`
|
||||
- `MODEL_FAILED`
|
||||
- `RATE_LIMITED`
|
||||
|
||||
## 3.4 Create verification job
|
||||
1. Method and route: `POST /core/verifications`
|
||||
2. Auth: required
|
||||
3. Request:
|
||||
```json
|
||||
{
|
||||
"type": "attire",
|
||||
"subjectType": "worker",
|
||||
"subjectId": "worker_123",
|
||||
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/file.pdf",
|
||||
"rules": {}
|
||||
}
|
||||
```
|
||||
4. Behavior:
|
||||
- validates `fileUri` ownership
|
||||
- requires file existence when `UPLOAD_MOCK=false` and `VERIFICATION_REQUIRE_FILE_EXISTS=true`
|
||||
- enqueues async verification
|
||||
5. Success `202`:
|
||||
```json
|
||||
{
|
||||
"verificationId": "ver_123",
|
||||
"status": "PENDING",
|
||||
"type": "attire",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
6. Errors:
|
||||
- `UNAUTHENTICATED`
|
||||
- `VALIDATION_ERROR`
|
||||
- `FORBIDDEN`
|
||||
- `NOT_FOUND`
|
||||
|
||||
## 3.5 Get verification status
|
||||
1. Method and route: `GET /core/verifications/{verificationId}`
|
||||
2. Auth: required
|
||||
3. Success `200`:
|
||||
```json
|
||||
{
|
||||
"verificationId": "ver_123",
|
||||
"status": "NEEDS_REVIEW",
|
||||
"type": "attire",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
4. Errors:
|
||||
- `UNAUTHENTICATED`
|
||||
- `FORBIDDEN`
|
||||
- `NOT_FOUND`
|
||||
|
||||
## 3.6 Review verification
|
||||
1. Method and route: `POST /core/verifications/{verificationId}/review`
|
||||
2. Auth: required
|
||||
3. Request:
|
||||
```json
|
||||
{
|
||||
"decision": "APPROVED",
|
||||
"note": "Manual review passed",
|
||||
"reasonCode": "MANUAL_REVIEW"
|
||||
}
|
||||
```
|
||||
4. Success `200`: status becomes `APPROVED` or `REJECTED`.
|
||||
5. Errors:
|
||||
- `UNAUTHENTICATED`
|
||||
- `VALIDATION_ERROR`
|
||||
- `FORBIDDEN`
|
||||
- `NOT_FOUND`
|
||||
|
||||
## 3.7 Retry verification
|
||||
1. Method and route: `POST /core/verifications/{verificationId}/retry`
|
||||
2. Auth: required
|
||||
3. Success `202`: status resets to `PENDING`.
|
||||
4. Errors:
|
||||
- `UNAUTHENTICATED`
|
||||
- `FORBIDDEN`
|
||||
- `NOT_FOUND`
|
||||
|
||||
## 3.8 Health
|
||||
1. Method and route: `GET /health`
|
||||
2. Success `200`:
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"service": "krow-core-api",
|
||||
"version": "dev",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 4) Locked defaults
|
||||
1. Validation library: `zod`.
|
||||
2. Validation schema location: `backend/core-api/src/contracts/`.
|
||||
3. Buckets:
|
||||
- `krow-workforce-dev-public`
|
||||
- `krow-workforce-dev-private`
|
||||
4. Model provider: Vertex AI Gemini.
|
||||
5. Max signed URL expiry: `900` seconds.
|
||||
6. LLM timeout: `20000` ms.
|
||||
7. LLM rate limit: `20` requests/minute/user.
|
||||
8. Verification access mode default: `authenticated`.
|
||||
9. Verification file existence check default: enabled (`VERIFICATION_REQUIRE_FILE_EXISTS=true`).
|
||||
10. Verification attire provider default in dev: `vertex` with model `gemini-2.0-flash-lite-001`.
|
||||
11. Verification government/certification providers: external adapters via configured provider URL/token.
|
||||
@@ -0,0 +1,269 @@
|
||||
# M4 Backend Foundation Implementation Plan (Dev First)
|
||||
|
||||
Date: 2026-02-24
|
||||
Owner: Wilfred (Technical Lead)
|
||||
Primary environment: `krow-workforce-dev`
|
||||
|
||||
## 1) Objective
|
||||
Build a secure, modular, and scalable backend foundation in `dev` without breaking the current frontend while we migrate high-risk writes from direct Data Connect mutations to backend command endpoints.
|
||||
|
||||
## 2) First-principles architecture rules
|
||||
1. Client apps are untrusted for business-critical writes.
|
||||
2. Backend is the enforcement layer for validation, permissions, and write orchestration.
|
||||
3. Multi-entity writes must be atomic, idempotent, and observable.
|
||||
4. Configuration and deployment must be reproducible by automation.
|
||||
5. Migration must be backward-compatible until each frontend flow is cut over.
|
||||
|
||||
## 3) Pre-coding gates (must be true before implementation starts)
|
||||
|
||||
## Gate A: Security boundary
|
||||
1. Frontend sends Firebase token only. No database credentials in client code.
|
||||
2. Every new backend endpoint validates Firebase token.
|
||||
3. Data Connect write access strategy is defined:
|
||||
- keep simple reads available to client
|
||||
- route high-risk writes through backend command endpoints
|
||||
4. Upload and signed URL paths are server-controlled.
|
||||
|
||||
## Gate B: Contract standards
|
||||
1. Standard error envelope is frozen:
|
||||
```json
|
||||
{
|
||||
"code": "STRING_CODE",
|
||||
"message": "Human readable message",
|
||||
"details": {},
|
||||
"requestId": "optional-request-id"
|
||||
}
|
||||
```
|
||||
2. Request validation layer is chosen and centralized.
|
||||
3. Route naming strategy is frozen:
|
||||
- canonical routes under `/core` and `/commands`
|
||||
- compatibility aliases preserved during migration (`/uploadFile`, `/createSignedUrl`, `/invokeLLM`)
|
||||
4. Validation standard is locked:
|
||||
- library: `zod`
|
||||
- schema location: `backend/<service>/src/contracts/` with `core/` and `commands/` subfolders
|
||||
|
||||
## Gate C: Atomicity and reliability
|
||||
1. Command endpoints support idempotency keys for retry-safe writes.
|
||||
2. Multi-step write flows are wrapped in single backend transaction boundaries.
|
||||
3. Domain conflict codes are defined for expected business failures.
|
||||
4. Idempotency storage is locked:
|
||||
- store in Cloud SQL table
|
||||
- key scope: `userId + route + idempotencyKey`
|
||||
- retain records for 24 hours
|
||||
- repeated key returns original response
|
||||
|
||||
## Gate D: Automation and operability
|
||||
1. Makefile is source of truth for backend setup and deploy in dev.
|
||||
2. Core deploy and smoke test commands exist before feature migration.
|
||||
3. Logging format and request tracing fields are standardized.
|
||||
|
||||
## 4) Security baseline for foundation phase
|
||||
|
||||
## 4.1 Authentication and authorization
|
||||
1. Foundation phase is authentication-first.
|
||||
2. Role-based access control is intentionally deferred.
|
||||
3. All handlers include a policy hook for future role checks (`can(action, resource, actor)`).
|
||||
|
||||
## 4.2 Data access control model
|
||||
1. Client retains Data Connect reads required for existing screens.
|
||||
2. High-risk writes move behind `/commands/*` endpoints.
|
||||
3. Backend mediates write interactions with Data Connect and Cloud SQL.
|
||||
|
||||
## 4.3 File and URL security
|
||||
1. Validate file type and size server-side.
|
||||
2. Separate public and private storage behavior.
|
||||
3. Signed URL creation checks ownership/prefix scope and expiry limits.
|
||||
4. Bucket policy split is locked:
|
||||
- `krow-workforce-dev-public`
|
||||
- `krow-workforce-dev-private`
|
||||
- private bucket access only through signed URL
|
||||
|
||||
## 4.4 Model invocation safety
|
||||
1. Enforce schema-constrained output.
|
||||
2. Apply per-user rate limits and request timeout.
|
||||
3. Log model failures with safe redaction (no sensitive prompt leakage in logs).
|
||||
4. Model provider and timeout defaults are locked:
|
||||
- provider: Vertex AI Gemini
|
||||
- max route timeout: 20 seconds
|
||||
- timeout error code: `MODEL_TIMEOUT`
|
||||
|
||||
## 4.5 Secrets and credentials
|
||||
1. Runtime secrets come from Secret Manager only.
|
||||
2. Service accounts use least-privilege roles.
|
||||
3. No secrets committed in repository files.
|
||||
|
||||
## 5) Modularity baseline
|
||||
|
||||
## 5.1 Backend module boundaries
|
||||
1. `core` module: upload, signed URL, model invocation, health.
|
||||
2. `commands` module: business writes and state transitions.
|
||||
3. `policy` module: validation and future role checks.
|
||||
4. `data` module: Data Connect adapters and transaction wrappers.
|
||||
5. `infra` module: logging, tracing, auth middleware, error mapping.
|
||||
|
||||
## 5.2 Contract separation
|
||||
1. Keep API request/response schemas in one location.
|
||||
2. Keep domain errors in one registry file.
|
||||
3. Keep route declarations thin; business logic in services.
|
||||
|
||||
## 5.3 Cloud runtime roles
|
||||
1. Cloud Run is the primary command and core API execution layer.
|
||||
2. Cloud Functions v2 is worker-only in this phase:
|
||||
- upload-related async handlers
|
||||
- notification jobs
|
||||
- model-related async helpers when needed
|
||||
|
||||
## 6) Automation baseline
|
||||
|
||||
## 6.1 Makefile requirements
|
||||
Add `makefiles/backend.mk` and wire it into root `Makefile` with at least:
|
||||
1. `make backend-enable-apis`
|
||||
2. `make backend-bootstrap-dev`
|
||||
3. `make backend-deploy-core`
|
||||
4. `make backend-deploy-commands`
|
||||
5. `make backend-deploy-workers`
|
||||
6. `make backend-smoke-core`
|
||||
7. `make backend-smoke-commands`
|
||||
8. `make backend-logs-core`
|
||||
|
||||
## 6.2 CI requirements
|
||||
1. Backend lint
|
||||
2. Backend tests
|
||||
3. Build/package
|
||||
4. Smoke test against deployed dev route(s)
|
||||
5. Block merge on failed checks
|
||||
|
||||
## 6.3 Session hygiene
|
||||
1. Update `TASKS.md` and `CHANGELOG.md` each working session.
|
||||
2. If a new service/API is added, Makefile target must be added in same change.
|
||||
|
||||
## 7) Migration safety contract (no frontend breakage)
|
||||
1. Backend routes ship first.
|
||||
2. Frontend migration is per-feature wave, not big bang.
|
||||
3. Keep compatibility aliases until clients migrate.
|
||||
4. Keep existing Data Connect reads during foundation.
|
||||
5. For each migrated write flow:
|
||||
- before/after behavior checklist
|
||||
- rollback path
|
||||
- smoke verification
|
||||
|
||||
## 8) Scope for foundation build
|
||||
1. Backend runtime/deploy foundation in dev.
|
||||
2. Core endpoints:
|
||||
- `POST /core/upload-file`
|
||||
- `POST /core/create-signed-url`
|
||||
- `POST /core/invoke-llm`
|
||||
- `GET /healthz`
|
||||
3. Compatibility aliases:
|
||||
- `POST /uploadFile`
|
||||
- `POST /createSignedUrl`
|
||||
- `POST /invokeLLM`
|
||||
4. Command layer scaffold for first migration routes.
|
||||
5. Initial migration of highest-risk write paths.
|
||||
|
||||
## 9) Implementation phases
|
||||
|
||||
## Phase 0: Baseline and contracts
|
||||
Deliverables:
|
||||
1. Freeze endpoint naming and compatibility aliases.
|
||||
2. Freeze error envelope and error code registry.
|
||||
3. Freeze auth middleware interface and policy hook interface.
|
||||
4. Publish route inventory from web/mobile direct writes.
|
||||
|
||||
Exit criteria:
|
||||
1. No unresolved contract ambiguity.
|
||||
2. Team agrees on auth-first now and role-map-later approach.
|
||||
|
||||
## Phase 1: Backend infra and automation
|
||||
Deliverables:
|
||||
1. `makefiles/backend.mk` with bootstrap, deploy, smoke, logs targets.
|
||||
2. Environment templates for backend runtime config.
|
||||
3. Secret Manager and service account setup automation.
|
||||
|
||||
Exit criteria:
|
||||
1. A fresh machine can deploy core backend to dev via Make commands.
|
||||
|
||||
## Phase 2: Core endpoint implementation
|
||||
Deliverables:
|
||||
1. `/core/upload-file`
|
||||
2. `/core/create-signed-url`
|
||||
3. `/core/invoke-llm`
|
||||
4. `/healthz`
|
||||
5. Compatibility aliases (`/uploadFile`, `/createSignedUrl`, `/invokeLLM`)
|
||||
|
||||
Exit criteria:
|
||||
1. API harness passes for core routes.
|
||||
2. Error, logging, and auth standards are enforced.
|
||||
|
||||
## Phase 3: Command layer scaffold
|
||||
Deliverables:
|
||||
1. `/commands/orders/create`
|
||||
2. `/commands/orders/{orderId}/cancel`
|
||||
3. `/commands/orders/{orderId}/update`
|
||||
4. `/commands/shifts/{shiftId}/change-status`
|
||||
5. `/commands/shifts/{shiftId}/assign-staff`
|
||||
6. `/commands/shifts/{shiftId}/accept`
|
||||
|
||||
Exit criteria:
|
||||
1. High-risk writes have backend command alternatives ready.
|
||||
|
||||
## Phase 4: Wave 1 frontend migration
|
||||
Deliverables:
|
||||
1. Replace direct writes in selected web/mobile flows.
|
||||
2. Keep reads stable.
|
||||
3. Verify no regressions in non-migrated screens.
|
||||
|
||||
Exit criteria:
|
||||
1. Migrated flows run through backend commands only.
|
||||
2. Rollback instructions validated.
|
||||
|
||||
## Phase 5: Hardening and handoff
|
||||
Deliverables:
|
||||
1. Runbook for deploy, rollback, and smoke.
|
||||
2. Backend CI pipeline active.
|
||||
3. Wave 2 and wave 3 migration task list defined.
|
||||
|
||||
Exit criteria:
|
||||
1. Foundation is reusable for staging/prod with environment changes only.
|
||||
|
||||
## 10) Wave 1 migration inventory (real call sites)
|
||||
|
||||
Web:
|
||||
1. `apps/web/src/features/operations/tasks/TaskBoard.tsx:100`
|
||||
2. `apps/web/src/features/operations/orders/OrderDetail.tsx:145`
|
||||
3. `apps/web/src/features/operations/orders/EditOrder.tsx:84`
|
||||
4. `apps/web/src/features/operations/orders/components/CreateOrderDialog.tsx:31`
|
||||
5. `apps/web/src/features/operations/orders/components/AssignStaffModal.tsx:60`
|
||||
6. `apps/web/src/features/workforce/documents/DocumentVault.tsx:99`
|
||||
|
||||
Mobile:
|
||||
1. `apps/mobile/packages/features/client/home/lib/src/presentation/widgets/shift_order_form_sheet.dart:232`
|
||||
2. `apps/mobile/packages/features/client/view_orders/lib/src/presentation/widgets/view_order_card.dart:1195`
|
||||
3. `apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart:68`
|
||||
4. `apps/mobile/packages/features/staff/shifts/lib/src/data/repositories_impl/shifts_repository_impl.dart:446`
|
||||
5. `apps/mobile/packages/features/client/authentication/lib/src/data/repositories_impl/auth_repository_impl.dart:257`
|
||||
6. `apps/mobile/packages/features/staff/profile_sections/onboarding/profile_info/lib/src/data/repositories/personal_info_repository_impl.dart:51`
|
||||
|
||||
## 11) Definition of done for foundation
|
||||
1. Core endpoints deployed in dev and validated.
|
||||
2. Command scaffolding in place for wave 1 writes.
|
||||
3. Auth-first protection active on all new routes.
|
||||
4. Idempotency + transaction model defined for command writes.
|
||||
5. Makefile and CI automation cover bootstrap/deploy/smoke paths.
|
||||
6. Frontend remains stable during migration.
|
||||
7. Role-map integration points are documented for next phase.
|
||||
|
||||
## 12) Locked defaults (approved)
|
||||
1. Idempotency key storage strategy:
|
||||
- Cloud SQL table, 24-hour retention, keyed by `userId + route + idempotencyKey`.
|
||||
2. Validation library and schema location:
|
||||
- `zod` in `backend/<service>/src/contracts/` (`core/`, `commands/`).
|
||||
3. Storage bucket naming and split:
|
||||
- `krow-workforce-dev-public` and `krow-workforce-dev-private`.
|
||||
4. Model provider and timeout:
|
||||
- Vertex AI Gemini, 20-second max timeout.
|
||||
5. Target response-time objectives (p95):
|
||||
- `/healthz` under 200ms
|
||||
- `/core/create-signed-url` under 500ms
|
||||
- `/commands/*` under 1500ms
|
||||
- `/core/invoke-llm` under 15000ms
|
||||
245
docs/MILESTONES/M4/planning/m4-core-api-frontend-guide.md
Normal file
245
docs/MILESTONES/M4/planning/m4-core-api-frontend-guide.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# M4 Core API Frontend Guide (Dev)
|
||||
|
||||
Status: Active
|
||||
Last updated: 2026-02-24
|
||||
Audience: Web and mobile frontend developers
|
||||
|
||||
## 1) Base URLs (dev)
|
||||
1. Core API: `https://krow-core-api-e3g6witsvq-uc.a.run.app`
|
||||
|
||||
## 2) Auth requirements
|
||||
1. Send Firebase ID token on protected routes:
|
||||
```http
|
||||
Authorization: Bearer <firebase-id-token>
|
||||
```
|
||||
2. Health route is public:
|
||||
- `GET /health`
|
||||
3. All other routes require Firebase token.
|
||||
|
||||
## 3) Standard error envelope
|
||||
```json
|
||||
{
|
||||
"code": "STRING_CODE",
|
||||
"message": "Human readable message",
|
||||
"details": {},
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 4) Core API endpoints
|
||||
|
||||
## 4.1 Upload file
|
||||
1. Route: `POST /core/upload-file`
|
||||
2. Alias: `POST /uploadFile`
|
||||
3. Content type: `multipart/form-data`
|
||||
4. Form fields:
|
||||
- `file` (required)
|
||||
- `visibility` (optional: `public` or `private`, default `private`)
|
||||
- `category` (optional)
|
||||
5. Accepted file types:
|
||||
- `application/pdf`
|
||||
- `image/jpeg`
|
||||
- `image/jpg`
|
||||
- `image/png`
|
||||
6. Max upload size: `10 MB` (default)
|
||||
7. Current behavior: real upload to Cloud Storage (not mock)
|
||||
8. Success `200` example:
|
||||
```json
|
||||
{
|
||||
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/173...",
|
||||
"contentType": "application/pdf",
|
||||
"size": 12345,
|
||||
"bucket": "krow-workforce-dev-private",
|
||||
"path": "uploads/<uid>/173..._file.pdf",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 4.2 Create signed URL
|
||||
1. Route: `POST /core/create-signed-url`
|
||||
2. Alias: `POST /createSignedUrl`
|
||||
3. Request body:
|
||||
```json
|
||||
{
|
||||
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/file.pdf",
|
||||
"expiresInSeconds": 300
|
||||
}
|
||||
```
|
||||
4. Security checks:
|
||||
- bucket must be allowed (`krow-workforce-dev-public` or `krow-workforce-dev-private`)
|
||||
- path must be owned by caller (`uploads/<caller_uid>/...`)
|
||||
- object must exist
|
||||
- `expiresInSeconds` must be `<= 900`
|
||||
5. Success `200` example:
|
||||
```json
|
||||
{
|
||||
"signedUrl": "https://storage.googleapis.com/...",
|
||||
"expiresAt": "2026-02-24T15:22:28.105Z",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
6. Typical errors:
|
||||
- `400 VALIDATION_ERROR` (bad payload or expiry too high)
|
||||
- `403 FORBIDDEN` (path not owned by caller)
|
||||
- `404 NOT_FOUND` (object does not exist)
|
||||
|
||||
## 4.3 Invoke model
|
||||
1. Route: `POST /core/invoke-llm`
|
||||
2. Alias: `POST /invokeLLM`
|
||||
3. Request body:
|
||||
```json
|
||||
{
|
||||
"prompt": "Return JSON with keys summary and risk.",
|
||||
"responseJsonSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"summary": { "type": "string" },
|
||||
"risk": { "type": "string" }
|
||||
},
|
||||
"required": ["summary", "risk"]
|
||||
},
|
||||
"fileUrls": []
|
||||
}
|
||||
```
|
||||
4. Current behavior: real Vertex model call (not mock)
|
||||
- model: `gemini-2.0-flash-001`
|
||||
- timeout: `20 seconds`
|
||||
5. Rate limit:
|
||||
- per-user `20 requests/minute` (default)
|
||||
- on limit: `429 RATE_LIMITED`
|
||||
- includes `Retry-After` header
|
||||
6. Success `200` example:
|
||||
```json
|
||||
{
|
||||
"result": { "summary": "text", "risk": "Low" },
|
||||
"model": "gemini-2.0-flash-001",
|
||||
"latencyMs": 367,
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 4.4 Create verification job
|
||||
1. Route: `POST /core/verifications`
|
||||
2. Auth: required
|
||||
3. Purpose: enqueue an async verification job for an uploaded file.
|
||||
4. Request body:
|
||||
```json
|
||||
{
|
||||
"type": "attire",
|
||||
"subjectType": "worker",
|
||||
"subjectId": "<worker-id>",
|
||||
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/file.pdf",
|
||||
"rules": {
|
||||
"dressCode": "black shoes"
|
||||
}
|
||||
}
|
||||
```
|
||||
5. Success `202` example:
|
||||
```json
|
||||
{
|
||||
"verificationId": "ver_123",
|
||||
"status": "PENDING",
|
||||
"type": "attire",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
6. Current machine processing behavior in dev:
|
||||
- `attire`: live vision check using Vertex Gemini Flash Lite model.
|
||||
- `government_id`: third-party adapter path (falls back to `NEEDS_REVIEW` if provider is not configured).
|
||||
- `certification`: third-party adapter path (falls back to `NEEDS_REVIEW` if provider is not configured).
|
||||
|
||||
## 4.5 Get verification status
|
||||
1. Route: `GET /core/verifications/{verificationId}`
|
||||
2. Auth: required
|
||||
3. Purpose: polling status from frontend.
|
||||
4. Success `200` example:
|
||||
```json
|
||||
{
|
||||
"verificationId": "ver_123",
|
||||
"status": "NEEDS_REVIEW",
|
||||
"type": "attire",
|
||||
"review": null,
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 4.6 Review verification
|
||||
1. Route: `POST /core/verifications/{verificationId}/review`
|
||||
2. Auth: required
|
||||
3. Purpose: final human decision for the verification.
|
||||
4. Request body:
|
||||
```json
|
||||
{
|
||||
"decision": "APPROVED",
|
||||
"note": "Manual review passed",
|
||||
"reasonCode": "MANUAL_REVIEW"
|
||||
}
|
||||
```
|
||||
5. Success `200` example:
|
||||
```json
|
||||
{
|
||||
"verificationId": "ver_123",
|
||||
"status": "APPROVED",
|
||||
"review": {
|
||||
"decision": "APPROVED",
|
||||
"reviewedBy": "<uid>"
|
||||
},
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 4.7 Retry verification
|
||||
1. Route: `POST /core/verifications/{verificationId}/retry`
|
||||
2. Auth: required
|
||||
3. Purpose: requeue verification to run again.
|
||||
4. Success `202` example: status resets to `PENDING`.
|
||||
|
||||
## 5) Frontend fetch examples (web)
|
||||
|
||||
## 5.1 Signed URL request
|
||||
```ts
|
||||
const token = await firebaseAuth.currentUser?.getIdToken();
|
||||
const res = await fetch('https://krow-core-api-e3g6witsvq-uc.a.run.app/core/create-signed-url', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
fileUri: 'gs://krow-workforce-dev-private/uploads/<uid>/file.pdf',
|
||||
expiresInSeconds: 300,
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
```
|
||||
|
||||
## 5.2 Model request
|
||||
```ts
|
||||
const token = await firebaseAuth.currentUser?.getIdToken();
|
||||
const res = await fetch('https://krow-core-api-e3g6witsvq-uc.a.run.app/core/invoke-llm', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prompt: 'Return JSON with status.',
|
||||
responseJsonSchema: {
|
||||
type: 'object',
|
||||
properties: { status: { type: 'string' } },
|
||||
required: ['status'],
|
||||
},
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
```
|
||||
|
||||
## 6) Notes for frontend team
|
||||
1. Use canonical `/core/*` routes for new work.
|
||||
2. Aliases exist only for migration compatibility.
|
||||
3. `requestId` in responses should be logged client-side for debugging.
|
||||
4. For 429 on model route, retry with exponential backoff and respect `Retry-After`.
|
||||
5. Verification routes are now available in dev under `/core/verifications*`.
|
||||
6. Current verification processing is async and returns machine statuses first (`PENDING`, `PROCESSING`, `NEEDS_REVIEW`, etc.).
|
||||
7. Full verification design and policy details:
|
||||
`docs/MILESTONES/M4/planning/m4-verification-architecture-contract.md`.
|
||||
166
docs/MILESTONES/M4/planning/m4-core-data-actors-scenarios.md
Normal file
166
docs/MILESTONES/M4/planning/m4-core-data-actors-scenarios.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# M4 Core Data Actors and Example Scenarios
|
||||
|
||||
Status: Working draft
|
||||
Date: 2026-02-25
|
||||
Owner: Technical Lead
|
||||
|
||||
## 1) Core data actors
|
||||
1. `Tenant`: staffing company boundary and data isolation root.
|
||||
2. `User`: human identity that signs in.
|
||||
3. `TenantMembership`: user role/context inside one tenant.
|
||||
4. `Business`: client account served by the tenant.
|
||||
5. `BusinessMembership`: maps users to a business with role/status (`owner`, `manager`, `approver`, `viewer`).
|
||||
6. `Vendor`: supplier account that can fulfill staffing demand.
|
||||
7. `VendorMembership`: maps users to a vendor with role/status (`owner`, `manager`, `scheduler`, `viewer`).
|
||||
8. `Workforce/Staff`: worker profile used for assignment and attendance.
|
||||
9. `StakeholderType`: typed category (`buyer`, `operator`, `vendor_partner`, `workforce`, `partner`, `procurement_partner`).
|
||||
10. `StakeholderProfile`: typed actor record inside a tenant.
|
||||
11. `StakeholderLink`: relationship between stakeholder profiles.
|
||||
|
||||
## 1.1 Current schema coverage (today)
|
||||
Current Data Connect handles this only partially:
|
||||
1. `Business.userId` supports one primary business user.
|
||||
2. `Vendor.userId` supports one primary vendor user.
|
||||
3. `TeamMember` can represent multiple users by team as a workaround.
|
||||
|
||||
This is why we need first-class membership tables:
|
||||
1. `business_memberships`
|
||||
2. `vendor_memberships`
|
||||
|
||||
Without those, client/vendor user partitioning is indirect and harder to enforce safely at scale.
|
||||
|
||||
## 2) Minimal actor map
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
T["Tenant"] --> TM["TenantMembership (global tenant access)"]
|
||||
U["User"] --> TM
|
||||
T --> B["Business"]
|
||||
T --> V["Vendor"]
|
||||
U --> BM["BusinessMembership"]
|
||||
BM --> B
|
||||
U --> VM["VendorMembership"]
|
||||
VM --> V
|
||||
U --> S["Workforce/Staff"]
|
||||
T --> SP["StakeholderProfile"]
|
||||
ST["StakeholderType"] --> SP
|
||||
SP --> SL["StakeholderLink"]
|
||||
B --> O["Orders/Shifts"]
|
||||
V --> O
|
||||
S --> O
|
||||
```
|
||||
|
||||
## 3) Scenario A: Legendary Event Staffing
|
||||
Context:
|
||||
1. Tenant is `Legendary Event Staffing and Entertainment`.
|
||||
2. Business is `Google Mountain View Cafes`.
|
||||
3. Legendary uses its own workforce, and can still route overflow to approved vendors.
|
||||
|
||||
Actor mapping (text):
|
||||
1. Tenant: `Legendary Event Staffing and Entertainment` (the company using Krow).
|
||||
2. User: `Wil` (ops lead), `Maria` (Google client manager), `Omar` (Google procurement approver), `Jose` (vendor scheduler), `Ana` (worker).
|
||||
3. TenantMembership:
|
||||
4. `Wil` is `admin` in Legendary tenant.
|
||||
5. `Maria` is `member` in Legendary tenant.
|
||||
6. `Omar` is `member` in Legendary tenant.
|
||||
7. `Jose` is `member` in Legendary tenant.
|
||||
8. `Ana` is `member` in Legendary tenant.
|
||||
9. BusinessMembership:
|
||||
10. `Maria` is `manager` in `Google Mountain View Cafes`.
|
||||
11. `Omar` is `approver` in `Google Mountain View Cafes`.
|
||||
12. VendorMembership:
|
||||
13. `Jose` is `scheduler` in `Legendary Staffing Pool A`.
|
||||
14. `Wil` is `owner` in `Legendary Staffing Pool A`.
|
||||
15. Business: `Google Mountain View Cafes` (client account under the tenant).
|
||||
16. Vendor: `Legendary Staffing Pool A` (or an external approved vendor).
|
||||
17. Workforce/Staff: `Ana` is a staff profile in workforce, linked to certifications and assignments.
|
||||
18. StakeholderType: `buyer`, `operator`, `vendor_partner`, `workforce`, `procurement_partner`.
|
||||
19. StakeholderProfile:
|
||||
20. `Google Procurement` = `buyer`.
|
||||
21. `Legendary Ops` = `operator`.
|
||||
22. `FoodBuy` = `procurement_partner`.
|
||||
23. StakeholderLink:
|
||||
24. `Google Procurement` `contracts_with` `Legendary Ops`.
|
||||
25. `Legendary Ops` `sources_from` `Legendary Staffing Pool A`.
|
||||
26. `Google Procurement` `reports_through` `FoodBuy`.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Tenant as "Tenant (Legendary)"
|
||||
participant BizUser as "Business user (Maria/Omar)"
|
||||
participant Ops as "Ops user (Wil)"
|
||||
participant VendorUser as "Vendor user (Jose)"
|
||||
participant Staff as "Workforce/Staff (Barista Ana)"
|
||||
participant StakeRel as "StakeholderLink"
|
||||
|
||||
BizUser->>Ops: "Create staffing request"
|
||||
Ops->>Tenant: "Create order under tenant scope"
|
||||
Ops->>StakeRel: "Resolve business-to-vendor/workforce relationships"
|
||||
Ops->>VendorUser: "Dispatch role demand"
|
||||
VendorUser->>Staff: "Confirm worker assignment"
|
||||
Staff-->>Ops: "Clock in/out and complete shift"
|
||||
Ops-->>BizUser: "Invoice/report generated with audit trail"
|
||||
```
|
||||
|
||||
## 4) Scenario B: Another tenant (external vendor-heavy)
|
||||
Context:
|
||||
1. Tenant is `Peakline Events`.
|
||||
2. Business is `NVIDIA Campus Dining`.
|
||||
3. Peakline primarily fulfills demand through external approved vendors.
|
||||
|
||||
Actor mapping (text):
|
||||
1. Tenant: `Peakline Events` (another staffing company using Krow).
|
||||
2. User: `Chris` (operations coordinator), `Nina` (client manager), `Sam` (vendor manager), `Leo` (worker).
|
||||
3. TenantMembership:
|
||||
4. `Chris` is `admin` in Peakline tenant.
|
||||
5. `Nina` is `member` in Peakline tenant.
|
||||
6. `Sam` is `member` in Peakline tenant.
|
||||
7. `Leo` is `member` in Peakline tenant.
|
||||
8. BusinessMembership:
|
||||
9. `Nina` is `manager` in `NVIDIA Campus Dining`.
|
||||
10. VendorMembership:
|
||||
11. `Sam` is `manager` in `Metro Staffing LLC`.
|
||||
12. Business: `NVIDIA Campus Dining` (client account under the tenant).
|
||||
13. Vendor: `Metro Staffing LLC` (approved external vendor for this tenant).
|
||||
14. Workforce/Staff: `Leo` is a workforce profile fulfilled through Metro Staffing for assignment and attendance.
|
||||
15. StakeholderType: `buyer`, `operator`, `vendor_partner`, `workforce`, `procurement_partner`.
|
||||
16. StakeholderProfile:
|
||||
17. `NVIDIA Procurement` = `buyer`.
|
||||
18. `Peakline Ops` = `operator`.
|
||||
19. `FoodBuy Regional` = `procurement_partner`.
|
||||
20. StakeholderLink:
|
||||
21. `NVIDIA Procurement` `contracts_with` `Peakline Ops`.
|
||||
22. `Peakline Ops` `sources_from` `Metro Staffing LLC`.
|
||||
23. `NVIDIA Procurement` `reports_through` `FoodBuy Regional`.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Tenant as "Tenant (Peakline Events)"
|
||||
participant BizUser as "Business user (Nina)"
|
||||
participant Ops as "Ops user (Chris)"
|
||||
participant VendorUser as "Vendor user (Sam)"
|
||||
participant Staff as "Workforce/Staff (Vendor worker)"
|
||||
|
||||
BizUser->>Ops: "Request recurring event staffing"
|
||||
Ops->>Tenant: "Create recurring order"
|
||||
Ops->>VendorUser: "Dispatch required roles"
|
||||
VendorUser->>Staff: "Provide available workers"
|
||||
Staff-->>Ops: "Attendance and completion events"
|
||||
Ops-->>BizUser: "Settlement + performance reports"
|
||||
```
|
||||
|
||||
## 5) Why this model scales
|
||||
1. New stakeholders can be added through `StakeholderType` and `StakeholderProfile` without changing core order/shift tables.
|
||||
2. Business and vendor user partitioning is explicit through membership tables, not hidden in one owner field.
|
||||
3. Multi-tenant isolation stays strict because all critical records resolve through `Tenant`.
|
||||
4. Legendary and non-Legendary operating models both fit the same structure.
|
||||
|
||||
## 6) Industry alignment (primary references)
|
||||
1. Google Cloud Identity Platform multi-tenancy:
|
||||
- https://docs.cloud.google.com/identity-platform/docs/multi-tenancy
|
||||
2. AWS SaaS tenant isolation fundamentals:
|
||||
- https://docs.aws.amazon.com/whitepapers/latest/saas-architecture-fundamentals/tenant-isolation.html
|
||||
3. B2B organization-aware login flows (Auth0 Organizations):
|
||||
- https://auth0.com/docs/manage-users/organizations/login-flows-for-organizations
|
||||
4. Supplier-side multi-user management patterns (Coupa Supplier Portal):
|
||||
- https://compass.coupa.com/en-us/products/product-documentation/supplier-resources/for-suppliers/coupa-supplier-portal/set-up-the-csp/users/manage-users
|
||||
@@ -1,47 +0,0 @@
|
||||
# M4 Planning Phase: Identified Discrepancies and Enhancements
|
||||
|
||||
## Feedback and Discrepancies (Based on M3 Review done by Iliana)
|
||||
|
||||
### Mobile Application (Client & Staff)
|
||||
- **Flexible Shift Locations:** Feedback from the M3 review indicated a need for the ability to specify different locations for individual positions within an order on the mobile app. Currently, the web application handles this by requiring a separate shift for each location.
|
||||
|
||||
- **Order Visibility Management:** Currently, when an order is created with multiple positions, the "View Order" page displays them as separate orders. This is due to UI (prototype) cannot support multi-position views in the order. We should consider adopting the "legacy" app's approach to group these positions under a single order for better clarity.
|
||||
|
||||
- **Cost Center Clarification:** The purpose and functionality of the "Cost Center" field in the Hub creation process is not clear.
|
||||
|
||||
- **Tax Form Data Completeness:** Feedback noted that while tax forms are visible in the Staff mobile application, they appear to be missing critical information. This not clear.
|
||||
|
||||
### Web Dashboard
|
||||
- **Role-Based Content Logic:** The current web dashboard prototype contains some logical inconsistencies regarding user roles:
|
||||
- **Client Dashboard:** Currently includes Staff Availability, Staff Directory, and Staff Onboarding. Since workers (Staff) are managed by Vendors, these pages should be moved to the Vendor dashboard.
|
||||
|
||||
- **Vendor Dashboard:** Currently includes "Teams and Hubs." Since Hubs are client-specific locations where staff clock in/out, these management pages should be moved to the Client dashboard.
|
||||
|
||||
- **Admin Dashboard Filtering:** The Admin dashboard requires improved filtering capabilities. Admins should be able to select specific Clients or Vendors to filter related data, such as viewing only the orders associated with a chosen partner.
|
||||
|
||||
## Proposed Features and Enhancements (Post-M3 Identification)
|
||||
|
||||
- **Feature: Navigation to Hub Details from Coverage Screen (#321)**
|
||||
- **Description:** Allow users to navigate directly to the Hub Details page by clicking on a hub within the Coverage Screen.
|
||||
|
||||
- **Feature: Dedicated Hub Details Screen with Order History (#320)**
|
||||
- **Description:** Develop a comprehensive Hub Details view that aggregates all hub-specific data, including historical order records.
|
||||
- **Benefit:** Centralizes information for better decision-making and easier access to historical data.
|
||||
|
||||
- **Feature: Dedicated Order Details Screen**
|
||||
- **Description:** Transition from displaying all order information on the primary "View Order" page to a dedicated "Order Details" screen. This screen will support viewing multiple positions within a single order.
|
||||
- **Benefit:**
|
||||
- **Improved UX:** Reduces complexity by grouping associated positions together and presenting them in a structured way.
|
||||
- **Performance:** Optimizes data loading by fetching detailed position information only when requested.
|
||||
|
||||
- **Feature: Optimized Clock-In Page (#350)**
|
||||
- **Description:** Remove the calendar component from the Clock-In page. Since workers only clock in for current-day assignments, the calendar is unnecessary.
|
||||
- **Benefit:** Simplifies the interface and reduces user confusion.
|
||||
|
||||
- **Feature: Contextual Shift Actions**
|
||||
- **Description:** Restrict the Clock-In page to show only active or upcoming shifts (starting within 30 minutes). Shift-specific actions (Clock-In/Clock-Out) should be performed within the specific Shift Details page.
|
||||
- **Reasoning:** This solves issues where staff cannot clock out of overnight shifts (shifts starting one day and ending the next) due to the current day-based UI.
|
||||
|
||||
- **Feature: Dedicated Emergency Contact Management (#356)**
|
||||
- **Description:** Replace the inline form in the "View Emergency Contact" page with a dedicated "Create Emergency Contact" screen.
|
||||
- **Benefit:** Standardizes the data entry process and improves UI organization within the Staff app.
|
||||
@@ -0,0 +1,168 @@
|
||||
# M4 Roadmap CSV Schema Reconciliation
|
||||
|
||||
Status: Draft for implementation alignment
|
||||
Date: 2026-02-25
|
||||
Owner: Technical Lead
|
||||
|
||||
## 1) Why this exists
|
||||
We reviewed the original product-roadmap exports to confirm that the target schema can support all stakeholder lanes as the platform grows.
|
||||
|
||||
This avoids two failure modes:
|
||||
1. Building command APIs on top of a schema that cannot represent required workflows.
|
||||
2. Hard-coding today's customer setup in a way that blocks future staffing companies.
|
||||
|
||||
## 2) Inputs reviewed
|
||||
All 13 roadmap exports from `/Users/wiel/Downloads`:
|
||||
1. `Krow App – Roadmap - Business App_ Google, Nvidia.csv`
|
||||
2. `Krow App – Roadmap - Client_ Google, Nvidia.csv`
|
||||
3. `Krow App – Roadmap - Compass- The Operator.csv`
|
||||
4. `Krow App – Roadmap - Employee App.csv`
|
||||
5. `Krow App – Roadmap - Features.csv`
|
||||
6. `Krow App – Roadmap - FoodBuy- Procurement.csv`
|
||||
7. `Krow App – Roadmap - KROW Dashboard.csv`
|
||||
8. `Krow App – Roadmap - Offenses.csv`
|
||||
9. `Krow App – Roadmap - Partner.csv`
|
||||
10. `Krow App – Roadmap - Roadmap.csv`
|
||||
11. `Krow App – Roadmap - Sectors_ BA, Flik ( The executors).csv`
|
||||
12. `Krow App – Roadmap - The Workforce_ Employees.csv`
|
||||
13. `Krow App – Roadmap - Vendor_ Legendary (Staffing).csv`
|
||||
|
||||
Parsed signal:
|
||||
1. 983 non-empty task lines.
|
||||
2. 1,263 planning rows with task/status/priority/reference signals.
|
||||
|
||||
## 3) What the roadmap is clearly asking for
|
||||
Cross-file recurring capabilities:
|
||||
1. Multi-party org model: client, operator, vendor, procurement, workforce, partner, sector execution.
|
||||
2. Orders and shift operations: recurring events, assignment, coverage, schedule management.
|
||||
3. Attendance and policy enforcement: clock-in/out, no-show, tardiness, cancellation, offense ladders.
|
||||
4. Compliance and document verification: certifications, insurance, legal docs, renewals, risk alerts.
|
||||
5. Finance and settlement: invoice lifecycle, disputes, remittance, payment history, aging, payroll/earnings.
|
||||
6. Reporting and prediction: dashboards, KPI, forecasting, scenario planning.
|
||||
|
||||
Repeated examples across many sheets:
|
||||
1. `Vendor Onboarding`, `Service Locations`, `Compliance`, `Certifications`.
|
||||
2. `All Invoices (Open/Pending/Overdue/Paid...)`, `Payment Summary`, `Remittance Advice Download`.
|
||||
3. Offense progression rules in `Offenses.csv` and `Employee App.csv` (warning -> suspension -> disable/block).
|
||||
|
||||
## 4) Stakeholder capability matrix (from roadmap exports)
|
||||
|
||||
| Stakeholder lane | Org network | Orders and shifts | Attendance and offense | Compliance docs | Finance | Reporting |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Client (Google, Nvidia) | Yes | Yes | Partial (visibility) | Yes | Yes | Yes |
|
||||
| Vendor (Legendary) | Yes | Yes | Yes | Yes | Yes | Yes |
|
||||
| Workforce (Employee app) | Limited | Yes | Yes | Yes | Earnings focus | Limited |
|
||||
| Operator (Compass) | Yes | Yes | KPI visibility | Yes | Yes | Yes |
|
||||
| Procurement (FoodBuy) | Yes | KPI/SLA focus | KPI/SLA focus | Yes | Yes | Yes |
|
||||
| KROW Dashboard | Cross-entity | Cross-entity | Cross-entity risk | Cross-entity | Cross-entity | Heavy |
|
||||
| Partner | Basic | Basic | Minimal | Yes | Basic | Basic |
|
||||
|
||||
Implication:
|
||||
1. This is a multi-tenant, multi-party workflow platform, not a single-role CRUD app.
|
||||
2. Schema must separate party identity, party relationships, and role-based permissions from workflow records.
|
||||
|
||||
## 5) Reconciliation against current Data Connect schema
|
||||
|
||||
What already exists and is useful:
|
||||
1. Core scheduling entities: `orders`, `shifts`, `shift_roles`, `applications`, `assignments`.
|
||||
2. Workforce entities: `staffs`, `workforce`, `staff_roles`.
|
||||
3. Financial entities: `invoices`, `recent_payments`, `vendor_rates`.
|
||||
4. Compliance entities: `documents`, `staff_documents`, `certificates`.
|
||||
|
||||
Current structural gaps for roadmap scale:
|
||||
1. No tenant boundary key on core tables (`tenant_id` missing).
|
||||
2. No first-class business user partitioning table (`business_memberships` missing).
|
||||
3. No first-class vendor user partitioning table (`vendor_memberships` missing).
|
||||
4. No first-class stakeholder profile/link model for buyer/operator/partner/sector relationships.
|
||||
5. Attendance history is not first-class (check in/out only inside `applications`).
|
||||
6. No offense policy, offense event, or enforcement action tables.
|
||||
7. Finance is coarse (invoice + recent payment), missing line items, payment runs, remittance artifact model.
|
||||
8. Sensitive bank fields are currently modeled directly in `accounts` (`accountNumber`, `routeNumber`).
|
||||
9. Many core workflow fields are JSON (`orders.assignedStaff`, `orders.shifts`, `shift.managers`, `assignment.managers`).
|
||||
10. Money still uses float in critical tables.
|
||||
|
||||
Connector boundary gap:
|
||||
1. 147 Data Connect mutation operations exist.
|
||||
2. 36 of those are high-risk core workflow mutations (`order`, `shift`, `shiftRole`, `application`, `assignment`, `invoice`, `vendor`, `business`, `workForce`, `teamMember`, `account`).
|
||||
|
||||
## 6) Target schema additions required before full command rollout
|
||||
|
||||
### 6.1 Tenant and stakeholder graph
|
||||
1. `tenants`
|
||||
2. `tenant_memberships`
|
||||
3. `business_memberships`
|
||||
4. `vendor_memberships`
|
||||
5. `stakeholder_types`
|
||||
6. `stakeholder_profiles`
|
||||
7. `stakeholder_links`
|
||||
8. `role_bindings` (scoped to tenant/team/hub/business/vendor/resource)
|
||||
|
||||
### 6.2 Attendance and timesheet reliability
|
||||
1. `attendance_events` (append-only clock-in/out/NFC/manual-corrected)
|
||||
2. `attendance_sessions` (derived per shift role assignment)
|
||||
3. `timesheets` (approval state and pay-eligible snapshot)
|
||||
4. `timesheet_adjustments` (manual corrections with audit reason)
|
||||
|
||||
### 6.3 Offense and policy enforcement
|
||||
1. `offense_policies` (per tenant or per business)
|
||||
2. `offense_rules` (threshold and consequence ladder)
|
||||
3. `offense_events` (who, what, when, evidence)
|
||||
4. `enforcement_actions` (warning, suspension, disable, block-from-client)
|
||||
|
||||
### 6.4 Compliance and verification
|
||||
1. `verification_jobs`
|
||||
2. `verification_reviews`
|
||||
3. `verification_events`
|
||||
4. `compliance_requirements` (per role, tenant, business, or client contract)
|
||||
|
||||
### 6.5 Finance completeness
|
||||
1. `invoice_line_items`
|
||||
2. `invoice_status_history`
|
||||
3. `payment_runs`
|
||||
4. `payment_allocations`
|
||||
5. `remittance_documents`
|
||||
6. `account_token_refs` (tokenized provider refs, no raw account/routing)
|
||||
|
||||
## 7) Minimal target relationship view
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
TENANT ||--o{ TENANT_MEMBERSHIP : has
|
||||
BUSINESS ||--o{ BUSINESS_MEMBERSHIP : has
|
||||
VENDOR ||--o{ VENDOR_MEMBERSHIP : has
|
||||
USER ||--o{ BUSINESS_MEMBERSHIP : belongs_to
|
||||
USER ||--o{ VENDOR_MEMBERSHIP : belongs_to
|
||||
TENANT ||--o{ STAKEHOLDER_PROFILE : has
|
||||
STAKEHOLDER_PROFILE ||--o{ STAKEHOLDER_LINK : links_to
|
||||
|
||||
TENANT ||--o{ BUSINESS : owns
|
||||
TENANT ||--o{ VENDOR : owns
|
||||
BUSINESS ||--o{ ORDER : requests
|
||||
VENDOR ||--o{ ORDER : fulfills
|
||||
ORDER ||--o{ SHIFT : expands_to
|
||||
SHIFT ||--o{ SHIFT_ROLE : requires
|
||||
SHIFT_ROLE ||--o{ APPLICATION : receives
|
||||
APPLICATION ||--o{ ASSIGNMENT : becomes
|
||||
|
||||
ASSIGNMENT ||--o{ ATTENDANCE_EVENT : emits
|
||||
ASSIGNMENT ||--o{ TIMESHEET : settles
|
||||
OFFENSE_POLICY ||--o{ OFFENSE_RULE : defines
|
||||
ASSIGNMENT ||--o{ OFFENSE_EVENT : may_trigger
|
||||
OFFENSE_EVENT ||--o{ ENFORCEMENT_ACTION : causes
|
||||
|
||||
ORDER ||--o{ INVOICE : bills
|
||||
INVOICE ||--o{ INVOICE_LINE_ITEM : contains
|
||||
PAYMENT_RUN ||--o{ PAYMENT_ALLOCATION : allocates
|
||||
INVOICE ||--o{ PAYMENT_ALLOCATION : receives
|
||||
PAYMENT_RUN ||--o{ REMITTANCE_DOCUMENT : publishes
|
||||
```
|
||||
|
||||
## 8) First-principles rules we should lock now
|
||||
1. Every command-critical table includes `tenant_id`.
|
||||
2. High-risk writes go through command APIs only.
|
||||
3. Money uses exact numeric type (or integer cents), never float.
|
||||
4. Core workflow state is relational and constrained, not JSON blobs.
|
||||
5. Every irreversible state change has append-only audit event rows.
|
||||
|
||||
## 9) Decision
|
||||
This roadmap evidence supports continuing with the target architecture direction (command boundary + multi-tenant schema), but we should add attendance/offense/settlement/stakeholder-graph tables before full command rollout on mission-critical flows.
|
||||
490
docs/MILESTONES/M4/planning/m4-target-schema-blueprint.md
Normal file
490
docs/MILESTONES/M4/planning/m4-target-schema-blueprint.md
Normal file
@@ -0,0 +1,490 @@
|
||||
# M4 Target Schema Blueprint (Command-Ready)
|
||||
|
||||
Status: Draft for team alignment
|
||||
Date: 2026-02-25
|
||||
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.
|
||||
4. Business-side users and vendor-side users are partitioned with dedicated membership tables, not only one `userId` owner field.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## 1.2 Stakeholder wheel mapping (current baseline)
|
||||
The stakeholder labels from the customer workshop map to schema as follows:
|
||||
|
||||
1. Buyer (Procurements):
|
||||
- Buyer users inside a business/client account.
|
||||
- Schema anchor: `users` + `tenant_memberships` + `business_memberships`.
|
||||
2. Enterprises (Operator):
|
||||
- Tenant operator/admin users running staffing operations.
|
||||
- Schema anchor: `tenants`, `tenant_memberships`, `role_bindings`.
|
||||
3. Sectors (Execution):
|
||||
- Operational segments or business units executing events.
|
||||
- Schema anchor: `teams`, `team_hubs`, `team_hud_departments`, `roles`.
|
||||
4. Approved Vendor:
|
||||
- Supplier companies approved to fulfill staffing demand.
|
||||
- Schema anchor: `vendors`, `vendor_memberships`, `workforce`, `vendor_rates`, `vendor_benefit_plans`.
|
||||
5. Workforce:
|
||||
- Individual workers/staff and their assignments.
|
||||
- Schema anchor: `staffs`, `staff_roles`, `applications`, `assignments`, `certificates`, `staff_documents`.
|
||||
6. Partner:
|
||||
- External integration or service partner (future).
|
||||
- Schema anchor: `stakeholder_profiles` extension path + scoped role bindings.
|
||||
|
||||
Rule:
|
||||
1. Start with baseline stakeholders above.
|
||||
2. Add new stakeholders via extension tables and role bindings, not by changing core scheduling and finance tables.
|
||||
|
||||
## 1.3 Future stakeholder expansion model
|
||||
To add stakeholders later without breaking core schema:
|
||||
1. Add `stakeholder_types` (registry).
|
||||
2. Add `stakeholder_profiles` (`tenant_id`, `type`, `status`, `metadata`).
|
||||
3. Add `stakeholder_links` (relationship graph across stakeholders).
|
||||
4. Bind permissions through `role_bindings` with scope (`tenant`, `team`, `hub`, `business`, or specific resource).
|
||||
|
||||
## 1.4 Roadmap CSV evidence snapshot
|
||||
Evidence source:
|
||||
1. `docs/MILESTONES/M4/planning/m4-roadmap-csv-schema-reconciliation.md`
|
||||
|
||||
What the exports confirmed:
|
||||
1. The product is multi-party and multi-tenant by design (client, operator, vendor, workforce, procurement, partner, dashboard).
|
||||
2. Attendance and offense enforcement are core business workflows, not side features.
|
||||
3. Finance requires more than invoices (payment runs, remittance, status history, dispute/audit trace).
|
||||
4. Compliance requires asynchronous verification and requirement templates by tenant/business/role.
|
||||
|
||||
## 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. `business_memberships` (new; user access to business account scope)
|
||||
4. `vendor_memberships` (new; user access to vendor account scope)
|
||||
5. `team_members` (membership + scope per team)
|
||||
6. `roles` (new)
|
||||
7. `permissions` (new)
|
||||
8. `role_bindings` (new; who has which role in which scope)
|
||||
|
||||
Rules:
|
||||
1. Unique tenant membership: `(tenant_id, user_id)`.
|
||||
2. Unique business membership: `(business_id, user_id)`.
|
||||
3. Unique vendor membership: `(vendor_id, user_id)`.
|
||||
4. Unique team membership: `(team_id, user_id)`.
|
||||
5. Access checks resolve through tenant membership first, then business/vendor/team 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.
|
||||
3. Business and vendor routes must enforce membership scope before data access.
|
||||
|
||||
## 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)`.
|
||||
|
||||
## 4.9 Attendance, Timesheets, and Offense Governance
|
||||
Tables:
|
||||
1. `attendance_events` (append-only: clock-in/out, source, correction metadata)
|
||||
2. `attendance_sessions` (derived work session per assignment)
|
||||
3. `timesheets` (approval-ready payroll snapshot)
|
||||
4. `timesheet_adjustments` (manual edits with reason and actor)
|
||||
5. `offense_policies` (tenant/business scoped policy set)
|
||||
6. `offense_rules` (threshold ladder and consequence)
|
||||
7. `offense_events` (actual violation events)
|
||||
8. `enforcement_actions` (warning, suspension, disable, block)
|
||||
|
||||
Rules:
|
||||
1. Attendance corrections are additive events, not destructive overwrites.
|
||||
2. Offense consequences are computed from policy + history and persisted as explicit actions.
|
||||
3. Manual overrides require actor, reason, and timestamp in audit trail.
|
||||
|
||||
## 4.10 Stakeholder Network Extensibility
|
||||
Tables:
|
||||
1. `stakeholder_types` (buyer, operator, vendor, workforce, partner, future types)
|
||||
2. `stakeholder_profiles` (tenant-scoped typed profile)
|
||||
3. `stakeholder_links` (explicit relationship graph between profiles)
|
||||
|
||||
Rules:
|
||||
1. New stakeholder categories are added by data, not by schema rewrites to core workflow tables.
|
||||
2. Permission scope resolves through role bindings plus stakeholder links where needed.
|
||||
3. Scheduling and finance records remain stable while stakeholder topology evolves.
|
||||
|
||||
## 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
|
||||
USER ||--o{ BUSINESS_MEMBERSHIP : belongs_to
|
||||
USER ||--o{ VENDOR_MEMBERSHIP : belongs_to
|
||||
BUSINESS ||--o{ BUSINESS_MEMBERSHIP : has
|
||||
VENDOR ||--o{ VENDOR_MEMBERSHIP : has
|
||||
|
||||
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
|
||||
ASSIGNMENT ||--o{ ATTENDANCE_EVENT : emits
|
||||
ASSIGNMENT ||--o{ TIMESHEET : settles
|
||||
OFFENSE_POLICY ||--o{ OFFENSE_RULE : defines
|
||||
ASSIGNMENT ||--o{ OFFENSE_EVENT : may_trigger
|
||||
OFFENSE_EVENT ||--o{ ENFORCEMENT_ACTION : causes
|
||||
|
||||
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
|
||||
INVOICE ||--o{ INVOICE_LINE_ITEM : details
|
||||
PAYMENT_RUN ||--o{ PAYMENT_ALLOCATION : allocates
|
||||
INVOICE ||--o{ PAYMENT_ALLOCATION : receives
|
||||
PAYMENT_RUN ||--o{ REMITTANCE_DOCUMENT : publishes
|
||||
|
||||
ORDER ||--o{ DOMAIN_EVENT : emits
|
||||
SHIFT ||--o{ DOMAIN_EVENT : emits
|
||||
ASSIGNMENT ||--o{ DOMAIN_EVENT : emits
|
||||
STAKEHOLDER_TYPE ||--o{ STAKEHOLDER_PROFILE : classifies
|
||||
STAKEHOLDER_PROFILE ||--o{ STAKEHOLDER_LINK : relates
|
||||
```
|
||||
|
||||
## 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. `attendance_events`: unique idempotency tuple (for example `(assignment_id, source_event_id)`).
|
||||
8. `offense_rules`: unique `(policy_id, trigger_type, threshold_count)`.
|
||||
|
||||
## 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. `attendance_events (assignment_id, event_time, event_type)`.
|
||||
9. `offense_events (staff_id, occurred_at, offense_type, status)`.
|
||||
10. `invoice_line_items (invoice_id, line_type, created_at)`.
|
||||
|
||||
## 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
|
||||
- add attendance and offense base tables"]
|
||||
P3 --> P4["Phase 4: Command rollout
|
||||
- command writes on hardened schema
|
||||
- emit domain events + idempotency
|
||||
- enforce RBAC for command routes
|
||||
- add finance settlement tables for payment runs and remittance"]
|
||||
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).
|
||||
7. Attendance and offense tables are ready for policy-driven command routes.
|
||||
8. Finance settlement tables (`invoice_line_items`, `payment_runs`, `payment_allocations`) are available.
|
||||
|
||||
## 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
|
||||
```
|
||||
264
docs/MILESTONES/M4/planning/m4-target-schema-models-and-keys.md
Normal file
264
docs/MILESTONES/M4/planning/m4-target-schema-models-and-keys.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# M4 Target Schema Models, Keys, and Relationships
|
||||
|
||||
Status: Draft for architecture workshop
|
||||
Date: 2026-02-25
|
||||
Owner: Technical Lead
|
||||
|
||||
## 1) Purpose
|
||||
This document is the model-level view for slide creation:
|
||||
1. Key fields per model (`PK`, `FK`, unique keys).
|
||||
2. How models relate to each other.
|
||||
3. A first-principles structure for scale across tenant, business, vendor, and workforce flows.
|
||||
|
||||
## 2) Identity and access models
|
||||
|
||||
### 2.1 Model keys
|
||||
|
||||
| Model | Primary key | Foreign keys | Important unique keys |
|
||||
|---|---|---|---|
|
||||
| `users` | `id` | - | `email` (optional unique) |
|
||||
| `tenants` | `id` | - | `slug` |
|
||||
| `tenant_memberships` | `id` | `tenant_id -> tenants.id`, `user_id -> users.id` | `(tenant_id, user_id)` |
|
||||
| `business_memberships` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id`, `user_id -> users.id` | `(business_id, user_id)` |
|
||||
| `vendor_memberships` | `id` | `tenant_id -> tenants.id`, `vendor_id -> vendors.id`, `user_id -> users.id` | `(vendor_id, user_id)` |
|
||||
| `roles` | `id` | `tenant_id -> tenants.id` | `(tenant_id, name)` |
|
||||
| `permissions` | `id` | - | `code` |
|
||||
| `role_bindings` | `id` | `tenant_id -> tenants.id`, `role_id -> roles.id`, `user_id -> users.id` | `(tenant_id, role_id, user_id, scope_type, scope_id)` |
|
||||
|
||||
### 2.2 Diagram
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
USERS ||--o{ TENANT_MEMBERSHIPS : has
|
||||
TENANTS ||--o{ TENANT_MEMBERSHIPS : has
|
||||
|
||||
USERS ||--o{ BUSINESS_MEMBERSHIPS : has
|
||||
BUSINESSES ||--o{ BUSINESS_MEMBERSHIPS : has
|
||||
TENANTS ||--o{ BUSINESS_MEMBERSHIPS : scopes
|
||||
|
||||
USERS ||--o{ VENDOR_MEMBERSHIPS : has
|
||||
VENDORS ||--o{ VENDOR_MEMBERSHIPS : has
|
||||
TENANTS ||--o{ VENDOR_MEMBERSHIPS : scopes
|
||||
|
||||
TENANTS ||--o{ ROLES : defines
|
||||
ROLES ||--o{ ROLE_BINDINGS : used_by
|
||||
USERS ||--o{ ROLE_BINDINGS : receives
|
||||
TENANTS ||--o{ ROLE_BINDINGS : scopes
|
||||
```
|
||||
```
|
||||
|
||||
## 3) Organization and stakeholder models
|
||||
|
||||
### 3.1 Model keys
|
||||
|
||||
| Model | Primary key | Foreign keys | Important unique keys |
|
||||
|---|---|---|---|
|
||||
| `businesses` | `id` | `tenant_id -> tenants.id` | `(tenant_id, business_name)` |
|
||||
| `vendors` | `id` | `tenant_id -> tenants.id` | `(tenant_id, company_name)` |
|
||||
| `teams` | `id` | `tenant_id -> tenants.id` | `(tenant_id, team_name)` |
|
||||
| `team_hubs` | `id` | `team_id -> teams.id` | `(team_id, hub_name)` |
|
||||
| `team_hud_departments` | `id` | `team_hub_id -> team_hubs.id` | `(team_hub_id, name)` |
|
||||
| `stakeholder_types` | `id` | - | `code` |
|
||||
| `stakeholder_profiles` | `id` | `tenant_id -> tenants.id`, `stakeholder_type_id -> stakeholder_types.id` | `(tenant_id, stakeholder_type_id, name)` |
|
||||
| `stakeholder_links` | `id` | `tenant_id -> tenants.id`, `from_profile_id -> stakeholder_profiles.id`, `to_profile_id -> stakeholder_profiles.id` | `(tenant_id, from_profile_id, to_profile_id, relation_type)` |
|
||||
|
||||
### 3.2 Diagram
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
TENANTS ||--o{ BUSINESSES : owns
|
||||
TENANTS ||--o{ VENDORS : owns
|
||||
TENANTS ||--o{ TEAMS : owns
|
||||
TEAMS ||--o{ TEAM_HUBS : has
|
||||
TEAM_HUBS ||--o{ TEAM_HUD_DEPARTMENTS : has
|
||||
|
||||
STAKEHOLDER_TYPES ||--o{ STAKEHOLDER_PROFILES : classifies
|
||||
TENANTS ||--o{ STAKEHOLDER_PROFILES : owns
|
||||
STAKEHOLDER_PROFILES ||--o{ STAKEHOLDER_LINKS : from
|
||||
STAKEHOLDER_PROFILES ||--o{ STAKEHOLDER_LINKS : to
|
||||
TENANTS ||--o{ STAKEHOLDER_LINKS : scopes
|
||||
```
|
||||
```
|
||||
|
||||
## 4) Workforce, orders, and assignments
|
||||
|
||||
### 4.1 Model keys
|
||||
|
||||
| Model | Primary key | Foreign keys | Important unique keys |
|
||||
|---|---|---|---|
|
||||
| `staffs` | `id` | `tenant_id -> tenants.id`, `user_id -> users.id` | `(tenant_id, user_id)` (nullable until activation if needed) |
|
||||
| `staff_roles` | `id` | `staff_id -> staffs.id`, `role_id -> roles.id` | `(staff_id, role_id)` |
|
||||
| `workforce` | `id` | `tenant_id -> tenants.id`, `vendor_id -> vendors.id`, `staff_id -> staffs.id` | active `(vendor_id, staff_id)` |
|
||||
| `orders` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id`, `vendor_id -> vendors.id` | `(tenant_id, external_ref)` optional |
|
||||
| `order_schedule_rules` | `id` | `order_id -> orders.id` | `(order_id, rule_type, effective_from)` |
|
||||
| `shifts` | `id` | `tenant_id -> tenants.id`, `order_id -> orders.id` | `(order_id, start_time, end_time)` |
|
||||
| `shift_roles` | `id` | `shift_id -> shifts.id`, `role_id -> roles.id` | `(shift_id, role_id)` |
|
||||
| `shift_managers` | `id` | `shift_id -> shifts.id`, `team_member_id -> team_members.id` | `(shift_id, team_member_id)` |
|
||||
| `applications` | `id` | `tenant_id -> tenants.id`, `shift_id -> shifts.id`, `role_id -> roles.id`, `staff_id -> staffs.id` | `(shift_id, role_id, staff_id)` |
|
||||
| `assignments` | `id` | `tenant_id -> tenants.id`, `shift_role_id -> shift_roles.id`, `workforce_id -> workforce.id` | `(shift_role_id, workforce_id)` active |
|
||||
|
||||
### 4.2 Diagram
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
TENANTS ||--o{ STAFFS : owns
|
||||
USERS ||--o| STAFFS : identity
|
||||
STAFFS ||--o{ STAFF_ROLES : has
|
||||
ROLES ||--o{ STAFF_ROLES : classifies
|
||||
|
||||
TENANTS ||--o{ ORDERS : scopes
|
||||
BUSINESSES ||--o{ ORDERS : requests
|
||||
VENDORS ||--o{ ORDERS : fulfills
|
||||
ORDERS ||--o{ ORDER_SCHEDULE_RULES : configures
|
||||
ORDERS ||--o{ SHIFTS : expands_to
|
||||
SHIFTS ||--o{ SHIFT_ROLES : requires
|
||||
ROLES ||--o{ SHIFT_ROLES : typed_as
|
||||
SHIFTS ||--o{ SHIFT_MANAGERS : has
|
||||
|
||||
VENDORS ||--o{ WORKFORCE : contracts
|
||||
STAFFS ||--o{ WORKFORCE : linked
|
||||
|
||||
SHIFT_ROLES ||--o{ APPLICATIONS : receives
|
||||
STAFFS ||--o{ APPLICATIONS : applies
|
||||
SHIFT_ROLES ||--o{ ASSIGNMENTS : allocates
|
||||
WORKFORCE ||--o{ ASSIGNMENTS : executes
|
||||
```
|
||||
```
|
||||
|
||||
## 5) Attendance and offense governance
|
||||
|
||||
### 5.1 Model keys
|
||||
|
||||
| Model | Primary key | Foreign keys | Important unique keys |
|
||||
|---|---|---|---|
|
||||
| `attendance_events` | `id` | `tenant_id -> tenants.id`, `assignment_id -> assignments.id` | `(assignment_id, source_event_id)` |
|
||||
| `attendance_sessions` | `id` | `tenant_id -> tenants.id`, `assignment_id -> assignments.id` | one open session per assignment |
|
||||
| `timesheets` | `id` | `tenant_id -> tenants.id`, `assignment_id -> assignments.id`, `staff_id -> staffs.id` | `(assignment_id)` |
|
||||
| `timesheet_adjustments` | `id` | `timesheet_id -> timesheets.id`, `actor_user_id -> users.id` | - |
|
||||
| `offense_policies` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id` nullable | `(tenant_id, name, business_id)` |
|
||||
| `offense_rules` | `id` | `policy_id -> offense_policies.id` | `(policy_id, trigger_type, threshold_count)` |
|
||||
| `offense_events` | `id` | `tenant_id -> tenants.id`, `staff_id -> staffs.id`, `assignment_id -> assignments.id` nullable, `rule_id -> offense_rules.id` nullable | `(staff_id, occurred_at, offense_type)` |
|
||||
| `enforcement_actions` | `id` | `offense_event_id -> offense_events.id`, `actor_user_id -> users.id` | - |
|
||||
|
||||
### 5.2 Diagram
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
ASSIGNMENTS ||--o{ ATTENDANCE_EVENTS : emits
|
||||
ASSIGNMENTS ||--o{ ATTENDANCE_SESSIONS : opens
|
||||
ASSIGNMENTS ||--o{ TIMESHEETS : settles
|
||||
TIMESHEETS ||--o{ TIMESHEET_ADJUSTMENTS : adjusts
|
||||
USERS ||--o{ TIMESHEET_ADJUSTMENTS : made_by
|
||||
|
||||
TENANTS ||--o{ OFFENSE_POLICIES : defines
|
||||
BUSINESSES ||--o{ OFFENSE_POLICIES : overrides
|
||||
OFFENSE_POLICIES ||--o{ OFFENSE_RULES : contains
|
||||
STAFFS ||--o{ OFFENSE_EVENTS : incurs
|
||||
OFFENSE_RULES ||--o{ OFFENSE_EVENTS : triggered_by
|
||||
OFFENSE_EVENTS ||--o{ ENFORCEMENT_ACTIONS : causes
|
||||
USERS ||--o{ ENFORCEMENT_ACTIONS : applied_by
|
||||
```
|
||||
```
|
||||
|
||||
## 6) Compliance and verification
|
||||
|
||||
### 6.1 Model keys
|
||||
|
||||
| Model | Primary key | Foreign keys | Important unique keys |
|
||||
|---|---|---|---|
|
||||
| `documents` | `id` | `tenant_id -> tenants.id` | `(tenant_id, document_type, name)` |
|
||||
| `staff_documents` | `id` | `staff_id -> staffs.id`, `document_id -> documents.id` | `(staff_id, document_id)` |
|
||||
| `certificates` | `id` | `staff_id -> staffs.id` | `(staff_id, certificate_number)` optional |
|
||||
| `compliance_requirements` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id` nullable, `role_id -> roles.id` nullable | `(tenant_id, requirement_type, business_id, role_id)` |
|
||||
| `verification_jobs` | `id` | `tenant_id -> tenants.id`, `staff_id -> staffs.id`, `document_id -> documents.id` nullable | `(tenant_id, idempotency_key)` |
|
||||
| `verification_reviews` | `id` | `verification_job_id -> verification_jobs.id`, `reviewer_user_id -> users.id` | - |
|
||||
| `verification_events` | `id` | `verification_job_id -> verification_jobs.id` | - |
|
||||
|
||||
### 6.2 Diagram
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
STAFFS ||--o{ STAFF_DOCUMENTS : uploads
|
||||
DOCUMENTS ||--o{ STAFF_DOCUMENTS : references
|
||||
STAFFS ||--o{ CERTIFICATES : has
|
||||
|
||||
TENANTS ||--o{ COMPLIANCE_REQUIREMENTS : defines
|
||||
BUSINESSES ||--o{ COMPLIANCE_REQUIREMENTS : overrides
|
||||
ROLES ||--o{ COMPLIANCE_REQUIREMENTS : role_scope
|
||||
|
||||
STAFFS ||--o{ VERIFICATION_JOBS : subject
|
||||
VERIFICATION_JOBS ||--o{ VERIFICATION_REVIEWS : reviewed
|
||||
VERIFICATION_JOBS ||--o{ VERIFICATION_EVENTS : logs
|
||||
USERS ||--o{ VERIFICATION_REVIEWS : reviewer
|
||||
```
|
||||
```
|
||||
|
||||
## 7) Finance and settlement
|
||||
|
||||
### 7.1 Model keys
|
||||
|
||||
| Model | Primary key | Foreign keys | Important unique keys |
|
||||
|---|---|---|---|
|
||||
| `invoices` | `id` | `tenant_id -> tenants.id`, `order_id -> orders.id`, `business_id -> businesses.id`, `vendor_id -> vendors.id` | `(tenant_id, invoice_number)` |
|
||||
| `invoice_line_items` | `id` | `invoice_id -> invoices.id`, `assignment_id -> assignments.id` nullable | `(invoice_id, line_number)` |
|
||||
| `invoice_status_history` | `id` | `invoice_id -> invoices.id`, `actor_user_id -> users.id` | - |
|
||||
| `payment_runs` | `id` | `tenant_id -> tenants.id` | `(tenant_id, run_reference)` |
|
||||
| `payment_allocations` | `id` | `payment_run_id -> payment_runs.id`, `invoice_id -> invoices.id` | `(payment_run_id, invoice_id)` |
|
||||
| `remittance_documents` | `id` | `payment_run_id -> payment_runs.id` | `(payment_run_id, document_url)` |
|
||||
| `account_token_refs` | `id` | `tenant_id -> tenants.id`, `owner_business_id -> businesses.id` nullable, `owner_vendor_id -> vendors.id` nullable | one primary per owner |
|
||||
|
||||
### 7.2 Diagram
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
ORDERS ||--o{ INVOICES : billed
|
||||
BUSINESSES ||--o{ INVOICES : billed_to
|
||||
VENDORS ||--o{ INVOICES : billed_from
|
||||
INVOICES ||--o{ INVOICE_LINE_ITEMS : contains
|
||||
INVOICES ||--o{ INVOICE_STATUS_HISTORY : transitions
|
||||
USERS ||--o{ INVOICE_STATUS_HISTORY : changed_by
|
||||
|
||||
PAYMENT_RUNS ||--o{ PAYMENT_ALLOCATIONS : allocates
|
||||
INVOICES ||--o{ PAYMENT_ALLOCATIONS : receives
|
||||
PAYMENT_RUNS ||--o{ REMITTANCE_DOCUMENTS : publishes
|
||||
|
||||
TENANTS ||--o{ ACCOUNT_TOKEN_REFS : scopes
|
||||
BUSINESSES ||--o{ ACCOUNT_TOKEN_REFS : business_owner
|
||||
VENDORS ||--o{ ACCOUNT_TOKEN_REFS : vendor_owner
|
||||
```
|
||||
```
|
||||
|
||||
## 8) Audit and reliability
|
||||
|
||||
### 8.1 Model keys
|
||||
|
||||
| Model | Primary key | Foreign keys | Important unique keys |
|
||||
|---|---|---|---|
|
||||
| `domain_events` | `id` | `tenant_id -> tenants.id`, `actor_user_id -> users.id` | `(tenant_id, aggregate_type, aggregate_id, sequence)` |
|
||||
| `idempotency_keys` | `id` | `tenant_id -> tenants.id`, `actor_user_id -> users.id` | `(tenant_id, actor_user_id, route, key)` |
|
||||
| `activity_logs` | `id` | `tenant_id -> tenants.id`, `user_id -> users.id` | `(tenant_id, created_at, id)` |
|
||||
|
||||
### 8.2 Diagram
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
TENANTS ||--o{ DOMAIN_EVENTS : scopes
|
||||
USERS ||--o{ DOMAIN_EVENTS : actor
|
||||
TENANTS ||--o{ IDEMPOTENCY_KEYS : scopes
|
||||
USERS ||--o{ IDEMPOTENCY_KEYS : actor
|
||||
TENANTS ||--o{ ACTIVITY_LOGS : scopes
|
||||
USERS ||--o{ ACTIVITY_LOGS : actor
|
||||
```
|
||||
```
|
||||
|
||||
## 9) Practical note for current system
|
||||
Current schema already has:
|
||||
1. `businesses.userId` (single business owner user).
|
||||
2. `vendors.userId` (single vendor owner user).
|
||||
3. `team_members` (multi-user workaround).
|
||||
|
||||
Target schema improves this by adding explicit:
|
||||
1. `business_memberships`
|
||||
2. `vendor_memberships`
|
||||
|
||||
This is the key upgrade for clean client/vendor user partitioning.
|
||||
@@ -0,0 +1,233 @@
|
||||
# M4 Verification Architecture Contract (Attire, Government ID, Certification)
|
||||
|
||||
Status: Partially implemented in dev (core endpoints + async in-memory processor)
|
||||
Date: 2026-02-24
|
||||
Owner: Technical Lead
|
||||
|
||||
## Implementation status today (dev)
|
||||
1. Implemented routes:
|
||||
- `POST /core/verifications`
|
||||
- `GET /core/verifications/{verificationId}`
|
||||
- `POST /core/verifications/{verificationId}/review`
|
||||
- `POST /core/verifications/{verificationId}/retry`
|
||||
2. Current processor is in-memory and non-persistent (for fast frontend integration in dev).
|
||||
3. Next hardening step is persistent job storage and worker execution before staging.
|
||||
4. Attire uses a live Vertex vision model path with `gemini-2.0-flash-lite-001` by default.
|
||||
5. Government ID and certification use third-party adapter contracts (provider URL/token envs) and fall back to `NEEDS_REVIEW` when providers are not configured.
|
||||
|
||||
## 1) Goal
|
||||
Define a single backend verification pipeline for:
|
||||
1. `attire`
|
||||
2. `government_id`
|
||||
3. `certification`
|
||||
|
||||
This contract gives the team exact endpoint behavior, state flow, and ownership before coding.
|
||||
|
||||
## 2) Principles
|
||||
1. Upload is evidence intake, not final verification.
|
||||
2. Verification runs asynchronously in backend workers.
|
||||
3. Model output is a signal, not legal truth.
|
||||
4. High-risk identity decisions require stronger validation and human audit trail.
|
||||
5. Every decision is traceable (`who`, `what`, `when`, `why`).
|
||||
|
||||
## 3) Verification types and policy
|
||||
|
||||
## 3.1 Attire
|
||||
1. Primary check: vision model + rule checks.
|
||||
2. Typical output: `AUTO_PASS`, `AUTO_FAIL`, or `NEEDS_REVIEW`.
|
||||
3. Manual override is always allowed.
|
||||
|
||||
## 3.2 Government ID
|
||||
1. Required path for mission-critical use: third-party identity verification provider.
|
||||
2. Model/OCR can pre-parse fields but does not replace identity verification.
|
||||
3. Final status should require either provider success or manual approval by authorized reviewer.
|
||||
|
||||
## 3.3 Certification
|
||||
1. Preferred path: verify against issuer API/registry when available.
|
||||
2. If no issuer API: OCR extraction + manual review.
|
||||
3. Keep evidence of the source used for validation.
|
||||
|
||||
## 4) State model
|
||||
1. `PENDING`
|
||||
2. `PROCESSING`
|
||||
3. `AUTO_PASS`
|
||||
4. `AUTO_FAIL`
|
||||
5. `NEEDS_REVIEW`
|
||||
6. `APPROVED`
|
||||
7. `REJECTED`
|
||||
8. `ERROR`
|
||||
|
||||
Rules:
|
||||
1. `AUTO_*` and `NEEDS_REVIEW` are machine outcomes.
|
||||
2. `APPROVED` and `REJECTED` are human outcomes.
|
||||
3. All transitions are append-only in audit events.
|
||||
|
||||
## 5) API contract
|
||||
|
||||
## 5.1 Create verification job
|
||||
1. Route: `POST /core/verifications`
|
||||
2. Auth: required
|
||||
3. Purpose: enqueue verification job for previously uploaded file.
|
||||
4. Request:
|
||||
```json
|
||||
{
|
||||
"type": "attire",
|
||||
"subjectType": "staff",
|
||||
"subjectId": "staff_123",
|
||||
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/item.jpg",
|
||||
"rules": {
|
||||
"attireType": "shoes",
|
||||
"expectedColor": "black"
|
||||
},
|
||||
"metadata": {
|
||||
"shiftId": "shift_123"
|
||||
}
|
||||
}
|
||||
```
|
||||
5. Success `202`:
|
||||
```json
|
||||
{
|
||||
"verificationId": "ver_123",
|
||||
"status": "PENDING",
|
||||
"type": "attire",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 5.2 Get verification status
|
||||
1. Route: `GET /core/verifications/{verificationId}`
|
||||
2. Auth: required
|
||||
3. Purpose: polling from frontend.
|
||||
4. Success `200`:
|
||||
```json
|
||||
{
|
||||
"verificationId": "ver_123",
|
||||
"type": "attire",
|
||||
"status": "NEEDS_REVIEW",
|
||||
"confidence": 0.62,
|
||||
"reasons": ["Color uncertain"],
|
||||
"extracted": {
|
||||
"detectedType": "shoe",
|
||||
"detectedColor": "dark"
|
||||
},
|
||||
"provider": {
|
||||
"name": "vertex-attire",
|
||||
"reference": "gemini-2.0-flash-lite-001"
|
||||
},
|
||||
"review": null,
|
||||
"createdAt": "2026-02-24T15:00:00Z",
|
||||
"updatedAt": "2026-02-24T15:00:04Z",
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 5.3 Review override
|
||||
1. Route: `POST /core/verifications/{verificationId}/review`
|
||||
2. Auth: required (reviewer role later; auth-first now + explicit reviewer id logging)
|
||||
3. Purpose: final human decision and audit reason.
|
||||
4. Request:
|
||||
```json
|
||||
{
|
||||
"decision": "APPROVED",
|
||||
"note": "Document matches required certification",
|
||||
"reasonCode": "MANUAL_REVIEW"
|
||||
}
|
||||
```
|
||||
5. Success `200`:
|
||||
```json
|
||||
{
|
||||
"verificationId": "ver_123",
|
||||
"status": "APPROVED",
|
||||
"review": {
|
||||
"decision": "APPROVED",
|
||||
"reviewedBy": "user_456",
|
||||
"reviewedAt": "2026-02-24T15:02:00Z",
|
||||
"note": "Document matches required certification",
|
||||
"reasonCode": "MANUAL_REVIEW"
|
||||
},
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 5.4 Retry verification job
|
||||
1. Route: `POST /core/verifications/{verificationId}/retry`
|
||||
2. Auth: required
|
||||
3. Purpose: rerun failed or updated checks.
|
||||
4. Success `202`: status resets to `PENDING`.
|
||||
|
||||
## 6) Worker execution flow
|
||||
1. API validates payload and ownership of `fileUri`.
|
||||
2. API writes `verification_jobs` row with `PENDING`.
|
||||
3. Worker consumes job, marks `PROCESSING`.
|
||||
4. Worker selects processor by type:
|
||||
- `attire` -> model + rule scorer
|
||||
- `government_id` -> provider adapter (+ optional OCR pre-check)
|
||||
- `certification` -> issuer API adapter or OCR adapter
|
||||
5. Worker writes machine outcome (`AUTO_PASS`, `AUTO_FAIL`, `NEEDS_REVIEW`, or `ERROR`).
|
||||
6. Frontend polls status route.
|
||||
7. Reviewer finalizes with `APPROVED` or `REJECTED` where needed.
|
||||
|
||||
## 7) Data model (minimal)
|
||||
|
||||
## 7.1 Table: `verification_jobs`
|
||||
1. `id` (pk)
|
||||
2. `type` (`attire|government_id|certification`)
|
||||
3. `subject_type`, `subject_id`
|
||||
4. `owner_uid`
|
||||
5. `file_uri`
|
||||
6. `status`
|
||||
7. `confidence` (nullable)
|
||||
8. `reasons_json`
|
||||
9. `extracted_json`
|
||||
10. `provider_name`, `provider_ref`
|
||||
11. `created_at`, `updated_at`
|
||||
|
||||
## 7.2 Table: `verification_reviews`
|
||||
1. `id` (pk)
|
||||
2. `verification_id` (fk)
|
||||
3. `decision` (`APPROVED|REJECTED`)
|
||||
4. `reviewed_by`
|
||||
5. `note`
|
||||
6. `reason_code`
|
||||
7. `reviewed_at`
|
||||
|
||||
## 7.3 Table: `verification_events`
|
||||
1. `id` (pk)
|
||||
2. `verification_id` (fk)
|
||||
3. `from_status`, `to_status`
|
||||
4. `actor_type` (`system|reviewer`)
|
||||
5. `actor_id`
|
||||
6. `details_json`
|
||||
7. `created_at`
|
||||
|
||||
## 8) Security and compliance notes
|
||||
1. Restrict verification file paths to owner-owned upload prefixes.
|
||||
2. Never expose raw private bucket URLs directly.
|
||||
3. Keep third-party provider secrets in Secret Manager.
|
||||
4. Log request and decision IDs for every transition.
|
||||
5. For government ID, keep provider response reference and verification timestamp.
|
||||
|
||||
## 9) Provider configuration (environment variables)
|
||||
1. Attire model:
|
||||
- `VERIFICATION_ATTIRE_PROVIDER=vertex`
|
||||
- `VERIFICATION_ATTIRE_MODEL=gemini-2.0-flash-lite-001`
|
||||
2. Government ID provider:
|
||||
- `VERIFICATION_GOV_ID_PROVIDER_URL`
|
||||
- `VERIFICATION_GOV_ID_PROVIDER_TOKEN` (Secret Manager recommended)
|
||||
3. Certification provider:
|
||||
- `VERIFICATION_CERT_PROVIDER_URL`
|
||||
- `VERIFICATION_CERT_PROVIDER_TOKEN` (Secret Manager recommended)
|
||||
4. Provider timeout:
|
||||
- `VERIFICATION_PROVIDER_TIMEOUT_MS` (default `8000`)
|
||||
|
||||
## 10) Frontend integration pattern
|
||||
1. Upload file via existing `POST /core/upload-file`.
|
||||
2. Create verification job with returned `fileUri`.
|
||||
3. Poll `GET /core/verifications/{id}` until terminal state.
|
||||
4. Show machine status and confidence.
|
||||
5. For `NEEDS_REVIEW`, show pending-review UI state.
|
||||
|
||||
## 11) Delivery split (recommended)
|
||||
1. Wave A (fast): attire verification pipeline end-to-end.
|
||||
2. Wave B: certification verification with issuer adapter + review.
|
||||
3. Wave C: government ID provider integration + reviewer flow hardening.
|
||||
Reference in New Issue
Block a user