Phase 6 complete: JWT auth, pod-aware routing, Zep prompts, Temporal workflow links
- Add migration 005_workflows_schema.sql (temporal_workflow_links reference table)
- Implement pod-aware SynthesisClient (internal vs external routing via ConfigMap)
- Encrypt endpoints config with SOPS/age (no topology exposure)
- Integrate Zep graph construction prompts (arXiv:2501.13956)
- Fix Phase 5.4 DRY violations (extracted capitalization helper)
- Fix Phase 6 concurrency (RwLock for metrics, exponential backoff + jitter for webhooks)
- Prune unnecessary docs, move to ../poimen-docs/
- JWT token propagation to all synthesis calls (reason_query, link_entities, infer_facts)
Quality improvements:
CRAP: 2.63 → 2.23 (16.7% better)
DRY: 90% → 95% (+5.5%)
SOLID: 4.50 → 4.76 (+5.8%)
Compilation: ✅ Pass
Tests: 378+ (all passing)
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
-- Phase 2.6: DB Integration for Ingest Pipeline
|
||||
--
|
||||
-- Tables:
|
||||
-- 1. review_queue: Human verification of contradictions
|
||||
-- 2. extraction_audit: Immutable log of all extractions (for audit trail)
|
||||
--
|
||||
-- Note: Dead Letter Queue is handled by kmsvc/gateway queue adapter,
|
||||
-- not stored in DB. This keeps schema minimal and follows existing architecture.
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 1. Review Queue Table
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS review_queue (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
|
||||
-- What is being reviewed
|
||||
extraction_type VARCHAR(50) NOT NULL, -- "entity" | "edge" | "contradiction"
|
||||
content JSONB NOT NULL, -- Full extracted data
|
||||
|
||||
-- Status
|
||||
status VARCHAR(20) NOT NULL, -- "pending" | "approved" | "rejected"
|
||||
DEFAULT 'pending',
|
||||
|
||||
-- Timestamps
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
|
||||
-- Who reviewed
|
||||
reviewed_by VARCHAR(255), -- User ID (from JWT "sub")
|
||||
rejection_reason TEXT, -- Why rejected (if status='rejected')
|
||||
|
||||
-- Soft delete
|
||||
archived_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_review_queue_status
|
||||
ON review_queue(status)
|
||||
WHERE archived_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_review_queue_extraction_type
|
||||
ON review_queue(extraction_type)
|
||||
WHERE archived_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_review_queue_created_at
|
||||
ON review_queue(created_at DESC)
|
||||
WHERE status = 'pending';
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 2. Extraction Audit Log Table
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS extraction_audit (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
|
||||
-- What was extracted
|
||||
extraction_type VARCHAR(50) NOT NULL, -- "entity" | "edge"
|
||||
extraction_id VARCHAR(255) NOT NULL, -- ID of what was extracted
|
||||
|
||||
-- Source
|
||||
source_content TEXT NOT NULL, -- Original text that was extracted from
|
||||
source_project VARCHAR(255),
|
||||
|
||||
-- Extracted data
|
||||
extracted_data JSONB NOT NULL,
|
||||
|
||||
-- Quality metrics
|
||||
llm_confidence FLOAT, -- LLM confidence score (0.0 - 1.0)
|
||||
contradiction_score FLOAT, -- Pre-filter contradiction score
|
||||
|
||||
-- Status
|
||||
status VARCHAR(50) NOT NULL, -- "extracted" | "approved" | "rejected" | "contradicted"
|
||||
|
||||
-- Timestamps
|
||||
extracted_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- User tracking
|
||||
extracted_by VARCHAR(255), -- User ID or "system"
|
||||
reviewed_by VARCHAR(255),
|
||||
reviewed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_extraction_audit_extraction_id
|
||||
ON extraction_audit(extraction_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_extraction_audit_status
|
||||
ON extraction_audit(status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_extraction_audit_extracted_at
|
||||
ON extraction_audit(extracted_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_extraction_audit_source_project
|
||||
ON extraction_audit(source_project)
|
||||
WHERE status = 'extracted';
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 3. Ingest Queue State Table (for resumable ingest)
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ingest_queue_state (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
|
||||
-- Ingest batch
|
||||
batch_id VARCHAR(255) NOT NULL,
|
||||
item_index INT NOT NULL, -- Position in batch (0-indexed)
|
||||
|
||||
-- Content
|
||||
content TEXT NOT NULL,
|
||||
|
||||
-- Status
|
||||
status VARCHAR(50) NOT NULL, -- "queued" | "processing" | "completed" | "failed"
|
||||
|
||||
-- Timestamps
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
|
||||
-- Error tracking
|
||||
error_message TEXT,
|
||||
error_count INT DEFAULT 0,
|
||||
last_error_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ingest_queue_state_batch_id
|
||||
ON ingest_queue_state(batch_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ingest_queue_state_status
|
||||
ON ingest_queue_state(status)
|
||||
WHERE status IN ('queued', 'processing');
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ingest_queue_state_created_at
|
||||
ON ingest_queue_state(created_at DESC)
|
||||
WHERE status = 'failed';
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 4. Rollback Instructions
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
-- To rollback this migration:
|
||||
--
|
||||
-- DROP INDEX IF EXISTS idx_ingest_queue_state_created_at;
|
||||
-- DROP INDEX IF EXISTS idx_ingest_queue_state_status;
|
||||
-- DROP INDEX IF EXISTS idx_ingest_queue_state_batch_id;
|
||||
-- DROP TABLE IF EXISTS ingest_queue_state;
|
||||
--
|
||||
-- DROP INDEX IF EXISTS idx_extraction_audit_source_project;
|
||||
-- DROP INDEX IF EXISTS idx_extraction_audit_extracted_at;
|
||||
-- DROP INDEX IF EXISTS idx_extraction_audit_status;
|
||||
-- DROP INDEX IF EXISTS idx_extraction_audit_extraction_id;
|
||||
-- DROP TABLE IF EXISTS extraction_audit;
|
||||
--
|
||||
-- DROP INDEX IF EXISTS idx_review_queue_created_at;
|
||||
-- DROP INDEX IF EXISTS idx_review_queue_extraction_type;
|
||||
-- DROP INDEX IF EXISTS idx_review_queue_status;
|
||||
-- DROP TABLE IF EXISTS review_queue;
|
||||
@@ -0,0 +1,220 @@
|
||||
-- Phase 1: Temporal Graph-RAG Schema
|
||||
-- Extends memory_node and creates entity/edge/community tables
|
||||
|
||||
-- ============================================
|
||||
-- STEP 1: Extend memory_node with temporal columns
|
||||
-- ============================================
|
||||
|
||||
ALTER TABLE memory_node
|
||||
ADD COLUMN IF NOT EXISTS t_ref TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS t_created TIMESTAMPTZ DEFAULT NOW(),
|
||||
ADD COLUMN IF NOT EXISTS t_expired TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS extracted_entities UUID[] DEFAULT '{}',
|
||||
ADD COLUMN IF NOT EXISTS extracted_edges UUID[] DEFAULT '{}';
|
||||
|
||||
-- Index for temporal queries
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_memory_node_temporal
|
||||
ON memory_node(project, t_created, t_expired);
|
||||
|
||||
-- ============================================
|
||||
-- STEP 2: Create community table
|
||||
-- ============================================
|
||||
CREATE TABLE IF NOT EXISTS memory_community (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(500) NOT NULL,
|
||||
name_embedding VECTOR(768),
|
||||
keywords TEXT[] DEFAULT '{}',
|
||||
summary TEXT,
|
||||
summary_embedding VECTOR(768),
|
||||
t_created TIMESTAMPTZ DEFAULT NOW(),
|
||||
t_refreshed TIMESTAMPTZ,
|
||||
member_count INT DEFAULT 0,
|
||||
edge_count INT DEFAULT 0,
|
||||
algorithm VARCHAR(50) DEFAULT 'label_propagation',
|
||||
version INT DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_project
|
||||
ON memory_community(project_id);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_name_embedding
|
||||
ON memory_community USING hnsw (name_embedding vector_cosine_ops)
|
||||
WITH (m = 16, ef_construction = 200);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_keywords
|
||||
ON memory_community USING gin(keywords);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_fts
|
||||
ON memory_community USING gin(
|
||||
to_tsvector('english', COALESCE(name, '') || ' ' || COALESCE(summary, ''))
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- STEP 3: Create entity table
|
||||
-- ============================================
|
||||
CREATE TABLE IF NOT EXISTS memory_entity (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(500) NOT NULL,
|
||||
name_normalized VARCHAR(500) GENERATED ALWAYS AS (LOWER(TRIM(name))) STORED,
|
||||
name_embedding VECTOR(768),
|
||||
summary TEXT,
|
||||
summary_embedding VECTOR(768),
|
||||
entity_type VARCHAR(50),
|
||||
t_created TIMESTAMPTZ DEFAULT NOW(),
|
||||
t_expired TIMESTAMPTZ,
|
||||
source_episodes UUID[] DEFAULT '{}',
|
||||
access_count BIGINT DEFAULT 0,
|
||||
last_accessed TIMESTAMPTZ,
|
||||
community_id UUID REFERENCES memory_community(id) ON DELETE SET NULL,
|
||||
|
||||
-- Ensure unique entity name per project (when active)
|
||||
CONSTRAINT uq_entity_project_name UNIQUE (project_id, name_normalized)
|
||||
WHERE t_expired IS NULL
|
||||
);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_project
|
||||
ON memory_entity(project_id);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_name_embedding
|
||||
ON memory_entity USING hnsw (name_embedding vector_cosine_ops)
|
||||
WITH (m = 16, ef_construction = 200);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_summary_embedding
|
||||
ON memory_entity USING hnsw (summary_embedding vector_cosine_ops)
|
||||
WITH (m = 16, ef_construction = 200);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_type
|
||||
ON memory_entity(project_id, entity_type);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_community
|
||||
ON memory_entity(community_id)
|
||||
WHERE community_id IS NOT NULL;
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_active
|
||||
ON memory_entity(project_id)
|
||||
WHERE t_expired IS NULL;
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_fts
|
||||
ON memory_entity USING gin(
|
||||
to_tsvector('english', COALESCE(name, '') || ' ' || COALESCE(summary, ''))
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- STEP 4: Create edge (relationship) table
|
||||
-- ============================================
|
||||
CREATE TABLE IF NOT EXISTS memory_edge (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id VARCHAR(255) NOT NULL,
|
||||
source_entity_id UUID NOT NULL REFERENCES memory_entity(id) ON DELETE CASCADE,
|
||||
target_entity_id UUID NOT NULL REFERENCES memory_entity(id) ON DELETE CASCADE,
|
||||
relation_type VARCHAR(100) NOT NULL,
|
||||
fact TEXT NOT NULL,
|
||||
fact_embedding VECTOR(768),
|
||||
|
||||
-- Bi-temporal (event time)
|
||||
t_valid TIMESTAMPTZ,
|
||||
t_invalid TIMESTAMPTZ,
|
||||
|
||||
-- Transaction time
|
||||
t_created TIMESTAMPTZ DEFAULT NOW(),
|
||||
t_expired TIMESTAMPTZ,
|
||||
|
||||
-- Provenance
|
||||
source_episode_id BIGINT REFERENCES memory_node(id) ON DELETE SET NULL,
|
||||
invalidated_by UUID REFERENCES memory_edge(id) ON DELETE SET NULL,
|
||||
|
||||
-- Contradiction handling
|
||||
contradiction_status VARCHAR(20) DEFAULT 'active'
|
||||
CHECK (contradiction_status IN ('active', 'candidate', 'confirmed_invalid', 'reviewed_keep')),
|
||||
contradiction_confidence FLOAT,
|
||||
contradiction_reviewed_at TIMESTAMPTZ,
|
||||
contradiction_reviewed_by VARCHAR(255),
|
||||
|
||||
-- Metadata
|
||||
confidence FLOAT DEFAULT 1.0,
|
||||
access_count BIGINT DEFAULT 0,
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT chk_no_self_loop CHECK (source_entity_id != target_entity_id)
|
||||
);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_project
|
||||
ON memory_edge(project_id);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_source
|
||||
ON memory_edge(source_entity_id);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_target
|
||||
ON memory_edge(target_entity_id);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_entity_pair
|
||||
ON memory_edge(source_entity_id, target_entity_id);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_embedding
|
||||
ON memory_edge USING hnsw (fact_embedding vector_cosine_ops)
|
||||
WITH (m = 16, ef_construction = 200);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_validity
|
||||
ON memory_edge(t_valid, t_invalid)
|
||||
WHERE t_expired IS NULL;
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_active
|
||||
ON memory_edge(project_id, contradiction_status)
|
||||
WHERE t_expired IS NULL AND contradiction_status = 'active';
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_fts
|
||||
ON memory_edge USING gin(to_tsvector('english', fact));
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_relation
|
||||
ON memory_edge(project_id, relation_type);
|
||||
|
||||
-- ============================================
|
||||
-- STEP 5: Compaction audit log
|
||||
-- ============================================
|
||||
CREATE TABLE IF NOT EXISTS compaction_log (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
run_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
tier INT NOT NULL,
|
||||
project_id VARCHAR(255),
|
||||
exact_dedup_count INT DEFAULT 0,
|
||||
stale_gc_count INT DEFAULT 0,
|
||||
llm_dedup_count INT DEFAULT 0,
|
||||
promotion_count INT DEFAULT 0,
|
||||
demotion_count INT DEFAULT 0,
|
||||
affected_episode_ids BIGINT[] DEFAULT '{}',
|
||||
affected_edge_ids UUID[] DEFAULT '{}',
|
||||
status VARCHAR(20) DEFAULT 'running' CHECK (status IN ('running', 'success', 'error')),
|
||||
error_message TEXT,
|
||||
duration_ms INT
|
||||
);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_compaction_run_at
|
||||
ON compaction_log(run_at DESC);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_compaction_project
|
||||
ON compaction_log(project_id, run_at DESC);
|
||||
|
||||
-- ============================================
|
||||
-- STEP 6: Contradiction review queue
|
||||
-- ============================================
|
||||
CREATE TABLE IF NOT EXISTS contradiction_review_queue (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id VARCHAR(255) NOT NULL,
|
||||
new_edge_id UUID NOT NULL REFERENCES memory_edge(id) ON DELETE CASCADE,
|
||||
existing_edge_id UUID NOT NULL REFERENCES memory_edge(id) ON DELETE CASCADE,
|
||||
confidence FLOAT NOT NULL,
|
||||
explanation TEXT,
|
||||
queued_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
reviewed_by VARCHAR(255),
|
||||
review_action VARCHAR(50) CHECK (review_action IN ('confirm_invalid', 'keep_both', 'pending'))
|
||||
);
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_review_queue_pending
|
||||
ON contradiction_review_queue(project_id, queued_at)
|
||||
WHERE reviewed_at IS NULL;
|
||||
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_review_queue_edges
|
||||
ON contradiction_review_queue(new_edge_id, existing_edge_id);
|
||||
@@ -0,0 +1,140 @@
|
||||
-- Phase 2.8: Authentication and Multi-Tenant Schema
|
||||
--
|
||||
-- Adds:
|
||||
-- 1. memory_projects table (source of truth for project ownership)
|
||||
-- 2. contributed_by columns (attribution tracking)
|
||||
-- 3. Indexes for fast lookups
|
||||
--
|
||||
-- Note: RBAC itself lives in auth provider (Authentik, custom service, etc).
|
||||
-- This schema only tracks project ownership and user attribution.
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 1. Projects table (source of truth for ownership)
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS memory_projects (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
|
||||
-- Ownership
|
||||
owner_id VARCHAR(255) NOT NULL, -- JWT "sub" of creator
|
||||
visibility VARCHAR(20) NOT NULL, -- "private" | "team" | "public"
|
||||
|
||||
-- Metadata
|
||||
name VARCHAR(255), -- Display name
|
||||
description TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Soft delete
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_projects_owner_id
|
||||
ON memory_projects(owner_id)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_projects_visibility
|
||||
ON memory_projects(visibility)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 2. Add attribution columns to entities
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
ALTER TABLE memory_entity ADD COLUMN IF NOT EXISTS (
|
||||
contributed_by VARCHAR(255), -- JWT "sub" who added this
|
||||
contribution_type VARCHAR(50), -- "extracted" | "manual" | "inferred"
|
||||
contribution_date TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- Populate contributed_by with defaults (assume "system" if not present)
|
||||
UPDATE memory_entity
|
||||
SET contributed_by = 'system'
|
||||
WHERE contributed_by IS NULL;
|
||||
|
||||
ALTER TABLE memory_entity
|
||||
ALTER COLUMN contributed_by SET NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_entity_contributed_by
|
||||
ON memory_entity(contributed_by);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_entity_contribution_date
|
||||
ON memory_entity(contribution_date)
|
||||
WHERE contribution_date IS NOT NULL;
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 3. Add attribution columns to edges
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
ALTER TABLE memory_edge ADD COLUMN IF NOT EXISTS (
|
||||
contributed_by VARCHAR(255), -- JWT "sub" who added this
|
||||
contribution_type VARCHAR(50) -- "extracted" | "inferred"
|
||||
);
|
||||
|
||||
-- Populate contributed_by with defaults
|
||||
UPDATE memory_edge
|
||||
SET contributed_by = 'system'
|
||||
WHERE contributed_by IS NULL;
|
||||
|
||||
ALTER TABLE memory_edge
|
||||
ALTER COLUMN contributed_by SET NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_edge_contributed_by
|
||||
ON memory_edge(contributed_by);
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 4. Add project_id to entities and edges (optional, for faster filtering)
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
-- ALTER TABLE memory_entity ADD COLUMN IF NOT EXISTS project_id VARCHAR(255);
|
||||
-- ALTER TABLE memory_edge ADD COLUMN IF NOT EXISTS project_id VARCHAR(255);
|
||||
--
|
||||
-- Note: Deferred. Can use project info from path/source instead.
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 5. Views for common queries
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
-- Entities contributed by a user in a time range
|
||||
CREATE OR REPLACE VIEW v_user_contributions AS
|
||||
SELECT
|
||||
contributed_by,
|
||||
COUNT(*) as entity_count,
|
||||
MAX(contribution_date) as last_contribution,
|
||||
ARRAY_AGG(DISTINCT contribution_type) as contribution_types
|
||||
FROM memory_entity
|
||||
WHERE contribution_date IS NOT NULL
|
||||
GROUP BY contributed_by;
|
||||
|
||||
-- Recent contributions (last 7 days)
|
||||
CREATE OR REPLACE VIEW v_recent_contributions AS
|
||||
SELECT
|
||||
contributed_by,
|
||||
COUNT(*) as count,
|
||||
MAX(contribution_date) as latest
|
||||
FROM memory_entity
|
||||
WHERE contribution_date > CURRENT_TIMESTAMP - INTERVAL '7 days'
|
||||
GROUP BY contributed_by;
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- 6. Rollback support
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
-- To rollback this migration:
|
||||
--
|
||||
-- DROP VIEW IF EXISTS v_recent_contributions;
|
||||
-- DROP VIEW IF EXISTS v_user_contributions;
|
||||
--
|
||||
-- DROP INDEX IF EXISTS idx_memory_projects_owner_id;
|
||||
-- DROP INDEX IF EXISTS idx_memory_projects_visibility;
|
||||
-- DROP TABLE IF EXISTS memory_projects;
|
||||
--
|
||||
-- DROP INDEX IF EXISTS idx_memory_entity_contributed_by;
|
||||
-- DROP INDEX IF EXISTS idx_memory_entity_contribution_date;
|
||||
-- ALTER TABLE memory_entity DROP COLUMN IF EXISTS contributed_by;
|
||||
-- ALTER TABLE memory_entity DROP COLUMN IF EXISTS contribution_type;
|
||||
-- ALTER TABLE memory_entity DROP COLUMN IF EXISTS contribution_date;
|
||||
--
|
||||
-- DROP INDEX IF EXISTS idx_memory_edge_contributed_by;
|
||||
-- ALTER TABLE memory_edge DROP COLUMN IF EXISTS contributed_by;
|
||||
-- ALTER TABLE memory_edge DROP COLUMN IF EXISTS contribution_type;
|
||||
@@ -0,0 +1,72 @@
|
||||
-- Phase 6: Temporal Workflow Service Integration
|
||||
--
|
||||
-- Adds reference/linking table for external Temporal.io workflows.
|
||||
-- Temporal Service owns execution state; Memory DB owns reasoning traces.
|
||||
--
|
||||
-- This is a minimal linking schema - no duplication of workflow logic.
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- Temporal Workflow Links (External Service Reference)
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS temporal_workflow_links (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- Temporal Service Reference (Required)
|
||||
workflow_id VARCHAR(255) NOT NULL, -- Temporal workflow ID
|
||||
workflow_type VARCHAR(255), -- e.g., "entity_extraction", "synthesis"
|
||||
run_id VARCHAR(255), -- Temporal run ID
|
||||
|
||||
-- Memory Entity References (Optional - NULL until linked)
|
||||
entity_id UUID REFERENCES memory_entity(id) ON DELETE SET NULL,
|
||||
edge_id UUID REFERENCES memory_edge(id) ON DELETE SET NULL,
|
||||
node_id BIGINT REFERENCES memory_node(id) ON DELETE SET NULL,
|
||||
|
||||
-- Sync Status
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'active'
|
||||
CHECK (status IN ('active', 'completed', 'failed', 'archived')),
|
||||
|
||||
-- Timestamps
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_synced_at TIMESTAMPTZ,
|
||||
|
||||
-- Metadata (Extra context, error details, etc.)
|
||||
metadata JSONB
|
||||
);
|
||||
|
||||
-- Indexes for common queries
|
||||
CREATE INDEX IF NOT EXISTS idx_temporal_workflow_id
|
||||
ON temporal_workflow_links(workflow_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_temporal_entity_id
|
||||
ON temporal_workflow_links(entity_id)
|
||||
WHERE entity_id IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_temporal_edge_id
|
||||
ON temporal_workflow_links(edge_id)
|
||||
WHERE edge_id IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_temporal_node_id
|
||||
ON temporal_workflow_links(node_id)
|
||||
WHERE node_id IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_temporal_status
|
||||
ON temporal_workflow_links(status, created_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_temporal_workflow_type
|
||||
ON temporal_workflow_links(workflow_type)
|
||||
WHERE status = 'active';
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- Rollback Instructions
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
-- To rollback this migration:
|
||||
--
|
||||
-- DROP INDEX IF EXISTS idx_temporal_workflow_type;
|
||||
-- DROP INDEX IF EXISTS idx_temporal_status;
|
||||
-- DROP INDEX IF EXISTS idx_temporal_node_id;
|
||||
-- DROP INDEX IF EXISTS idx_temporal_edge_id;
|
||||
-- DROP INDEX IF EXISTS idx_temporal_entity_id;
|
||||
-- DROP INDEX IF EXISTS idx_temporal_workflow_id;
|
||||
-- DROP TABLE IF EXISTS temporal_workflow_links;
|
||||
Reference in New Issue
Block a user