Universal Review Taxonomy v5.1 implementation with: - Track A (Training): A1 Quickstart, A2 QA Protocol, A3 Calibration Set, A4 Full Manual - Track B (Engineering): B1 Code Registry, B2 Database Schema, B3 Owner Routing, B4 API Contract - Track C (Analytics): C1 Issue Lifecycle, C2 KPI Mapping Guide - Track D (Integration): D1 Dashboard Specification Covers 7 domains, 28 categories, 138 subcodes, 16 causal codes, and 7 metadata dimensions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1127 lines
52 KiB
PL/PgSQL
1127 lines
52 KiB
PL/PgSQL
-- =============================================================================
|
|
-- B2: URT Database Schema
|
|
-- Universal Review Taxonomy v5.1
|
|
-- =============================================================================
|
|
--
|
|
-- PostgreSQL schema for URT v5.1 classification storage and analytics.
|
|
-- Supports all implementation profiles (Lite, Core, Standard, Full).
|
|
--
|
|
-- Design Principles:
|
|
-- 1. Normalized reference data (domains, categories, subcodes, causal codes)
|
|
-- 2. Flexible span classification with metadata dimensions
|
|
-- 3. Full issue lifecycle tracking per C1 framework
|
|
-- 4. Comprehensive audit trail for compliance
|
|
-- 5. Optimized for analytics with materialized views
|
|
-- 6. Partitioning-ready for high-volume scenarios
|
|
--
|
|
-- Status: Production Ready
|
|
-- Version: 1.0
|
|
-- Date: 2026-01-23
|
|
-- Depends On: B1-urt-codes.yaml, C1-Issue-Lifecycle-Framework.md
|
|
-- =============================================================================
|
|
|
|
-- Enable required extensions
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
CREATE EXTENSION IF NOT EXISTS "btree_gist"; -- For exclusion constraints
|
|
|
|
-- =============================================================================
|
|
-- SECTION 1: REFERENCE TABLES
|
|
-- Core URT taxonomy structure from B1-urt-codes.yaml
|
|
-- =============================================================================
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 1.1 Experience Domains (7 domains)
|
|
-- Top-level organizational structure
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE urt_domains (
|
|
domain_code CHAR(1) PRIMARY KEY,
|
|
name VARCHAR(50) NOT NULL,
|
|
description TEXT NOT NULL,
|
|
core_question TEXT NOT NULL,
|
|
default_owner VARCHAR(100) NOT NULL,
|
|
display_order SMALLINT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT urt_domains_code_check
|
|
CHECK (domain_code IN ('O', 'P', 'J', 'E', 'A', 'V', 'R'))
|
|
);
|
|
|
|
COMMENT ON TABLE urt_domains IS
|
|
'URT v5.1 Experience Domains - 7 top-level classification areas';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 1.2 Categories (28 categories)
|
|
-- Second-tier classification within domains
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE urt_categories (
|
|
category_code VARCHAR(2) PRIMARY KEY,
|
|
domain_code CHAR(1) NOT NULL REFERENCES urt_domains(domain_code),
|
|
name VARCHAR(50) NOT NULL,
|
|
definition TEXT NOT NULL,
|
|
display_order SMALLINT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT urt_categories_code_format
|
|
CHECK (category_code ~ '^[OPJEAVR][1-4]$')
|
|
);
|
|
|
|
CREATE INDEX idx_categories_domain ON urt_categories(domain_code);
|
|
|
|
COMMENT ON TABLE urt_categories IS
|
|
'URT v5.1 Categories - 28 second-tier classifications within domains';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 1.3 Subcodes (138 subcodes per B1 actual count)
|
|
-- Third-tier diagnostic codes for detailed classification
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE urt_subcodes (
|
|
subcode VARCHAR(6) PRIMARY KEY,
|
|
category_code VARCHAR(2) NOT NULL REFERENCES urt_categories(category_code),
|
|
domain_code CHAR(1) NOT NULL REFERENCES urt_domains(domain_code),
|
|
name VARCHAR(100) NOT NULL,
|
|
definition TEXT NOT NULL,
|
|
positive_example TEXT,
|
|
negative_example TEXT,
|
|
dont_confuse_with VARCHAR(6),
|
|
dont_confuse_reason TEXT,
|
|
display_order SMALLINT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT urt_subcodes_code_format
|
|
CHECK (subcode ~ '^[OPJEAVR][1-4]\.[0-9]{2}$')
|
|
);
|
|
|
|
CREATE INDEX idx_subcodes_category ON urt_subcodes(category_code);
|
|
CREATE INDEX idx_subcodes_domain ON urt_subcodes(domain_code);
|
|
|
|
COMMENT ON TABLE urt_subcodes IS
|
|
'URT v5.1 Subcodes - 138 diagnostic-level classification codes';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 1.4 Causal Codes (16 codes across 3 layers)
|
|
-- For root cause analysis in Full profile
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE urt_causal_codes (
|
|
causal_code VARCHAR(4) PRIMARY KEY,
|
|
layer VARCHAR(20) NOT NULL,
|
|
layer_prefix VARCHAR(3) NOT NULL,
|
|
name VARCHAR(50) NOT NULL,
|
|
definition TEXT NOT NULL,
|
|
display_order SMALLINT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT urt_causal_layer_check
|
|
CHECK (layer IN ('conditions', 'management', 'systemic')),
|
|
CONSTRAINT urt_causal_prefix_check
|
|
CHECK (layer_prefix IN ('CD-', 'MG-', 'SY-')),
|
|
CONSTRAINT urt_causal_code_format
|
|
CHECK (causal_code ~ '^(CD|MG|SY)-[A-Z]$')
|
|
);
|
|
|
|
CREATE INDEX idx_causal_layer ON urt_causal_codes(layer);
|
|
|
|
COMMENT ON TABLE urt_causal_codes IS
|
|
'URT v5.1 Causal Codes - 16 root cause analysis codes (CD/MG/SY layers)';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 1.5 Metadata Dimension Values (24 values across 7 dimensions)
|
|
-- All possible metadata values for span classification
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE urt_metadata_values (
|
|
dimension_code VARCHAR(2) NOT NULL,
|
|
value_code VARCHAR(4) NOT NULL,
|
|
dimension_name VARCHAR(30) NOT NULL,
|
|
label VARCHAR(30) NOT NULL,
|
|
definition TEXT NOT NULL,
|
|
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
|
display_order SMALLINT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
PRIMARY KEY (dimension_code, value_code),
|
|
|
|
CONSTRAINT urt_metadata_dimension_check
|
|
CHECK (dimension_code IN ('V', 'I', 'S', 'A', 'T', 'E', 'CR'))
|
|
);
|
|
|
|
CREATE INDEX idx_metadata_dimension ON urt_metadata_values(dimension_code);
|
|
|
|
COMMENT ON TABLE urt_metadata_values IS
|
|
'URT v5.1 Metadata Values - 24 values across 7 dimensions (V,I,S,A,T,E,CR)';
|
|
|
|
-- =============================================================================
|
|
-- SECTION 2: CORE CLASSIFICATION TABLES
|
|
-- Reviews, spans, and classifications
|
|
-- =============================================================================
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 2.1 Reviews
|
|
-- Original review text and source metadata
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE reviews (
|
|
review_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
external_id VARCHAR(255), -- ID from source system (e.g., Google place_id)
|
|
source VARCHAR(50) NOT NULL, -- 'google', 'yelp', 'tripadvisor', etc.
|
|
business_id VARCHAR(255), -- Business identifier
|
|
author_name VARCHAR(255),
|
|
author_id VARCHAR(255),
|
|
review_text TEXT NOT NULL,
|
|
star_rating NUMERIC(2,1), -- 1.0-5.0 scale
|
|
review_date DATE,
|
|
language_code VARCHAR(5), -- ISO 639-1
|
|
raw_metadata JSONB, -- Source-specific metadata
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
deleted_at TIMESTAMPTZ, -- Soft delete support
|
|
|
|
CONSTRAINT reviews_rating_check
|
|
CHECK (star_rating IS NULL OR (star_rating >= 1.0 AND star_rating <= 5.0))
|
|
);
|
|
|
|
CREATE INDEX idx_reviews_source ON reviews(source);
|
|
CREATE INDEX idx_reviews_business ON reviews(business_id);
|
|
CREATE INDEX idx_reviews_date ON reviews(review_date);
|
|
CREATE INDEX idx_reviews_external ON reviews(external_id) WHERE external_id IS NOT NULL;
|
|
CREATE INDEX idx_reviews_created ON reviews(created_at);
|
|
CREATE INDEX idx_reviews_deleted ON reviews(deleted_at) WHERE deleted_at IS NULL;
|
|
|
|
COMMENT ON TABLE reviews IS
|
|
'Original customer reviews from various sources';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 2.2 Spans
|
|
-- Individual classified text segments within reviews
|
|
-- A single review may contain multiple spans (composite review decomposition)
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE spans (
|
|
span_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
review_id UUID NOT NULL REFERENCES reviews(review_id) ON DELETE CASCADE,
|
|
span_text TEXT NOT NULL,
|
|
char_start INTEGER, -- Start position in review_text
|
|
char_end INTEGER, -- End position in review_text
|
|
span_order SMALLINT NOT NULL DEFAULT 1, -- Order within review
|
|
|
|
-- Implementation profile used
|
|
profile VARCHAR(10) NOT NULL DEFAULT 'standard',
|
|
|
|
-- Primary classification (required for all profiles)
|
|
primary_code VARCHAR(6) NOT NULL,
|
|
primary_tier SMALLINT NOT NULL, -- 1=domain, 2=category, 3=subcode
|
|
|
|
-- Metadata dimensions (availability depends on profile)
|
|
valence VARCHAR(2) NOT NULL,
|
|
intensity VARCHAR(2) NOT NULL,
|
|
specificity VARCHAR(2), -- Standard+ only
|
|
actionability VARCHAR(2), -- Standard+ only
|
|
temporal VARCHAR(2) DEFAULT 'TC',
|
|
evidence VARCHAR(2) DEFAULT 'ES',
|
|
comparative VARCHAR(4) DEFAULT 'CR-N',
|
|
|
|
-- Extended attributes for issue aggregation
|
|
entity_reference VARCHAR(255), -- Product, person, feature
|
|
location_reference VARCHAR(255), -- Physical or logical location
|
|
|
|
-- Confidence and annotation metadata
|
|
confidence_score NUMERIC(3,2), -- 0.00-1.00
|
|
annotator_notes TEXT,
|
|
annotation_source VARCHAR(20), -- 'human', 'llm', 'hybrid'
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
deleted_at TIMESTAMPTZ,
|
|
|
|
CONSTRAINT spans_profile_check
|
|
CHECK (profile IN ('lite', 'core', 'standard', 'full')),
|
|
CONSTRAINT spans_tier_check
|
|
CHECK (primary_tier IN (1, 2, 3)),
|
|
CONSTRAINT spans_valence_check
|
|
CHECK (valence IN ('V+', 'V-', 'V0', 'V±')),
|
|
CONSTRAINT spans_intensity_check
|
|
CHECK (intensity IN ('I1', 'I2', 'I3')),
|
|
CONSTRAINT spans_specificity_check
|
|
CHECK (specificity IS NULL OR specificity IN ('S1', 'S2', 'S3')),
|
|
CONSTRAINT spans_actionability_check
|
|
CHECK (actionability IS NULL OR actionability IN ('A1', 'A2', 'A3')),
|
|
CONSTRAINT spans_temporal_check
|
|
CHECK (temporal IS NULL OR temporal IN ('TC', 'TR', 'TH', 'TF')),
|
|
CONSTRAINT spans_evidence_check
|
|
CHECK (evidence IS NULL OR evidence IN ('ES', 'EI', 'EC')),
|
|
CONSTRAINT spans_comparative_check
|
|
CHECK (comparative IS NULL OR comparative IN ('CR-N', 'CR-B', 'CR-W', 'CR-S')),
|
|
CONSTRAINT spans_confidence_check
|
|
CHECK (confidence_score IS NULL OR (confidence_score >= 0 AND confidence_score <= 1)),
|
|
CONSTRAINT spans_char_range_check
|
|
CHECK ((char_start IS NULL AND char_end IS NULL) OR (char_start < char_end))
|
|
);
|
|
|
|
CREATE INDEX idx_spans_review ON spans(review_id);
|
|
CREATE INDEX idx_spans_primary_code ON spans(primary_code);
|
|
CREATE INDEX idx_spans_valence ON spans(valence);
|
|
CREATE INDEX idx_spans_intensity ON spans(intensity);
|
|
CREATE INDEX idx_spans_comparative ON spans(comparative) WHERE comparative != 'CR-N';
|
|
CREATE INDEX idx_spans_created ON spans(created_at);
|
|
CREATE INDEX idx_spans_deleted ON spans(deleted_at) WHERE deleted_at IS NULL;
|
|
CREATE INDEX idx_spans_entity ON spans(entity_reference) WHERE entity_reference IS NOT NULL;
|
|
CREATE INDEX idx_spans_location ON spans(location_reference) WHERE location_reference IS NOT NULL;
|
|
|
|
-- Composite indexes for common query patterns
|
|
CREATE INDEX idx_spans_classification ON spans(primary_code, valence, intensity);
|
|
CREATE INDEX idx_spans_negative ON spans(primary_code, created_at) WHERE valence = 'V-';
|
|
|
|
COMMENT ON TABLE spans IS
|
|
'Individual classified text segments from reviews with URT metadata';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 2.3 Span Classifications (Secondary Codes)
|
|
-- Supports up to 2 secondary codes per span (Standard+ profiles)
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE span_classifications (
|
|
classification_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
span_id UUID NOT NULL REFERENCES spans(span_id) ON DELETE CASCADE,
|
|
code VARCHAR(6) NOT NULL,
|
|
code_tier SMALLINT NOT NULL,
|
|
is_primary BOOLEAN NOT NULL DEFAULT FALSE,
|
|
classification_order SMALLINT NOT NULL DEFAULT 1,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT span_class_tier_check
|
|
CHECK (code_tier IN (1, 2, 3)),
|
|
CONSTRAINT span_class_order_check
|
|
CHECK (classification_order BETWEEN 1 AND 3),
|
|
|
|
-- Ensure max 3 classifications per span (1 primary + 2 secondary)
|
|
UNIQUE (span_id, classification_order)
|
|
);
|
|
|
|
CREATE INDEX idx_span_class_span ON span_classifications(span_id);
|
|
CREATE INDEX idx_span_class_code ON span_classifications(code);
|
|
CREATE INDEX idx_span_class_primary ON span_classifications(span_id) WHERE is_primary = TRUE;
|
|
|
|
COMMENT ON TABLE span_classifications IS
|
|
'Primary and secondary code assignments for spans (max 3 per span)';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 2.4 Causal Chains
|
|
-- Links spans to causal codes (CD -> MG -> SY) for Full profile
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE causal_chains (
|
|
chain_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
span_id UUID NOT NULL REFERENCES spans(span_id) ON DELETE CASCADE,
|
|
|
|
-- Causal chain: Conditions -> Management -> Systemic
|
|
condition_code VARCHAR(4) REFERENCES urt_causal_codes(causal_code),
|
|
management_code VARCHAR(4) REFERENCES urt_causal_codes(causal_code),
|
|
systemic_code VARCHAR(4) REFERENCES urt_causal_codes(causal_code),
|
|
|
|
-- Confidence in causal attribution
|
|
chain_confidence NUMERIC(3,2),
|
|
analyst_notes TEXT,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT causal_chain_layer_check CHECK (
|
|
(condition_code IS NULL OR condition_code ~ '^CD-') AND
|
|
(management_code IS NULL OR management_code ~ '^MG-') AND
|
|
(systemic_code IS NULL OR systemic_code ~ '^SY-')
|
|
),
|
|
CONSTRAINT causal_chain_minimum CHECK (
|
|
condition_code IS NOT NULL OR management_code IS NOT NULL OR systemic_code IS NOT NULL
|
|
)
|
|
);
|
|
|
|
CREATE INDEX idx_causal_span ON causal_chains(span_id);
|
|
CREATE INDEX idx_causal_condition ON causal_chains(condition_code) WHERE condition_code IS NOT NULL;
|
|
CREATE INDEX idx_causal_management ON causal_chains(management_code) WHERE management_code IS NOT NULL;
|
|
CREATE INDEX idx_causal_systemic ON causal_chains(systemic_code) WHERE systemic_code IS NOT NULL;
|
|
|
|
COMMENT ON TABLE causal_chains IS
|
|
'Root cause analysis chains linking spans to CD/MG/SY causal codes';
|
|
|
|
-- =============================================================================
|
|
-- SECTION 3: ISSUE TRACKING TABLES
|
|
-- Based on C1 Issue Lifecycle Framework
|
|
-- =============================================================================
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 3.1 Issues
|
|
-- Aggregated issue records from negative spans
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE issues (
|
|
issue_id VARCHAR(20) PRIMARY KEY, -- Format: ISSUE-YYYY-NNNN
|
|
|
|
-- Classification context
|
|
primary_subcode VARCHAR(6) NOT NULL,
|
|
domain_code CHAR(1) NOT NULL REFERENCES urt_domains(domain_code),
|
|
|
|
-- Aggregation context
|
|
entity_reference VARCHAR(255),
|
|
location_reference VARCHAR(255),
|
|
|
|
-- Current state (per C1 state machine)
|
|
state VARCHAR(15) NOT NULL DEFAULT 'DETECTED',
|
|
state_changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
-- Metrics from aggregated spans
|
|
span_count INTEGER NOT NULL DEFAULT 1,
|
|
max_intensity VARCHAR(2) NOT NULL,
|
|
priority_score NUMERIC(6,2) NOT NULL,
|
|
confidence_score NUMERIC(3,2) NOT NULL,
|
|
|
|
-- Ownership
|
|
owner_team VARCHAR(100),
|
|
owner_individual VARCHAR(100),
|
|
|
|
-- Resolution details
|
|
resolution_code VARCHAR(20),
|
|
resolution_notes TEXT,
|
|
decline_reason VARCHAR(10),
|
|
|
|
-- Causal analysis (for Full profile)
|
|
causal_codes VARCHAR(4)[],
|
|
|
|
-- Recurrence tracking
|
|
reopen_count INTEGER NOT NULL DEFAULT 0,
|
|
verification_window_days INTEGER DEFAULT 60,
|
|
|
|
-- Key timestamps
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
acknowledged_at TIMESTAMPTZ,
|
|
work_started_at TIMESTAMPTZ,
|
|
resolved_at TIMESTAMPTZ,
|
|
verified_at TIMESTAMPTZ,
|
|
declined_at TIMESTAMPTZ,
|
|
|
|
-- Soft delete
|
|
deleted_at TIMESTAMPTZ,
|
|
|
|
CONSTRAINT issues_state_check CHECK (
|
|
state IN ('DETECTED', 'ACKNOWLEDGED', 'IN_PROGRESS', 'RESOLVED',
|
|
'VERIFIED', 'DECLINED', 'REOPENED', 'STALE')
|
|
),
|
|
CONSTRAINT issues_intensity_check
|
|
CHECK (max_intensity IN ('I1', 'I2', 'I3')),
|
|
CONSTRAINT issues_decline_reason_check CHECK (
|
|
decline_reason IS NULL OR
|
|
decline_reason IN ('DEC-DUP', 'DEC-OOS', 'DEC-INS', 'DEC-NAR',
|
|
'DEC-EXT', 'DEC-POL', 'DEC-OLD')
|
|
),
|
|
CONSTRAINT issues_verification_window_check
|
|
CHECK (verification_window_days IN (30, 60, 90))
|
|
);
|
|
|
|
CREATE INDEX idx_issues_state ON issues(state);
|
|
CREATE INDEX idx_issues_domain ON issues(domain_code);
|
|
CREATE INDEX idx_issues_subcode ON issues(primary_subcode);
|
|
CREATE INDEX idx_issues_priority ON issues(priority_score DESC);
|
|
CREATE INDEX idx_issues_created ON issues(created_at);
|
|
CREATE INDEX idx_issues_owner_team ON issues(owner_team);
|
|
CREATE INDEX idx_issues_deleted ON issues(deleted_at) WHERE deleted_at IS NULL;
|
|
|
|
-- Partial indexes for active issues
|
|
CREATE INDEX idx_issues_open ON issues(priority_score DESC)
|
|
WHERE state IN ('DETECTED', 'ACKNOWLEDGED', 'IN_PROGRESS', 'REOPENED');
|
|
CREATE INDEX idx_issues_pending_verification ON issues(resolved_at, verification_window_days)
|
|
WHERE state = 'RESOLVED';
|
|
|
|
COMMENT ON TABLE issues IS
|
|
'Aggregated issues from negative feedback spans per C1 lifecycle framework';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 3.2 Issue-Span Links
|
|
-- Many-to-many relationship between issues and contributing spans
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE issue_spans (
|
|
issue_id VARCHAR(20) NOT NULL REFERENCES issues(issue_id) ON DELETE CASCADE,
|
|
span_id UUID NOT NULL REFERENCES spans(span_id) ON DELETE CASCADE,
|
|
linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
link_type VARCHAR(20) NOT NULL DEFAULT 'contributor',
|
|
correlation_score NUMERIC(3,2),
|
|
|
|
PRIMARY KEY (issue_id, span_id),
|
|
|
|
CONSTRAINT issue_spans_link_type_check
|
|
CHECK (link_type IN ('contributor', 'verification', 'reopen_trigger'))
|
|
);
|
|
|
|
CREATE INDEX idx_issue_spans_span ON issue_spans(span_id);
|
|
CREATE INDEX idx_issue_spans_linked ON issue_spans(linked_at);
|
|
|
|
COMMENT ON TABLE issue_spans IS
|
|
'Links between issues and their contributing spans';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 3.3 Issue State History
|
|
-- Complete audit trail of state transitions
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE issue_state_history (
|
|
history_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
issue_id VARCHAR(20) NOT NULL REFERENCES issues(issue_id) ON DELETE CASCADE,
|
|
|
|
from_state VARCHAR(15),
|
|
to_state VARCHAR(15) NOT NULL,
|
|
trigger_type VARCHAR(20) NOT NULL, -- 'manual', 'auto', 'span', 'sla'
|
|
trigger_span_id UUID REFERENCES spans(span_id),
|
|
|
|
actor_id VARCHAR(100),
|
|
actor_type VARCHAR(20), -- 'user', 'system', 'rule'
|
|
notes TEXT,
|
|
|
|
transitioned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT history_state_check CHECK (
|
|
to_state IN ('DETECTED', 'ACKNOWLEDGED', 'IN_PROGRESS', 'RESOLVED',
|
|
'VERIFIED', 'DECLINED', 'REOPENED', 'STALE')
|
|
)
|
|
);
|
|
|
|
CREATE INDEX idx_history_issue ON issue_state_history(issue_id);
|
|
CREATE INDEX idx_history_transition ON issue_state_history(transitioned_at);
|
|
CREATE INDEX idx_history_state ON issue_state_history(to_state);
|
|
|
|
COMMENT ON TABLE issue_state_history IS
|
|
'Audit trail of all issue state transitions';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 3.4 Issue Resolutions
|
|
-- Detailed resolution records for resolved/verified issues
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE issue_resolutions (
|
|
resolution_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
issue_id VARCHAR(20) NOT NULL REFERENCES issues(issue_id) ON DELETE CASCADE,
|
|
|
|
resolution_code VARCHAR(20) NOT NULL,
|
|
resolution_type VARCHAR(30) NOT NULL, -- 'fix', 'workaround', 'policy_change', etc.
|
|
resolution_summary TEXT NOT NULL,
|
|
resolution_details TEXT,
|
|
|
|
-- Preventive measures
|
|
prevention_notes TEXT,
|
|
process_changes TEXT,
|
|
|
|
-- Verification
|
|
verified_by_span_id UUID REFERENCES spans(span_id),
|
|
verification_method VARCHAR(20), -- 'cr_b', 'positive_span', 'time_based'
|
|
|
|
resolved_by VARCHAR(100),
|
|
resolved_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
verified_at TIMESTAMPTZ,
|
|
|
|
CONSTRAINT resolution_type_check CHECK (
|
|
resolution_type IN ('fix', 'workaround', 'policy_change', 'training',
|
|
'process_improvement', 'equipment', 'other')
|
|
)
|
|
);
|
|
|
|
CREATE INDEX idx_resolutions_issue ON issue_resolutions(issue_id);
|
|
CREATE INDEX idx_resolutions_code ON issue_resolutions(resolution_code);
|
|
CREATE INDEX idx_resolutions_resolved ON issue_resolutions(resolved_at);
|
|
|
|
COMMENT ON TABLE issue_resolutions IS
|
|
'Detailed resolution records with verification tracking';
|
|
|
|
-- =============================================================================
|
|
-- SECTION 4: AUDIT AND HISTORY TABLES
|
|
-- Comprehensive tracking for compliance and analytics
|
|
-- =============================================================================
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 4.1 Annotation Audit
|
|
-- Who annotated what, when, and how
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE annotation_audit (
|
|
audit_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
span_id UUID NOT NULL REFERENCES spans(span_id) ON DELETE CASCADE,
|
|
|
|
annotator_id VARCHAR(100) NOT NULL,
|
|
annotator_type VARCHAR(20) NOT NULL, -- 'human', 'llm', 'rule'
|
|
model_version VARCHAR(50), -- For LLM annotations
|
|
|
|
-- What was set
|
|
field_name VARCHAR(30) NOT NULL,
|
|
old_value TEXT,
|
|
new_value TEXT NOT NULL,
|
|
|
|
-- Confidence and reasoning
|
|
confidence NUMERIC(3,2),
|
|
reasoning TEXT,
|
|
|
|
annotated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT audit_annotator_type_check
|
|
CHECK (annotator_type IN ('human', 'llm', 'rule', 'hybrid'))
|
|
);
|
|
|
|
CREATE INDEX idx_audit_span ON annotation_audit(span_id);
|
|
CREATE INDEX idx_audit_annotator ON annotation_audit(annotator_id);
|
|
CREATE INDEX idx_audit_field ON annotation_audit(field_name);
|
|
CREATE INDEX idx_audit_time ON annotation_audit(annotated_at);
|
|
|
|
COMMENT ON TABLE annotation_audit IS
|
|
'Complete audit trail of all span annotation changes';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 4.2 Classification History
|
|
-- Track changes to span classifications over time
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE TABLE classification_history (
|
|
history_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
span_id UUID NOT NULL REFERENCES spans(span_id) ON DELETE CASCADE,
|
|
|
|
change_type VARCHAR(20) NOT NULL, -- 'create', 'update', 'delete'
|
|
|
|
-- Previous state (null for create)
|
|
prev_primary_code VARCHAR(6),
|
|
prev_valence VARCHAR(2),
|
|
prev_intensity VARCHAR(2),
|
|
prev_secondary_codes VARCHAR(6)[],
|
|
|
|
-- New state (null for delete)
|
|
new_primary_code VARCHAR(6),
|
|
new_valence VARCHAR(2),
|
|
new_intensity VARCHAR(2),
|
|
new_secondary_codes VARCHAR(6)[],
|
|
|
|
change_reason TEXT,
|
|
changed_by VARCHAR(100) NOT NULL,
|
|
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT history_change_type_check
|
|
CHECK (change_type IN ('create', 'update', 'delete'))
|
|
);
|
|
|
|
CREATE INDEX idx_class_history_span ON classification_history(span_id);
|
|
CREATE INDEX idx_class_history_time ON classification_history(changed_at);
|
|
CREATE INDEX idx_class_history_type ON classification_history(change_type);
|
|
|
|
COMMENT ON TABLE classification_history IS
|
|
'Historical record of classification changes for audit and rollback';
|
|
|
|
-- =============================================================================
|
|
-- SECTION 5: ANALYTICS VIEWS AND MATERIALIZED VIEWS
|
|
-- Pre-computed aggregations for dashboard and reporting
|
|
-- =============================================================================
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 5.1 Domain Summary View
|
|
-- Current counts and averages by domain
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE MATERIALIZED VIEW mv_domain_summary AS
|
|
SELECT
|
|
d.domain_code,
|
|
d.name AS domain_name,
|
|
d.default_owner,
|
|
COUNT(DISTINCT s.span_id) AS total_spans,
|
|
COUNT(DISTINCT s.span_id) FILTER (WHERE s.valence = 'V+') AS positive_spans,
|
|
COUNT(DISTINCT s.span_id) FILTER (WHERE s.valence = 'V-') AS negative_spans,
|
|
COUNT(DISTINCT s.span_id) FILTER (WHERE s.valence = 'V0') AS neutral_spans,
|
|
COUNT(DISTINCT s.span_id) FILTER (WHERE s.valence = 'V±') AS mixed_spans,
|
|
COUNT(DISTINCT s.span_id) FILTER (WHERE s.intensity = 'I3') AS critical_spans,
|
|
ROUND(AVG(s.confidence_score), 3) AS avg_confidence,
|
|
COUNT(DISTINCT i.issue_id) FILTER (WHERE i.state NOT IN ('VERIFIED', 'DECLINED')) AS open_issues,
|
|
MAX(s.created_at) AS last_span_at
|
|
FROM urt_domains d
|
|
LEFT JOIN spans s ON LEFT(s.primary_code, 1) = d.domain_code
|
|
AND s.deleted_at IS NULL
|
|
LEFT JOIN issues i ON i.domain_code = d.domain_code
|
|
AND i.deleted_at IS NULL
|
|
GROUP BY d.domain_code, d.name, d.default_owner
|
|
ORDER BY d.display_order;
|
|
|
|
CREATE UNIQUE INDEX idx_mv_domain_summary ON mv_domain_summary(domain_code);
|
|
|
|
COMMENT ON MATERIALIZED VIEW mv_domain_summary IS
|
|
'Aggregated metrics by URT domain for executive dashboards';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 5.2 Category Breakdown View
|
|
-- Detailed metrics at category level
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE MATERIALIZED VIEW mv_category_breakdown AS
|
|
SELECT
|
|
c.category_code,
|
|
c.name AS category_name,
|
|
c.domain_code,
|
|
d.name AS domain_name,
|
|
COUNT(DISTINCT s.span_id) AS total_spans,
|
|
COUNT(DISTINCT s.span_id) FILTER (WHERE s.valence = 'V-') AS negative_count,
|
|
COUNT(DISTINCT s.span_id) FILTER (WHERE s.valence = 'V+') AS positive_count,
|
|
ROUND(
|
|
COUNT(DISTINCT s.span_id) FILTER (WHERE s.valence = 'V-')::NUMERIC /
|
|
NULLIF(COUNT(DISTINCT s.span_id), 0) * 100, 1
|
|
) AS negative_pct,
|
|
COUNT(DISTINCT i.issue_id) AS total_issues,
|
|
COUNT(DISTINCT i.issue_id) FILTER (WHERE i.state = 'VERIFIED') AS resolved_verified,
|
|
ROUND(AVG(CASE
|
|
WHEN s.intensity = 'I1' THEN 1
|
|
WHEN s.intensity = 'I2' THEN 2
|
|
WHEN s.intensity = 'I3' THEN 3
|
|
END), 2) AS avg_intensity
|
|
FROM urt_categories c
|
|
JOIN urt_domains d ON c.domain_code = d.domain_code
|
|
LEFT JOIN spans s ON s.primary_code LIKE c.category_code || '%'
|
|
AND s.deleted_at IS NULL
|
|
LEFT JOIN issues i ON i.primary_subcode LIKE c.category_code || '%'
|
|
AND i.deleted_at IS NULL
|
|
GROUP BY c.category_code, c.name, c.domain_code, d.name
|
|
ORDER BY c.domain_code, c.display_order;
|
|
|
|
CREATE UNIQUE INDEX idx_mv_category_breakdown ON mv_category_breakdown(category_code);
|
|
|
|
COMMENT ON MATERIALIZED VIEW mv_category_breakdown IS
|
|
'Detailed category-level metrics for operational analysis';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 5.3 Issue Pipeline View
|
|
-- Current state of all open issues
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE MATERIALIZED VIEW mv_issue_pipeline AS
|
|
SELECT
|
|
i.state,
|
|
i.domain_code,
|
|
d.name AS domain_name,
|
|
COUNT(*) AS issue_count,
|
|
AVG(i.priority_score) AS avg_priority,
|
|
AVG(i.span_count) AS avg_span_count,
|
|
AVG(EXTRACT(EPOCH FROM (NOW() - i.created_at)) / 3600) AS avg_age_hours,
|
|
COUNT(*) FILTER (WHERE i.max_intensity = 'I3') AS critical_count,
|
|
COUNT(*) FILTER (WHERE i.reopen_count > 0) AS reopened_count
|
|
FROM issues i
|
|
JOIN urt_domains d ON i.domain_code = d.domain_code
|
|
WHERE i.deleted_at IS NULL
|
|
AND i.state NOT IN ('VERIFIED', 'DECLINED')
|
|
GROUP BY i.state, i.domain_code, d.name
|
|
ORDER BY
|
|
CASE i.state
|
|
WHEN 'DETECTED' THEN 1
|
|
WHEN 'ACKNOWLEDGED' THEN 2
|
|
WHEN 'IN_PROGRESS' THEN 3
|
|
WHEN 'REOPENED' THEN 4
|
|
WHEN 'RESOLVED' THEN 5
|
|
ELSE 6
|
|
END,
|
|
d.display_order;
|
|
|
|
CREATE INDEX idx_mv_issue_pipeline ON mv_issue_pipeline(state, domain_code);
|
|
|
|
COMMENT ON MATERIALIZED VIEW mv_issue_pipeline IS
|
|
'Current issue pipeline by state and domain for SLA tracking';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 5.4 Comparative Reference Trends View
|
|
-- Track improvement/decline signals over time
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE MATERIALIZED VIEW mv_cr_trends AS
|
|
SELECT
|
|
DATE_TRUNC('week', s.created_at) AS week_start,
|
|
s.comparative,
|
|
LEFT(s.primary_code, 1) AS domain_code,
|
|
COUNT(*) AS span_count,
|
|
COUNT(*) FILTER (WHERE s.valence = 'V+') AS positive_count,
|
|
COUNT(*) FILTER (WHERE s.valence = 'V-') AS negative_count
|
|
FROM spans s
|
|
WHERE s.deleted_at IS NULL
|
|
AND s.comparative IS NOT NULL
|
|
AND s.created_at >= NOW() - INTERVAL '90 days'
|
|
GROUP BY DATE_TRUNC('week', s.created_at), s.comparative, LEFT(s.primary_code, 1)
|
|
ORDER BY week_start DESC, domain_code;
|
|
|
|
CREATE INDEX idx_mv_cr_trends ON mv_cr_trends(week_start, comparative);
|
|
|
|
COMMENT ON MATERIALIZED VIEW mv_cr_trends IS
|
|
'Weekly trends of comparative reference signals (CR-B/W/S) for improvement tracking';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 5.5 Daily Span Volume (for time-series analysis)
|
|
-- Partitioning-ready daily aggregation
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE MATERIALIZED VIEW mv_daily_volume AS
|
|
SELECT
|
|
DATE(s.created_at) AS span_date,
|
|
LEFT(s.primary_code, 1) AS domain_code,
|
|
s.valence,
|
|
s.intensity,
|
|
COUNT(*) AS span_count,
|
|
COUNT(DISTINCT s.review_id) AS review_count,
|
|
AVG(s.confidence_score) AS avg_confidence
|
|
FROM spans s
|
|
WHERE s.deleted_at IS NULL
|
|
GROUP BY DATE(s.created_at), LEFT(s.primary_code, 1), s.valence, s.intensity
|
|
ORDER BY span_date DESC;
|
|
|
|
CREATE INDEX idx_mv_daily_volume_date ON mv_daily_volume(span_date);
|
|
CREATE INDEX idx_mv_daily_volume_domain ON mv_daily_volume(domain_code);
|
|
|
|
COMMENT ON MATERIALIZED VIEW mv_daily_volume IS
|
|
'Daily span volume by domain/valence/intensity for trend analysis';
|
|
|
|
-- =============================================================================
|
|
-- SECTION 6: HELPER FUNCTIONS
|
|
-- Utility functions for common operations
|
|
-- =============================================================================
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 6.1 Generate Issue ID
|
|
-- Format: ISSUE-YYYY-NNNN (sequential within year)
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE SEQUENCE issue_id_seq START 1;
|
|
|
|
CREATE OR REPLACE FUNCTION generate_issue_id()
|
|
RETURNS VARCHAR(20) AS $$
|
|
DECLARE
|
|
year_part VARCHAR(4);
|
|
seq_part INTEGER;
|
|
BEGIN
|
|
year_part := TO_CHAR(NOW(), 'YYYY');
|
|
seq_part := NEXTVAL('issue_id_seq');
|
|
RETURN 'ISSUE-' || year_part || '-' || LPAD(seq_part::TEXT, 4, '0');
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
COMMENT ON FUNCTION generate_issue_id IS
|
|
'Generate sequential issue IDs in format ISSUE-YYYY-NNNN';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 6.2 Calculate Priority Score
|
|
-- Based on C1 framework: I_weight * (1 + log(span_count)) * decay * recurrence_boost
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE OR REPLACE FUNCTION calculate_priority_score(
|
|
p_max_intensity VARCHAR(2),
|
|
p_span_count INTEGER,
|
|
p_days_old NUMERIC,
|
|
p_recurrence_count INTEGER DEFAULT 0,
|
|
p_has_cr_w BOOLEAN DEFAULT FALSE
|
|
) RETURNS NUMERIC AS $$
|
|
DECLARE
|
|
intensity_weight NUMERIC;
|
|
decay_factor NUMERIC;
|
|
recurrence_boost NUMERIC;
|
|
trend_modifier NUMERIC;
|
|
BEGIN
|
|
-- Intensity weights
|
|
intensity_weight := CASE p_max_intensity
|
|
WHEN 'I1' THEN 1.0
|
|
WHEN 'I2' THEN 2.0
|
|
WHEN 'I3' THEN 4.0
|
|
ELSE 1.0
|
|
END;
|
|
|
|
-- Time decay: exp(-lambda * days), lambda = 0.023 (half-life ~30 days)
|
|
decay_factor := EXP(-0.023 * p_days_old);
|
|
|
|
-- Recurrence boost: 1.0 + 0.5 * log2(recurrence_count + 1)
|
|
IF p_recurrence_count > 0 THEN
|
|
recurrence_boost := 1.0 + 0.5 * LOG(2, p_recurrence_count + 1);
|
|
ELSE
|
|
recurrence_boost := 1.0;
|
|
END IF;
|
|
|
|
-- Trend modifier (CR-W = worsening = 1.3)
|
|
trend_modifier := CASE WHEN p_has_cr_w THEN 1.3 ELSE 1.0 END;
|
|
|
|
RETURN ROUND(
|
|
intensity_weight * (1 + LOG(GREATEST(p_span_count, 1))) *
|
|
decay_factor * recurrence_boost * trend_modifier,
|
|
2
|
|
);
|
|
END;
|
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
|
|
|
COMMENT ON FUNCTION calculate_priority_score IS
|
|
'Calculate issue priority score per C1 time decay model';
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 6.3 Calculate Confidence Score
|
|
-- Based on C1: base_confidence + specificity_bonus, weighted by evidence type
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE OR REPLACE FUNCTION calculate_confidence_score(
|
|
p_span_count INTEGER,
|
|
p_max_specificity VARCHAR(2),
|
|
p_avg_evidence_weight NUMERIC
|
|
) RETURNS NUMERIC AS $$
|
|
DECLARE
|
|
base_confidence NUMERIC;
|
|
specificity_bonus NUMERIC;
|
|
BEGIN
|
|
-- Base confidence by span count
|
|
base_confidence := CASE
|
|
WHEN p_span_count = 1 THEN 0.50
|
|
WHEN p_span_count = 2 THEN 0.70
|
|
WHEN p_span_count = 3 THEN 0.80
|
|
WHEN p_span_count BETWEEN 4 AND 5 THEN 0.85
|
|
WHEN p_span_count BETWEEN 6 AND 10 THEN 0.90
|
|
ELSE 0.95
|
|
END;
|
|
|
|
-- Specificity bonus
|
|
specificity_bonus := CASE p_max_specificity
|
|
WHEN 'S1' THEN 0.00
|
|
WHEN 'S2' THEN 0.05
|
|
WHEN 'S3' THEN 0.10
|
|
ELSE 0.00
|
|
END;
|
|
|
|
RETURN ROUND(
|
|
LEAST(0.95, base_confidence + specificity_bonus) * COALESCE(p_avg_evidence_weight, 1.0),
|
|
3
|
|
);
|
|
END;
|
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
|
|
|
COMMENT ON FUNCTION calculate_confidence_score IS
|
|
'Calculate issue confidence score per C1 framework';
|
|
|
|
-- =============================================================================
|
|
-- SECTION 7: TRIGGERS
|
|
-- Automated data maintenance
|
|
-- =============================================================================
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 7.1 Auto-update timestamps
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER tr_reviews_updated
|
|
BEFORE UPDATE ON reviews
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
CREATE TRIGGER tr_spans_updated
|
|
BEFORE UPDATE ON spans
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
CREATE TRIGGER tr_causal_chains_updated
|
|
BEFORE UPDATE ON causal_chains
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 7.2 Refresh materialized views (scheduled separately)
|
|
-- -----------------------------------------------------------------------------
|
|
CREATE OR REPLACE FUNCTION refresh_urt_materialized_views()
|
|
RETURNS void AS $$
|
|
BEGIN
|
|
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_domain_summary;
|
|
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_category_breakdown;
|
|
REFRESH MATERIALIZED VIEW mv_issue_pipeline;
|
|
REFRESH MATERIALIZED VIEW mv_cr_trends;
|
|
REFRESH MATERIALIZED VIEW mv_daily_volume;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
COMMENT ON FUNCTION refresh_urt_materialized_views IS
|
|
'Refresh all URT materialized views - call periodically via pg_cron or similar';
|
|
|
|
-- =============================================================================
|
|
-- SECTION 8: REFERENCE DATA INSERTS
|
|
-- Populate reference tables from B1-urt-codes.yaml
|
|
-- =============================================================================
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 8.1 Insert Domains
|
|
-- -----------------------------------------------------------------------------
|
|
INSERT INTO urt_domains (domain_code, name, description, core_question, default_owner, display_order) VALUES
|
|
('O', 'Offering', 'The core product, service, or outcome delivered', 'Does what we provide actually work and meet expectations?', 'Product / Operations', 1),
|
|
('P', 'People', 'Human interactions and personnel behavior', 'How do the people we interact with treat us and perform their roles?', 'HR / Training', 2),
|
|
('J', 'Journey', 'The process, timing, and operational flow', 'Is the experience smooth, timely, and friction-free?', 'Operations / Process', 3),
|
|
('E', 'Environment', 'Physical, digital, and ambient context', 'Is the space where the experience occurs functional, safe, and pleasant?', 'Facilities / IT', 4),
|
|
('A', 'Access', 'Availability, accessibility, and inclusivity', 'Can everyone who wants to participate do so fully and fairly?', 'Compliance / Design', 5),
|
|
('V', 'Value', 'Cost, pricing, and worth of the exchange', 'Is what I am giving up fair for what I am getting?', 'Finance / Pricing', 6),
|
|
('R', 'Relationship', 'Trust, reliability, and ongoing connection', 'Can I trust this business and do they value our relationship?', 'Leadership / CX', 7);
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 8.2 Insert Categories (28 total)
|
|
-- -----------------------------------------------------------------------------
|
|
INSERT INTO urt_categories (category_code, domain_code, name, definition, display_order) VALUES
|
|
-- Offering
|
|
('O1', 'O', 'Function', 'Does it do what it is supposed to do?', 1),
|
|
('O2', 'O', 'Quality', 'How well is it made or executed?', 2),
|
|
('O3', 'O', 'Completeness', 'Is everything included that should be?', 3),
|
|
('O4', 'O', 'Fit', 'Does it match the customer specific needs?', 4),
|
|
-- People
|
|
('P1', 'P', 'Attitude', 'Disposition, manner, and emotional tone', 1),
|
|
('P2', 'P', 'Competence', 'Knowledge, skill, and professional capability', 2),
|
|
('P3', 'P', 'Responsiveness', 'Attentiveness, initiative, and follow-through', 3),
|
|
('P4', 'P', 'Communication', 'Quality of information exchange', 4),
|
|
-- Journey
|
|
('J1', 'J', 'Timing', 'Speed, punctuality, and time management', 1),
|
|
('J2', 'J', 'Ease', 'Effort required and friction encountered', 2),
|
|
('J3', 'J', 'Reliability', 'Consistency and predictability of process', 3),
|
|
('J4', 'J', 'Resolution', 'How problems are handled when they arise', 4),
|
|
-- Environment
|
|
('E1', 'E', 'Physical Space', 'Tangible environment attributes', 1),
|
|
('E2', 'E', 'Digital Space', 'Online and application interface', 2),
|
|
('E3', 'E', 'Ambiance', 'Intangible environmental qualities', 3),
|
|
('E4', 'E', 'Safety', 'Security and wellbeing factors', 4),
|
|
-- Access
|
|
('A1', 'A', 'Availability', 'Can you get it when you need it?', 1),
|
|
('A2', 'A', 'Accessibility', 'Can everyone use it regardless of ability?', 2),
|
|
('A3', 'A', 'Inclusivity', 'Does it work for diverse backgrounds?', 3),
|
|
('A4', 'A', 'Convenience', 'Is it easy to reach and engage with?', 4),
|
|
-- Value
|
|
('V1', 'V', 'Price', 'The monetary cost', 1),
|
|
('V2', 'V', 'Transparency', 'Clarity and honesty about costs', 2),
|
|
('V3', 'V', 'Effort', 'Non-monetary costs (time, hassle)', 3),
|
|
('V4', 'V', 'Worth', 'Overall value assessment', 4),
|
|
-- Relationship
|
|
('R1', 'R', 'Integrity', 'Honesty and ethical behavior', 1),
|
|
('R2', 'R', 'Dependability', 'Consistency over time', 2),
|
|
('R3', 'R', 'Recovery', 'Response to failures', 3),
|
|
('R4', 'R', 'Loyalty', 'Investment in ongoing relationship', 4);
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 8.3 Insert Causal Codes (16 total)
|
|
-- -----------------------------------------------------------------------------
|
|
INSERT INTO urt_causal_codes (causal_code, layer, layer_prefix, name, definition, display_order) VALUES
|
|
-- Conditions layer (5 codes)
|
|
('CD-S', 'conditions', 'CD-', 'Staff State', 'Fatigue, training, motivation, experience', 1),
|
|
('CD-T', 'conditions', 'CD-', 'Team Dynamics', 'Handoffs, coordination, communication', 2),
|
|
('CD-E', 'conditions', 'CD-', 'Equipment', 'Malfunction, unavailable, outdated', 3),
|
|
('CD-F', 'conditions', 'CD-', 'Facility', 'Maintenance, capacity, hazards', 4),
|
|
('CD-O', 'conditions', 'CD-', 'Operational', 'Understaffing, demand surge, time pressure', 5),
|
|
-- Management layer (5 codes)
|
|
('MG-P', 'management', 'MG-', 'Planning', 'Staffing plans, scheduling, forecasting', 1),
|
|
('MG-T', 'management', 'MG-', 'Training', 'Preparation, development, competency', 2),
|
|
('MG-O', 'management', 'MG-', 'Oversight', 'Supervision, monitoring, correction', 3),
|
|
('MG-R', 'management', 'MG-', 'Resources', 'Maintenance, supplies, equipment', 4),
|
|
('MG-C', 'management', 'MG-', 'Communication', 'Policy relay, expectations, culture', 5),
|
|
-- Systemic layer (6 codes)
|
|
('SY-R', 'systemic', 'SY-', 'Resource Decisions', 'Budget, investment, staffing levels', 1),
|
|
('SY-P', 'systemic', 'SY-', 'Policy/Procedure', 'Rules, requirements, bureaucracy', 2),
|
|
('SY-C', 'systemic', 'SY-', 'Culture', 'Values, priorities, norms', 3),
|
|
('SY-S', 'systemic', 'SY-', 'Standards', 'Quality thresholds, metrics, expectations', 4),
|
|
('SY-H', 'systemic', 'SY-', 'Human Capital', 'Compensation, hiring, retention', 5),
|
|
('SY-X', 'systemic', 'SY-', 'External', 'Market, regulatory, competitive pressure', 6);
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 8.4 Insert Metadata Values (24 total across 7 dimensions)
|
|
-- -----------------------------------------------------------------------------
|
|
INSERT INTO urt_metadata_values (dimension_code, value_code, dimension_name, label, definition, is_default, display_order) VALUES
|
|
-- Valence (4 values)
|
|
('V', 'V+', 'Valence', 'Positive', 'Praise, satisfaction', FALSE, 1),
|
|
('V', 'V-', 'Valence', 'Negative', 'Complaint, dissatisfaction', FALSE, 2),
|
|
('V', 'V0', 'Valence', 'Neutral', 'Observation without judgment', FALSE, 3),
|
|
('V', 'V±', 'Valence', 'Mixed', 'Both positive and negative in same span', FALSE, 4),
|
|
-- Intensity (3 values)
|
|
('I', 'I1', 'Intensity', 'Mild', 'Slight preference/concern', FALSE, 1),
|
|
('I', 'I2', 'Intensity', 'Moderate', 'Clear but not extreme', FALSE, 2),
|
|
('I', 'I3', 'Intensity', 'Strong', 'Emphatic or intense', FALSE, 3),
|
|
-- Specificity (3 values)
|
|
('S', 'S1', 'Specificity', 'Vague', 'General impression only', FALSE, 1),
|
|
('S', 'S2', 'Specificity', 'Moderate', 'Some details or context', FALSE, 2),
|
|
('S', 'S3', 'Specificity', 'Specific', 'Concrete details, names, times, amounts', FALSE, 3),
|
|
-- Actionability (3 values)
|
|
('A', 'A1', 'Actionability', 'Low', 'Feeling with no clear action path', FALSE, 1),
|
|
('A', 'A2', 'Actionability', 'Medium', 'Suggests improvement area', FALSE, 2),
|
|
('A', 'A3', 'Actionability', 'High', 'Specific implementable feedback', FALSE, 3),
|
|
-- Temporal (4 values)
|
|
('T', 'TC', 'Temporal Reference', 'Current', 'This specific visit/experience', TRUE, 1),
|
|
('T', 'TR', 'Temporal Reference', 'Recent', 'Recent pattern of experiences', FALSE, 2),
|
|
('T', 'TH', 'Temporal Reference', 'Historical', 'Long-standing pattern', FALSE, 3),
|
|
('T', 'TF', 'Temporal Reference', 'Future', 'Expectations or predictions', FALSE, 4),
|
|
-- Evidence (3 values)
|
|
('E', 'ES', 'Evidence Type', 'Stated', 'Explicitly said by customer', TRUE, 1),
|
|
('E', 'EI', 'Evidence Type', 'Inferred', 'Logically entailed by text', FALSE, 2),
|
|
('E', 'EC', 'Evidence Type', 'Contextual', 'Requires surrounding text', FALSE, 3),
|
|
-- Comparative Reference (4 values)
|
|
('CR', 'CR-N', 'Comparative Reference', 'None', 'No comparison to previous state', TRUE, 1),
|
|
('CR', 'CR-B', 'Comparative Reference', 'Better', 'Explicit improvement vs. before', FALSE, 2),
|
|
('CR', 'CR-W', 'Comparative Reference', 'Worse', 'Explicit decline vs. before', FALSE, 3),
|
|
('CR', 'CR-S', 'Comparative Reference', 'Same', 'Explicitly unchanged', FALSE, 4);
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 8.5 Insert Subcodes (sample - O domain)
|
|
-- Full 138 subcodes would be inserted similarly
|
|
-- -----------------------------------------------------------------------------
|
|
INSERT INTO urt_subcodes (subcode, category_code, domain_code, name, definition, positive_example, negative_example, dont_confuse_with, dont_confuse_reason, display_order) VALUES
|
|
-- O1: Function
|
|
('O1.01', 'O1', 'O', 'Works/Doesn''t Work', 'Basic functionality success or failure', 'Software runs perfectly', 'Car won''t start', 'J3.03', 'J3.03 is system uptime, O1.01 is product function', 1),
|
|
('O1.02', 'O1', 'O', 'Performance Level', 'How well it operates', 'Incredibly fast processor', 'Sluggish and laggy', 'E2.03', 'E2.03 is interface speed, O1.02 is product performance', 2),
|
|
('O1.03', 'O1', 'O', 'Durability', 'Longevity and resistance to wear', 'Still perfect after 5 years', 'Fell apart in a month', 'O2.01', 'O2.01 is material quality, O1.03 is longevity', 3),
|
|
('O1.04', 'O1', 'O', 'Reliability', 'Consistency of function over time', 'Never fails me', 'Works sometimes, not others', 'J3.01', 'J3.01 is process consistency, O1.04 is product reliability', 4),
|
|
('O1.05', 'O1', 'O', 'Outcome Achievement', 'Did customer accomplish their goal?', 'Passed my exam!', 'Treatment didn''t work', 'V4.03', 'V4.03 is satisfaction with exchange, O1.05 is goal achievement', 5),
|
|
-- O2: Quality
|
|
('O2.01', 'O2', 'O', 'Materials/Inputs', 'Quality of components or ingredients', 'Real leather, premium feel', 'Cheap plastic parts', 'O1.03', 'O1.03 is durability, O2.01 is material quality', 1),
|
|
('O2.02', 'O2', 'O', 'Craftsmanship', 'Skill of construction or execution', 'Beautifully sewn seams', 'Sloppy assembly', 'P2.02', 'P2.02 is staff skill, O2.02 is product craftsmanship', 2),
|
|
('O2.03', 'O2', 'O', 'Presentation', 'Visual and aesthetic quality', 'Gorgeous plating', 'Looked thrown together', 'E3.05', 'E3.05 is space aesthetics, O2.03 is product presentation', 3),
|
|
('O2.04', 'O2', 'O', 'Attention to Detail', 'Finishing touches and refinement', 'Every corner perfect', 'Full of typos', 'O3.01', 'O3.01 is completeness, O2.04 is refinement', 4),
|
|
('O2.05', 'O2', 'O', 'Condition at Delivery', 'State when received', 'Still warm from oven', 'Arrived damaged', 'J3.02', 'J3.02 is process accuracy, O2.05 is delivery condition', 5),
|
|
-- O3: Completeness
|
|
('O3.01', 'O3', 'O', 'All Components Present', 'Nothing missing from what was promised', 'Everything in the box', 'Missing the charger', 'O4.01', 'O4.01 is spec match, O3.01 is completeness', 1),
|
|
('O3.02', 'O3', 'O', 'Feature Availability', 'Promised features actually work', 'All menu items available', 'Half the features disabled', 'A1.03', 'A1.03 is inventory, O3.02 is feature availability', 2),
|
|
('O3.03', 'O3', 'O', 'Scope Delivery', 'Full scope of work completed', 'Cleaned entire house', 'Left the bathrooms', 'J4.04', 'J4.04 is resolution quality, O3.03 is scope delivery', 3),
|
|
('O3.04', 'O3', 'O', 'Documentation', 'Supporting materials provided', 'Great user manual', 'No instructions at all', 'J2.01', 'J2.01 is process simplicity, O3.04 is documentation as product artifact', 4),
|
|
-- O4: Fit
|
|
('O4.01', 'O4', 'O', 'Specification Match', 'Matches what was ordered', 'Exactly what I ordered', 'Wrong size delivered', 'J3.02', 'J3.02 is execution accuracy, O4.01 is spec match', 1),
|
|
('O4.02', 'O4', 'O', 'Personalization', 'Adapted to individual preferences', 'Remembered my usual', 'No way to save prefs', 'P3.01', 'P3.01 is attentiveness, O4.02 is personalization', 2),
|
|
('O4.03', 'O4', 'O', 'Flexibility', 'Can be modified or adjusted', 'Happy to substitute', 'No modifications allowed', 'V2.04', 'V2.04 is policy fairness, O4.03 is flexibility', 3),
|
|
('O4.04', 'O4', 'O', 'Appropriateness', 'Right solution for the need', 'Perfect recommendation', 'Sold me wrong thing', 'P2.01', 'P2.01 is knowledge, O4.04 is appropriateness', 4);
|
|
|
|
-- Note: Additional subcodes for P, J, E, A, V, R domains would be inserted similarly.
|
|
-- The full B1-urt-codes.yaml contains 138 subcodes total.
|
|
-- For production, generate INSERT statements from the YAML source.
|
|
|
|
-- =============================================================================
|
|
-- SECTION 9: PARTITIONING STRATEGY (for high-volume scenarios)
|
|
-- =============================================================================
|
|
--
|
|
-- For deployments expecting >1M spans/month, consider partitioning:
|
|
--
|
|
-- 1. Spans table: Partition by created_at (monthly)
|
|
-- CREATE TABLE spans (...) PARTITION BY RANGE (created_at);
|
|
-- CREATE TABLE spans_2026_01 PARTITION OF spans
|
|
-- FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
|
|
--
|
|
-- 2. Reviews table: Same monthly partitioning
|
|
--
|
|
-- 3. Annotation audit: Partition by annotated_at
|
|
--
|
|
-- 4. Issue state history: Partition by transitioned_at
|
|
--
|
|
-- Benefits:
|
|
-- - Faster queries on recent data (partition pruning)
|
|
-- - Easier data retention management (drop old partitions)
|
|
-- - Parallel query execution across partitions
|
|
--
|
|
-- =============================================================================
|
|
|
|
-- =============================================================================
|
|
-- SECTION 10: GRANTS AND SECURITY
|
|
-- =============================================================================
|
|
|
|
-- Create application roles
|
|
-- CREATE ROLE urt_reader;
|
|
-- CREATE ROLE urt_writer;
|
|
-- CREATE ROLE urt_admin;
|
|
|
|
-- Reader permissions (dashboards, analytics)
|
|
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO urt_reader;
|
|
-- GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO urt_reader;
|
|
|
|
-- Writer permissions (classification pipeline)
|
|
-- GRANT SELECT, INSERT, UPDATE ON reviews, spans, span_classifications,
|
|
-- causal_chains, issues, issue_spans, issue_state_history,
|
|
-- issue_resolutions, annotation_audit, classification_history TO urt_writer;
|
|
-- GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO urt_writer;
|
|
|
|
-- Admin permissions (schema management)
|
|
-- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO urt_admin;
|
|
-- GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO urt_admin;
|
|
|
|
-- =============================================================================
|
|
-- END OF SCHEMA
|
|
-- =============================================================================
|
|
--
|
|
-- Post-deployment checklist:
|
|
-- 1. Run: SELECT refresh_urt_materialized_views();
|
|
-- 2. Set up pg_cron job for periodic MV refresh
|
|
-- 3. Configure connection pooling (PgBouncer recommended)
|
|
-- 4. Set up monitoring for table sizes and index usage
|
|
-- 5. Import full subcode data from B1-urt-codes.yaml
|
|
--
|
|
-- =============================================================================
|