Database Schema

Praetor uses PostgreSQL with Row-Level Security (RLS) for multi-tenant isolation. The schema consists of 50+ tables organized by domain.

Database Schema

Table of Contents


Overview

Praetor uses PostgreSQL with Row-Level Security (RLS) for multi-tenant isolation. The schema consists of 50+ tables organized by domain.

Key Characteristics

  • 205 migrations: Incremental schema evolution (latest: Final Phase 2 — organizations, discovery engine, realtime, context)
  • RLS on all tenant tables: Automatic tenant isolation (using both app.current_tenant_id and app.tenant_id config settings)
  • JSONB for flexible data: Metadata, configurations, answers
  • UUID primary keys: Globally unique identifiers
  • Timestamp tracking: created_at, updated_at on all tables
  • pgvector support: Vector embeddings for semantic search (optional)

Schema Organization

DomainTablesPurpose
Multi-tenancy3Tenant, user, preferences
Organizations4Organizations, members, invitations, agency projects
Projects5Project, phases, features
Questions6Templates, instances, answers
Tasks5Tasks, actions, activity
Agents4Memory, state, checkpoints
Specs4Compiled specs, artifacts
Events3Audit, events, feedback
Patterns3Pattern library, detections
Permissions5Roles, policies, assignments
Templates4Template storage, variables, mappings
Workflows5BPMN workflows, validation, bindings
Gaps2Analysis runs, findings
Standards3Schema registry, versions, references
Integrations3Integration artifacts, mappings, credentials
Requirements Discovery5Conversational requirements gathering, extractions, feature seeds
Knowledge Base1Pattern-feature mappings
Discovery Engine10Objectives, obligations, sessions, research tasks, audit trail
Realtime Infrastructure4Channels, events, heartbeat, connection stats
Context Engineering6Budget profiles, artifacts, cache, requests, dependencies
Kit System5Kit definitions, credentials, executions, outputs, discovery cache

Multi-Tenancy Architecture

Row-Level Security (RLS)

All tenant-scoped tables use RLS for automatic isolation:

-- Enable RLS on table
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

-- Create policy
CREATE POLICY tenant_isolation ON projects
  USING (tenant_id = current_setting('app.current_tenant_id')::uuid);

-- Set tenant context (done by API middleware)
SET app.current_tenant_id = 'tenant-uuid';

Tenant Context Flow

Request → Auth Middleware → Extract Tenant → SET app.current_tenant_id → Query

Core Tables

tenants

Organization/tenant accounts.

CREATE TABLE tenants (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR(255) NOT NULL,
  slug VARCHAR(100) UNIQUE NOT NULL,
  settings JSONB DEFAULT '{}',
  billing_plan VARCHAR(50) DEFAULT 'free',
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);
ColumnTypeDescription
idUUIDPrimary key
nameVARCHAROrganization name
slugVARCHARURL-safe identifier
settingsJSONBTenant-specific settings
billing_planVARCHARSubscription tier

users

User accounts within tenants.

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  email VARCHAR(255) NOT NULL,
  name VARCHAR(255),
  role VARCHAR(50) DEFAULT 'member',
  stytch_user_id VARCHAR(255),
  last_login_at TIMESTAMP,

  -- Phase 2 additions (Migration 175)
  user_type user_type NOT NULL DEFAULT 'individual',
  organization_id UUID REFERENCES organizations(id) ON DELETE SET NULL,

  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(tenant_id, email)
);

-- RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON users
  USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
ColumnTypeDescription
tenant_idUUIDParent tenant
emailVARCHARUser email (unique per tenant)
roleVARCHARmember, admin, owner
stytch_user_idVARCHARStytch auth provider ID
user_typeuser_type'individual', 'agency_member', 'customer'
organization_idUUIDOrganization membership (null for individuals)

projects

Main project records with phase tracking.

CREATE TABLE projects (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  name VARCHAR(255) NOT NULL,
  description TEXT,
  status VARCHAR(50) DEFAULT 'active',
  phase VARCHAR(50) DEFAULT 'CREATED',
  template VARCHAR(100),

  -- Phase timing columns
  discovery_started_at TIMESTAMP,
  discovery_completed_at TIMESTAMP,
  feature_selection_started_at TIMESTAMP,
  feature_selection_completed_at TIMESTAMP,
  plan_generation_started_at TIMESTAMP,
  plan_generation_completed_at TIMESTAMP,
  planning_started_at TIMESTAMP,
  planning_completed_at TIMESTAMP,
  visual_discovery_started_at TIMESTAMP,
  visual_discovery_completed_at TIMESTAMP,
  specification_started_at TIMESTAMP,
  specification_completed_at TIMESTAMP,

  -- Metadata
  metadata JSONB DEFAULT '{}',
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_projects_tenant ON projects(tenant_id);
CREATE INDEX idx_projects_status ON projects(status);
CREATE INDEX idx_projects_phase ON projects(phase);
ColumnTypeDescription
phaseVARCHARCREATED, DISCOVERY, FEATURE_SELECTION, PLAN_GENERATION, PLANNING, VISUAL_DISCOVERY, SPECIFICATION
templateVARCHARe-commerce, saas, marketplace, etc.
*_started_atTIMESTAMPPhase start times
*_completed_atTIMESTAMPPhase completion times

Question Flow Tables

question_templates

Master question definitions.

CREATE TABLE question_templates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  template_key VARCHAR(100) UNIQUE NOT NULL,
  category VARCHAR(100) NOT NULL,
  question_text TEXT NOT NULL,
  description TEXT,
  answer_type VARCHAR(50) NOT NULL,
  options JSONB,
  validation_rules JSONB,
  display_order INTEGER DEFAULT 0,
  is_required BOOLEAN DEFAULT true,
  applicable_templates VARCHAR(100)[] DEFAULT '{}',
  created_at TIMESTAMP DEFAULT NOW()
);
ColumnTypeDescription
template_keyVARCHARUnique identifier (e.g., 'project_type')
categoryVARCHARGrouping (basics, users, features)
answer_typeVARCHARtext, select, multi_select, boolean
optionsJSONBFor select types: array of value/label pairs
applicable_templatesVARCHAR[]Which project templates use this

graph_nodes

Discovery questions (hierarchical graph structure).

