66a70ab31c
New table: decision_traces (status, reason, evidence, fix plan per control)
New API:
POST/GET/PUT /v1/decision-traces (CRUD for decisions)
GET /v1/decision-traces/stats (compliance dashboard)
GET /v1/controls/{id}/full-trace (Regulation→Obligation→Control→Decision→Evidence)
454 tests pass, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
59 lines
1.8 KiB
PL/PgSQL
59 lines
1.8 KiB
PL/PgSQL
-- Migration 006: Decision Traces (G1)
|
|
-- Schema: compliance
|
|
-- Run: ssh macmini "docker exec -i bp-core-postgres psql -U breakpilot -d breakpilot_db" < control-pipeline/migrations/006_decision_traces.sql
|
|
|
|
SET search_path TO compliance, public;
|
|
|
|
CREATE TABLE IF NOT EXISTS decision_traces (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
control_uuid UUID NOT NULL,
|
|
regulation_id VARCHAR(100),
|
|
obligation_id VARCHAR(100),
|
|
|
|
-- Decision
|
|
status VARCHAR(30) NOT NULL DEFAULT 'not_assessed'
|
|
CHECK (status IN ('not_assessed', 'compliant', 'partially_compliant',
|
|
'not_compliant', 'not_applicable', 'under_remediation')),
|
|
decision_reason TEXT,
|
|
decided_by VARCHAR(100),
|
|
decided_at TIMESTAMPTZ,
|
|
|
|
-- Fix/Remediation
|
|
fix_strategy TEXT,
|
|
fix_owner VARCHAR(100),
|
|
fix_target_date DATE,
|
|
fix_completed_date DATE,
|
|
|
|
-- Evidence
|
|
evidence_ids JSONB DEFAULT '[]',
|
|
confidence NUMERIC(3,2) DEFAULT 0.0,
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID,
|
|
project_id UUID,
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_dt_control ON decision_traces(control_uuid);
|
|
CREATE INDEX IF NOT EXISTS idx_dt_status ON decision_traces(status);
|
|
CREATE INDEX IF NOT EXISTS idx_dt_tenant ON decision_traces(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_dt_decided_at ON decision_traces(decided_at);
|
|
|
|
-- Updated-at trigger
|
|
CREATE OR REPLACE FUNCTION update_decision_traces_updated_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS trg_decision_traces_updated_at ON decision_traces;
|
|
CREATE TRIGGER trg_decision_traces_updated_at
|
|
BEFORE UPDATE ON decision_traces
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_decision_traces_updated_at();
|