-- Migration: 007_versioning_schema.sql -- Phase 7.1: Entity & Edge Versioning -- Tracks full snapshots on every mutation for rollback capability BEGIN; -- Entity version snapshots (immutable) CREATE TABLE IF NOT EXISTS memory_entity_version ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), entity_id VARCHAR(255) NOT NULL, version_num INTEGER NOT NULL, operation VARCHAR(20) NOT NULL CHECK (operation IN ('create', 'update', 'delete')), -- Full snapshot JSONB snapshot JSONB NOT NULL, -- Audit metadata changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), changed_by VARCHAR(255) NOT NULL, -- JWT sub claim fields_changed TEXT[] DEFAULT '{}', -- Temporal t_created TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Constraints UNIQUE(entity_id, version_num), CONSTRAINT valid_version_num CHECK (version_num > 0) ); -- Edge version snapshots CREATE TABLE IF NOT EXISTS memory_edge_version ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), edge_id UUID NOT NULL, version_num INTEGER NOT NULL, operation VARCHAR(20) NOT NULL CHECK (operation IN ('create', 'update', 'delete')), -- Full snapshot JSONB snapshot JSONB NOT NULL, -- Audit metadata changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), changed_by VARCHAR(255) NOT NULL, fields_changed TEXT[] DEFAULT '{}', -- Temporal t_created TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Constraints UNIQUE(edge_id, version_num), CONSTRAINT valid_version_num CHECK (version_num > 0) ); -- Indexes for efficient lookups CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_version_entity_id ON memory_entity_version(entity_id, version_num DESC); CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_version_changed_at ON memory_entity_version(changed_at DESC); CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_version_changed_by ON memory_entity_version(changed_by); CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_version_edge_id ON memory_edge_version(edge_id, version_num DESC); CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_version_changed_at ON memory_edge_version(changed_at DESC); CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_version_changed_by ON memory_edge_version(changed_by); -- Immutability enforcement: version tables are append-only CREATE OR REPLACE FUNCTION prevent_version_table_modification() RETURNS TRIGGER AS $$ BEGIN RAISE EXCEPTION 'Version tables are immutable'; END; $$ LANGUAGE plpgsql; CREATE TRIGGER memory_entity_version_immutable BEFORE UPDATE OR DELETE ON memory_entity_version FOR EACH ROW EXECUTE FUNCTION prevent_version_table_modification(); CREATE TRIGGER memory_edge_version_immutable BEFORE UPDATE OR DELETE ON memory_edge_version FOR EACH ROW EXECUTE FUNCTION prevent_version_table_modification(); COMMIT; -- Rollback (for reference): -- DROP TRIGGER memory_entity_version_immutable ON memory_entity_version; -- DROP TRIGGER memory_edge_version_immutable ON memory_edge_version; -- DROP FUNCTION prevent_version_table_modification(); -- DROP TABLE memory_entity_version; -- DROP TABLE memory_edge_version;