apiVersion: v1 kind: List items: - apiVersion: v1 kind: ConfigMap metadata: name: hwlab-v03-postgres-init namespace: hwlab-v03 labels: app.kubernetes.io/name: hwlab-v03-postgres app.kubernetes.io/part-of: hwlab hwlab.pikastech.local/environment: v03 hwlab.pikastech.local/gitops-target: v03 hwlab.pikastech.local/profile: v03 hwlab.pikastech.local/source-commit: b9513d1de3d6052233271013efaa211ef7cf8f5d data: 0001_cloud_core_skeleton.sql: > -- SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-24-p0-aggregate-event-stream. -- 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_event_sequences ( aggregate_id TEXT PRIMARY KEY, aggregate_type TEXT NOT NULL DEFAULT 'trace', session_id TEXT, turn_id TEXT, trace_id TEXT, last_seq INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE INDEX IF NOT EXISTS idx_workbench_event_sequences_session ON workbench_event_sequences(session_id, updated_at DESC, aggregate_id); CREATE INDEX IF NOT EXISTS idx_workbench_event_sequences_trace ON workbench_event_sequences(trace_id, updated_at DESC, aggregate_id); CREATE TABLE IF NOT EXISTS workbench_events ( event_seq BIGSERIAL PRIMARY KEY, event_id TEXT NOT NULL UNIQUE, aggregate_id TEXT NOT NULL, aggregate_type TEXT NOT NULL DEFAULT 'trace', aggregate_seq INTEGER NOT NULL, session_id TEXT, turn_id TEXT, trace_id TEXT, message_id TEXT, source_run_id TEXT, source_command_id TEXT, source_seq INTEGER NOT NULL DEFAULT 0, source_event_id TEXT, event_type TEXT NOT NULL DEFAULT 'event', projection_revision INTEGER NOT NULL DEFAULT 0, terminal BOOLEAN NOT NULL DEFAULT false, sealed BOOLEAN NOT NULL DEFAULT false, payload_json TEXT NOT NULL DEFAULT '{}', occurred_at TEXT NOT NULL, committed_at TEXT NOT NULL ); CREATE UNIQUE INDEX IF NOT EXISTS idx_workbench_events_aggregate_seq ON workbench_events(aggregate_id, aggregate_seq); CREATE UNIQUE INDEX IF NOT EXISTS idx_workbench_events_aggregate_source_event ON workbench_events(aggregate_id, source_event_id) WHERE source_event_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_workbench_events_trace_seq ON workbench_events(trace_id, event_seq); CREATE INDEX IF NOT EXISTS idx_workbench_events_session_seq ON workbench_events(session_id, event_seq); CREATE INDEX IF NOT EXISTS idx_workbench_events_type_seq ON workbench_events(event_type, event_seq); CREATE TABLE IF NOT EXISTS workbench_session_inputs ( input_id TEXT PRIMARY KEY, session_id TEXT NOT NULL, turn_id TEXT, trace_id TEXT, message_id TEXT, command_id TEXT, delivery TEXT NOT NULL DEFAULT 'queue', admitted_seq INTEGER NOT NULL DEFAULT 0, promoted_seq INTEGER, status TEXT NOT NULL DEFAULT 'admitted', error_code TEXT, source_event_id TEXT, input_json TEXT NOT NULL DEFAULT '{}', created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE INDEX IF NOT EXISTS idx_workbench_session_inputs_session_seq ON workbench_session_inputs(session_id, admitted_seq, input_id); CREATE INDEX IF NOT EXISTS idx_workbench_session_inputs_trace ON workbench_session_inputs(trace_id, input_id); CREATE INDEX IF NOT EXISTS idx_workbench_session_inputs_command ON workbench_session_inputs(command_id, input_id) WHERE command_id IS NOT NULL; CREATE UNIQUE INDEX IF NOT EXISTS idx_workbench_session_inputs_source_event ON workbench_session_inputs(session_id, source_event_id) WHERE source_event_id IS NOT NULL; 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, event_seq BIGINT, aggregate_id TEXT, aggregate_seq INTEGER NOT NULL DEFAULT 0, projection_revision INTEGER NOT NULL DEFAULT 0, 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 ); ALTER TABLE workbench_projection_outbox ADD COLUMN IF NOT EXISTS event_seq BIGINT; ALTER TABLE workbench_projection_outbox ADD COLUMN IF NOT EXISTS aggregate_id TEXT; ALTER TABLE workbench_projection_outbox ADD COLUMN IF NOT EXISTS aggregate_seq INTEGER NOT NULL DEFAULT 0; ALTER TABLE workbench_projection_outbox ADD COLUMN IF NOT EXISTS projection_revision INTEGER NOT NULL DEFAULT 0; 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); CREATE INDEX IF NOT EXISTS idx_workbench_projection_outbox_event_seq ON workbench_projection_outbox(event_seq) WHERE event_seq IS NOT NULL; 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-v7', 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; - apiVersion: v1 kind: Service metadata: name: hwlab-v03-postgres namespace: hwlab-v03 labels: app.kubernetes.io/name: hwlab-v03-postgres app.kubernetes.io/part-of: hwlab hwlab.pikastech.local/environment: v03 hwlab.pikastech.local/gitops-target: v03 hwlab.pikastech.local/profile: v03 hwlab.pikastech.local/source-commit: b9513d1de3d6052233271013efaa211ef7cf8f5d spec: type: ClusterIP selector: app.kubernetes.io/name: hwlab-v03-postgres ports: - name: postgres port: 5432 targetPort: postgres - apiVersion: apps/v1 kind: StatefulSet metadata: name: hwlab-v03-postgres namespace: hwlab-v03 labels: app.kubernetes.io/name: hwlab-v03-postgres app.kubernetes.io/part-of: hwlab hwlab.pikastech.local/environment: v03 hwlab.pikastech.local/gitops-target: v03 hwlab.pikastech.local/profile: v03 hwlab.pikastech.local/source-commit: b9513d1de3d6052233271013efaa211ef7cf8f5d spec: serviceName: hwlab-v03-postgres replicas: 1 selector: matchLabels: app.kubernetes.io/name: hwlab-v03-postgres template: metadata: labels: app.kubernetes.io/name: hwlab-v03-postgres app.kubernetes.io/part-of: hwlab hwlab.pikastech.local/environment: v03 hwlab.pikastech.local/gitops-target: v03 hwlab.pikastech.local/profile: v03 hwlab.pikastech.local/migration-sha256: 880f77326c6dd41a annotations: hwlab.pikastech.local/migration-sha256: 880f77326c6dd41a994931567da1d0372af0b3c3f8aef1e2f69e3d5ed4343274 spec: containers: - name: postgres image: 127.0.0.1:5000/hwlab/postgres:16-alpine imagePullPolicy: IfNotPresent env: - name: POSTGRES_DB value: hwlab_v03 - name: POSTGRES_USER value: hwlab_v03 - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: hwlab-v03-postgres key: POSTGRES_PASSWORD ports: - name: postgres containerPort: 5432 readinessProbe: tcpSocket: port: postgres initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: postgres initialDelaySeconds: 30 periodSeconds: 20 volumeMounts: - name: data mountPath: /var/lib/postgresql/data - name: init mountPath: /docker-entrypoint-initdb.d readOnly: true volumes: - name: init configMap: name: hwlab-v03-postgres-init volumeClaimTemplates: - metadata: name: data spec: accessModes: - ReadWriteOnce resources: requests: storage: 8Gi