Files
growqr-backend/drizzle/0007_grow_event_backbone.sql
2026-06-04 16:12:32 +05:30

124 lines
4.7 KiB
SQL

-- Grow event backbone: raw event inbox, service session links, artifacts, and QScore projections.
-- Postgres stores every raw GrowEvent first. Actors/projectors can rebuild from this stream.
CREATE TABLE IF NOT EXISTS grow_events (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
org_id TEXT,
source TEXT NOT NULL,
type TEXT NOT NULL,
category TEXT NOT NULL DEFAULT 'service' CHECK (category IN ('mission', 'service', 'artifact', 'usage', 'qscore', 'entitlement', 'system')),
occurred_at TIMESTAMPTZ NOT NULL,
received_at TIMESTAMPTZ NOT NULL DEFAULT now(),
mission JSONB,
subject JSONB,
correlation JSONB,
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
raw JSONB,
dedupe_key TEXT,
processing_status TEXT NOT NULL DEFAULT 'pending' CHECK (processing_status IN ('pending', 'processing', 'processed', 'failed', 'unresolved')),
processing_error TEXT,
processed_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS grow_events_user_idx
ON grow_events(user_id, occurred_at DESC);
CREATE INDEX IF NOT EXISTS grow_events_status_idx
ON grow_events(processing_status, received_at ASC);
CREATE INDEX IF NOT EXISTS grow_events_source_idx
ON grow_events(source, type, occurred_at DESC);
CREATE UNIQUE INDEX IF NOT EXISTS grow_events_dedupe_idx
ON grow_events(dedupe_key);
CREATE TABLE IF NOT EXISTS mission_service_sessions (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
mission_instance_id TEXT REFERENCES grow_active_missions(instance_id) ON DELETE SET NULL,
mission_id TEXT,
stage_id TEXT,
service_id TEXT NOT NULL,
external_id TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
metadata JSONB,
last_event_id TEXT REFERENCES grow_events(id) ON DELETE SET NULL,
last_checked_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS mission_service_sessions_user_idx
ON mission_service_sessions(user_id, updated_at DESC);
CREATE UNIQUE INDEX IF NOT EXISTS mission_service_sessions_external_idx
ON mission_service_sessions(service_id, external_id);
CREATE INDEX IF NOT EXISTS mission_service_sessions_mission_idx
ON mission_service_sessions(mission_instance_id, stage_id);
CREATE TABLE IF NOT EXISTS mission_artifacts (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
mission_instance_id TEXT REFERENCES grow_active_missions(instance_id) ON DELETE CASCADE,
mission_id TEXT,
stage_id TEXT,
source_event_id TEXT REFERENCES grow_events(id) ON DELETE SET NULL,
service_id TEXT,
external_id TEXT,
type TEXT NOT NULL,
title TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'ready',
summary TEXT,
content_md TEXT,
metadata JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS mission_artifacts_user_idx
ON mission_artifacts(user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS mission_artifacts_mission_idx
ON mission_artifacts(mission_instance_id, created_at DESC);
CREATE INDEX IF NOT EXISTS mission_artifacts_external_idx
ON mission_artifacts(service_id, external_id);
CREATE TABLE IF NOT EXISTS grow_qscore_signals (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
source_event_id TEXT REFERENCES grow_events(id) ON DELETE SET NULL,
signal_id TEXT NOT NULL,
score DOUBLE PRECISION NOT NULL,
present BOOLEAN NOT NULL DEFAULT TRUE,
source TEXT,
raw JSONB,
occurred_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS grow_qscore_signals_user_idx
ON grow_qscore_signals(user_id, occurred_at DESC);
CREATE INDEX IF NOT EXISTS grow_qscore_signals_signal_idx
ON grow_qscore_signals(signal_id, occurred_at DESC);
CREATE TABLE IF NOT EXISTS grow_qscore_latest (
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
signal_id TEXT NOT NULL,
score DOUBLE PRECISION NOT NULL,
present BOOLEAN NOT NULL DEFAULT TRUE,
source TEXT,
source_event_id TEXT REFERENCES grow_events(id) ON DELETE SET NULL,
raw JSONB,
occurred_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (user_id, signal_id)
);
CREATE INDEX IF NOT EXISTS grow_qscore_latest_user_idx
ON grow_qscore_latest(user_id, updated_at DESC);
CREATE TABLE IF NOT EXISTS grow_qscore_projection_state (
user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
score INTEGER NOT NULL DEFAULT 0,
signal_count INTEGER NOT NULL DEFAULT 0,
dimensions JSONB,
summary TEXT,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);