-- HWLAB L1 Cloud Core schema skeleton.
-- This file is source schema only; applying it to DEV/PROD is a separate
-- operator action and is not performed by source tests or smokes.

CREATE TABLE IF NOT EXISTS projects (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL DEFAULT '',
  status TEXT NOT NULL DEFAULT 'pending',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS users (
  id TEXT PRIMARY KEY,
  username TEXT NOT NULL UNIQUE,
  display_name TEXT NOT NULL DEFAULT '',
  role TEXT NOT NULL CHECK (role IN ('admin', 'user')),
  status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
  password_hash TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS user_sessions (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL REFERENCES users(id),
  session_token_hash TEXT NOT NULL UNIQUE,
  created_at TEXT NOT NULL,
  last_seen_at TEXT NOT NULL,
  expires_at TEXT NOT NULL,
  revoked_at TEXT
);

CREATE TABLE IF NOT EXISTS gateway_sessions (
  id TEXT PRIMARY KEY,
  project_id TEXT,
  gateway_service_id TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'pending',
  started_at TEXT,
  ended_at TEXT,
  gateway_session_json TEXT NOT NULL DEFAULT '{}'
);

CREATE TABLE IF NOT EXISTS box_resources (
  id TEXT PRIMARY KEY,
  project_id TEXT,
  gateway_session_id TEXT,
  resource_state TEXT NOT NULL DEFAULT 'available',
  labels_json TEXT NOT NULL DEFAULT '{}',
  resource_json TEXT NOT NULL DEFAULT '{}',
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS box_capabilities (
  id TEXT PRIMARY KEY,
  box_resource_id TEXT NOT NULL,
  capability_type TEXT NOT NULL,
  capability_json TEXT NOT NULL DEFAULT '{}',
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS wiring_configs (
  id TEXT PRIMARY KEY,
  project_id TEXT,
  wiring_json TEXT NOT NULL DEFAULT '{}',
  status TEXT NOT NULL DEFAULT 'pending',
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS patch_panel_status (
  id TEXT PRIMARY KEY,
  gateway_session_id TEXT,
  wiring_config_id TEXT,
  status_json TEXT NOT NULL DEFAULT '{}',
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hardware_operations (
  id TEXT PRIMARY KEY,
  project_id TEXT,
  requested_by TEXT,
  operation_type TEXT NOT NULL,
  operation_json TEXT NOT NULL DEFAULT '{}',
  status TEXT NOT NULL DEFAULT 'requested',
  requested_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS audit_events (
  id TEXT PRIMARY KEY,
  request_id TEXT NOT NULL,
  actor TEXT NOT NULL,
  source TEXT NOT NULL,
  operation TEXT NOT NULL,
  target TEXT NOT NULL,
  result TEXT NOT NULL,
  timestamp TEXT NOT NULL,
  event_json TEXT NOT NULL DEFAULT '{}'
);

CREATE TABLE IF NOT EXISTS agent_sessions (
  id TEXT PRIMARY KEY,
  project_id TEXT,
  agent_id TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'pending',
  started_at TEXT,
  ended_at TEXT,
  owner_user_id TEXT REFERENCES users(id),
  conversation_id TEXT,
  thread_id TEXT,
  last_trace_id TEXT,
  session_json TEXT NOT NULL DEFAULT '{}',
  updated_at TEXT
);

ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS owner_user_id TEXT REFERENCES users(id);
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS conversation_id TEXT;
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS thread_id TEXT;
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS last_trace_id TEXT;
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS session_json TEXT NOT NULL DEFAULT '{}';
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS updated_at TEXT;
CREATE INDEX IF NOT EXISTS idx_agent_sessions_owner ON agent_sessions(owner_user_id);
CREATE INDEX IF NOT EXISTS idx_agent_sessions_conversation ON agent_sessions(conversation_id);
CREATE INDEX IF NOT EXISTS idx_agent_sessions_active_updated ON agent_sessions(updated_at DESC, id DESC) WHERE status <> 'archived';
CREATE INDEX IF NOT EXISTS idx_agent_sessions_owner_active_updated ON agent_sessions(owner_user_id, updated_at DESC, id DESC) WHERE status <> 'archived';

CREATE TABLE IF NOT EXISTS account_workspaces (
  id TEXT PRIMARY KEY,
  owner_user_id TEXT NOT NULL REFERENCES users(id),
  project_id TEXT NOT NULL,
  name TEXT NOT NULL DEFAULT 'Default Workbench',
  status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'archived')),
  is_default BOOLEAN NOT NULL DEFAULT true,
  selected_conversation_id TEXT,
  selected_agent_session_id TEXT,
  active_trace_id TEXT,
  provider_profile TEXT,
  workspace_json TEXT NOT NULL DEFAULT '{}',
  revision INTEGER NOT NULL DEFAULT 1,
  updated_by_session_id TEXT,
  updated_by_client TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_account_workspaces_default ON account_workspaces(owner_user_id, project_id) WHERE is_default = TRUE AND status = 'active';
CREATE INDEX IF NOT EXISTS idx_account_workspaces_owner ON account_workspaces(owner_user_id, project_id, updated_at DESC);

CREATE TABLE IF NOT EXISTS worker_sessions (
  id TEXT PRIMARY KEY,
  agent_session_id TEXT,
  worker_id TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'pending',
  started_at TEXT,
  ended_at TEXT
);

CREATE TABLE IF NOT EXISTS agent_trace_events (
  id TEXT PRIMARY KEY,
  trace_id TEXT NOT NULL,
  agent_session_id TEXT,
  worker_session_id TEXT,
  level TEXT NOT NULL DEFAULT 'info',
  message TEXT NOT NULL DEFAULT '',
  event_json TEXT NOT NULL DEFAULT '{}',
  occurred_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_agent_trace_events_trace_order ON agent_trace_events(trace_id, occurred_at, id);
CREATE INDEX IF NOT EXISTS idx_agent_trace_events_session_order ON agent_trace_events(agent_session_id, occurred_at, id);
CREATE INDEX IF NOT EXISTS idx_agent_trace_events_worker_order ON agent_trace_events(worker_session_id, occurred_at, id);

CREATE TABLE IF NOT EXISTS workbench_projection_state (
  trace_id TEXT PRIMARY KEY,
  session_id TEXT,
  conversation_id TEXT,
  thread_id TEXT,
  run_id TEXT NOT NULL,
  command_id TEXT NOT NULL,
  last_agentrun_seq INTEGER NOT NULL DEFAULT 0,
  last_projected_seq INTEGER NOT NULL DEFAULT 0,
  upstream_latest_seq INTEGER,
  projection_status TEXT NOT NULL DEFAULT 'projecting',
  projection_health TEXT NOT NULL DEFAULT 'healthy',
  result_sync_state TEXT NOT NULL DEFAULT 'not_started',
  last_projected_at TEXT,
  last_result_sync_at TEXT,
  last_error_code TEXT,
  last_error_message TEXT,
  failure_count INTEGER NOT NULL DEFAULT 0,
  next_retry_at TEXT,
  projection_json TEXT NOT NULL DEFAULT '{}',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_workbench_projection_state_status_retry_updated ON workbench_projection_state(projection_status, next_retry_at, updated_at);
CREATE INDEX IF NOT EXISTS idx_workbench_projection_state_run_command ON workbench_projection_state(run_id, command_id);
CREATE INDEX IF NOT EXISTS idx_workbench_projection_state_session_updated ON workbench_projection_state(session_id, updated_at DESC);

CREATE TABLE IF NOT EXISTS workbench_sessions (
  session_id TEXT PRIMARY KEY,
  owner_user_id TEXT REFERENCES users(id),
  project_id TEXT,
  conversation_id TEXT,
  thread_id TEXT,
  status TEXT NOT NULL DEFAULT 'unknown',
  last_trace_id TEXT,
  projected_seq INTEGER NOT NULL DEFAULT 0,
  source_seq INTEGER NOT NULL DEFAULT 0,
  source_event_id TEXT,
  terminal BOOLEAN NOT NULL DEFAULT false,
  sealed BOOLEAN NOT NULL DEFAULT false,
  session_json TEXT NOT NULL DEFAULT '{}',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_workbench_sessions_owner_updated ON workbench_sessions(owner_user_id, updated_at DESC, session_id);
CREATE INDEX IF NOT EXISTS idx_workbench_sessions_status_updated ON workbench_sessions(status, updated_at DESC, session_id);

CREATE TABLE IF NOT EXISTS workbench_messages (
  message_id TEXT PRIMARY KEY,
  session_id TEXT NOT NULL,
  turn_id TEXT,
  trace_id TEXT,
  role TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'unknown',
  projected_seq INTEGER NOT NULL DEFAULT 0,
  source_seq INTEGER NOT NULL DEFAULT 0,
  source_event_id TEXT,
  terminal BOOLEAN NOT NULL DEFAULT false,
  sealed BOOLEAN NOT NULL DEFAULT false,
  message_json TEXT NOT NULL DEFAULT '{}',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_workbench_messages_session_updated ON workbench_messages(session_id, updated_at ASC, message_id);
CREATE INDEX IF NOT EXISTS idx_workbench_messages_trace ON workbench_messages(trace_id, message_id);
CREATE INDEX IF NOT EXISTS idx_workbench_messages_turn ON workbench_messages(turn_id, message_id);

CREATE TABLE IF NOT EXISTS workbench_parts (
  part_id TEXT PRIMARY KEY,
  message_id TEXT NOT NULL,
  session_id TEXT NOT NULL,
  turn_id TEXT,
  trace_id TEXT,
  part_index INTEGER NOT NULL DEFAULT 0,
  part_type TEXT NOT NULL DEFAULT 'text',
  status TEXT NOT NULL DEFAULT 'unknown',
  projected_seq INTEGER NOT NULL DEFAULT 0,
  source_seq INTEGER NOT NULL DEFAULT 0,
  source_event_id TEXT,
  terminal BOOLEAN NOT NULL DEFAULT false,
  sealed BOOLEAN NOT NULL DEFAULT false,
  part_json TEXT NOT NULL DEFAULT '{}',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_workbench_parts_message_index ON workbench_parts(message_id, part_index, part_id);
CREATE INDEX IF NOT EXISTS idx_workbench_parts_session_trace ON workbench_parts(session_id, trace_id, part_index);

CREATE TABLE IF NOT EXISTS workbench_turns (
  turn_id TEXT PRIMARY KEY,
  session_id TEXT NOT NULL,
  trace_id TEXT,
  message_id TEXT,
  status TEXT NOT NULL DEFAULT 'unknown',
  projected_seq INTEGER NOT NULL DEFAULT 0,
  source_seq INTEGER NOT NULL DEFAULT 0,
  source_event_id TEXT,
  terminal BOOLEAN NOT NULL DEFAULT false,
  sealed BOOLEAN NOT NULL DEFAULT false,
  final_response_json TEXT NOT NULL DEFAULT 'null',
  diagnostic_json TEXT NOT NULL DEFAULT '{}',
  turn_json TEXT NOT NULL DEFAULT '{}',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_workbench_turns_session_updated ON workbench_turns(session_id, updated_at DESC, turn_id);
CREATE INDEX IF NOT EXISTS idx_workbench_turns_trace ON workbench_turns(trace_id, turn_id);

CREATE TABLE IF NOT EXISTS workbench_trace_events (
  id TEXT PRIMARY KEY,
  trace_id TEXT NOT NULL,
  session_id TEXT,
  turn_id TEXT,
  message_id TEXT,
  source_seq INTEGER NOT NULL DEFAULT 0,
  source_event_id TEXT,
  projected_seq INTEGER NOT NULL DEFAULT 0,
  event_type TEXT NOT NULL DEFAULT 'event',
  terminal BOOLEAN NOT NULL DEFAULT false,
  sealed BOOLEAN NOT NULL DEFAULT false,
  event_json TEXT NOT NULL DEFAULT '{}',
  occurred_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_workbench_trace_events_trace_seq ON workbench_trace_events(trace_id, source_seq, projected_seq, id);
CREATE INDEX IF NOT EXISTS idx_workbench_trace_events_session_seq ON workbench_trace_events(session_id, source_seq, projected_seq, id);

WITH duplicate_trace_source_events AS (
  SELECT
    id,
    ROW_NUMBER() OVER (
      PARTITION BY trace_id, source_event_id
      ORDER BY occurred_at, id
    ) AS duplicate_rank
  FROM workbench_trace_events
  WHERE trace_id IS NOT NULL AND source_event_id IS NOT NULL
), trace_source_event_rewrites AS (
  SELECT
    id,
    'workbench_trace_events:' || id AS stable_source_event_id
  FROM duplicate_trace_source_events
  WHERE duplicate_rank > 1
)
UPDATE workbench_trace_events AS target
SET
  source_event_id = trace_source_event_rewrites.stable_source_event_id,
  updated_at = COALESCE(NULLIF(target.updated_at, ''), target.occurred_at, CURRENT_TIMESTAMP::text)
FROM trace_source_event_rewrites
WHERE target.id = trace_source_event_rewrites.id
  AND target.source_event_id IS DISTINCT FROM trace_source_event_rewrites.stable_source_event_id;

WITH trace_event_projected_seq_conflicts AS (
  SELECT trace_id
  FROM workbench_trace_events
  WHERE trace_id IS NOT NULL
  GROUP BY trace_id
  HAVING COUNT(*) <> COUNT(DISTINCT projected_seq)
), trace_event_resequence AS (
  SELECT
    workbench_trace_events.id,
    (ROW_NUMBER() OVER (
      PARTITION BY workbench_trace_events.trace_id
      ORDER BY
        CASE WHEN workbench_trace_events.projected_seq > 0 THEN 0 ELSE 1 END,
        workbench_trace_events.projected_seq,
        workbench_trace_events.occurred_at,
        workbench_trace_events.id
    ))::integer AS durable_projected_seq
  FROM workbench_trace_events
  INNER JOIN trace_event_projected_seq_conflicts
    ON trace_event_projected_seq_conflicts.trace_id = workbench_trace_events.trace_id
)
UPDATE workbench_trace_events AS target
SET
  projected_seq = trace_event_resequence.durable_projected_seq,
  source_seq = CASE
    WHEN target.source_seq > 0 THEN target.source_seq
    ELSE trace_event_resequence.durable_projected_seq
  END,
  updated_at = COALESCE(NULLIF(target.updated_at, ''), target.occurred_at, CURRENT_TIMESTAMP::text)
FROM trace_event_resequence
WHERE target.id = trace_event_resequence.id
  AND (
    target.projected_seq IS DISTINCT FROM trace_event_resequence.durable_projected_seq
    OR target.source_seq <= 0
  );

DROP INDEX IF EXISTS idx_workbench_trace_events_trace_source_event;
CREATE UNIQUE INDEX idx_workbench_trace_events_trace_source_event ON workbench_trace_events(trace_id, source_event_id) WHERE source_event_id IS NOT NULL;
DROP INDEX IF EXISTS idx_workbench_trace_events_trace_projected_seq;
CREATE UNIQUE INDEX idx_workbench_trace_events_trace_projected_seq ON workbench_trace_events(trace_id, projected_seq);

CREATE TABLE IF NOT EXISTS workbench_projection_checkpoints (
  trace_id TEXT PRIMARY KEY,
  session_id TEXT,
  turn_id TEXT,
  run_id TEXT,
  command_id TEXT,
  projected_seq INTEGER NOT NULL DEFAULT 0,
  source_seq INTEGER NOT NULL DEFAULT 0,
  source_event_id TEXT,
  projection_status TEXT NOT NULL DEFAULT 'projecting',
  projection_health TEXT NOT NULL DEFAULT 'healthy',
  terminal BOOLEAN NOT NULL DEFAULT false,
  sealed BOOLEAN NOT NULL DEFAULT false,
  diagnostic_json TEXT NOT NULL DEFAULT '{}',
  checkpoint_json TEXT NOT NULL DEFAULT '{}',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_workbench_projection_checkpoints_status_updated ON workbench_projection_checkpoints(projection_status, updated_at DESC, trace_id);
CREATE INDEX IF NOT EXISTS idx_workbench_projection_checkpoints_session_updated ON workbench_projection_checkpoints(session_id, updated_at DESC, trace_id);

CREATE TABLE IF NOT EXISTS workbench_projection_outbox (
  outbox_seq BIGSERIAL PRIMARY KEY,
  trace_id TEXT NOT NULL,
  session_id TEXT,
  turn_id TEXT,
  message_id TEXT,
  projected_seq INTEGER NOT NULL DEFAULT 0,
  source_seq INTEGER NOT NULL DEFAULT 0,
  source_event_id TEXT,
  commit_type TEXT NOT NULL DEFAULT 'event',
  terminal BOOLEAN NOT NULL DEFAULT false,
  sealed BOOLEAN NOT NULL DEFAULT false,
  payload_json TEXT NOT NULL DEFAULT '{}',
  created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_workbench_projection_outbox_trace_seq ON workbench_projection_outbox(trace_id, outbox_seq);
CREATE INDEX IF NOT EXISTS idx_workbench_projection_outbox_session_seq ON workbench_projection_outbox(session_id, outbox_seq);
CREATE INDEX IF NOT EXISTS idx_workbench_projection_outbox_after_seq ON workbench_projection_outbox(outbox_seq);

INSERT INTO workbench_sessions (
  session_id,
  owner_user_id,
  project_id,
  conversation_id,
  thread_id,
  status,
  last_trace_id,
  projected_seq,
  source_seq,
  source_event_id,
  terminal,
  sealed,
  session_json,
  created_at,
  updated_at
)
SELECT
  id,
  owner_user_id,
  project_id,
  conversation_id,
  thread_id,
  status,
  last_trace_id,
  0,
  0,
  id,
  status IN ('completed', 'failed', 'canceled', 'cancelled', 'archived'),
  status IN ('completed', 'failed', 'canceled', 'cancelled', 'archived') AND ended_at IS NOT NULL,
  COALESCE(NULLIF(session_json, ''), '{}'),
  COALESCE(started_at, updated_at, CURRENT_TIMESTAMP::text),
  COALESCE(updated_at, ended_at, started_at, CURRENT_TIMESTAMP::text)
FROM agent_sessions
WHERE id IS NOT NULL
ON CONFLICT (session_id) DO NOTHING;

INSERT INTO workbench_trace_events (
  id,
  trace_id,
  session_id,
  turn_id,
  message_id,
  source_seq,
  source_event_id,
  projected_seq,
  event_type,
  terminal,
  sealed,
  event_json,
  occurred_at,
  updated_at
)
SELECT
  source_trace_events.id,
  source_trace_events.trace_id,
  source_trace_events.agent_session_id,
  source_trace_events.trace_id,
  NULL,
  source_trace_events.durable_projection_seq,
  source_trace_events.id,
  source_trace_events.durable_projection_seq,
  'agent_trace_event',
  false,
  false,
  COALESCE(NULLIF(source_trace_events.event_json, ''), '{}'),
  source_trace_events.occurred_at,
  source_trace_events.occurred_at
FROM (
  SELECT
    agent_trace_events.*,
    (ROW_NUMBER() OVER (PARTITION BY trace_id ORDER BY occurred_at, id))::integer AS durable_projection_seq
  FROM agent_trace_events
  WHERE id IS NOT NULL AND trace_id IS NOT NULL
) AS source_trace_events
ON CONFLICT DO NOTHING;

INSERT INTO workbench_turns (
  turn_id,
  session_id,
  trace_id,
  message_id,
  status,
  projected_seq,
  source_seq,
  source_event_id,
  terminal,
  sealed,
  final_response_json,
  diagnostic_json,
  turn_json,
  created_at,
  updated_at
)
SELECT
  trace_id,
  COALESCE(session_id, trace_id),
  trace_id,
  NULL,
  projection_status,
  last_projected_seq,
  last_agentrun_seq,
  trace_id || ':' || last_agentrun_seq::text,
  projection_status IN ('caught_up', 'terminal'),
  projection_status = 'terminal',
  'null',
  COALESCE(NULLIF(projection_json, ''), '{}'),
  COALESCE(NULLIF(projection_json, ''), '{}'),
  COALESCE(created_at, updated_at, CURRENT_TIMESTAMP::text),
  COALESCE(updated_at, created_at, CURRENT_TIMESTAMP::text)
FROM workbench_projection_state
WHERE trace_id IS NOT NULL
ON CONFLICT (turn_id) DO NOTHING;

INSERT INTO workbench_projection_checkpoints (
  trace_id,
  session_id,
  turn_id,
  run_id,
  command_id,
  projected_seq,
  source_seq,
  source_event_id,
  projection_status,
  projection_health,
  terminal,
  sealed,
  diagnostic_json,
  checkpoint_json,
  created_at,
  updated_at
)
SELECT
  trace_id,
  session_id,
  trace_id,
  run_id,
  command_id,
  last_projected_seq,
  last_agentrun_seq,
  trace_id || ':' || last_agentrun_seq::text,
  projection_status,
  projection_health,
  projection_status IN ('caught_up', 'terminal'),
  projection_status = 'terminal',
  COALESCE(NULLIF(projection_json, ''), '{}'),
  COALESCE(NULLIF(projection_json, ''), '{}'),
  COALESCE(created_at, updated_at, CURRENT_TIMESTAMP::text),
  COALESCE(updated_at, created_at, CURRENT_TIMESTAMP::text)
FROM workbench_projection_state
WHERE trace_id IS NOT NULL
ON CONFLICT (trace_id) DO NOTHING;

CREATE TABLE IF NOT EXISTS evidence_records (
  id TEXT PRIMARY KEY,
  project_id TEXT,
  operation_id TEXT,
  evidence_type TEXT NOT NULL,
  uri TEXT NOT NULL DEFAULT '',
  metadata_json TEXT NOT NULL DEFAULT '{}',
  created_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hwlab_schema_migrations (
  id TEXT PRIMARY KEY,
  schema_version TEXT NOT NULL,
  applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
  migration_json TEXT NOT NULL DEFAULT '{}'
);

INSERT INTO hwlab_schema_migrations (id, schema_version, applied_at, migration_json)
VALUES (
  '0001_cloud_core_skeleton',
  'runtime-durable-postgres-v5',
  CURRENT_TIMESTAMP,
  '{"path":"internal/db/migrations/0001_cloud_core_skeleton.sql","runtime":"cloud-api"}'
)
ON CONFLICT (id) DO UPDATE SET
  schema_version = EXCLUDED.schema_version,
  migration_json = EXCLUDED.migration_json;