CREATE TABLE graph_nodes (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
  parent_id UUID REFERENCES graph_nodes(id) ON DELETE CASCADE,
  type VARCHAR(50) NOT NULL, -- 'section', 'question', 'suggestion'
  template_key VARCHAR(100),

  content JSONB NOT NULL,
  -- content: {
  --   question_text: string,
  --   description: string,
  --   variants: [{type, text, confidence}],
  --   answer_type: string,
  --   options: [{value, label}]
  -- }

  status VARCHAR(50) DEFAULT 'pending',
  display_order INTEGER DEFAULT 0,
  dependencies UUID[] DEFAULT '{}',
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_graph_nodes_project ON graph_nodes(project_id);
CREATE INDEX idx_graph_nodes_parent ON graph_nodes(parent_id);
CREATE INDEX idx_graph_nodes_status ON graph_nodes(project_id, status);
ColumnTypeDescription
typeVARCHARsection, question, suggestion
contentJSONBFull question content including variants
statusVARCHARpending, active, answered, skipped
dependenciesUUID[]Questions that must be answered first

plan_questions

Plan-phase questions (AI-generated, feature-driven).

CREATE TABLE plan_questions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
  question_plan_id UUID REFERENCES question_plans(id) ON DELETE CASCADE,
  feature_id UUID REFERENCES feature_selections(id),

  question_text TEXT NOT NULL,
  description TEXT,
  answer_type VARCHAR(50) NOT NULL,
  options JSONB,

  -- Prefill data
  prefilled_answer TEXT,
  confidence_score DECIMAL(3,2) DEFAULT 0,
  prefill_source VARCHAR(50), -- 'persona', 'pattern', 'cascade', 'inference'
  prefill_explanation TEXT,

  -- User response
  user_answer TEXT,
  status VARCHAR(50) DEFAULT 'pending',

  -- Organization
  group_key VARCHAR(100),
  display_order INTEGER DEFAULT 0,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_plan_questions_project ON plan_questions(project_id);
CREATE INDEX idx_plan_questions_plan ON plan_questions(question_plan_id);
CREATE INDEX idx_plan_questions_status ON plan_questions(project_id, status);
ColumnTypeDescription
question_plan_idUUIDParent question plan
feature_idUUIDRelated feature (if feature-specific)
prefilled_answerTEXTAI-suggested answer
confidence_scoreDECIMAL0.00-1.00 confidence
prefill_sourceVARCHARpersona, pattern, cascade, inference
user_answerTEXTUser's actual answer

answers

All answers (for both discovery and plan questions).

CREATE TABLE answers (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
  question_id UUID NOT NULL, -- Can be graph_node or plan_question
  question_type VARCHAR(50) NOT NULL, -- 'discovery' or 'plan'

  value JSONB NOT NULL,
  -- value: {
  --   raw: string | string[] | boolean | number,
  --   formatted: string,
  --   selectedVariant: string,
  --   confidence: number,
  --   notes: string
  -- }

  source VARCHAR(50) DEFAULT 'user', -- 'user', 'prefill_accepted', 'agent'
  answered_by UUID REFERENCES users(id),
  answered_at TIMESTAMP DEFAULT NOW(),

  -- Processing status
  processed BOOLEAN DEFAULT false,
  processing_results JSONB,

  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_answers_project ON answers(project_id);
CREATE INDEX idx_answers_question ON answers(question_id);
ColumnTypeDescription
question_typeVARCHAR'discovery' (graph_nodes) or 'plan' (plan_questions)
valueJSONBAnswer value with metadata
sourceVARCHARuser, prefill_accepted, agent
processing_resultsJSONBPost-answer processing results

question_plans

Metadata for generated question plans.

CREATE TABLE question_plans (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  total_questions INTEGER DEFAULT 0,
  questions_by_feature JSONB, -- {feature_id: count}
  questions_by_group JSONB,   -- {group_key: count}

  prefill_stats JSONB,
  -- prefill_stats: {
  --   total: number,
  --   high_confidence: number,
  --   medium_confidence: number,
  --   low_confidence: number,
  --   by_source: {persona: n, pattern: n, ...}
  -- }

  status VARCHAR(50) DEFAULT 'generating',
  generated_at TIMESTAMP,
  created_at TIMESTAMP DEFAULT NOW()
);

question_pools

Pool-based question storage (primary source for plan generation).

CREATE TABLE question_pools (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR(255) NOT NULL,
  category VARCHAR(100),
  industry VARCHAR(100)[],
  scenario_type VARCHAR(100),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_question_pools_category ON question_pools(category);
CREATE INDEX idx_question_pools_industry ON question_pools USING GIN(industry);
ColumnTypeDescription
nameVARCHARPool display name (e.g., "Authentication Questions")
categoryVARCHARPool category for grouping
industryVARCHAR[]Industry filters (e.g., ["ecommerce", "saas"])
scenario_typeVARCHARScenario classification

Related Services:

  • DBQuestionPoolLoader (src/services/flow/db-question-pool-loader.ts)
  • Fallback to JSON files in /src/data/json/question-pools/ if DB empty

pool_questions

Individual questions within pools.

CREATE TABLE pool_questions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  pool_id UUID REFERENCES question_pools(id) ON DELETE CASCADE,
  base_prompt TEXT NOT NULL,
  question_type VARCHAR(50),
  priority INTEGER DEFAULT 0,
  display_order INTEGER,
  prerequisite_questions UUID[],
  metadata JSONB,
  embedding vector(1536), -- For semantic search
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_pool_questions_pool ON pool_questions(pool_id);
CREATE INDEX idx_pool_questions_type ON pool_questions(question_type);
CREATE INDEX idx_pool_questions_display_order ON pool_questions(pool_id, display_order);
ColumnTypeDescription
pool_idUUIDParent question pool
base_promptTEXTQuestion text template
question_typeVARCHARQuestion classification
priorityINTEGERSorting priority
prerequisite_questionsUUID[]Questions that must be answered first
embeddingvector(1536)Semantic search vector

Usage:

  • Primary source for Feature-Driven Plan Generator
  • Loaded via getPoolQuestions(poolIds) in DBQuestionPoolLoader

pool_activation_rules

Rules for activating question pools based on context.

CREATE TABLE pool_activation_rules (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  pool_id UUID REFERENCES question_pools(id) ON DELETE CASCADE,
  trigger_type VARCHAR(50) NOT NULL, -- 'keyword', 'pattern', 'domain', 'entity'
  trigger_value TEXT NOT NULL,
  confidence_threshold NUMERIC(3,2) DEFAULT 0.80,
  industry_filter VARCHAR(100)[],
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_pool_activation_pool ON pool_activation_rules(pool_id);
CREATE INDEX idx_pool_activation_trigger ON pool_activation_rules(trigger_type);
ColumnTypeDescription
trigger_typeVARCHARkeyword, pattern, domain, entity
trigger_valueTEXTTrigger pattern or keyword
confidence_thresholdNUMERICMinimum confidence for activation (0.0-1.0)
industry_filterVARCHAR[]Industry restrictions

Fallback:

  • Legacy hardcoded rules in src/services/flow/pool-activation-rules.ts
  • Used only if DB rules are empty

ontology_domain_templates

Domain-specific entity and workflow templates (secondary question source).

CREATE TABLE ontology_domain_templates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  domain VARCHAR(50) NOT NULL, -- 'ecommerce', 'saas', 'healthcare', 'internal-tools', 'general'
  category VARCHAR(50) NOT NULL, -- 'entity', 'workflow', 'integration'
  canonical_name VARCHAR(255) NOT NULL,
  display_name VARCHAR(255),
  template JSONB NOT NULL,
  -- template: {
  --   suggestedAttributes: [{name, type, required, description}],
  --   statusEnum: string[],
  --   transitions: [{from, to, trigger, conditions}],
  --   businessRules: string[],
  --   requiredQuestions: string[], // ← Questions embedded here!
  --   relatedEntities: string[]
  -- }
  keywords TEXT[],
  embedding vector(1536),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_domain_templates_domain ON ontology_domain_templates(domain);
CREATE INDEX idx_domain_templates_category ON ontology_domain_templates(category);
CREATE INDEX idx_domain_templates_keywords ON ontology_domain_templates USING GIN(keywords);
ColumnTypeDescription
domainVARCHARTarget domain (ecommerce, saas, etc.)
categoryVARCHARentity, workflow, integration
templateJSONBComplete template with requiredQuestions array
embeddingvector(1536)Semantic matching vector

Usage:

  • DomainTemplateService.getDomainKnowledge() (src/core/ontology/domain-template-service.ts)
  • QA Loop Orchestrator Layer 2/3 (lines 486-534)
  • Follow-up Generator for context enrichment

ontology_pattern_templates

Architectural pattern templates.

CREATE TABLE ontology_pattern_templates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  pattern_name VARCHAR(255) NOT NULL,
  display_name VARCHAR(255),
  category VARCHAR(100),
  complexity VARCHAR(50), -- 'simple', 'moderate', 'complex'
  template JSONB NOT NULL,
  -- template: {
  --   description: string,
  --   steps: [{name, description}],
  --   requiredQuestions: string[],
  --   technicalConsiderations: string[],
  --   bestPractices: string[]
  -- }
  keywords TEXT[],
  compatible_domains TEXT[],
  embedding vector(1536),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_pattern_templates_category ON ontology_pattern_templates(category);
CREATE INDEX idx_pattern_templates_complexity ON ontology_pattern_templates(complexity);
ColumnTypeDescription
pattern_nameVARCHARPattern identifier
complexityVARCHARsimple, moderate, complex
templateJSONBPattern definition with requiredQuestions
compatible_domainsTEXT[]Applicable domain types

ontology_integration_templates

Third-party integration templates.

CREATE TABLE ontology_integration_templates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  provider VARCHAR(255) NOT NULL, -- 'stripe', 'sendgrid', 'twilio', etc.
  display_name VARCHAR(255),
  category VARCHAR(100), -- 'payment', 'email', 'auth', 'storage'
  capabilities TEXT[],
  required_config TEXT[],
  template JSONB NOT NULL,
  -- template: {
  --   apiEndpoints: [{method, path, description}],
  --   webhookEvents: string[],
  --   authMethods: string[],
  --   requiredQuestions: string[],
  --   configurationSteps: string[]
  -- }
  docs_reference TEXT,
  embedding vector(1536),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_integration_templates_provider ON ontology_integration_templates(provider);
CREATE INDEX idx_integration_templates_category ON ontology_integration_templates(category);
CREATE INDEX idx_integration_templates_capabilities ON ontology_integration_templates USING GIN(capabilities);
ColumnTypeDescription
providerVARCHARService provider name
categoryVARCHARIntegration type
capabilitiesTEXT[]Integration capabilities
templateJSONBFull integration spec with requiredQuestions

feature_driven_plans

Feature-driven question plan metadata.

CREATE TABLE feature_driven_plans (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
  curation_id UUID REFERENCES feature_curations(id) ON DELETE CASCADE,
  agent_id VARCHAR(255),
  status VARCHAR(50) DEFAULT 'active', -- 'active', 'completed', 'archived'
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_feature_driven_plans_project ON feature_driven_plans(project_id);
CREATE INDEX idx_feature_driven_plans_curation ON feature_driven_plans(curation_id);
CREATE INDEX idx_feature_driven_plans_status ON feature_driven_plans(status);
ColumnTypeDescription
curation_idUUIDRelated feature curation
agent_idVARCHARGenerating agent identifier
statusVARCHARPlan lifecycle status

Generated By:

  • FeatureDrivenPlanGenerator.generatePlan() (src/services/flow/feature-driven-plan-generator.ts:42-148)

plan_sections

Sections within feature-driven plans (typically one per feature).

CREATE TABLE plan_sections (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  plan_id UUID REFERENCES feature_driven_plans(id) ON DELETE CASCADE,
  feature_id UUID REFERENCES feature_catalog(id),
  title VARCHAR(255) NOT NULL,
  description TEXT,
  display_order INTEGER,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_plan_sections_plan ON plan_sections(plan_id);
CREATE INDEX idx_plan_sections_feature ON plan_sections(feature_id);
CREATE INDEX idx_plan_sections_order ON plan_sections(plan_id, display_order);
ColumnTypeDescription
plan_idUUIDParent plan
feature_idUUIDRelated feature from catalog
display_orderINTEGERSection ordering

canonical_questions (UNUSED ❌)

⚠️ WARNING: This table is populated but NEVER queried in active code.

CREATE TABLE canonical_questions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  entity_id UUID,
  question_text TEXT NOT NULL,
  category VARCHAR(100),
  priority INTEGER,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_canonical_questions_entity ON canonical_questions(entity_id);
CREATE INDEX idx_canonical_questions_category ON canonical_questions(category);
ColumnTypeDescription
entity_idUUIDRelated entity (unclear reference)
question_textTEXTQuestion content
categoryVARCHARQuestion classification

Status:

  • Populated by scripts/populate-question-pools.ts
  • Zero references in active code (grepped entire codebase)
  • Intended purpose unclear

entity_questions (UNUSED ❌)

⚠️ WARNING: This table is populated but NEVER queried in active code.

CREATE TABLE entity_questions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  entity_id UUID REFERENCES kb_canonical_entities(id) ON DELETE CASCADE,
  question_text TEXT NOT NULL,
  question_type VARCHAR(50),
  context JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_entity_questions_entity ON entity_questions(entity_id);
CREATE INDEX idx_entity_questions_type ON entity_questions(question_type);
ColumnTypeDescription
entity_idUUIDReferences kb_canonical_entities
question_typeVARCHARQuestion classification
contextJSONBAdditional context data

Status:

  • Populated by scripts/generate-entity-questions.ts
  • Zero references in active code
  • Intended purpose: Entity-specific questions (never integrated)

kb_naics_standard_mappings (PARTIAL USE ✅)

NAICS code to canonical entity mappings.

CREATE TABLE kb_naics_standard_mappings (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  naics_code VARCHAR(6) REFERENCES kb_naics_codes(code) ON DELETE CASCADE,
  entity_id UUID REFERENCES kb_canonical_entities(id) ON DELETE CASCADE,
  relevance_score NUMERIC(3,2),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(naics_code, entity_id)
);

CREATE INDEX idx_kb_naics_mappings_code ON kb_naics_standard_mappings(naics_code);
CREATE INDEX idx_kb_naics_mappings_entity ON kb_naics_standard_mappings(entity_id);
ColumnTypeDescription
naics_codeVARCHAR(6)Industry classification code
entity_idUUIDCanonical entity ID
relevance_scoreNUMERIC0.0-1.0 relevance score

Usage:

  • FeatureDrivenPlanGenerator.getEntityPoolQuestionsForIndustry() (lines 195-319)
  • Maps industry → entities → pool questions

kb_canonical_entities (PARTIAL USE ✅)

Canonical entity definitions with synonyms.

CREATE TABLE kb_canonical_entities (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  entity_name VARCHAR(255) NOT NULL,
  entity_type VARCHAR(100), -- 'user', 'resource', 'action', 'concept'
  synonyms TEXT[],
  description TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_kb_canonical_entities_type ON kb_canonical_entities(entity_type);
CREATE INDEX idx_kb_canonical_entities_synonyms ON kb_canonical_entities USING GIN(synonyms);
ColumnTypeDescription
entity_nameVARCHARCanonical name (e.g., "Order")
entity_typeVARCHAREntity classification
synonymsTEXT[]Alternative names (e.g., ["Purchase", "Transaction"])

Usage:

  • ✅ Entity synonym matching in plan generation
  • ✅ Industry-to-entity mapping via NAICS

Task Management Tables

tasks

User and agent tasks.

CREATE TABLE tasks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  type VARCHAR(50) NOT NULL, -- 'PROVIDE', 'DECIDE', 'REVIEW', 'CONNECT'
  title VARCHAR(255) NOT NULL,
  description TEXT,
  status VARCHAR(50) DEFAULT 'pending',
  priority VARCHAR(20) DEFAULT 'medium',

  assignee_type VARCHAR(20) DEFAULT 'user', -- 'user' or 'agent'
  assignee_id UUID,
  due_date TIMESTAMP,

  -- Task Mode UI routing
  component_key VARCHAR(100), -- Routes to TaskComponent (e.g., 'permission-editor')

  -- Dependencies
  dependencies UUID[] DEFAULT '{}',
  blocked_by UUID[] DEFAULT '{}',

  -- Completion
  completed_at TIMESTAMP,
  completed_by UUID,
  completion_notes TEXT,

  metadata JSONB DEFAULT '{}',
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_tasks_project ON tasks(project_id);
CREATE INDEX idx_tasks_status ON tasks(project_id, status);
CREATE INDEX idx_tasks_assignee ON tasks(assignee_type, assignee_id);
ColumnTypeDescription
typeVARCHARPROVIDE, DECIDE, REVIEW, CONNECT
statusVARCHARpending, in_progress, completed, blocked
assignee_typeVARCHARuser or agent
dependenciesUUID[]Task IDs that must complete first

task_actions

Available actions on tasks.

CREATE TABLE task_actions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,

  type VARCHAR(50) NOT NULL,
  -- Types: FORM_SUBMIT, SINGLE_SELECT, MULTI_SELECT, TEXT_INPUT,
  --        FILE_UPLOAD, APPROVE_REJECT, EXTERNAL_LINK

  label VARCHAR(255) NOT NULL,
  description TEXT,
  config JSONB NOT NULL,
  -- config varies by type:
  -- FORM_SUBMIT: {formId, submitUrl}
  -- SINGLE_SELECT: {options: [{value, label}]}
  -- APPROVE_REJECT: {approveLabel, rejectLabel}

  display_order INTEGER DEFAULT 0,
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_task_actions_task ON task_actions(task_id);

task_targets

Resources/components associated with tasks.

CREATE TABLE task_targets (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
  target_type VARCHAR(50) NOT NULL, -- 'question', 'feature', 'spec_section'
  target_id UUID NOT NULL,
  metadata JSONB DEFAULT '{}',
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_task_targets_task ON task_targets(task_id);
CREATE INDEX idx_task_targets_target ON task_targets(target_type, target_id);

action_submissions

Submitted task actions (idempotent storage).

CREATE TABLE action_submissions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
  action_id UUID REFERENCES task_actions(id) ON DELETE CASCADE,

  idempotency_key VARCHAR(255) UNIQUE NOT NULL,
  submitted_by UUID REFERENCES users(id),
  submitted_at TIMESTAMP DEFAULT NOW(),

  data JSONB NOT NULL,
  result JSONB,

  -- Output binding results
  bindings_executed JSONB,

  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_action_submissions_task ON action_submissions(task_id);
CREATE INDEX idx_action_submissions_idempotency ON action_submissions(idempotency_key);
ColumnTypeDescription
idempotency_keyVARCHARPrevents duplicate submissions
dataJSONBSubmitted action data
resultJSONBProcessed result
bindings_executedJSONBOutput binding results

task_activity

Audit log for task changes.

CREATE TABLE task_activity (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,

  activity_type VARCHAR(50) NOT NULL,
  -- Types: created, assigned, status_changed, action_submitted,
  --        completed, blocked, unblocked

  actor_type VARCHAR(20), -- 'user', 'agent', 'system'
  actor_id UUID,
  details JSONB,
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_task_activity_task ON task_activity(task_id);
CREATE INDEX idx_task_activity_type ON task_activity(activity_type);

Agent Execution Tables

agent_memory

Persistent agent conversation history.

CREATE TABLE agent_memory (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  session_id UUID NOT NULL,
  agent_name VARCHAR(100) NOT NULL,

  message_role VARCHAR(20) NOT NULL, -- 'user', 'assistant', 'system'
  message_content TEXT NOT NULL,
  tokens_used INTEGER DEFAULT 0,

  metadata JSONB DEFAULT '{}',
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_agent_memory_session ON agent_memory(session_id);
CREATE INDEX idx_agent_memory_tenant ON agent_memory(tenant_id);
CREATE INDEX idx_agent_memory_agent ON agent_memory(agent_name);
CREATE INDEX idx_agent_memory_created ON agent_memory(created_at);
ColumnTypeDescription
session_idUUIDGroups messages by session
agent_nameVARCHARWhich agent owns this memory
message_roleVARCHARuser, assistant, system
tokens_usedINTEGERToken count for this message

agent_executor_state

Execution state snapshots.

CREATE TABLE agent_executor_state (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  execution_id UUID UNIQUE NOT NULL,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  status VARCHAR(50) DEFAULT 'pending',
  -- Status: pending, running, paused, completed, failed

  strategy VARCHAR(50) NOT NULL,
  agents JSONB NOT NULL, -- [{name, role, status}]

  current_step INTEGER DEFAULT 0,
  total_steps INTEGER,

  started_at TIMESTAMP,
  completed_at TIMESTAMP,

  -- Budget tracking
  tokens_used INTEGER DEFAULT 0,
  tokens_limit INTEGER,
  cost_incurred DECIMAL(10,4) DEFAULT 0,
  cost_limit DECIMAL(10,4),

  -- Results
  intermediate_results JSONB DEFAULT '{}',
  final_result JSONB,
  error JSONB,

  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_executor_state_project ON agent_executor_state(project_id);
CREATE INDEX idx_executor_state_status ON agent_executor_state(status);

checkpoint_snapshots

Checkpoint data for recovery.

CREATE TABLE checkpoint_snapshots (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  execution_id UUID REFERENCES agent_executor_state(execution_id) ON DELETE CASCADE,

  step_index INTEGER NOT NULL,
  step_name VARCHAR(100) NOT NULL,

  state JSONB NOT NULL,
  -- state: {
  --   completedSteps: string[],
  --   pendingSteps: string[],
  --   intermediateResults: {},
  --   agentStates: {},
  --   tokensUsed: number,
  --   costIncurred: number
  -- }

  created_at TIMESTAMP DEFAULT NOW(),
  expires_at TIMESTAMP -- Auto-cleanup after expiry
);

CREATE INDEX idx_checkpoints_execution ON checkpoint_snapshots(execution_id);
CREATE INDEX idx_checkpoints_expires ON checkpoint_snapshots(expires_at);

budget_limits

Budget configuration per project.

CREATE TABLE budget_limits (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE UNIQUE,

  max_tokens INTEGER DEFAULT 100000,
  max_cost_usd DECIMAL(10,4) DEFAULT 5.00,
  warning_threshold DECIMAL(3,2) DEFAULT 0.80,
  on_limit_reached VARCHAR(20) DEFAULT 'pause', -- 'pause', 'fail', 'warn'

  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Feature and Pattern Tables

feature_candidate_sets

Generated feature candidates.

CREATE TABLE feature_candidate_sets (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  candidates JSONB NOT NULL,
  -- candidates: [{
  --   id: uuid,
  --   name: string,
  --   description: string,
  --   category: string,
  --   complexity: string,
  --   score: number,
  --   signals_matched: string[]
  -- }]

  generation_metadata JSONB,
  generated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_feature_candidates_project ON feature_candidate_sets(project_id);

feature_selections

User's selected features.

CREATE TABLE feature_selections (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
  candidate_set_id UUID REFERENCES feature_candidate_sets(id),

  feature_id UUID NOT NULL, -- From candidate set
  name VARCHAR(255) NOT NULL,
  description TEXT,
  category VARCHAR(100),
  complexity VARCHAR(20),

  selected BOOLEAN DEFAULT true,
  selection_order INTEGER,
  user_notes TEXT,

  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_feature_selections_project ON feature_selections(project_id);
CREATE INDEX idx_feature_selections_selected ON feature_selections(project_id, selected);

pattern_detections

Detected patterns for a project.

CREATE TABLE pattern_detections (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  pattern_id VARCHAR(100) NOT NULL, -- From pattern library
  pattern_name VARCHAR(255) NOT NULL,
  confidence_score DECIMAL(3,2) NOT NULL,

  signals_matched JSONB, -- Which signals triggered this
  questions_suppressed UUID[], -- Questions made unnecessary
  spec_additions JSONB, -- Additions to spec

  applied BOOLEAN DEFAULT false,
  applied_at TIMESTAMP,

  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_pattern_detections_project ON pattern_detections(project_id);
CREATE INDEX idx_pattern_detections_pattern ON pattern_detections(pattern_id);

mistake_patterns

Learned error patterns for self-healing.

CREATE TABLE mistake_patterns (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

  pattern_type VARCHAR(50) NOT NULL,
  -- Types: circular_reference, missing_dependency, invalid_state,
  --        timeout, budget_exceeded, validation_failure

  pattern_signature VARCHAR(255) UNIQUE NOT NULL, -- Hash of error context
  description TEXT,

  occurrences INTEGER DEFAULT 1,
  first_seen TIMESTAMP DEFAULT NOW(),
  last_seen TIMESTAMP DEFAULT NOW(),

  auto_fix_available BOOLEAN DEFAULT false,
  fix_strategy JSONB,

  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_mistake_patterns_type ON mistake_patterns(pattern_type);
CREATE INDEX idx_mistake_patterns_signature ON mistake_patterns(pattern_signature);

Specification Tables

compiled_specs

Cached compiled specifications.

CREATE TABLE compiled_specs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  version VARCHAR(20) NOT NULL,
  format VARCHAR(20) DEFAULT 'v1', -- 'v1' or 'v2'

  spec JSONB NOT NULL, -- Full SpecV1/V2 object
  validation_result JSONB,

  compiled_at TIMESTAMP DEFAULT NOW(),
  compiled_by VARCHAR(50), -- 'user' or agent name

  -- Source tracking
  source_hash VARCHAR(64), -- Hash of inputs for cache invalidation
  inputs_snapshot JSONB
);

CREATE INDEX idx_compiled_specs_project ON compiled_specs(project_id);
CREATE INDEX idx_compiled_specs_version ON compiled_specs(project_id, version);

discovery_output

Compiled discovery phase output.

CREATE TABLE discovery_output (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE UNIQUE,

  summary TEXT,
  extracted_entities JSONB,
  -- extracted_entities: [{
  --   type: 'user' | 'resource' | 'action',
  --   name: string,
  --   attributes: {},
  --   confidence: number,
  --   source_questions: uuid[]
  -- }]

  ontology_coverage DECIMAL(3,2),
  key_decisions JSONB,
  compiled_at TIMESTAMP DEFAULT NOW()
);

planning_artifacts

Planning phase outputs (blueprint, plan).

CREATE TABLE planning_artifacts (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  artifact_type VARCHAR(50) NOT NULL, -- 'blueprint', 'implementation_plan'
  content JSONB NOT NULL,

  status VARCHAR(50) DEFAULT 'draft',
  approved_at TIMESTAMP,
  approved_by UUID,

  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_planning_artifacts_project ON planning_artifacts(project_id);
CREATE INDEX idx_planning_artifacts_type ON planning_artifacts(artifact_type);

design_discovery_output

Visual discovery phase output.

CREATE TABLE design_discovery_output (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE UNIQUE,

  design_preferences JSONB,
  -- design_preferences: {
  --   colorPalette: {},
  --   typography: {},
  --   layoutPreferences: {},
  --   stylePreferences: []
  -- }

  research_findings JSONB,
  -- research_findings: {
  --   competitors: [],
  --   inspirations: [],
  --   patterns: []
  -- }

  compiled_design_system JSONB,
  compiled_at TIMESTAMP DEFAULT NOW()
);

Permissions Tables

permission_roles

Role definitions with capability arrays.

CREATE TABLE permission_roles (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  name VARCHAR(255) NOT NULL,
  description TEXT,
  capabilities TEXT[] DEFAULT '{}',
  -- capabilities: ['user:read', 'user:write', 'project:create', etc.]

  is_system_role BOOLEAN DEFAULT false,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(tenant_id, name)
);
ColumnTypeDescription
capabilitiesTEXT[]Array of capability strings
is_system_roleBOOLEANSystem-defined roles cannot be deleted

permission_policies

ABAC policy definitions with condition evaluation.

CREATE TABLE permission_policies (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  name VARCHAR(255) NOT NULL,
  description TEXT,

  effect VARCHAR(10) NOT NULL, -- 'allow' or 'deny'
  actions TEXT[] NOT NULL,     -- ['user:read', 'user:write']
  resources TEXT[] NOT NULL,   -- ['user:*', 'project:123']
  conditions JSONB,
  -- conditions: {
  --   "user.department": {"$eq": "engineering"},
  --   "resource.status": {"$in": ["active", "pending"]}
  -- }

  priority INTEGER DEFAULT 0,
  enabled BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

role_assignments

User/resource role assignments.

CREATE TABLE role_assignments (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  role_id UUID REFERENCES permission_roles(id) ON DELETE CASCADE,
  user_id UUID REFERENCES users(id) ON DELETE CASCADE,

  scope_type VARCHAR(50), -- 'tenant', 'project', 'resource'
  scope_id UUID,          -- Tenant, project, or resource ID

  assigned_at TIMESTAMP DEFAULT NOW(),
  assigned_by UUID REFERENCES users(id),
  expires_at TIMESTAMP,

  UNIQUE(role_id, user_id, scope_type, scope_id)
);

policy_evaluations

Policy evaluation audit log.

CREATE TABLE policy_evaluations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,

  user_id UUID REFERENCES users(id),
  action VARCHAR(100) NOT NULL,
  resource_type VARCHAR(100) NOT NULL,
  resource_id UUID,

  result VARCHAR(20) NOT NULL, -- 'allow' or 'deny'
  matched_policies UUID[],     -- Policy IDs that matched
  evaluation_time_ms INTEGER,

  evaluated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_policy_evaluations_user ON policy_evaluations(user_id);
CREATE INDEX idx_policy_evaluations_resource ON policy_evaluations(resource_type, resource_id);

permission_audit_log

Detailed permission audit trail.

CREATE TABLE permission_audit_log (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,

  event_type VARCHAR(50) NOT NULL,
  -- Types: role_created, role_assigned, policy_evaluated, access_denied

  actor_id UUID REFERENCES users(id),
  target_user_id UUID REFERENCES users(id),
  target_role_id UUID REFERENCES permission_roles(id),
  target_policy_id UUID REFERENCES permission_policies(id),

  details JSONB,
  created_at TIMESTAMP DEFAULT NOW()
);

Document Template Tables

document_templates

Template storage with metadata.

CREATE TABLE document_templates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  name VARCHAR(255) NOT NULL,
  description TEXT,
  template_type VARCHAR(50) NOT NULL, -- 'contract', 'invoice', 'report', etc.
  format VARCHAR(20) NOT NULL,        -- 'docx', 'pdf', 'html', 'markdown'

  file_path VARCHAR(500) NOT NULL,
  file_size INTEGER,

  status VARCHAR(50) DEFAULT 'draft',
  version INTEGER DEFAULT 1,

  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

template_variables

Extracted variables from templates.

CREATE TABLE template_variables (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  template_id UUID REFERENCES document_templates(id) ON DELETE CASCADE,

  variable_name VARCHAR(255) NOT NULL,
  variable_type VARCHAR(50) NOT NULL, -- 'text', 'number', 'date', 'boolean', 'list'
  description TEXT,

  default_value TEXT,
  is_required BOOLEAN DEFAULT false,
  validation_rules JSONB,

  created_at TIMESTAMP DEFAULT NOW()
);

template_mappings

Variable to source mappings.

CREATE TABLE template_mappings (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  template_id UUID REFERENCES document_templates(id) ON DELETE CASCADE,
  variable_id UUID REFERENCES template_variables(id) ON DELETE CASCADE,

  source_type VARCHAR(50) NOT NULL,
  -- Types: 'entity_field', 'api_response', 'workflow_output', 'constant', 'computed'

  source_path VARCHAR(500), -- JSONPath or dot notation
  transform_expression TEXT, -- Optional transform logic

  created_at TIMESTAMP DEFAULT NOW()
);

template_renders

Render history and cache.

CREATE TABLE template_renders (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  template_id UUID REFERENCES document_templates(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  output_format VARCHAR(20) NOT NULL, -- 'pdf', 'html', 'docx', 'email'
  output_path VARCHAR(500),

  variables_used JSONB,
  render_status VARCHAR(50) DEFAULT 'pending',
  error_message TEXT,

  rendered_at TIMESTAMP DEFAULT NOW(),
  rendered_by UUID REFERENCES users(id)
);

BPMN Workflow Tables

workflow_definitions

BPMN workflow storage.

CREATE TABLE workflow_definitions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  name VARCHAR(255) NOT NULL,
  description TEXT,

  bpmn_xml TEXT NOT NULL,
  bpmn_file_path VARCHAR(500),

  status VARCHAR(50) DEFAULT 'draft',
  version INTEGER DEFAULT 1,

  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

workflow_models

Normalized workflow models (JSON representation).

CREATE TABLE workflow_models (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  workflow_id UUID REFERENCES workflow_definitions(id) ON DELETE CASCADE,

  normalized_model JSONB NOT NULL,
  -- normalized_model: {
  --   nodes: [{id, type, name, properties}],
  --   edges: [{id, source, target, condition}],
  --   metadata: {}
  -- }

  created_at TIMESTAMP DEFAULT NOW()
);

workflow_validations

Validation results for workflows.

CREATE TABLE workflow_validations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  workflow_id UUID REFERENCES workflow_definitions(id) ON DELETE CASCADE,

  validation_level VARCHAR(50) NOT NULL, -- 'structural', 'semantic', 'platform'
  passed BOOLEAN NOT NULL,

  errors JSONB,   -- [{code, message, elementId, severity}]
  warnings JSONB, -- [{code, message, elementId}]

  validated_at TIMESTAMP DEFAULT NOW()
);

workflow_versions

Workflow version history.

CREATE TABLE workflow_versions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  workflow_id UUID REFERENCES workflow_definitions(id) ON DELETE CASCADE,

  version INTEGER NOT NULL,
  bpmn_xml TEXT NOT NULL,
  change_notes TEXT,

  created_at TIMESTAMP DEFAULT NOW(),
  created_by UUID REFERENCES users(id),

  UNIQUE(workflow_id, version)
);

workflow_bindings

Element bindings to templates, integrations, permissions.

CREATE TABLE workflow_bindings (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  workflow_id UUID REFERENCES workflow_definitions(id) ON DELETE CASCADE,

  element_id VARCHAR(255) NOT NULL, -- BPMN element ID
  element_type VARCHAR(50) NOT NULL, -- 'UserTask', 'ServiceTask', etc.

  binding_type VARCHAR(50) NOT NULL, -- 'template', 'integration', 'permission'
  binding_target_id UUID NOT NULL,

  config JSONB, -- Element-specific configuration

  created_at TIMESTAMP DEFAULT NOW()
);

Gaps Analysis Tables

gap_runs

Gaps analysis run metadata.

CREATE TABLE gap_runs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  run_type VARCHAR(50) NOT NULL, -- 'full', 'incremental'
  detectors_run TEXT[],          -- Detector IDs that ran

  findings_count INTEGER DEFAULT 0,
  tasks_generated INTEGER DEFAULT 0,

  status VARCHAR(50) DEFAULT 'running',
  started_at TIMESTAMP DEFAULT NOW(),
  completed_at TIMESTAMP,
  error_message TEXT
);

gap_findings

Detector findings with stable IDs.

CREATE TABLE gap_findings (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  run_id UUID REFERENCES gap_runs(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  finding_type VARCHAR(50) NOT NULL, -- 'workflow_gap', 'permission_gap', etc.
  detector_id VARCHAR(100) NOT NULL,

  severity VARCHAR(20) NOT NULL, -- 'blocker', 'warning', 'info'
  title VARCHAR(255) NOT NULL,
  description TEXT,

  artifact_type VARCHAR(50),
  artifact_id UUID,

  resolution_task_id UUID REFERENCES tasks(id),
  resolved BOOLEAN DEFAULT false,
  resolved_at TIMESTAMP,

  created_at TIMESTAMP DEFAULT NOW()
);

Standards Registry Tables

standards_artifacts

Schema registry for canonical domain objects.

CREATE TABLE standards_artifacts (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,

  kind VARCHAR(50) NOT NULL, -- 'json-schema', 'openapi', 'asyncapi', 'c4'
  namespace VARCHAR(100) NOT NULL, -- 'core', 'domain', 'project:{id}'
  artifact_id VARCHAR(255) NOT NULL, -- '$id' from schema

  name VARCHAR(255) NOT NULL,
  description TEXT,

  content JSONB NOT NULL,
  format VARCHAR(50) NOT NULL, -- 'json', 'yaml'

  version VARCHAR(50) DEFAULT '1.0.0',
  status VARCHAR(50) DEFAULT 'active', -- 'draft', 'active', 'deprecated'

  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),

  UNIQUE(namespace, artifact_id, version)
);

standards_versions

Schema version history.

CREATE TABLE standards_versions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  artifact_id UUID REFERENCES standards_artifacts(id) ON DELETE CASCADE,

  version VARCHAR(50) NOT NULL,
  content JSONB NOT NULL,

  change_summary TEXT,
  breaking_change BOOLEAN DEFAULT false,

  created_at TIMESTAMP DEFAULT NOW(),
  created_by UUID REFERENCES users(id),

  UNIQUE(artifact_id, version)
);

standards_references

Reference graph for dependencies.

CREATE TABLE standards_references (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

  source_artifact_id UUID REFERENCES standards_artifacts(id) ON DELETE CASCADE,
  target_artifact_id UUID REFERENCES standards_artifacts(id) ON DELETE CASCADE,

  reference_type VARCHAR(50) NOT NULL, -- '$ref', 'extends', 'imports'
  reference_path VARCHAR(500), -- JSONPath to reference location

  created_at TIMESTAMP DEFAULT NOW(),

  UNIQUE(source_artifact_id, target_artifact_id, reference_path)
);

Integration Tables

integration_artifacts

Integration configurations.

CREATE TABLE integration_artifacts (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  integration_type VARCHAR(50) NOT NULL, -- 'curated', 'contract'
  provider VARCHAR(100) NOT NULL, -- 'stripe', 'gmail', 'custom', etc.

  contract_standard_id UUID REFERENCES standards_artifacts(id),
  -- For contract-first integrations

  config JSONB NOT NULL,
  -- config: {
  --   apiKey: "{{secret}}",
  --   baseUrl: "https://api.example.com",
  --   operations: [...]
  -- }

  status VARCHAR(50) DEFAULT 'active',
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

integration_mappings

Schema mappings for integrations.

CREATE TABLE integration_mappings (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  integration_id UUID REFERENCES integration_artifacts(id) ON DELETE CASCADE,

  operation_id VARCHAR(255) NOT NULL,

  source_schema_id UUID REFERENCES standards_artifacts(id),
  target_schema_id UUID REFERENCES standards_artifacts(id),

  mapping_rules JSONB NOT NULL,
  -- mapping_rules: [{
  --   sourcePath: "user.email",
  --   targetPath: "email",
  --   transform: "toLowerCase()"
  -- }]

  created_at TIMESTAMP DEFAULT NOW()
);

integration_credentials

Encrypted credential storage.

CREATE TABLE integration_credentials (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  integration_id UUID REFERENCES integration_artifacts(id) ON DELETE CASCADE,

  credential_type VARCHAR(50) NOT NULL, -- 'api_key', 'oauth', 'jwt'
  encrypted_value TEXT NOT NULL,

  expires_at TIMESTAMP,
  last_used_at TIMESTAMP,

  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Event and Audit Tables

event_log

System event persistence.

CREATE TABLE event_log (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  event_type VARCHAR(100) NOT NULL,
  payload JSONB NOT NULL,

  actor_type VARCHAR(20), -- 'user', 'agent', 'system'
  actor_id UUID,

  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_event_log_tenant ON event_log(tenant_id);
CREATE INDEX idx_event_log_project ON event_log(project_id);
CREATE INDEX idx_event_log_type ON event_log(event_type);
CREATE INDEX idx_event_log_created ON event_log(created_at);

feedback_events

User feedback tracking.

CREATE TABLE feedback_events (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
  user_id UUID REFERENCES users(id),

  feedback_type VARCHAR(50) NOT NULL,
  -- Types: answer_correction, suggestion_dismissed, rating, comment

  target_type VARCHAR(50), -- 'question', 'prefill', 'suggestion'
  target_id UUID,

  feedback_data JSONB NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_feedback_events_project ON feedback_events(project_id);
CREATE INDEX idx_feedback_events_type ON feedback_events(feedback_type);

background_tasks

Async task queue.

CREATE TABLE background_tasks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,

  task_type VARCHAR(100) NOT NULL,
  -- Types: post_answer_processing, feature_curation, research_task,
  --        spec_compilation, etc.

  payload JSONB NOT NULL,
  priority INTEGER DEFAULT 0,

  status VARCHAR(50) DEFAULT 'pending',
  -- Status: pending, running, completed, failed

  attempts INTEGER DEFAULT 0,
  max_attempts INTEGER DEFAULT 3,

  started_at TIMESTAMP,
  completed_at TIMESTAMP,
  error JSONB,

  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_background_tasks_status ON background_tasks(status, priority);
CREATE INDEX idx_background_tasks_project ON background_tasks(project_id);

Requirements Discovery Tables

requirements_discovery_contexts

Structured requirements extracted from conversational discovery phase.

CREATE TABLE requirements_discovery_contexts (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  tenant_id UUID NOT NULL,

  -- Completion tracking
  is_complete BOOLEAN NOT NULL DEFAULT FALSE,
  completed_at TIMESTAMPTZ,

  -- Purpose & Problem Section
  purpose_and_problem JSONB NOT NULL DEFAULT '{}'::jsonb,

  -- Users & Personas Section
  users_and_personas JSONB NOT NULL DEFAULT '{}'::jsonb,

  -- Success Criteria Section
  success_criteria JSONB NOT NULL DEFAULT '{}'::jsonb,

  -- Constraints Section
  constraints JSONB NOT NULL DEFAULT '{}'::jsonb,

  -- Non-Functional Screening Section
  non_functional_screening JSONB NOT NULL DEFAULT '{}'::jsonb,

  -- Domain-Specific Section (optional)
  domain_specific JSONB,
  domain_type VARCHAR(50),

  -- Metadata
  version INTEGER NOT NULL DEFAULT 1,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_by TEXT,

  UNIQUE(project_id)
);

CREATE INDEX idx_requirements_discovery_contexts_project
  ON requirements_discovery_contexts(project_id);
CREATE INDEX idx_requirements_discovery_contexts_tenant
  ON requirements_discovery_contexts(tenant_id);
CREATE INDEX idx_requirements_discovery_contexts_complete
  ON requirements_discovery_contexts(project_id, is_complete);

-- GIN indexes for JSONB queries
CREATE INDEX idx_requirements_discovery_purpose_gin
  ON requirements_discovery_contexts USING GIN (purpose_and_problem);
CREATE INDEX idx_requirements_discovery_users_gin
  ON requirements_discovery_contexts USING GIN (users_and_personas);
CREATE INDEX idx_requirements_discovery_success_gin
  ON requirements_discovery_contexts USING GIN (success_criteria);

JSONB Field Structures:

purpose_and_problem:

{
  coreProblem: { value: string, confidence: number },
  triggerEvent: { value: string, confidence?: number },
  currentSolution: { value: string, confidence?: number },
  problemImpact: { value: string, confidence?: number }
}

users_and_personas:

{
  primaryUser: { value: string, confidence: number },
  secondaryUsers: { value: string[], confidence?: number },
  jobsToBeDone: { value: string[], confidence: number },
  painPoints: { value: string[], confidence: number },
  endUserTechnicalLevel: { value: string, confidence?: number }
}

success_criteria:

{
  measuresOfSuccess: { value: string, confidence: number },
  dealbreakers: { value: string[], confidence?: number }
}

constraints:

{
  timeline: { value: string, confidence: number },
  budget: { value: string, confidence?: number },
  technicalConstraints: { value: string[], confidence?: number },
  existingSystems: { value: string[], confidence?: number }
}

non_functional_screening:

{
  sensitiveData: { value: string, confidence: number },
  expectedScale: { value: string, confidence: number }
}

requirements_discovery_messages

Conversation messages for requirements discovery phase.

CREATE TABLE requirements_discovery_messages (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  discovery_context_id UUID NOT NULL REFERENCES requirements_discovery_contexts(id) ON DELETE CASCADE,
  project_id UUID NOT NULL,
  tenant_id UUID NOT NULL,

  -- Message content
  role VARCHAR(20) NOT NULL CHECK (role IN ('user', 'agent')),
  content TEXT NOT NULL,

  -- UI configuration for agent messages
  ui_config JSONB,

  -- Agent metadata (for agent messages)
  extraction_data JSONB,
  question_delivered VARCHAR(255),

  -- Timestamps
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  INDEX idx_requirements_discovery_messages_context (discovery_context_id, created_at),
  INDEX idx_requirements_discovery_messages_project (project_id)
);
ColumnTypeDescription
roleVARCHAR'user' or 'agent'
contentTEXTMessage text
ui_configJSONBObject with type ("freeform" or "choice" or "hybrid"), choices? array, allowEscape boolean
extraction_dataJSONBObject with field (string), value (any), confidence (number), type ("fact", "decision", or "inference")

requirements_discovery_extractions

Audit trail of all requirement extractions.

CREATE TABLE requirements_discovery_extractions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  discovery_context_id UUID NOT NULL REFERENCES requirements_discovery_contexts(id) ON DELETE CASCADE,
  message_id UUID NOT NULL REFERENCES requirements_discovery_messages(id) ON DELETE CASCADE,
  project_id UUID NOT NULL,
  tenant_id UUID NOT NULL,

  -- Extraction details
  field_path VARCHAR(255) NOT NULL,
  extracted_value JSONB NOT NULL,
  confidence NUMERIC(3,2) NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
  extraction_type VARCHAR(20) NOT NULL CHECK (extraction_type IN ('fact', 'decision', 'inference')),

  -- Source tracking
  source_turn_number INTEGER NOT NULL,
  source_message_snippet TEXT,

  -- Timestamps
  extracted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  INDEX idx_requirements_discovery_extractions_context (discovery_context_id),
  INDEX idx_requirements_discovery_extractions_field (discovery_context_id, field_path)
);
ColumnTypeDescription
field_pathVARCHARDot-path to field (e.g., "purpose_and_problem.coreProblem")
confidenceNUMERIC(3,2)AI confidence score (0.00-1.00)
extraction_typeVARCHAR'fact' (stated), 'decision' (implied), 'inference' (derived)
source_turn_numberINTEGERWhich conversation turn this came from

feature_seeds

Feature prioritization hints derived from discovery.

CREATE TABLE feature_seeds (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  tenant_id UUID NOT NULL,

  -- Seed metadata
  seed_type VARCHAR(50) NOT NULL CHECK (seed_type IN ('jtbd_direct', 'pain_solver', 'dealbreaker', 'constraint_driven')),
  feature_name TEXT NOT NULL,
  priority VARCHAR(20) NOT NULL CHECK (priority IN ('critical', 'high', 'medium', 'low')),
  confidence NUMERIC(3,2) NOT NULL DEFAULT 0.8 CHECK (confidence >= 0 AND confidence <= 1),

  -- Source tracking
  source_field VARCHAR(100),
  rationale TEXT NOT NULL,

  -- Discovery context snapshot
  discovery_version INTEGER NOT NULL,
  created_from_discovery_id UUID REFERENCES requirements_discovery_contexts(id) ON DELETE SET NULL,

  -- Metadata
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  INDEX idx_feature_seeds_project (project_id),
  INDEX idx_feature_seeds_priority (project_id, priority)
);
ColumnTypeDescription
seed_typeVARCHARHow this feature was derived
priorityVARCHARImportance level
rationaleTEXTWhy this feature is suggested

Seed Types:

  • jtbd_direct: Directly from Jobs To Be Done
  • pain_solver: Addresses stated pain point
  • dealbreaker: Required per success criteria
  • constraint_driven: Necessitated by constraints

feature_curation_sessions

Links requirements discovery to feature selection phase.

CREATE TABLE feature_curation_sessions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  tenant_id UUID NOT NULL,

  -- Link to discovery context
  discovery_context_id UUID REFERENCES requirements_discovery_contexts(id) ON DELETE SET NULL,

  -- Cached context from discovery
  user_profiles JSONB,
  priority_signals JSONB,
  constraints JSONB,

  -- Session status
  status VARCHAR(20) NOT NULL DEFAULT 'ready' CHECK (status IN ('ready', 'in_progress', 'completed', 'abandoned')),

  -- Timestamps
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  completed_at TIMESTAMPTZ,

  INDEX idx_feature_curation_sessions_project (project_id),
  INDEX idx_feature_curation_sessions_discovery (discovery_context_id)
);

Knowledge Base Enhancements

kb_pattern_feature_mappings

Junction table linking knowledge base patterns to features.

CREATE TABLE kb_pattern_feature_mappings (
  pattern_id VARCHAR(100) NOT NULL REFERENCES kb_patterns(id) ON DELETE CASCADE,
  feature_id TEXT NOT NULL REFERENCES feature_catalog(id) ON DELETE CASCADE,
  relevance_score NUMERIC(3,2) NOT NULL DEFAULT 0.90
    CHECK (relevance_score >= 0 AND relevance_score <= 1),
  mapping_source VARCHAR(50) NOT NULL DEFAULT 'ai_generated'
    CHECK (mapping_source IN ('ai_generated', 'manual', 'rule_based')),
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  PRIMARY KEY (pattern_id, feature_id)
);

CREATE INDEX idx_kb_pfm_feature_id ON kb_pattern_feature_mappings(feature_id);
CREATE INDEX idx_kb_pfm_pattern_id ON kb_pattern_feature_mappings(pattern_id);
ColumnTypeDescription
pattern_idVARCHARFK to kb_patterns.id
feature_idTEXTFK to feature_catalog.id
relevance_scoreNUMERIC(3,2)How relevant pattern is to feature (0-1)
mapping_sourceVARCHARHow mapping was created

Mapping Sources:

  • ai_generated: Created by AI during feature regeneration
  • manual: Added by administrator
  • rule_based: Created by deterministic rules

kb_patterns (enhanced)

New column added in migration 095:

ALTER TABLE kb_patterns ADD COLUMN cloud_provider VARCHAR(20) DEFAULT 'agnostic';
CREATE INDEX idx_kb_patterns_cloud_provider ON kb_patterns(cloud_provider);
ColumnTypeDescription
cloud_providerVARCHAR(20)'agnostic', 'aws', 'gcp', 'azure'

Cloud Provider Values:

  • agnostic: Works on any cloud (12-Factor, CNCF, MITRE, OWASP, NIST)
  • aws: AWS Well-Architected patterns
  • gcp: Google Cloud Architecture Framework patterns
  • azure: Azure Well-Architected patterns (future)

feature_catalog (enhanced)

New column added in migration 095:

ALTER TABLE feature_catalog ADD COLUMN related_patterns TEXT[] DEFAULT '{}';
CREATE INDEX idx_feature_catalog_related_patterns
  ON feature_catalog USING GIN(related_patterns);
ColumnTypeDescription
related_patternsTEXT[]Array of pattern IDs from kb_patterns

Phase 2 Schema Updates (Migrations 175-205)

Updated: February 2026

Final Phase 2 execution added 31 migrations introducing multi-tenant organizations, discovery engine enhancements, realtime infrastructure, and context engineering.

Multi-Tenant Organizations System

organizations

Organizations (agencies/companies) that manage projects and invite customers.

CREATE TABLE organizations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
  name TEXT NOT NULL,
  slug TEXT NOT NULL UNIQUE,
  display_name TEXT,
  owner_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
  settings JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ColumnTypeDescription
slugTEXTURL-safe unique identifier
owner_idUUIDUser who created and owns this organization
settingsJSONBOrganization-level settings

RLS: Uses app.tenant_id configuration setting for tenant isolation.


organization_members

Organization membership with role-based access.

CREATE TABLE organization_members (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
  user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  role TEXT NOT NULL CHECK (role IN ('owner', 'admin', 'member')),
  joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(organization_id, user_id)
);
ColumnTypeDescription
roleTEXT'owner' (full control), 'admin' (manage members), 'member' (read-only)

customer_invitations

Customer invitation system for agencies to invite clients.

CREATE TABLE customer_invitations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
  email TEXT NOT NULL,
  token TEXT NOT NULL UNIQUE,
  role_id UUID,
  invited_by UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
  status TEXT NOT NULL CHECK (status IN ('pending', 'accepted', 'expired', 'revoked')) DEFAULT 'pending',
  expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '7 days'),
  accepted_at TIMESTAMPTZ,
  accepted_by_user_id UUID REFERENCES users(id),
  message TEXT,
  metadata JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ColumnTypeDescription
tokenTEXTUnique invitation token (UUID)
expires_atTIMESTAMPTZDefault 7 days from creation
statusTEXTInvitation lifecycle status

agency_projects

Links organizations to projects (agency-customer project assignment).

CREATE TABLE agency_projects (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  customer_user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
  status TEXT NOT NULL CHECK (status IN ('active', 'completed', 'archived')) DEFAULT 'active',
  assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(organization_id, project_id)
);

Discovery Engine Enhancements

discovery_objectives

Discovery objective definitions that configure what the discovery engine should elicit for each context type.

CREATE TABLE discovery_objectives (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  context_type TEXT NOT NULL CHECK (context_type IN ('business_profile', 'idea_discovery', 'project_discovery')),
  version TEXT NOT NULL DEFAULT '1.0.0',
  display_name TEXT NOT NULL,
  config JSONB NOT NULL DEFAULT '{}',
  active BOOLEAN NOT NULL DEFAULT true,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE UNIQUE INDEX idx_discovery_objectives_active_context
  ON discovery_objectives(context_type) WHERE active = true;
ColumnTypeDescription
context_typeTEXT'business_profile', 'idea_discovery', or 'project_discovery'
configJSONBDiscoveryObjectiveDefinition — defines obligations, phases, completion criteria
activeBOOLEANOnly one active objective per context_type

elicitation_sessions (extended)

New columns added in Migration 181:

ALTER TABLE elicitation_sessions
  ADD COLUMN objective_id UUID REFERENCES discovery_objectives(id),
  ADD COLUMN session_phase VARCHAR DEFAULT 'opening',
  ADD COLUMN working_memory_snapshot JSONB,
  ADD COLUMN completeness_score NUMERIC(3,2) DEFAULT 0 CHECK (completeness_score >= 0 AND completeness_score <= 1),
  ADD COLUMN turn_count INTEGER DEFAULT 0;
ColumnTypeDescription
objective_idUUIDLinks to discovery objective configuration
session_phaseVARCHARCurrent phase ('opening', 'exploration', 'validation', 'closing')
working_memory_snapshotJSONBAgent's working memory state
completeness_scoreNUMERIC(3,2)0.0-1.0 completeness score
turn_countINTEGERNumber of conversation turns

elicitation_obligations (extended)

Massively extended in Migration 182 (21 new columns):

-- Extended obligation_status enum
ALTER TYPE obligation_status ADD VALUE 'partial';
ALTER TYPE obligation_status ADD VALUE 'satisfied';
ALTER TYPE obligation_status ADD VALUE 'deferred';
ALTER TYPE obligation_status ADD VALUE 'skipped';

-- New columns
ALTER TABLE elicitation_obligations
  ADD COLUMN collection_type TEXT,
  ADD COLUMN widget_type TEXT,
  ADD COLUMN dependencies JSONB DEFAULT '[]',
  ADD COLUMN source TEXT,
  ADD COLUMN origin_layer TEXT,
  ADD COLUMN origin_source_id UUID,
  ADD COLUMN pool_id TEXT,
  ADD COLUMN task_priority INTEGER DEFAULT 5,
  ADD COLUMN negotiation_state JSONB,
  ADD COLUMN confidence NUMERIC(3,2) DEFAULT 0 CHECK (confidence >= 0 AND confidence <= 1),
  ADD COLUMN extracted_value JSONB,
  ADD COLUMN attempts INTEGER DEFAULT 0,
  ADD COLUMN max_attempts INTEGER DEFAULT 3,
  ADD COLUMN last_attempt_turn INTEGER,
  ADD COLUMN satisfied_at_turn INTEGER,
  ADD COLUMN category TEXT,
  ADD COLUMN field_path VARCHAR(500),
  ADD COLUMN agent_notes TEXT,
  ADD COLUMN follow_up_strategy TEXT,
  ADD COLUMN satisfied_when TEXT,
  ADD COLUMN expertise_required TEXT,
  ADD COLUMN embedding vector(1536); -- Optional pgvector

Key new columns:

ColumnTypeDescription
collection_typeTEXTHow to collect: direct_question, inferred, research, etc.
widget_typeTEXTUI widget type for collecting this obligation
confidenceNUMERIC(3,2)Confidence level (0.00-1.00) in the extracted value
extracted_valueJSONBThe extracted/inferred value for this obligation
negotiation_stateJSONBState of value negotiation if user disagrees
field_pathVARCHAR(500)Dot-notation path for nested field (e.g., constraints.performance.latency)
dependenciesJSONBArray of obligation keys this depends on
attemptsINTEGERNumber of collection attempts
satisfied_at_turnINTEGERWhich turn the obligation was satisfied

obligation_events

Audit trail for obligation lifecycle events.

CREATE TABLE obligation_events (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  session_id UUID NOT NULL,
  obligation_key TEXT NOT NULL,
  event_type TEXT NOT NULL CHECK (event_type IN (
    'created', 'status_changed', 'confidence_updated', 'value_extracted',
    'negotiation', 'dependency_resolved', 'merged', 'split'
  )),
  old_status TEXT,
  new_status TEXT,
  old_confidence NUMERIC(3,2),
  new_confidence NUMERIC(3,2),
  source TEXT,
  metadata JSONB DEFAULT '{}',
  turn_number INTEGER,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_obligation_events_session ON obligation_events(session_id, created_at);
CREATE INDEX idx_obligation_events_key ON obligation_events(session_id, obligation_key);
CREATE INDEX idx_obligation_events_type ON obligation_events(event_type);

Event Types:

  • created — Obligation first created
  • status_changed — Status transition (e.g., pending → satisfied)
  • confidence_updated — Confidence score changed
  • value_extracted — Value extracted from conversation
  • negotiation — User disagreed with extracted value
  • dependency_resolved — Dependent obligation was satisfied
  • merged — Obligation merged with another
  • split — Obligation split into multiple

discovery_session_summaries

Periodic summaries of discovery sessions.

CREATE TABLE discovery_session_summaries (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  session_id UUID NOT NULL,
  tenant_id UUID NOT NULL,
  summary_text TEXT,
  obligation_snapshot JSONB,
  research_summary JSONB,
  turn_range_start INTEGER,
  turn_range_end INTEGER,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_discovery_session_summaries_session ON discovery_session_summaries(session_id);
CREATE INDEX idx_discovery_session_summaries_tenant ON discovery_session_summaries(tenant_id);

discovery_research_tasks

Background research tasks triggered during discovery.

CREATE TABLE discovery_research_tasks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  session_id UUID NOT NULL,
  tenant_id UUID NOT NULL,
  obligation_key TEXT,
  task_type TEXT NOT NULL CHECK (task_type IN (
    'competitive_analysis', 'tech_feasibility', 'market_research',
    'domain_research', 'integration_research'
  )),
  query TEXT NOT NULL,
  status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'failed')) DEFAULT 'pending',
  result JSONB,
  error_message TEXT,
  priority INTEGER DEFAULT 5,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  started_at TIMESTAMPTZ,
  completed_at TIMESTAMPTZ
);

CREATE INDEX idx_discovery_research_tasks_session ON discovery_research_tasks(session_id);
CREATE INDEX idx_discovery_research_tasks_status ON discovery_research_tasks(status, priority);

Task Types:

  • competitive_analysis — Research competitors and alternatives
  • tech_feasibility — Assess technical viability
  • market_research — Market size and trends
  • domain_research — Industry-specific knowledge
  • integration_research — Third-party integrations

question_pools (enriched)

New column added in Migration 186:

ALTER TABLE question_pools
  ADD COLUMN metadata JSONB DEFAULT '{}';

This enables enrichment with:

  • Industry mappings
  • Domain classifications
  • Quality metrics
  • Semantic tags

Realtime Infrastructure

realtime_channels

Registry of active realtime channels for pub/sub messaging.

CREATE TABLE realtime_channels (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL,
  channel_name TEXT NOT NULL,
  channel_type TEXT NOT NULL CHECK (channel_type IN ('project', 'session', 'organization', 'user', 'system')),
  entity_id UUID,
  config JSONB DEFAULT '{}',
  is_active BOOLEAN NOT NULL DEFAULT true,
  last_activity_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE UNIQUE INDEX idx_realtime_channels_name ON realtime_channels(tenant_id, channel_name);
CREATE INDEX idx_realtime_channels_active ON realtime_channels(is_active) WHERE is_active = true;
ColumnTypeDescription
channel_typeTEXTScoping type: project, session, organization, user, or system
entity_idUUIDEntity this channel is scoped to (if applicable)

realtime_events_log

Log of realtime events published through channels.

CREATE TABLE realtime_events_log (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL,
  channel_id UUID REFERENCES realtime_channels(id) ON DELETE CASCADE,
  event_type TEXT NOT NULL,
  event_data JSONB NOT NULL,
  published_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_realtime_events_log_channel ON realtime_events_log(channel_id, published_at);
CREATE INDEX idx_realtime_events_log_type ON realtime_events_log(event_type);

realtime_heartbeat_config

Configuration for SSE heartbeat intervals per channel type.

CREATE TABLE realtime_heartbeat_config (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  channel_type TEXT NOT NULL UNIQUE CHECK (channel_type IN ('project', 'session', 'organization', 'user', 'system')),
  heartbeat_interval_ms INTEGER NOT NULL DEFAULT 30000,
  timeout_ms INTEGER NOT NULL DEFAULT 90000,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ColumnTypeDescription
heartbeat_interval_msINTEGERHow often to send heartbeat pings (default 30s)
timeout_msINTEGERConnection timeout threshold (default 90s)

realtime_connection_stats

Connection statistics and health monitoring.

CREATE TABLE realtime_connection_stats (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL,
  channel_id UUID REFERENCES realtime_channels(id) ON DELETE CASCADE,
  connection_id TEXT NOT NULL,
  client_info JSONB,
  connected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  disconnected_at TIMESTAMPTZ,
  last_heartbeat_at TIMESTAMPTZ,
  message_count INTEGER DEFAULT 0,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_realtime_connection_stats_channel ON realtime_connection_stats(channel_id);
CREATE INDEX idx_realtime_connection_stats_active ON realtime_connection_stats(disconnected_at) WHERE disconnected_at IS NULL;

Context Engineering

context_budget_profiles

Context budget configurations per phase/agent.

CREATE TABLE context_budget_profiles (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  profile_name TEXT NOT NULL UNIQUE,
  target_type TEXT NOT NULL CHECK (target_type IN ('phase', 'agent', 'workflow')),
  target_identifier TEXT NOT NULL,
  max_tokens INTEGER NOT NULL,
  reserved_tokens INTEGER DEFAULT 0,
  priority INTEGER DEFAULT 5,
  config JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ColumnTypeDescription
target_typeTEXTWhat this budget applies to (phase, agent, or workflow)
max_tokensINTEGERMaximum context tokens allowed
reserved_tokensINTEGERTokens reserved for system prompts

context_artifacts

Context artifact definitions (specs, docs, code, etc.).

CREATE TABLE context_artifacts (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  artifact_type TEXT NOT NULL CHECK (artifact_type IN (
    'spec', 'code', 'doc', 'schema', 'test', 'plan', 'research'
  )),
  artifact_key TEXT NOT NULL,
  content TEXT,
  content_hash TEXT,
  size_bytes INTEGER,
  token_count INTEGER,
  metadata JSONB DEFAULT '{}',
  version INTEGER DEFAULT 1,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(project_id, artifact_type, artifact_key)
);

CREATE INDEX idx_context_artifacts_project ON context_artifacts(project_id);
CREATE INDEX idx_context_artifacts_type ON context_artifacts(artifact_type);

context_assembly_cache

Cached assembled context for fast retrieval.

CREATE TABLE context_assembly_cache (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  cache_key TEXT NOT NULL,
  assembled_context TEXT NOT NULL,
  artifact_ids UUID[],
  total_tokens INTEGER,
  expires_at TIMESTAMPTZ NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(project_id, cache_key)
);

CREATE INDEX idx_context_assembly_cache_project ON context_assembly_cache(project_id);
CREATE INDEX idx_context_assembly_cache_expires ON context_assembly_cache(expires_at);

context_requests

Log of context assembly requests.

CREATE TABLE context_requests (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  request_type TEXT NOT NULL CHECK (request_type IN ('phase', 'agent', 'workflow', 'ad_hoc')),
  requester_identifier TEXT NOT NULL,
  requested_artifacts TEXT[],
  resolved_artifacts UUID[],
  total_tokens INTEGER,
  budget_exceeded BOOLEAN DEFAULT false,
  cache_hit BOOLEAN DEFAULT false,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_context_requests_project ON context_requests(project_id, created_at);

context_artifact_dependencies

Dependency graph between artifacts.

CREATE TABLE context_artifact_dependencies (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  artifact_id UUID NOT NULL REFERENCES context_artifacts(id) ON DELETE CASCADE,
  depends_on_artifact_id UUID NOT NULL REFERENCES context_artifacts(id) ON DELETE CASCADE,
  dependency_type TEXT NOT NULL CHECK (dependency_type IN ('imports', 'references', 'extends', 'includes')),
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(artifact_id, depends_on_artifact_id)
);

CREATE INDEX idx_context_artifact_deps_artifact ON context_artifact_dependencies(artifact_id);
CREATE INDEX idx_context_artifact_deps_depends ON context_artifact_dependencies(depends_on_artifact_id);

pcs_change_events

Project Context Service change event log (cascade detection).

CREATE TABLE pcs_change_events (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  artifact_id UUID REFERENCES context_artifacts(id) ON DELETE SET NULL,
  event_type TEXT NOT NULL CHECK (event_type IN ('created', 'updated', 'deleted')),
  old_content_hash TEXT,
  new_content_hash TEXT,
  affected_artifacts UUID[],
  cascade_detected BOOLEAN DEFAULT false,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_pcs_change_events_project ON pcs_change_events(project_id, created_at);
CREATE INDEX idx_pcs_change_events_artifact ON pcs_change_events(artifact_id);

Migration 203-205: Discovery RBAC and Code Activation

Migration 203 (migrate_code_activation_rules):

  • Moved code activation rules from pool_activation_rules to proper discovery obligation model
  • No new tables, data migration only

Migration 204 (set_eligible_contexts):

  • Added eligible_contexts metadata to discovery objectives
  • No schema changes, data update only

Migration 205 (discovery_cr_rbac):

  • Enhanced discovery-related Change Request RBAC
  • No new tables, policy updates only

Kit System Tables

Migrations 208-212 — Extensible code generation and platform integration framework.

See also: 16-kit-system.md for complete Kit System documentation

kit_definitions

Registry of available Kits. Populated from kit.yaml files during Kit registration/import.

CREATE TABLE kit_definitions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,

  -- Identity (from kit.yaml)
  kit_id VARCHAR(100) NOT NULL,
  display_name VARCHAR(255) NOT NULL,
  description TEXT,
  version VARCHAR(50) NOT NULL,
  kit_type VARCHAR(20) NOT NULL CHECK (kit_type IN ('file', 'platform', 'api', 'hybrid', 'composite')),
  status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'deprecated', 'draft')),
  author VARCHAR(100) DEFAULT 'praetor',

  -- Platform info (nullable for file/codegen kits)
  platform_name VARCHAR(100),
  platform_category VARCHAR(50),

  -- Parsed kit.yaml (full manifest for runtime access)
  manifest JSONB NOT NULL,

  -- Supported operations
  supported_phases TEXT[] NOT NULL,

  -- Output definitions (denormalized for quick queries)
  output_count INTEGER NOT NULL DEFAULT 0,

  -- Kit package location
  package_path VARCHAR(500),

  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),

  UNIQUE(tenant_id, kit_id, version)
);

CREATE INDEX idx_kit_definitions_tenant ON kit_definitions(tenant_id);
CREATE INDEX idx_kit_definitions_category ON kit_definitions(platform_category);
CREATE INDEX idx_kit_definitions_status ON kit_definitions(status);

RLS: Tenant-isolated using app.tenant_id config setting.

Key Features:

  • Stores full kit.yaml manifest as JSONB for runtime access
  • Supports 5 kit types: file, platform, api, hybrid, composite
  • Tracks supported phases (SPEC, CHECK, DISCOVER, PLAN, VALIDATE, APPLY, VERIFY)
  • Denormalized output_count for quick queries
  • Version-aware with unique constraint on (tenant_id, kit_id, version)

kit_credentials

Encrypted platform credentials per project. Stores API keys, OAuth tokens, and other credential types.

CREATE TABLE kit_credentials (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
  kit_id VARCHAR(100) NOT NULL,

  credential_type VARCHAR(20) NOT NULL CHECK (credential_type IN ('oauth2', 'api_key', 'basic', 'custom')),
  encrypted_data TEXT NOT NULL,
  connection_info JSONB,

  -- OAuth2 specific
  token_expires_at TIMESTAMPTZ,
  refresh_token_encrypted TEXT,

  -- Status
  status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'expired', 'revoked')),
  last_checked_at TIMESTAMPTZ,
  last_check_status VARCHAR(20) CHECK (last_check_status IS NULL OR last_check_status IN ('succeeded', 'failed')),

  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),

  UNIQUE(tenant_id, project_id, kit_id)
);

CREATE INDEX idx_kit_credentials_project ON kit_credentials(project_id);
CREATE INDEX idx_kit_credentials_tenant ON kit_credentials(tenant_id);

RLS: Tenant-isolated using app.tenant_id config setting.

Key Features:

  • Stores encrypted credentials (never in plaintext)
  • Supports 4 credential types: oauth2, api_key, basic, custom
  • OAuth2 token expiration and refresh token management
  • Health check tracking (last_checked_at, last_check_status)
  • One credential per (tenant, project, kit) combination

kit_executions

Every Kit run is recorded. One row per execution.

CREATE TABLE kit_executions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
  user_id UUID REFERENCES users(id),

  -- What was executed
  kit_definition_id UUID REFERENCES kit_definitions(id),
  kit_id VARCHAR(100) NOT NULL,
  kit_version VARCHAR(50) NOT NULL,

  -- Execution status: pending → running → completed | failed | partial
  status VARCHAR(20) NOT NULL DEFAULT 'pending'
    CHECK (status IN ('pending', 'running', 'completed', 'failed', 'partial')),
  started_at TIMESTAMPTZ,
  completed_at TIMESTAMPTZ,
  execution_time_ms INTEGER,

  -- Input snapshot (what context was provided)
  -- NOT credentials — never store in execution log
  input_snapshot JSONB,

  -- Results
  output_count INTEGER DEFAULT 0,
  outputs_valid INTEGER DEFAULT 0,
  outputs_warnings INTEGER DEFAULT 0,
  outputs_failed INTEGER DEFAULT 0,

  -- Token usage
  token_usage_prompt INTEGER DEFAULT 0,
  token_usage_completion INTEGER DEFAULT 0,
  token_usage_total INTEGER DEFAULT 0,
  estimated_cost_usd NUMERIC(10, 6),

  -- Bundle
  bundle_path VARCHAR(500),
  bundle_size_bytes INTEGER,

  -- Error info (if failed)
  error_message TEXT,
  error_step VARCHAR(100),
  error_details JSONB,

  -- Workflow tracking (Inngest/Mastra)
  workflow_run_id VARCHAR(255),
  inngest_run_id VARCHAR(255),

  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_kit_executions_project ON kit_executions(project_id);
CREATE INDEX idx_kit_executions_status ON kit_executions(status);
CREATE INDEX idx_kit_executions_kit ON kit_executions(kit_id);
CREATE INDEX idx_kit_executions_tenant ON kit_executions(tenant_id);

RLS: Tenant-isolated using app.tenant_id config setting.

Key Features:

  • Complete audit trail of all Kit executions
  • Status progression: pending → running → completed|failed|partial
  • Token usage and cost tracking per execution
  • Denormalized output statistics (valid, warnings, failed)
  • Workflow tracking for Inngest/Mastra orchestration
  • Error details with step identification for debugging
  • Bundle metadata (path, size) for download access

kit_execution_outputs

Individual output file results within an execution.

CREATE TABLE kit_execution_outputs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  execution_id UUID REFERENCES kit_executions(id) ON DELETE CASCADE,

  -- Output identity
  output_id VARCHAR(100) NOT NULL,
  filename VARCHAR(255) NOT NULL,
  output_type VARCHAR(20) NOT NULL,

  -- Status
  status VARCHAR(20) NOT NULL CHECK (status IN ('valid', 'warnings', 'failed')),

  -- Validation results
  validation_errors INTEGER DEFAULT 0,
  validation_warnings INTEGER DEFAULT 0,
  validation_auto_fixed INTEGER DEFAULT 0,
  repair_attempts INTEGER DEFAULT 0,
  validation_details JSONB,

  -- Content (stored in object storage, not DB)
  content_path VARCHAR(500),
  content_size_bytes INTEGER,
  content_hash VARCHAR(64),

  -- Token usage for this output
  token_usage_prompt INTEGER DEFAULT 0,
  token_usage_completion INTEGER DEFAULT 0,

  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_kit_exec_outputs_execution ON kit_execution_outputs(execution_id);

RLS: No separate RLS — inherits from kit_executions via JOIN. Access controlled at the API layer.

Key Features:

  • One row per generated output file
  • Validation tracking (errors, warnings, auto-fixes)
  • Repair attempt counter for cognitive recovery
  • Content stored in object storage (S3-compatible) with path reference
  • Content hash for integrity verification
  • Per-output token usage tracking

kit_discover_cache

Cached platform state from DISCOVER phase. Avoids re-reading platform state on every execution within a reasonable window.

CREATE TABLE kit_discover_cache (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
  kit_id VARCHAR(100) NOT NULL,

  -- Cached state
  existing_state JSONB NOT NULL,
  implications JSONB,
  connection_info JSONB,

  -- Cache control
  discovered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  expires_at TIMESTAMPTZ NOT NULL,
  invalidated BOOLEAN DEFAULT false,

  UNIQUE(tenant_id, project_id, kit_id)
);

CREATE INDEX idx_kit_discover_cache_lookup
  ON kit_discover_cache(tenant_id, project_id, kit_id)
  WHERE invalidated = false;

RLS: Tenant-isolated using app.tenant_id config setting.

Key Features:

  • TTL-based cache expiration (discovered_at + TTL)
  • Manual invalidation support (invalidated flag)
  • Stores existing platform state (resources, configurations)
  • Stores implications (warnings, conflicts, recommendations)
  • One cache entry per (tenant, project, kit) combination
  • Partial index for fast lookups of active (non-invalidated) cache entries

Relationships Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                         DATABASE RELATIONSHIPS                              │
│                                                                             │
│  ┌─────────────┐                                                           │
│  │   tenants   │                                                           │
│  └──────┬──────┘                                                           │
│         │ 1:N                                                              │
│         ▼                                                                  │
│  ┌─────────────┐      ┌─────────────┐                                     │
│  │    users    │      │  projects   │◀──────────────────────┐             │
│  └─────────────┘      └──────┬──────┘                       │             │
│                              │ 1:N                          │             │
│         ┌────────────────────┼────────────────────┐         │             │
│         │                    │                    │         │             │
│         ▼                    ▼                    ▼         │             │
│  ┌─────────────┐      ┌─────────────┐      ┌─────────────┐ │             │
│  │ graph_nodes │      │plan_questions│     │   tasks     │ │             │
│  │ (discovery) │      │   (plan)    │      └──────┬──────┘ │             │
│  └──────┬──────┘      └──────┬──────┘             │        │             │
│         │                    │                    │        │             │
│         └────────────────────┼────────────────────┘        │             │
│                              │                             │             │
│                              ▼                             │             │
│                       ┌─────────────┐                      │             │
│                       │   answers   │                      │             │
│                       └─────────────┘                      │             │
│                                                            │             │
│  ┌─────────────────────────────────────────────────────────┘             │
│  │                                                                       │
│  │  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐   │
│  │  │feature_selections│  │ compiled_specs   │  │agent_executor_   │   │
│  │  └──────────────────┘  └──────────────────┘  │     state        │   │
│  │                                              └────────┬─────────┘   │
│  │                                                       │             │
│  │  ┌──────────────────┐  ┌──────────────────┐          │             │
│  │  │pattern_detections│  │discovery_output  │          ▼             │
│  │  └──────────────────┘  └──────────────────┘  ┌──────────────────┐   │
│  │                                              │checkpoint_       │   │
│  │  ┌──────────────────┐  ┌──────────────────┐ │   snapshots      │   │
│  │  │planning_artifacts│  │design_discovery_ │  └──────────────────┘   │
│  │  └──────────────────┘  │     output       │                        │
│  │                        └──────────────────┘                        │
│  │                                                                     │
│  └─────────────────────────────────────────────────────────────────────┘
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    STANDALONE TABLES                                 │   │
│  │                                                                      │   │
│  │  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐  │   │
│  │  │question_templates│  │ mistake_patterns │  │  agent_memory    │  │   │
│  │  │   (global)       │  │    (learned)     │  │   (sessions)     │  │   │
│  │  └──────────────────┘  └──────────────────┘  └──────────────────┘  │   │
│  │                                                                      │   │
│  │  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐  │   │
│  │  │   event_log      │  │ feedback_events  │  │background_tasks  │  │   │
│  │  │   (audit)        │  │   (learning)     │  │    (queue)       │  │   │
│  │  └──────────────────┘  └──────────────────┘  └──────────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

See also: 10-api-reference.md for API details, 02-product-architecture.md for system overview

Last Updated: February 2025

Command Palette

Search for a command to run...