diff --git a/goal-state.md b/goal-state.md index fab49a8..83e9e36 100644 --- a/goal-state.md +++ b/goal-state.md @@ -155,13 +155,17 @@ The path is the **navigation identifier**: it tells you where the equipment live The plan commits to a **multi-identifier model** — the v2 implementation design surfaced that production usage requires more than UUID + path. Every equipment instance carries **five identifiers**, all exposed as OPC UA properties on the equipment node so external systems can resolve by their preferred identifier without a sidecar service: -| Identifier | Required? | Uniqueness | Mutable? | Purpose | -|---|---|---|---|---| -| **EquipmentUuid** | Yes | Fleet-wide | **Immutable** | Downstream events, canonical model joins, cross-system lineage. RFC 4122 UUIDv4 (random). Not derived from the path. | -| **EquipmentId** | Yes | Within cluster | **Immutable after publish** | Internal logical key for cross-generation config diffs. Not user-facing. | -| **MachineCode** | Yes | Within cluster | Mutable (rename tracked) | **Operator-facing colloquial name** (e.g., `machine_001`). This is what operators say on the radio and write in runbooks — UUID and path are not mnemonic enough for daily operations. Surfaced prominently in Admin UI alongside ZTag. | -| **ZTag** | Optional | Fleet-wide | Mutable (re-assigned by ERP) | **ERP equipment identifier.** Primary identifier for browsing in Admin UI per operational request. | -| **SAPID** | Optional | Fleet-wide | Mutable (re-assigned by SAP PM) | **SAP PM equipment identifier.** Required for maintenance system join. | +| Identifier | Required? | Uniqueness | Mutable? | Who sets it? | Purpose | +|---|---|---|---|---|---| +| **EquipmentUuid** | Yes | Fleet-wide | **Immutable** | System-generated (UUIDv4) | Downstream events, canonical model joins, cross-system lineage. Not derived from the path. | +| **EquipmentId** | Yes | Within cluster | **Immutable** | **System-generated** (`'EQ-' + first 12 hex chars of EquipmentUuid`) | Internal logical key for cross-generation config diffs. Never operator-supplied, never editable, never present in CSV imports. System-generation eliminates the corruption path where operator typos or bulk-import renames would mint duplicate equipment identities and permanently split downstream UUID-keyed lineage. | +| **MachineCode** | Yes | Within cluster | Mutable (rename tracked) | **Operator-set** | **Operator-facing colloquial name** (e.g., `machine_001`). What operators say on the radio and write in runbooks. Surfaced prominently in Admin UI alongside ZTag. | +| **ZTag** | Optional | Fleet-wide | Mutable (re-assigned by ERP) | **Operator-set** (sourced from ERP) | **ERP equipment identifier.** Primary identifier for browsing in Admin UI per operational request. Fleet-wide uniqueness enforced via `ExternalIdReservation` table outside generation versioning (see below). | +| **SAPID** | Optional | Fleet-wide | Mutable (re-assigned by SAP PM) | **Operator-set** (sourced from SAP PM) | **SAP PM equipment identifier.** Required for maintenance system join. Fleet-wide uniqueness enforced via `ExternalIdReservation` table (see below). | + +**Three operator-set fields** (MachineCode, ZTag, SAPID), **two system-generated** (EquipmentUuid, EquipmentId). CSV imports match by `EquipmentUuid` for updates; rows without UUID create new equipment with system-generated identifiers. + +**`ExternalIdReservation` table — fleet-wide uniqueness for ZTag and SAPID across rollback and re-enable.** Fleet-wide uniqueness for external identifiers cannot be expressed within generation-versioned tables because old generations and disabled equipment can still hold the same values — rollback or re-enable would silently reintroduce duplicates that corrupt downstream ERP/SAP joins. A dedicated `ExternalIdReservation` table sits **outside generation versioning**; `sp_PublishGeneration` reserves IDs atomically at publish; FleetAdmin-only `sp_ReleaseExternalIdReservation` (audit-logged, requires reason) is the only path to free a value for reuse. This is a precedent: some cross-generation invariants need their own non-versioned tables. When ACL design is scoped (see OtOpcUa → Authorization model), check whether any ACL grant has a similar rollback-reuse hazard. **Path** (the 5-level UNS hierarchy address) is a **sixth** identifier but is **not** stored on the equipment node as a flat property — it is the node's **browse path** by construction. Path can change (equipment moves, area renamed); UUID and EquipmentId cannot.