# Data Model And Migration Boundaries ## Naming Model - Internal canonical naming uses `Machine` and `Part`. - API/UI compatibility keeps `asset` and `component` terminology where already exposed. - In domain aliases: - `type Asset = Machine` - `type Component = Part` ## Core Persistence Areas - Asset registry: `machines` - Component registry: `parts` - Installations: `installations` - Observations: `observations` - Timeline: `timeline_events` - Failures: `failure_events` - Asset firmware state: `machine_firmware_states` - Asset sensor readings: `machine_sensor_readings` - Component history (canonical): `component_change_events`, `component_state_snapshots` - Asset history (canonical): `asset_change_events`, `asset_state_snapshots` - History jobs/audit: `history_recompute_jobs`, `history_admin_audit` ## Sensor Readings `machine_sensor_readings` is a last-known-value projection for hardware sensors per asset. - Primary key: `(machine_id, sensor_type, sensor_name)`. - `sensor_type` is one of: `fan`, `power`, `temperature`, `other`. - `value_json` stores type-specific numeric readings (see JSON import contract). - Updated on every ingest — not history-backed (no events, no snapshots). - `collected_at` reflects the source observation time, not ingest time. Out of active model: - `projects` is legacy and not used by runtime handlers/contracts. ## Migration Policy - Migrations are stored in `migrations/`. - Schema migrations are applied at startup. - Legacy entities removed in migration `0016_remove_legacy_features` remain out of active scope. - History architecture was introduced in migration `0017_history_core`. - Timeline UI/filter denormalized facets were added in migration `0018_timeline_ui_facets`. - Installation slot/location (`installations.slot_name`) was added in migration `0019_installation_slot_name`. - History-first `component_type` projection/storage backfill was introduced in migration `0020_component_type_history_first`. ## Canonical State Model (History-First) - `parts` and `machines` are current-state projections optimized for reads. - `component_change_events` / `asset_change_events` are canonical state-change logs. - `component_state_snapshots` / `asset_state_snapshots` store full after-state snapshots for each state-changing event. - `timeline_events`, `installations`, `failure_events`, and `machine_firmware_states` are projections and may be rebuilt from history. - `observations` is a raw ingest trace/audit store and must not be used as source-of-truth for current component/asset state in runtime UI/API flows. - `parts.component_type` and `installations.slot_name` are the runtime-readable projections for component type and current slot/location (not `observations.details`). ## History Event Storage Rules - History events are versioned per entity (`version` is monotonic per component/asset). - State-changing writes persist: 1. change event 2. after-state snapshot 3. projection updates (`parts` / `machines` and related projections) 4. timeline projection rows - History event deletion is soft-delete (`is_deleted`) for normal user/admin flow. - Hard restore is destructive for future history rows and is audited in `history_admin_audit`. ## Timeline Projection Linkage `timeline_events` includes logical linkage back to history: - `logical_entity_type` - `logical_event_id` - `correlation_id` - `is_deleted` This linkage is required for recompute, rollback/delete propagation, and paired asset/component timeline rendering. `timeline_events` also stores denormalized UI/filter facets (projection-only, rebuildable from history + registry state), including: - `source_type` - `visual_action` - `component_serial` - `component_model` (used as v1 `part_number`) - `asset_serial` - `asset_name_cached` - `slot_name` - `device_name` - `changed_fields_json` These fields are not canonical state and must be treated as timeline projection helpers only. ## Installation Slot (Current Components UI) - `installations.slot_name` is the canonical editable slot/location for a component attached to a server. - Asset page `Current Components` bulk actions (`Add / Edit / Remove`) read/write slot through history-driven component installation state (`installation.slot_name` in component snapshots) and synchronize projection `installations.slot_name`. - Runtime invariant (app-level validation): - for the same `machine_id`, open installations (`removed_at IS NULL`) must not share the same non-empty `slot_name`. - Projection-update nuance: - slot uniqueness validation must only block writes when `slot_name` is actually changing for the installation row (status-only edits must not fail because of pre-existing duplicate slot data).