{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://hwlab.pikastech.local/protocol/schema-drift-map.json",
  "title": "HWLAB Schema Drift Map",
  "version": 1,
  "sources": {
    "protocol": "protocol/schemas",
    "dbMigration": "internal/db/migrations/0001_cloud_core_skeleton.sql",
    "audit": "protocol/schemas/audit-event.schema.json",
    "evidenceChain": "protocol/evidence-chain.md",
    "evidence": "protocol/schemas/evidence-record.schema.json",
    "deployManifest": "deploy/deploy.yaml",
    "deploy": "deploy/deploy.schema.json"
  },
  "summary": {
    "aligned": [
      "projectId",
      "operationId",
      "auditId",
      "traceId",
      "evidenceId",
      "serviceId",
      "environment",
      "resourceId",
      "sessionId"
    ],
    "gapCount": 12,
    "notes": [
      "This map describes naming drift only and does not change any storage implementation.",
      "A field can be aligned at the protocol level while still requiring a DB migration rename or mapping layer before real persistence."
    ]
  },
  "entities": [
    {
      "name": "hardware_operation",
      "protocol": "protocol/schemas/hardware-operation.schema.json",
      "dbTable": "hardware_operations",
      "drift": [
        {
          "field": "operationId",
          "protocolField": "operationId",
          "dbColumn": "id",
          "status": "aligned"
        },
        {
          "field": "projectId",
          "protocolField": "projectId",
          "dbColumn": "project_id",
          "status": "aligned"
        },
        {
          "field": "gatewaySessionId",
          "protocolField": "gatewaySessionId",
          "dbColumn": "gateway_session_id",
          "status": "aligned"
        },
        {
          "field": "agentSessionId",
          "protocolField": "agentSessionId",
          "dbColumn": "agent_session_id",
          "status": "aligned"
        },
        {
          "field": "workerSessionId",
          "protocolField": "workerSessionId",
          "dbColumn": "worker_session_id",
          "status": "aligned"
        },
        {
          "field": "resourceId",
          "protocolField": "resourceId",
          "dbColumn": "operation_json.resourceId",
          "status": "gap",
          "gap": "The skeleton table has no dedicated resource column and currently stores the operation payload as opaque JSON."
        },
        {
          "field": "capabilityId",
          "protocolField": "capabilityId",
          "dbColumn": "operation_json.capabilityId",
          "status": "gap",
          "gap": "The skeleton table has no dedicated capability column and currently stores the operation payload as opaque JSON."
        },
        {
          "field": "requestedBy",
          "protocolField": "requestedBy",
          "dbColumn": "requested_by",
          "status": "aligned"
        },
        {
          "field": "status",
          "protocolField": "status",
          "dbColumn": "status",
          "status": "aligned"
        },
        {
          "field": "environment",
          "protocolField": "environment",
          "dbColumn": "operation_json.environment",
          "status": "gap",
          "gap": "The skeleton table does not carry a dedicated environment column for the operation row."
        },
        {
          "field": "requestedAt",
          "protocolField": "requestedAt",
          "dbColumn": "requested_at",
          "status": "aligned"
        },
        {
          "field": "updatedAt",
          "protocolField": "updatedAt",
          "dbColumn": "updated_at",
          "status": "aligned"
        }
      ]
    },
    {
      "name": "audit_event",
      "protocol": "protocol/schemas/audit-event.schema.json",
      "dbTable": "audit_events",
      "drift": [
        {
          "field": "auditId",
          "protocolField": "auditId",
          "dbColumn": "id",
          "status": "aligned"
        },
        {
          "field": "traceId",
          "protocolField": "traceId",
          "dbColumn": "request_id",
          "status": "gap",
          "gap": "The migration still uses request_id instead of traceId."
        },
        {
          "field": "actorType",
          "protocolField": "actorType",
          "dbColumn": "actor",
          "status": "gap",
          "gap": "The migration stores a single actor string instead of a typed actorType field."
        },
        {
          "field": "actorId",
          "protocolField": "actorId",
          "dbColumn": "source",
          "status": "gap",
          "gap": "The migration stores source instead of a stable actorId."
        },
        {
          "field": "action",
          "protocolField": "action",
          "dbColumn": "operation",
          "status": "gap",
          "gap": "The migration uses operation instead of the protocol action name."
        },
        {
          "field": "targetType",
          "protocolField": "targetType",
          "dbColumn": "target",
          "status": "gap",
          "gap": "The migration stores a single target string instead of targetType and targetId."
        },
        {
          "field": "targetId",
          "protocolField": "targetId",
          "dbColumn": "target",
          "status": "gap",
          "gap": "The migration stores a single target string instead of targetType and targetId."
        },
        {
          "field": "projectId",
          "protocolField": "projectId",
          "dbColumn": "event_json.projectId",
          "status": "aligned",
          "notes": "Present in protocol and fixtures, but not a dedicated migration column."
        },
        {
          "field": "gatewaySessionId",
          "protocolField": "gatewaySessionId",
          "dbColumn": "event_json.gatewaySessionId",
          "status": "aligned",
          "notes": "Optional correlation field in protocol; still payload-only in the skeleton."
        },
        {
          "field": "workerSessionId",
          "protocolField": "workerSessionId",
          "dbColumn": "event_json.workerSessionId",
          "status": "aligned",
          "notes": "Optional correlation field in protocol; still payload-only in the skeleton."
        },
        {
          "field": "operationId",
          "protocolField": "operationId",
          "dbColumn": "event_json.operationId",
          "status": "aligned",
          "notes": "Present in protocol and fixtures, but not a dedicated migration column."
        },
        {
          "field": "serviceId",
          "protocolField": "serviceId",
          "dbColumn": "event_json.serviceId",
          "status": "aligned",
          "notes": "Frozen service ID appears in payload only."
        },
        {
          "field": "environment",
          "protocolField": "environment",
          "dbColumn": "event_json.environment",
          "status": "aligned",
          "notes": "MVP environment is carried in payload only."
        },
        {
          "field": "occurredAt",
          "protocolField": "occurredAt",
          "dbColumn": "timestamp",
          "status": "gap",
          "gap": "The migration uses timestamp instead of occurredAt."
        }
      ]
    },
    {
      "name": "evidence_chain",
      "protocol": "protocol/evidence-chain.md",
      "dbTable": null,
      "drift": [
        {
          "field": "chainId",
          "protocolField": "chainId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "schemaVersion",
          "protocolField": "schemaVersion",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "scenario",
          "protocolField": "scenario",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "title",
          "protocolField": "title",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "projectId",
          "protocolField": "projectId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "operationId",
          "protocolField": "operationId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "traceId",
          "protocolField": "traceId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "environment",
          "protocolField": "environment",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "gatewaySessionId",
          "protocolField": "gatewaySessionId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "agentSessionId",
          "protocolField": "agentSessionId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "workerSessionId",
          "protocolField": "workerSessionId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "resourceId",
          "protocolField": "resourceId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "capabilityId",
          "protocolField": "capabilityId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "hardwareOperation",
          "protocolField": "hardwareOperation",
          "dbColumn": "protocol/schemas/hardware-operation.schema.json",
          "status": "aligned"
        },
        {
          "field": "auditEvents",
          "protocolField": "auditEvents",
          "dbColumn": "protocol/schemas/audit-event.schema.json",
          "status": "aligned"
        },
        {
          "field": "traceEvents",
          "protocolField": "traceEvents",
          "dbColumn": "protocol/schemas/trace-event.schema.json",
          "status": "aligned"
        },
        {
          "field": "evidenceRecords",
          "protocolField": "evidenceRecords",
          "dbColumn": "protocol/schemas/evidence-record.schema.json",
          "status": "aligned"
        }
      ]
    },
    {
      "name": "trace_event",
      "protocol": "protocol/schemas/trace-event.schema.json",
      "dbTable": "agent_trace_events",
      "drift": [
        {
          "field": "traceEventId",
          "protocolField": "traceEventId",
          "dbColumn": "id",
          "status": "aligned"
        },
        {
          "field": "traceId",
          "protocolField": "traceId",
          "dbColumn": "trace_id",
          "status": "aligned"
        },
        {
          "field": "projectId",
          "protocolField": "projectId",
          "dbColumn": "event_json.projectId",
          "status": "aligned",
          "notes": "Project scope is payload-only in the skeleton."
        },
        {
          "field": "agentSessionId",
          "protocolField": "agentSessionId",
          "dbColumn": "agent_session_id",
          "status": "aligned"
        },
        {
          "field": "workerSessionId",
          "protocolField": "workerSessionId",
          "dbColumn": "worker_session_id",
          "status": "aligned"
        },
        {
          "field": "operationId",
          "protocolField": "operationId",
          "dbColumn": "event_json.operationId",
          "status": "aligned",
          "notes": "Operation scope is payload-only in the skeleton."
        },
        {
          "field": "serviceId",
          "protocolField": "serviceId",
          "dbColumn": "event_json.serviceId",
          "status": "aligned"
        },
        {
          "field": "level",
          "protocolField": "level",
          "dbColumn": "level",
          "status": "aligned"
        },
        {
          "field": "message",
          "protocolField": "message",
          "dbColumn": "message",
          "status": "aligned"
        },
        {
          "field": "environment",
          "protocolField": "environment",
          "dbColumn": "event_json.environment",
          "status": "aligned"
        },
        {
          "field": "occurredAt",
          "protocolField": "occurredAt",
          "dbColumn": "occurred_at",
          "status": "aligned"
        }
      ]
    },
    {
      "name": "evidence_record",
      "protocol": "protocol/schemas/evidence-record.schema.json",
      "dbTable": "evidence_records",
      "drift": [
        {
          "field": "evidenceId",
          "protocolField": "evidenceId",
          "dbColumn": "id",
          "status": "aligned"
        },
        {
          "field": "projectId",
          "protocolField": "projectId",
          "dbColumn": "project_id",
          "status": "aligned"
        },
        {
          "field": "operationId",
          "protocolField": "operationId",
          "dbColumn": "operation_id",
          "status": "aligned"
        },
        {
          "field": "agentSessionId",
          "protocolField": "agentSessionId",
          "dbColumn": "metadata_json.agentSessionId",
          "status": "aligned",
          "notes": "Optional in protocol and currently payload-only in the skeleton."
        },
        {
          "field": "workerSessionId",
          "protocolField": "workerSessionId",
          "dbColumn": "metadata_json.workerSessionId",
          "status": "aligned",
          "notes": "Optional in protocol and currently payload-only in the skeleton."
        },
        {
          "field": "kind",
          "protocolField": "kind",
          "dbColumn": "evidence_type",
          "status": "gap",
          "gap": "The migration uses evidence_type, but the protocol requires kind."
        },
        {
          "field": "uri",
          "protocolField": "uri",
          "dbColumn": "uri",
          "status": "aligned"
        },
        {
          "field": "sha256",
          "protocolField": "sha256",
          "dbColumn": "metadata_json.sha256",
          "status": "gap",
          "gap": "The migration has no dedicated sha256 column."
        },
        {
          "field": "serviceId",
          "protocolField": "serviceId",
          "dbColumn": "metadata_json.serviceId",
          "status": "aligned",
          "notes": "Frozen service ID is present in protocol and fixtures."
        },
        {
          "field": "environment",
          "protocolField": "environment",
          "dbColumn": "metadata_json.environment",
          "status": "aligned"
        },
        {
          "field": "createdAt",
          "protocolField": "createdAt",
          "dbColumn": "created_at",
          "status": "aligned"
        }
      ]
    },
    {
      "name": "deploy_manifest",
      "protocol": "deploy/deploy.schema.json",
      "dbTable": null,
      "drift": [
        {
          "field": "manifestVersion",
          "protocolField": "manifestVersion",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "environment",
          "protocolField": "environment",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "commitId",
          "protocolField": "commitId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "profiles.dev.name",
          "protocolField": "profiles.dev.name",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "services[].serviceId",
          "protocolField": "services[].serviceId",
          "dbColumn": "n/a",
          "status": "aligned"
        },
        {
          "field": "services[].replicas",
          "protocolField": "services[].replicas",
          "dbColumn": "n/a",
          "status": "aligned"
        }
      ]
    }
  ],
  "requiredKeyCoverage": {
    "projectId": {
      "protocol": [
        "hardware-operation",
        "audit-event",
        "trace-event",
        "evidence-record",
        "deploy-manifest"
      ],
      "dbMigration": [
        "projects.project_id",
        "gateway_sessions.project_id",
        "box_resources.project_id",
        "wiring_configs.project_id",
        "hardware_operations.project_id",
        "agent_sessions.project_id",
        "audit_events.event_json.projectId",
        "agent_trace_events.event_json.projectId",
        "evidence_records.project_id"
      ]
    },
    "operationId": {
      "protocol": [
        "hardware-operation",
        "audit-event",
        "trace-event",
        "evidence-record"
      ],
      "dbMigration": [
        "hardware_operations.id",
        "audit_events.event_json.operationId",
        "agent_trace_events.event_json.operationId",
        "evidence_records.operation_id"
      ]
    },
    "auditId": {
      "protocol": [
        "audit-event",
        "evidence-record.metadata.producedByAuditId",
        "trace-event.metadata.auditId"
      ],
      "dbMigration": [
        "audit_events.id",
        "audit_events.event_json.auditId",
        "agent_trace_events.event_json.auditId",
        "evidence_records.metadata_json.producedByAuditId"
      ]
    },
    "traceId": {
      "protocol": [
        "audit-event",
        "trace-event",
        "evidence-record.metadata.traceId"
      ],
      "dbMigration": [
        "audit_events.request_id",
        "agent_trace_events.trace_id",
        "evidence_records.metadata_json.traceId"
      ]
    },
    "evidenceId": {
      "protocol": [
        "evidence-record",
        "audit-event.targetId",
        "trace-event.metadata.evidenceId"
      ],
      "dbMigration": [
        "evidence_records.id",
        "audit_events.event_json.evidenceId",
        "agent_trace_events.event_json.evidenceId"
      ]
    },
    "serviceId": {
      "protocol": [
        "audit-event",
        "trace-event",
        "evidence-record",
        "deploy-manifest.services[].serviceId"
      ],
      "dbMigration": [
        "audit_events.event_json.serviceId",
        "agent_trace_events.event_json.serviceId",
        "evidence_records.metadata_json.serviceId",
        "gateway_sessions.gateway_service_id"
      ]
    },
    "environment": {
      "protocol": [
        "hardware-operation",
        "audit-event",
        "trace-event",
        "evidence-record",
        "deploy-manifest"
      ],
      "dbMigration": [
        "hardware_operations.operation_json.environment",
        "audit_events.event_json.environment",
        "agent_trace_events.event_json.environment",
        "evidence_records.metadata_json.environment"
      ]
    },
    "resourceId": {
      "protocol": [
        "hardware-operation",
        "audit-event.metadata.resourceId",
        "evidence-record.metadata.resourceId"
      ],
      "dbMigration": [
        "box_resources.id",
        "hardware_operations.operation_json.resourceId",
        "audit_events.event_json.resourceId",
        "evidence_records.metadata_json.resourceId"
      ]
    },
    "sessionId": {
      "protocol": [
        "gatewaySessionId",
        "agentSessionId",
        "workerSessionId"
      ],
      "dbMigration": [
        "gateway_sessions.id",
        "agent_sessions.id",
        "worker_sessions.id",
        "hardware_operations.gateway_session_id",
        "agent_trace_events.agent_session_id",
        "agent_trace_events.worker_session_id",
        "evidence_records.metadata_json.agentSessionId",
        "evidence_records.metadata_json.workerSessionId"
      ]
    }
  },
  "missingToFixBeforeRealPersistence": [
    {
      "field": "audit_events.request_id",
      "reason": "Must be renamed or mapped to traceId before audit rows are treated as protocol-shaped records."
    },
    {
      "field": "audit_events.actor",
      "reason": "Must be split into actorType and actorId or mapped through a persisted envelope."
    },
    {
      "field": "audit_events.source",
      "reason": "Must be renamed or mapped to actorId."
    },
    {
      "field": "audit_events.operation",
      "reason": "Must be renamed or mapped to action."
    },
    {
      "field": "audit_events.target",
      "reason": "Must be split into targetType and targetId."
    },
    {
      "field": "audit_events.timestamp",
      "reason": "Must be renamed or mapped to occurredAt."
    },
    {
      "field": "evidence_records.evidence_type",
      "reason": "Must be renamed or mapped to kind."
    },
    {
      "field": "evidence_records.metadata_json.sha256",
      "reason": "Must become a first-class persisted field or a tested mapping target."
    },
    {
      "field": "hardware_operations.operation_json.resourceId",
      "reason": "Must be exposed through a real column or tested extraction path."
    },
    {
      "field": "hardware_operations.operation_json.capabilityId",
      "reason": "Must be exposed through a real column or tested extraction path."
    }
  ]
}
