Data Model and State Flows
This page consolidates the old data-model draft, state-transition draft, SQL planning notes, and related OpenSpec constraints.
Core tables
The P0/v1.4.1 baseline uses eleven core tables:
usersrolesuser_rolesinspection_taskssamplessample_resultsexceptionsanalysis_jobsaudit_eventsapi_tokensuser_preferences
Data relationships
users ----< inspection_tasks ----< samples ----< sample_results
| | | \
| | | \-> exceptions
| | |
| | \-> analysis_jobs
|
\----< user_roles >---- roles
users ----< audit_events >---- auditable resources
users ----< api_tokens
users ---- user_preferences
exceptions uses resource_type + resource_id to reference tasks, samples, or result records.
audit_events also uses resource_type + resource_id so it can record events across tasks, samples, results, exceptions, and analysis jobs without coupling the audit log to one table.
Database implementation baseline
- target database:
MariaDB - charset:
utf8mb4 - primary key strategy:
BIGINT UNSIGNED AUTO_INCREMENT - time fields:
DATETIME - state fields:
VARCHAR(20) - JSON fields: used for result content and analysis job parameters / summaries
Governance baseline
v1.4.0 adds a baseline governance layer without changing the long-term Laravel ownership model:
X-Ocean-Actor-Idis the internal identity injection bridge.- SPA login uses database-backed bearer tokens stored in
api_tokens. users,roles, anduser_rolesremain the baseline identity/RBAC tables.- v1.4.1 user-facing workspace preferences are persisted in
user_preferencesand owned by a singleusersrow. - baseline seeded roles are
admin,inspector,analyst, andworker. - legacy payload identity fields remain accepted only as an attribution compatibility path.
audit_events.actor_sourcerecords whether attribution came fromrequest_headeror legacypayload.
user_preferences
user_preferences keeps user-specific workspace settings separate from global identity data:
user_idis unique and referencesusers.idlanguagestores the preferred UI locale, such aszh-Hansorendisplay_densitystores lightweight display preference, currentlycomfortableorcompactdefault_workspace_tabstores the preferred landing tab in the SPA workspacesettings_jsonis reserved for future per-user preference extensions without changing core identity semantics
The profile fields that identify a person remain in users (username, display_name, email, status). Role assignments remain in user_roles. Settings are not authorization data and must not be used for RBAC decisions.
State machines
inspection_tasks
States:
assignedin_progresssubmittedcompletedcancelled
assigned -> in_progress -> submitted -> completed
\ \
\----------------------------> cancelled
Current explicit P0 actions:
start:assigned -> in_progresssubmit:in_progress -> submitted
samples
States:
registeredreceivedtestingreviewedarchivedinvalid
registered -> received -> testing -> reviewed -> archived
\ \ \ \
\ \ \-----------> invalid
\------------\--------------------> invalid
Notes:
POST /api/samplesstarts atregistered- sample status is advanced by backend service rules in the current P0 phase
- explicit v1.0.0 P0 rule: creating a sample result moves
registeredorreceivedtotesting - explicit v1.0.0 P0 rule:
invalidandarchivedsamples cannot accept new result records and must return409 INVALID_STATE
sample_results
States:
draftsubmittedapprovedrejected
draft -> submitted -> approved
\
\-> rejected
Notes:
- result creation starts at
draft statusdrives the main current workflowreview_statusremains reserved for later expansion
exceptions
States:
openresolveddismissed
open -> resolved
\
\-> dismissed
Current P0 action:
resolve:open -> resolved
analysis_jobs
States:
queuedrunningsucceededfailedcancelled
queued -> running -> succeeded
| \
| \-> failed
\
\-> cancelled
Key retry rule:
- retrying a failed job must not revive the original record
- the original
failedrecord must remain in history - a retry creates a new
queuedrecord - each new
queuedrecord is handed to analysis workers through the Redis queue configured byANALYSIS_JOB_REDIS_QUEUE - Redis queue entries are handoff signals; the database record remains the durable state source
Initialization and migration principles
- Laravel migration and seeder are the long-term initialization path
- raw SQL drafts may remain reference material, but not the primary lifecycle path
- baseline roles should include
inspector,analyst,admin, andworker - baseline seeder should provide one idempotent core-chain example:
inspection_task + sample + sample_result + exception + analysis_job