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
- Multi-Tenancy Architecture
- Core Tables
- Question Flow Tables
- Task Management Tables
- Agent Execution Tables
- Feature and Pattern Tables
- Specification Tables
- Permissions Tables
- Document Template Tables
- BPMN Workflow Tables
- Gaps Analysis Tables
- Standards Registry Tables
- Integration Tables
- Event and Audit Tables
- Requirements Discovery Tables
- Knowledge Base Enhancements
- Phase 2 Schema Updates
- Kit System Tables
- Relationships Diagram
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_idandapp.tenant_idconfig 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
| Domain | Tables | Purpose |
|---|---|---|
| Multi-tenancy | 3 | Tenant, user, preferences |
| Organizations | 4 | Organizations, members, invitations, agency projects |
| Projects | 5 | Project, phases, features |
| Questions | 6 | Templates, instances, answers |
| Tasks | 5 | Tasks, actions, activity |
| Agents | 4 | Memory, state, checkpoints |
| Specs | 4 | Compiled specs, artifacts |
| Events | 3 | Audit, events, feedback |
| Patterns | 3 | Pattern library, detections |
| Permissions | 5 | Roles, policies, assignments |
| Templates | 4 | Template storage, variables, mappings |
| Workflows | 5 | BPMN workflows, validation, bindings |
| Gaps | 2 | Analysis runs, findings |
| Standards | 3 | Schema registry, versions, references |
| Integrations | 3 | Integration artifacts, mappings, credentials |
| Requirements Discovery | 5 | Conversational requirements gathering, extractions, feature seeds |
| Knowledge Base | 1 | Pattern-feature mappings |
| Discovery Engine | 10 | Objectives, obligations, sessions, research tasks, audit trail |
| Realtime Infrastructure | 4 | Channels, events, heartbeat, connection stats |
| Context Engineering | 6 | Budget profiles, artifacts, cache, requests, dependencies |
| Kit System | 5 | Kit 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 → QueryCore 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()
);| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| name | VARCHAR | Organization name |
| slug | VARCHAR | URL-safe identifier |
| settings | JSONB | Tenant-specific settings |
| billing_plan | VARCHAR | Subscription 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);| Column | Type | Description |
|---|---|---|
| tenant_id | UUID | Parent tenant |
| VARCHAR | User email (unique per tenant) | |
| role | VARCHAR | member, admin, owner |
| stytch_user_id | VARCHAR | Stytch auth provider ID |
| user_type | user_type | 'individual', 'agency_member', 'customer' |
| organization_id | UUID | Organization 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);| Column | Type | Description |
|---|---|---|
| phase | VARCHAR | CREATED, DISCOVERY, FEATURE_SELECTION, PLAN_GENERATION, PLANNING, VISUAL_DISCOVERY, SPECIFICATION |
| template | VARCHAR | e-commerce, saas, marketplace, etc. |
| *_started_at | TIMESTAMP | Phase start times |
| *_completed_at | TIMESTAMP | Phase 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()
);| Column | Type | Description |
|---|---|---|
| template_key | VARCHAR | Unique identifier (e.g., 'project_type') |
| category | VARCHAR | Grouping (basics, users, features) |
| answer_type | VARCHAR | text, select, multi_select, boolean |
| options | JSONB | For select types: array of value/label pairs |
| applicable_templates | VARCHAR[] | 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);| Column | Type | Description |
|---|---|---|
| type | VARCHAR | section, question, suggestion |
| content | JSONB | Full question content including variants |
| status | VARCHAR | pending, active, answered, skipped |
| dependencies | UUID[] | 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);| Column | Type | Description |
|---|---|---|
| question_plan_id | UUID | Parent question plan |
| feature_id | UUID | Related feature (if feature-specific) |
| prefilled_answer | TEXT | AI-suggested answer |
| confidence_score | DECIMAL | 0.00-1.00 confidence |
| prefill_source | VARCHAR | persona, pattern, cascade, inference |
| user_answer | TEXT | User'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);| Column | Type | Description |
|---|---|---|
| question_type | VARCHAR | 'discovery' (graph_nodes) or 'plan' (plan_questions) |
| value | JSONB | Answer value with metadata |
| source | VARCHAR | user, prefill_accepted, agent |
| processing_results | JSONB | Post-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);| Column | Type | Description |
|---|---|---|
| name | VARCHAR | Pool display name (e.g., "Authentication Questions") |
| category | VARCHAR | Pool category for grouping |
| industry | VARCHAR[] | Industry filters (e.g., ["ecommerce", "saas"]) |
| scenario_type | VARCHAR | Scenario 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);| Column | Type | Description |
|---|---|---|
| pool_id | UUID | Parent question pool |
| base_prompt | TEXT | Question text template |
| question_type | VARCHAR | Question classification |
| priority | INTEGER | Sorting priority |
| prerequisite_questions | UUID[] | Questions that must be answered first |
| embedding | vector(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);| Column | Type | Description |
|---|---|---|
| trigger_type | VARCHAR | keyword, pattern, domain, entity |
| trigger_value | TEXT | Trigger pattern or keyword |
| confidence_threshold | NUMERIC | Minimum confidence for activation (0.0-1.0) |
| industry_filter | VARCHAR[] | 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);| Column | Type | Description |
|---|---|---|
| domain | VARCHAR | Target domain (ecommerce, saas, etc.) |
| category | VARCHAR | entity, workflow, integration |
| template | JSONB | Complete template with requiredQuestions array |
| embedding | vector(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);| Column | Type | Description |
|---|---|---|
| pattern_name | VARCHAR | Pattern identifier |
| complexity | VARCHAR | simple, moderate, complex |
| template | JSONB | Pattern definition with requiredQuestions |
| compatible_domains | TEXT[] | 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);| Column | Type | Description |
|---|---|---|
| provider | VARCHAR | Service provider name |
| category | VARCHAR | Integration type |
| capabilities | TEXT[] | Integration capabilities |
| template | JSONB | Full 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);| Column | Type | Description |
|---|---|---|
| curation_id | UUID | Related feature curation |
| agent_id | VARCHAR | Generating agent identifier |
| status | VARCHAR | Plan 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);| Column | Type | Description |
|---|---|---|
| plan_id | UUID | Parent plan |
| feature_id | UUID | Related feature from catalog |
| display_order | INTEGER | Section 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);| Column | Type | Description |
|---|---|---|
| entity_id | UUID | Related entity (unclear reference) |
| question_text | TEXT | Question content |
| category | VARCHAR | Question 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);| Column | Type | Description |
|---|---|---|
| entity_id | UUID | References kb_canonical_entities |
| question_type | VARCHAR | Question classification |
| context | JSONB | Additional 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);| Column | Type | Description |
|---|---|---|
| naics_code | VARCHAR(6) | Industry classification code |
| entity_id | UUID | Canonical entity ID |
| relevance_score | NUMERIC | 0.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);| Column | Type | Description |
|---|---|---|
| entity_name | VARCHAR | Canonical name (e.g., "Order") |
| entity_type | VARCHAR | Entity classification |
| synonyms | TEXT[] | 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);| Column | Type | Description |
|---|---|---|
| type | VARCHAR | PROVIDE, DECIDE, REVIEW, CONNECT |
| status | VARCHAR | pending, in_progress, completed, blocked |
| assignee_type | VARCHAR | user or agent |
| dependencies | UUID[] | 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);| Column | Type | Description |
|---|---|---|
| idempotency_key | VARCHAR | Prevents duplicate submissions |
| data | JSONB | Submitted action data |
| result | JSONB | Processed result |
| bindings_executed | JSONB | Output 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);| Column | Type | Description |
|---|---|---|
| session_id | UUID | Groups messages by session |
| agent_name | VARCHAR | Which agent owns this memory |
| message_role | VARCHAR | user, assistant, system |
| tokens_used | INTEGER | Token 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)
);| Column | Type | Description |
|---|---|---|
| capabilities | TEXT[] | Array of capability strings |
| is_system_role | BOOLEAN | System-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)
);| Column | Type | Description |
|---|---|---|
| role | VARCHAR | 'user' or 'agent' |
| content | TEXT | Message text |
| ui_config | JSONB | Object with type ("freeform" or "choice" or "hybrid"), choices? array, allowEscape boolean |
| extraction_data | JSONB | Object 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)
);| Column | Type | Description |
|---|---|---|
| field_path | VARCHAR | Dot-path to field (e.g., "purpose_and_problem.coreProblem") |
| confidence | NUMERIC(3,2) | AI confidence score (0.00-1.00) |
| extraction_type | VARCHAR | 'fact' (stated), 'decision' (implied), 'inference' (derived) |
| source_turn_number | INTEGER | Which 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)
);| Column | Type | Description |
|---|---|---|
| seed_type | VARCHAR | How this feature was derived |
| priority | VARCHAR | Importance level |
| rationale | TEXT | Why this feature is suggested |
Seed Types:
jtbd_direct: Directly from Jobs To Be Donepain_solver: Addresses stated pain pointdealbreaker: Required per success criteriaconstraint_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);| Column | Type | Description |
|---|---|---|
| pattern_id | VARCHAR | FK to kb_patterns.id |
| feature_id | TEXT | FK to feature_catalog.id |
| relevance_score | NUMERIC(3,2) | How relevant pattern is to feature (0-1) |
| mapping_source | VARCHAR | How mapping was created |
Mapping Sources:
ai_generated: Created by AI during feature regenerationmanual: Added by administratorrule_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);| Column | Type | Description |
|---|---|---|
| cloud_provider | VARCHAR(20) | 'agnostic', 'aws', 'gcp', 'azure' |
Cloud Provider Values:
agnostic: Works on any cloud (12-Factor, CNCF, MITRE, OWASP, NIST)aws: AWS Well-Architected patternsgcp: Google Cloud Architecture Framework patternsazure: 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);| Column | Type | Description |
|---|---|---|
| related_patterns | TEXT[] | 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()
);| Column | Type | Description |
|---|---|---|
| slug | TEXT | URL-safe unique identifier |
| owner_id | UUID | User who created and owns this organization |
| settings | JSONB | Organization-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)
);| Column | Type | Description |
|---|---|---|
| role | TEXT | '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()
);| Column | Type | Description |
|---|---|---|
| token | TEXT | Unique invitation token (UUID) |
| expires_at | TIMESTAMPTZ | Default 7 days from creation |
| status | TEXT | Invitation 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;| Column | Type | Description |
|---|---|---|
| context_type | TEXT | 'business_profile', 'idea_discovery', or 'project_discovery' |
| config | JSONB | DiscoveryObjectiveDefinition — defines obligations, phases, completion criteria |
| active | BOOLEAN | Only 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;| Column | Type | Description |
|---|---|---|
| objective_id | UUID | Links to discovery objective configuration |
| session_phase | VARCHAR | Current phase ('opening', 'exploration', 'validation', 'closing') |
| working_memory_snapshot | JSONB | Agent's working memory state |
| completeness_score | NUMERIC(3,2) | 0.0-1.0 completeness score |
| turn_count | INTEGER | Number 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 pgvectorKey new columns:
| Column | Type | Description |
|---|---|---|
| collection_type | TEXT | How to collect: direct_question, inferred, research, etc. |
| widget_type | TEXT | UI widget type for collecting this obligation |
| confidence | NUMERIC(3,2) | Confidence level (0.00-1.00) in the extracted value |
| extracted_value | JSONB | The extracted/inferred value for this obligation |
| negotiation_state | JSONB | State of value negotiation if user disagrees |
| field_path | VARCHAR(500) | Dot-notation path for nested field (e.g., constraints.performance.latency) |
| dependencies | JSONB | Array of obligation keys this depends on |
| attempts | INTEGER | Number of collection attempts |
| satisfied_at_turn | INTEGER | Which 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 createdstatus_changed— Status transition (e.g., pending → satisfied)confidence_updated— Confidence score changedvalue_extracted— Value extracted from conversationnegotiation— User disagreed with extracted valuedependency_resolved— Dependent obligation was satisfiedmerged— Obligation merged with anothersplit— 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 alternativestech_feasibility— Assess technical viabilitymarket_research— Market size and trendsdomain_research— Industry-specific knowledgeintegration_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;| Column | Type | Description |
|---|---|---|
| channel_type | TEXT | Scoping type: project, session, organization, user, or system |
| entity_id | UUID | Entity 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()
);| Column | Type | Description |
|---|---|---|
| heartbeat_interval_ms | INTEGER | How often to send heartbeat pings (default 30s) |
| timeout_ms | INTEGER | Connection 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()
);| Column | Type | Description |
|---|---|---|
| target_type | TEXT | What this budget applies to (phase, agent, or workflow) |
| max_tokens | INTEGER | Maximum context tokens allowed |
| reserved_tokens | INTEGER | Tokens 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_rulesto 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