232 lines
11 KiB
PL/PgSQL
232 lines
11 KiB
PL/PgSQL
-- Migration: Create missing tables for guardrail MCP server
|
|
-- Description: Creates all 6 tables identified in the test report
|
|
-- Tables: prevention_rules, failure_registry, file_reads, task_attempts, uncertainty_tracking, production_code_tracking
|
|
|
|
-- Enable UUID extension
|
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
|
|
|
-- ============================================================================
|
|
-- Table 1: prevention_rules
|
|
-- Purpose: Store guardrail prevention rules for pattern matching
|
|
-- ============================================================================
|
|
CREATE TABLE IF NOT EXISTS prevention_rules (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
rule_id VARCHAR(50) UNIQUE NOT NULL CHECK (LENGTH(TRIM(rule_id)) > 0),
|
|
name VARCHAR(255) NOT NULL,
|
|
pattern TEXT NOT NULL,
|
|
pattern_hash VARCHAR(64),
|
|
message TEXT NOT NULL,
|
|
severity VARCHAR(20) NOT NULL CHECK (severity IN ('critical', 'error', 'warning', 'info')),
|
|
enabled BOOLEAN NOT NULL DEFAULT true,
|
|
document_id UUID,
|
|
category VARCHAR(50),
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Indexes for prevention_rules
|
|
CREATE INDEX IF NOT EXISTS idx_prevention_rules_enabled ON prevention_rules(enabled);
|
|
CREATE INDEX IF NOT EXISTS idx_prevention_rules_severity ON prevention_rules(severity);
|
|
CREATE INDEX IF NOT EXISTS idx_prevention_rules_category ON prevention_rules(category);
|
|
CREATE INDEX IF NOT EXISTS idx_prevention_rules_updated_at ON prevention_rules(updated_at DESC);
|
|
|
|
-- Comments
|
|
COMMENT ON TABLE prevention_rules IS 'Stores guardrail prevention rules for pattern matching and validation';
|
|
COMMENT ON COLUMN prevention_rules.rule_id IS 'Unique identifier for the rule (e.g., RULE-001)';
|
|
COMMENT ON COLUMN prevention_rules.pattern IS 'Regex pattern or text to match against';
|
|
COMMENT ON COLUMN prevention_rules.severity IS 'Critical=halt, Error=block, Warning=confirm, Info=log';
|
|
|
|
-- ============================================================================
|
|
-- Table 2: failure_registry
|
|
-- Purpose: Track failures and regressions for continuous learning
|
|
-- ============================================================================
|
|
CREATE TABLE IF NOT EXISTS failure_registry (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
failure_id VARCHAR(50) UNIQUE NOT NULL,
|
|
category VARCHAR(50) NOT NULL,
|
|
severity VARCHAR(10) NOT NULL CHECK (severity IN ('critical', 'high', 'medium', 'low')),
|
|
error_message TEXT NOT NULL,
|
|
root_cause TEXT,
|
|
affected_files TEXT[],
|
|
regression_pattern VARCHAR(255),
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'resolved', 'deprecated')),
|
|
project_slug VARCHAR(100),
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
) PARTITION BY RANGE (created_at);
|
|
|
|
-- Create initial partition for current month
|
|
CREATE TABLE IF NOT EXISTS failure_registry_y2026m02 PARTITION OF failure_registry
|
|
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
|
|
|
|
-- Indexes for failure_registry
|
|
CREATE INDEX IF NOT EXISTS idx_failure_registry_status ON failure_registry(status);
|
|
CREATE INDEX IF NOT EXISTS idx_failure_registry_category ON failure_registry(category);
|
|
CREATE INDEX IF NOT EXISTS idx_failure_registry_severity ON failure_registry(severity);
|
|
CREATE INDEX IF NOT EXISTS idx_failure_registry_project_slug ON failure_registry(project_slug);
|
|
CREATE INDEX IF NOT EXISTS idx_failure_registry_created_at ON failure_registry(created_at DESC);
|
|
|
|
-- Partial index for active failures
|
|
CREATE INDEX IF NOT EXISTS idx_failure_registry_active ON failure_registry(category, severity)
|
|
WHERE status = 'active';
|
|
|
|
-- Comments
|
|
COMMENT ON TABLE failure_registry IS 'Tracks failures and regressions for continuous learning and prevention';
|
|
COMMENT ON COLUMN failure_registry.failure_id IS 'Unique identifier for the failure (e.g., FAIL-001)';
|
|
COMMENT ON COLUMN failure_registry.regression_pattern IS 'Pattern to detect if this failure reoccurs';
|
|
|
|
-- ============================================================================
|
|
-- Table 3: file_reads
|
|
-- Purpose: Track file reads within sessions for audit and validation
|
|
-- ============================================================================
|
|
CREATE TABLE IF NOT EXISTS file_reads (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
session_id VARCHAR(100) NOT NULL,
|
|
file_path VARCHAR(500) NOT NULL,
|
|
read_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
content_hash VARCHAR(64),
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Indexes for file_reads
|
|
CREATE INDEX IF NOT EXISTS idx_file_reads_session ON file_reads(session_id);
|
|
CREATE INDEX IF NOT EXISTS idx_file_reads_path ON file_reads(file_path);
|
|
CREATE INDEX IF NOT EXISTS idx_file_reads_read_at ON file_reads(read_at DESC);
|
|
|
|
-- Unique constraint to prevent duplicate entries for same session+file
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_file_reads_session_path
|
|
ON file_reads(session_id, file_path);
|
|
|
|
-- Comments
|
|
COMMENT ON TABLE file_reads IS 'Tracks file reads within sessions for audit and validation purposes';
|
|
COMMENT ON COLUMN file_reads.content_hash IS 'SHA256 hash of file content at time of read';
|
|
|
|
-- ============================================================================
|
|
-- Table 4: task_attempts
|
|
-- Purpose: Track task attempts for three-strikes pattern detection
|
|
-- ============================================================================
|
|
CREATE TABLE IF NOT EXISTS task_attempts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
session_id VARCHAR(100) NOT NULL,
|
|
task_id VARCHAR(100),
|
|
attempt_number INTEGER NOT NULL CHECK (attempt_number > 0),
|
|
attempted_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
error_message TEXT,
|
|
error_category VARCHAR(50),
|
|
resolution VARCHAR(50) NOT NULL DEFAULT 'pending',
|
|
resolved_at TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT valid_error_category CHECK (
|
|
error_category IS NULL OR
|
|
error_category IN ('syntax', 'runtime', 'logic', 'timeout', 'other')
|
|
),
|
|
CONSTRAINT valid_resolution CHECK (
|
|
resolution IN ('pending', 'resolved', 'escalated', 'abandoned')
|
|
)
|
|
);
|
|
|
|
-- Indexes for task_attempts
|
|
CREATE INDEX IF NOT EXISTS idx_task_attempts_session ON task_attempts(session_id);
|
|
CREATE INDEX IF NOT EXISTS idx_task_attempts_task ON task_attempts(task_id);
|
|
CREATE INDEX IF NOT EXISTS idx_task_attempts_session_task ON task_attempts(session_id, task_id);
|
|
CREATE INDEX IF NOT EXISTS idx_task_attempts_attempted_at ON task_attempts(attempted_at DESC);
|
|
|
|
-- Partial index for pending attempts (most queried)
|
|
CREATE INDEX IF NOT EXISTS idx_task_attempts_pending ON task_attempts(session_id, task_id, resolution)
|
|
WHERE resolution = 'pending';
|
|
|
|
-- Comments
|
|
COMMENT ON TABLE task_attempts IS 'Tracks task attempts for three-strikes pattern detection';
|
|
COMMENT ON COLUMN task_attempts.attempt_number IS 'Sequential attempt number for this task';
|
|
COMMENT ON COLUMN task_attempts.resolution IS 'pending=active, resolved=fixed, escalated=human, abandoned=given up';
|
|
|
|
-- ============================================================================
|
|
-- Table 5: uncertainty_tracking
|
|
-- Purpose: Track uncertainty levels and decision-making context
|
|
-- ============================================================================
|
|
CREATE TABLE IF NOT EXISTS uncertainty_tracking (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
session_id VARCHAR(100) NOT NULL,
|
|
task_id VARCHAR(100),
|
|
uncertainty_level VARCHAR(50) NOT NULL,
|
|
decision_made TEXT,
|
|
context_data JSONB,
|
|
escalation_required BOOLEAN NOT NULL DEFAULT false,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT valid_uncertainty_level CHECK (uncertainty_level IN (
|
|
'critical',
|
|
'blocked',
|
|
'high',
|
|
'medium',
|
|
'investigating',
|
|
'low',
|
|
'resolved'
|
|
))
|
|
);
|
|
|
|
-- Indexes for uncertainty_tracking
|
|
CREATE INDEX IF NOT EXISTS idx_uncertainty_session_id ON uncertainty_tracking(session_id);
|
|
CREATE INDEX IF NOT EXISTS idx_uncertainty_task_id ON uncertainty_tracking(task_id);
|
|
CREATE INDEX IF NOT EXISTS idx_uncertainty_level ON uncertainty_tracking(uncertainty_level);
|
|
CREATE INDEX IF NOT EXISTS idx_uncertainty_escalation ON uncertainty_tracking(escalation_required) WHERE escalation_required = true;
|
|
CREATE INDEX IF NOT EXISTS idx_uncertainty_created_at ON uncertainty_tracking(created_at DESC);
|
|
|
|
-- Comments
|
|
COMMENT ON TABLE uncertainty_tracking IS 'Tracks uncertainty levels during MCP operations and decision-making context';
|
|
COMMENT ON COLUMN uncertainty_tracking.uncertainty_level IS 'Critical=system blocked; Blocked=unresolvable; High=major questions; Medium=some questions; Investigating=actively researching; Low=minor doubts; Resolved=clarity achieved';
|
|
COMMENT ON COLUMN uncertainty_tracking.escalation_required IS 'Whether human intervention is needed';
|
|
|
|
-- ============================================================================
|
|
-- Table 6: production_code_tracking
|
|
-- Purpose: Track production code existence for guardrail validation
|
|
-- ============================================================================
|
|
CREATE TYPE code_type_enum AS ENUM ('production', 'test', 'infrastructure');
|
|
|
|
CREATE TABLE IF NOT EXISTS production_code_tracking (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
session_id VARCHAR(255) NOT NULL,
|
|
file_path VARCHAR(500) NOT NULL,
|
|
code_type code_type_enum NOT NULL,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
verified_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
-- Constraints
|
|
CONSTRAINT unique_session_file UNIQUE (session_id, file_path)
|
|
);
|
|
|
|
-- Indexes for production_code_tracking
|
|
CREATE INDEX IF NOT EXISTS idx_production_code_session_id ON production_code_tracking(session_id);
|
|
CREATE INDEX IF NOT EXISTS idx_production_code_session_id_type ON production_code_tracking(session_id, code_type);
|
|
CREATE INDEX IF NOT EXISTS idx_production_code_created_at ON production_code_tracking(created_at);
|
|
|
|
-- Comments
|
|
COMMENT ON TABLE production_code_tracking IS 'Tracks production code existence for guardrail validation';
|
|
COMMENT ON COLUMN production_code_tracking.code_type IS 'production=main code, test=test files, infrastructure=config/build';
|
|
COMMENT ON COLUMN production_code_tracking.verified_at IS 'When the code was verified as still existing';
|
|
|
|
-- ============================================================================
|
|
-- Partition management function
|
|
-- ============================================================================
|
|
CREATE OR REPLACE FUNCTION create_failure_registry_partition()
|
|
RETURNS void AS $$
|
|
DECLARE
|
|
partition_date DATE;
|
|
partition_name TEXT;
|
|
start_date DATE;
|
|
end_date DATE;
|
|
BEGIN
|
|
partition_date := DATE_TRUNC('month', NOW());
|
|
partition_name := 'failure_registry_y' || TO_CHAR(partition_date, 'YYYY') || 'm' || TO_CHAR(partition_date, 'MM');
|
|
start_date := partition_date;
|
|
end_date := partition_date + INTERVAL '1 month';
|
|
|
|
EXECUTE format('CREATE TABLE IF NOT EXISTS %I PARTITION OF failure_registry FOR VALUES FROM (%L) TO (%L)',
|
|
partition_name, start_date, end_date);
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Create next month's partition proactively
|
|
SELECT create_failure_registry_partition();
|