-- ADR-090: 監控盲區治理 + 資產盤點 × 7 項自動化覆蓋矩陣永久化 DB -- 建立時間: 2026-04-18 下午 (台北時區) -- 建立者: ogt + Claude Opus 4.7 (1M context)(亞太) -- -- 上游: -- - 主戰略: docs/superpowers/specs/2026-04-18-blindspot-governance-capacity-l4.md §5.2 -- - ADR: docs/adr/ADR-090-monitoring-blindspot-governance.md -- - MEMORY: project_blindspot_governance.md -- -- 設計說明: -- 本檔建立 11 張表作為 AWOOOI L4 AIOps 的資產盤點 + 自動化覆蓋 + AI 協作稽核地基。 -- 目標: 把治理從 Markdown 搬進 PostgreSQL,讓 AI 四分工 (OpenClaw × NemoTron × -- Hermes × Claude LLM) 在結構化資料上做決策,且每次動作必留 trail。 -- -- 對應七大自動化引擎: -- E1 自動監控 / E2 自動告警 / E3 自動建規則 / E4 自動匹配 -- E5 自動 Playbook / E6 自動修復 / E7 自動 KM -- -- 執行順序: -- Step 0: pgcrypto extension (gen_random_uuid 需要) -- Step 1: asset_inventory — 全景資產主表 -- Step 2: asset_discovery_run — 每次盤點 header -- Step 3: asset_coverage_snapshot — 資產 × 7 自動化覆蓋矩陣 -- Step 4: asset_relationship — 資產依賴圖 (爆炸半徑) -- Step 5: alert_rule_catalog — 告警規則本身即資產 -- Step 6: asset_change_event — 資產變化追蹤 -- Step 7: asset_compliance_snapshot — SSL/CVE/secret/backup 合規 -- Step 8: host_capacity_snapshot — 主機容量快照 (NemoTron 每日 02:00 寫) -- Step 9: capacity_violation_event — 配額違規 -- Step 10: automation_operation_log — 所有 AI 自動化動作稽核主表 🔴 -- Step 11: ai_collaboration_trace — 多 Agent 協作逐步 (辯證歷程) -- Step 12: 驗收查詢 (comment-only) -- -- Idempotent 鐵律: -- - CREATE TABLE IF NOT EXISTS -- - CREATE INDEX IF NOT EXISTS -- - CHECK constraint 寫在 CREATE TABLE 內,依賴 IF NOT EXISTS 保護 -- - 本檔可重複執行安全 (rerun 不會破壞既有資料) -- -- 回滾: -- DROP TABLE IF EXISTS ai_collaboration_trace, automation_operation_log, -- capacity_violation_event, host_capacity_snapshot, asset_compliance_snapshot, -- asset_change_event, alert_rule_catalog, asset_relationship, -- asset_coverage_snapshot, asset_discovery_run, asset_inventory CASCADE; -- -- ============================================================================ -- Step 0: pgcrypto extension (gen_random_uuid) -- ============================================================================ CREATE EXTENSION IF NOT EXISTS pgcrypto; -- ============================================================================ -- Step 1: asset_inventory — 全景資產主表 -- 用途: 主機 / 容器 / K8s workload / DB / 網站 / API / 套件 / 日誌 / KM / 前端 / -- 後端 / 容器 / Gitea / CI-CD 全部無例外 -- 主寫者: scanner (asset_discovery) + NemoTron (capacity 欄位) -- ============================================================================ CREATE TABLE IF NOT EXISTS asset_inventory ( asset_id BIGSERIAL PRIMARY KEY, asset_key TEXT NOT NULL UNIQUE, asset_type TEXT NOT NULL, parent_asset_id BIGINT REFERENCES asset_inventory(asset_id), environment TEXT NOT NULL DEFAULT 'prod', host TEXT, namespace TEXT, name TEXT NOT NULL, metadata JSONB NOT NULL DEFAULT '{}'::jsonb, tags TEXT[] NOT NULL DEFAULT '{}', owner_team TEXT, criticality TEXT, data_classification TEXT, external BOOLEAN NOT NULL DEFAULT false, lifecycle_state TEXT NOT NULL DEFAULT 'active', source_repo TEXT, source_commit_sha TEXT, -- 容量欄位 (Layer 4 AI 巡檢用) cpu_avg_7d NUMERIC(5,2), mem_avg_7d NUMERIC(5,2), capacity_headroom NUMERIC(5,2), resource_limits JSONB, resource_requests JSONB, quota_violation_count INT NOT NULL DEFAULT 0, sla_target JSONB, cost_monthly_usd NUMERIC(10,2), -- 生命週期時間戳 first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), decommissioned_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT asset_inventory_criticality_valid CHECK (criticality IS NULL OR criticality IN ('P0','P1','P2','P3')), CONSTRAINT asset_inventory_data_class_valid CHECK (data_classification IS NULL OR data_classification IN ('public','internal','sensitive','secret')), CONSTRAINT asset_inventory_lifecycle_valid CHECK (lifecycle_state IN ('planned','provisioning','active','degraded','deprecated','decommissioned')), CONSTRAINT asset_inventory_type_valid CHECK (asset_type IN ( 'host','container','k8s_workload','k8s_resource','database','table', 'website','api_endpoint','package','log_stream','km_entry', 'frontend','backend','ci_pipeline','gitea_repo','monitoring_target', 'secret','volume','network','certificate','scheduled_job', 'message_queue','cache','dashboard','ai_agent','llm_model', 'third_party_service','backup_target' )) ); COMMENT ON TABLE asset_inventory IS 'ADR-090: 全景資產主表。每一個主機/容器/K8s workload/DB/網站/API/套件/...都有一筆,跨 run 沿用同 asset_id。'; CREATE INDEX IF NOT EXISTS idx_asset_inventory_type_host ON asset_inventory(asset_type, host); CREATE INDEX IF NOT EXISTS idx_asset_inventory_env_lifecycle ON asset_inventory(environment, lifecycle_state); CREATE INDEX IF NOT EXISTS idx_asset_inventory_metadata_gin ON asset_inventory USING GIN (metadata); CREATE INDEX IF NOT EXISTS idx_asset_inventory_tags_gin ON asset_inventory USING GIN (tags); CREATE INDEX IF NOT EXISTS idx_asset_inventory_active_last_seen ON asset_inventory(last_seen_at DESC) WHERE lifecycle_state = 'active'; -- 註: partial index 只索引 active 資產,按最近出現時間排序 -- ============================================================================ -- Step 2: asset_discovery_run — 每次盤點 header -- 用途: 記錄每次全景掃描的起止時間、掃描範圍、掃到什麼、新增/消失多少 -- 觸發: cron (每日) / ai (proactive_inspector) / human (手動) / incident -- ============================================================================ CREATE TABLE IF NOT EXISTS asset_discovery_run ( run_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), triggered_by TEXT NOT NULL, scope TEXT[] NOT NULL, scan_depth TEXT NOT NULL DEFAULT 'shallow', host_filter TEXT[], started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), ended_at TIMESTAMPTZ, status TEXT NOT NULL, total_assets INT, new_assets INT NOT NULL DEFAULT 0, modified_assets INT NOT NULL DEFAULT 0, disappeared_assets INT NOT NULL DEFAULT 0, tools_used JSONB, duration_ms INT, error TEXT, summary JSONB, CONSTRAINT asset_discovery_run_status_valid CHECK (status IN ('running','success','partial','failed','aborted')), CONSTRAINT asset_discovery_run_scan_depth_valid CHECK (scan_depth IN ('shallow','deep','full')) ); COMMENT ON TABLE asset_discovery_run IS 'ADR-090: 每次資產盤點的 header。run_id 作為下游 snapshot/event/change 的關聯主鍵。'; CREATE INDEX IF NOT EXISTS idx_asset_discovery_run_started ON asset_discovery_run(started_at DESC); CREATE INDEX IF NOT EXISTS idx_asset_discovery_run_status ON asset_discovery_run(status) WHERE status IN ('running','failed','partial'); -- ============================================================================ -- Step 3: asset_coverage_snapshot — 資產 × 7 項自動化 覆蓋矩陣 -- 用途: 每個資產在 7 個自動化維度上的覆蓋狀態 (green/yellow/red) -- 鐵律: 每次 discovery_run 為每個 asset 寫 7 筆 (7 dimensions) -- ============================================================================ CREATE TABLE IF NOT EXISTS asset_coverage_snapshot ( snapshot_id BIGSERIAL PRIMARY KEY, run_id UUID NOT NULL REFERENCES asset_discovery_run(run_id) ON DELETE CASCADE, asset_id BIGINT NOT NULL REFERENCES asset_inventory(asset_id), dimension TEXT NOT NULL, coverage_status TEXT NOT NULL, evidence JSONB NOT NULL DEFAULT '{}'::jsonb, gap_reason TEXT, recommended_action TEXT, confidence NUMERIC(3,2), detected_by TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT asset_coverage_snapshot_dimension_valid CHECK (dimension IN ( 'auto_monitoring','auto_alerting','auto_rule_creation', 'auto_rule_matching','auto_playbook','auto_remediation','auto_km_creation' )), CONSTRAINT asset_coverage_snapshot_status_valid CHECK (coverage_status IN ('green','yellow','red','unknown')), CONSTRAINT asset_coverage_snapshot_unique UNIQUE (run_id, asset_id, dimension) ); COMMENT ON TABLE asset_coverage_snapshot IS 'ADR-090: 計分卡。查 red COUNT 即覆蓋率 SLO。evidence 欄位串 playbook_id/km_entry_id/rule_name。'; CREATE INDEX IF NOT EXISTS idx_asset_coverage_snapshot_asset_dim ON asset_coverage_snapshot(asset_id, dimension); CREATE INDEX IF NOT EXISTS idx_asset_coverage_snapshot_red_yellow ON asset_coverage_snapshot(coverage_status) WHERE coverage_status IN ('red','yellow'); CREATE INDEX IF NOT EXISTS idx_asset_coverage_snapshot_run ON asset_coverage_snapshot(run_id); -- ============================================================================ -- Step 4: asset_relationship — 資產依賴圖 (爆炸半徑必需) -- 用途: 記錄資產之間的 depends_on / calls / stores_data_in / backs_up_to 關係 -- AI 用途: OpenClaw 計算 blast_radius 時查這張表 -- ============================================================================ CREATE TABLE IF NOT EXISTS asset_relationship ( relationship_id BIGSERIAL PRIMARY KEY, from_asset_id BIGINT NOT NULL REFERENCES asset_inventory(asset_id), to_asset_id BIGINT NOT NULL REFERENCES asset_inventory(asset_id), relationship_type TEXT NOT NULL, strength NUMERIC(3,2), metadata JSONB, first_detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), last_verified_at TIMESTAMPTZ, is_active BOOLEAN NOT NULL DEFAULT true, CONSTRAINT asset_relationship_type_valid CHECK (relationship_type IN ( 'depends_on','calls','stores_data_in','backs_up_to', 'routes_to','authenticates_via','monitors','alerts_to','logs_to' )), CONSTRAINT asset_relationship_strength_valid CHECK (strength IS NULL OR (strength >= 0 AND strength <= 1)), CONSTRAINT asset_relationship_unique UNIQUE (from_asset_id, to_asset_id, relationship_type), CONSTRAINT asset_relationship_no_self_loop CHECK (from_asset_id <> to_asset_id) ); COMMENT ON TABLE asset_relationship IS 'ADR-090: 資產依賴圖。AI 計算爆炸半徑必讀。edge 而非 tree,支援多重關係。'; CREATE INDEX IF NOT EXISTS idx_asset_relationship_from ON asset_relationship(from_asset_id) WHERE is_active; CREATE INDEX IF NOT EXISTS idx_asset_relationship_to ON asset_relationship(to_asset_id) WHERE is_active; CREATE INDEX IF NOT EXISTS idx_asset_relationship_type ON asset_relationship(relationship_type); -- ============================================================================ -- Step 5: alert_rule_catalog — 告警規則本身即資產 -- 用途: 把 alert_rules.yaml 升級為 DB-driven;記錄誰創的 / 何時 / 效能 / 生死 -- AI 用途: Hermes 做 noise_rate 分析 / 提建議 retire 低品質規則 -- ============================================================================ CREATE TABLE IF NOT EXISTS alert_rule_catalog ( rule_id BIGSERIAL PRIMARY KEY, rule_name TEXT NOT NULL UNIQUE, source TEXT NOT NULL, expr TEXT NOT NULL, duration_seconds INT, severity TEXT, labels JSONB, annotations JSONB, linked_asset_ids BIGINT[], created_by_agent TEXT, -- 規則品質追蹤 true_positive_count INT NOT NULL DEFAULT 0, false_positive_count INT NOT NULL DEFAULT 0, noise_rate NUMERIC(5,2), last_fired_at TIMESTAMPTZ, -- 信心與演化 confidence NUMERIC(3,2), review_status TEXT, superseded_by_rule_id BIGINT REFERENCES alert_rule_catalog(rule_id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT alert_rule_catalog_source_valid CHECK (source IN ('yaml_hardcoded','ai_generated','human_written','playbook_derived')), CONSTRAINT alert_rule_catalog_review_valid CHECK (review_status IS NULL OR review_status IN ('draft','approved','deprecated','retired')) ); COMMENT ON TABLE alert_rule_catalog IS 'ADR-090: 告警規則即一等資產。支援規則演化 (ai_generated) 與替代鏈 (superseded_by)。'; CREATE INDEX IF NOT EXISTS idx_alert_rule_catalog_source ON alert_rule_catalog(source); CREATE INDEX IF NOT EXISTS idx_alert_rule_catalog_assets_gin ON alert_rule_catalog USING GIN (linked_asset_ids); CREATE INDEX IF NOT EXISTS idx_alert_rule_catalog_review ON alert_rule_catalog(review_status) WHERE review_status IS NOT NULL; -- ============================================================================ -- Step 6: asset_change_event — 資產變化追蹤 (diff between runs) -- 用途: 兩次 discovery_run 之間的 delta。新增/消失/修改/覆蓋率變化 -- ============================================================================ CREATE TABLE IF NOT EXISTS asset_change_event ( event_id BIGSERIAL PRIMARY KEY, run_id UUID NOT NULL REFERENCES asset_discovery_run(run_id), asset_id BIGINT REFERENCES asset_inventory(asset_id), change_type TEXT NOT NULL, before_state JSONB, after_state JSONB, diff JSONB, detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), ai_analysis TEXT, CONSTRAINT asset_change_event_type_valid CHECK (change_type IN ( 'asset_added','asset_removed','asset_modified', 'coverage_improved','coverage_degraded', 'criticality_changed','owner_changed','lifecycle_changed' )) ); COMMENT ON TABLE asset_change_event IS 'ADR-090: 資產變化追蹤。兩次掃描的 diff 明確落地,LLM 可加 ai_analysis 解讀。'; CREATE INDEX IF NOT EXISTS idx_asset_change_event_run ON asset_change_event(run_id); CREATE INDEX IF NOT EXISTS idx_asset_change_event_asset_time ON asset_change_event(asset_id, detected_at DESC); -- ============================================================================ -- Step 7: asset_compliance_snapshot — 合規狀態 (SSL/CVE/secret/backup) -- 用途: 與 coverage 不同軸的合規追蹤。SSL cert 到期 / CVE 掃描 / secret 輪替 -- ============================================================================ CREATE TABLE IF NOT EXISTS asset_compliance_snapshot ( snapshot_id BIGSERIAL PRIMARY KEY, run_id UUID REFERENCES asset_discovery_run(run_id), asset_id BIGINT NOT NULL REFERENCES asset_inventory(asset_id), dimension TEXT NOT NULL, status TEXT NOT NULL, expires_at TIMESTAMPTZ, detail JSONB, remediation_deadline TIMESTAMPTZ, detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT asset_compliance_snapshot_dimension_valid CHECK (dimension IN ( 'ssl_cert_valid','cve_scan','secret_rotated','backup_tested', 'audit_log_enabled','access_reviewed','encryption_at_rest' )), CONSTRAINT asset_compliance_snapshot_status_valid CHECK (status IN ('compliant','warning','violation','unknown')) ); COMMENT ON TABLE asset_compliance_snapshot IS 'ADR-090: 合規狀態快照。與 coverage 不同軸,SSL/CVE/secret/backup 專用。'; CREATE INDEX IF NOT EXISTS idx_asset_compliance_snapshot_asset_dim ON asset_compliance_snapshot(asset_id, dimension); CREATE INDEX IF NOT EXISTS idx_asset_compliance_snapshot_expiring ON asset_compliance_snapshot(expires_at) WHERE expires_at IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_asset_compliance_snapshot_violations ON asset_compliance_snapshot(status) WHERE status IN ('warning','violation'); -- ============================================================================ -- Step 8: host_capacity_snapshot — 主機容量快照 -- 用途: NemoTron 每日 02:00 台北 自主容量巡檢寫入 -- Layer 4 核心表。hermes 做預測,openclaw 產建議,全寫這張 -- ============================================================================ CREATE TABLE IF NOT EXISTS host_capacity_snapshot ( snapshot_id BIGSERIAL PRIMARY KEY, host TEXT NOT NULL, captured_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), load1 NUMERIC(6,2), load5 NUMERIC(6,2), load15 NUMERIC(6,2), cpu_used_pct NUMERIC(5,2), cpu_iowait_pct NUMERIC(5,2), mem_used_pct NUMERIC(5,2), swap_used_pct NUMERIC(5,2), disk_used_pct JSONB, container_count INT, k8s_pod_count INT, top_cpu_offenders JSONB, top_mem_offenders JSONB, headroom_pct NUMERIC(5,2), ai_verdict TEXT, ai_reasoning TEXT, recommended_actions JSONB, written_by_agent TEXT NOT NULL, CONSTRAINT host_capacity_snapshot_verdict_valid CHECK (ai_verdict IS NULL OR ai_verdict IN ('safe','warning','critical','unknown')) ); COMMENT ON TABLE host_capacity_snapshot IS 'ADR-090: NemoTron 每日主機容量巡檢結果。Layer 4 AI 自主治理核心表。'; CREATE INDEX IF NOT EXISTS idx_host_capacity_snapshot_host_time ON host_capacity_snapshot(host, captured_at DESC); CREATE INDEX IF NOT EXISTS idx_host_capacity_snapshot_critical ON host_capacity_snapshot(ai_verdict) WHERE ai_verdict IN ('warning','critical'); -- ============================================================================ -- Step 9: capacity_violation_event — 配額違規事件 -- 用途: 記錄任何「缺 limit」「超 request」「主機飽和」的違規 -- ============================================================================ CREATE TABLE IF NOT EXISTS capacity_violation_event ( event_id BIGSERIAL PRIMARY KEY, asset_id BIGINT REFERENCES asset_inventory(asset_id), host TEXT, violation_type TEXT NOT NULL, threshold NUMERIC(10,2), actual_value NUMERIC(10,2), detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), auto_action TEXT, auto_action_op_id UUID, human_override TEXT, resolved_at TIMESTAMPTZ, CONSTRAINT capacity_violation_event_type_valid CHECK (violation_type IN ( 'no_limit_set','over_request','over_limit','host_saturation', 'over_sla_budget','unauthorized_new_deploy' )) ); COMMENT ON TABLE capacity_violation_event IS 'ADR-090: 配額違規稽核。每次 AI 偵測到資產無 limit/主機飽和/未授權部署 都寫一筆。'; CREATE INDEX IF NOT EXISTS idx_capacity_violation_event_asset_time ON capacity_violation_event(asset_id, detected_at DESC); CREATE INDEX IF NOT EXISTS idx_capacity_violation_event_unresolved ON capacity_violation_event(detected_at DESC) WHERE resolved_at IS NULL; -- ============================================================================ -- Step 10: automation_operation_log — 所有 AI 自動化動作稽核主表 🔴 -- 鐵律: 每一個 AI 自動化動作都必須寫一筆。缺筆 = 治理失效 -- ============================================================================ CREATE TABLE IF NOT EXISTS automation_operation_log ( op_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), operation_type TEXT NOT NULL, asset_id BIGINT REFERENCES asset_inventory(asset_id), incident_id BIGINT, run_id UUID REFERENCES asset_discovery_run(run_id), actor TEXT NOT NULL, input JSONB NOT NULL DEFAULT '{}'::jsonb, output JSONB NOT NULL DEFAULT '{}'::jsonb, dry_run_result JSONB, status TEXT NOT NULL, error TEXT, duration_ms INT, tokens_in INT, tokens_out INT, cost_usd NUMERIC(10,6), budget_bucket TEXT, parent_op_id UUID REFERENCES automation_operation_log(op_id), retry_count INT NOT NULL DEFAULT 0, retry_of_op_id UUID REFERENCES automation_operation_log(op_id), stderr_feed_back TEXT, tags TEXT[], created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT automation_operation_log_type_valid CHECK (operation_type IN ( 'monitor_configured','monitor_removed', 'alert_fired','alert_suppressed','alert_routed', 'rule_created','rule_updated','rule_matched','rule_rejected','rule_deprecated', 'playbook_generated','playbook_updated','playbook_executed', 'remediation_executed','remediation_verified','remediation_rolled_back', 'self_correction_attempted', 'km_created','km_updated','km_linked', 'asset_discovered','coverage_recalculated', 'capacity_recommendation','quota_enforced' )), CONSTRAINT automation_operation_log_status_valid CHECK (status IN ('pending','success','failed','dry_run','rolled_back')) ); COMMENT ON TABLE automation_operation_log IS 'ADR-090: 所有 AI 自動化動作稽核主表。retry_of_op_id + stderr_feed_back 支援引擎 4 閉環。'; CREATE INDEX IF NOT EXISTS idx_automation_operation_log_type_time ON automation_operation_log(operation_type, created_at DESC); CREATE INDEX IF NOT EXISTS idx_automation_operation_log_asset_time ON automation_operation_log(asset_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_automation_operation_log_incident ON automation_operation_log(incident_id) WHERE incident_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_automation_operation_log_actor_time ON automation_operation_log(actor, created_at DESC); CREATE INDEX IF NOT EXISTS idx_automation_operation_log_retry ON automation_operation_log(retry_of_op_id) WHERE retry_of_op_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_automation_operation_log_tags_gin ON automation_operation_log USING GIN (tags); -- ============================================================================ -- Step 11: ai_collaboration_trace — 多 Agent 協作逐步 (LLM × OpenClaw × NemoTron × Hermes) -- 用途: 每個 automation_operation_log 背後的 N 步 AI 決策過程 -- 最寶貴的語料: challenged_by + accepted 支援 RLHF fine-tune -- ============================================================================ CREATE TABLE IF NOT EXISTS ai_collaboration_trace ( trace_id BIGSERIAL PRIMARY KEY, op_id UUID NOT NULL REFERENCES automation_operation_log(op_id) ON DELETE CASCADE, step_order INT NOT NULL, agent TEXT NOT NULL, model TEXT, system_prompt_version TEXT, prompt TEXT, response JSONB, confidence NUMERIC(3,2), challenged_by TEXT[], accepted BOOLEAN, tokens_in INT, tokens_out INT, duration_ms INT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT ai_collaboration_trace_unique_step UNIQUE (op_id, step_order) ); COMMENT ON TABLE ai_collaboration_trace IS 'ADR-090: AI 多 Agent 協作逐步紀錄。challenged_by + accepted = RLHF 訓練語料金礦。'; CREATE INDEX IF NOT EXISTS idx_ai_collaboration_trace_op ON ai_collaboration_trace(op_id, step_order); CREATE INDEX IF NOT EXISTS idx_ai_collaboration_trace_agent_time ON ai_collaboration_trace(agent, created_at DESC); -- ============================================================================ -- Step 12: 驗收查詢 (執行後手動跑,驗證 11 張表都到位) -- ============================================================================ -- SELECT table_name -- FROM information_schema.tables -- WHERE table_schema = 'public' -- AND table_name IN ( -- 'asset_inventory', -- 'asset_discovery_run', -- 'asset_coverage_snapshot', -- 'asset_relationship', -- 'alert_rule_catalog', -- 'asset_change_event', -- 'asset_compliance_snapshot', -- 'host_capacity_snapshot', -- 'capacity_violation_event', -- 'automation_operation_log', -- 'ai_collaboration_trace' -- ) -- ORDER BY table_name; -- -- 預期: 11 筆 -- SELECT table_name, COUNT(*) AS column_count -- FROM information_schema.columns -- WHERE table_schema = 'public' -- AND table_name LIKE 'asset_%' OR table_name IN -- ('alert_rule_catalog','host_capacity_snapshot','capacity_violation_event', -- 'automation_operation_log','ai_collaboration_trace') -- GROUP BY table_name -- ORDER BY table_name; -- SELECT conname, conrelid::regclass AS table_name -- FROM pg_constraint -- WHERE conrelid IN ( -- 'asset_inventory'::regclass, -- 'asset_discovery_run'::regclass, -- 'asset_coverage_snapshot'::regclass, -- 'asset_relationship'::regclass, -- 'alert_rule_catalog'::regclass, -- 'asset_change_event'::regclass, -- 'asset_compliance_snapshot'::regclass, -- 'host_capacity_snapshot'::regclass, -- 'capacity_violation_event'::regclass, -- 'automation_operation_log'::regclass, -- 'ai_collaboration_trace'::regclass -- ) AND contype = 'c' -- CHECK constraints only -- ORDER BY table_name, conname; -- ============================================================================ -- END OF MIGRATION adr090_asset_inventory_foundation.sql -- 預計新增物件: 11 tables + 33 indexes + 20 CHECK constraints + 3 UNIQUE + 16 FK references -- 依賴: pgcrypto extension (for gen_random_uuid) -- 影響資料: 無 (純 DDL, 不動現有表) -- 回滾: 見檔案頭部 -- ============================================================================