fix: deliver validated AI insight responses
This commit is contained in:
192
migrations/000005_growth_coach.sql
Normal file
192
migrations/000005_growth_coach.sql
Normal file
@@ -0,0 +1,192 @@
|
||||
ALTER TABLE ai_insight_snapshots ADD COLUMN IF NOT EXISTS deterministic_copy jsonb;
|
||||
-- statement-break
|
||||
ALTER TABLE ai_insight_snapshots ADD COLUMN IF NOT EXISTS provider_request_id text;
|
||||
-- statement-break
|
||||
UPDATE ai_insight_snapshots SET deterministic_copy=ai_response WHERE deterministic_copy IS NULL;
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS growth_coach_profiles (
|
||||
id text PRIMARY KEY,
|
||||
merchant_id bigint NOT NULL REFERENCES merchants(id),
|
||||
terminal_id bigint NOT NULL REFERENCES terminals(id),
|
||||
terminal_source_key text NOT NULL,
|
||||
version integer NOT NULL CHECK (version > 0),
|
||||
is_current boolean NOT NULL DEFAULT true,
|
||||
business_description text NOT NULL CHECK (char_length(business_description) BETWEEN 20 AND 2000),
|
||||
niches jsonb NOT NULL,
|
||||
sales_channels jsonb NOT NULL,
|
||||
primary_goal text NOT NULL CHECK (char_length(primary_goal) BETWEEN 3 AND 500),
|
||||
constraints_text text NOT NULL DEFAULT '' CHECK (char_length(constraints_text) <= 1000),
|
||||
difficulty smallint NOT NULL DEFAULT 2 CHECK (difficulty BETWEEN 1 AND 5),
|
||||
actor_user_id bigint NOT NULL REFERENCES merchant_users(id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (merchant_id,terminal_id,version)
|
||||
);
|
||||
-- statement-break
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS growth_coach_profile_current_idx ON growth_coach_profiles(merchant_id,terminal_id) WHERE is_current;
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS growth_coach_plans (
|
||||
id text PRIMARY KEY,
|
||||
merchant_id bigint NOT NULL REFERENCES merchants(id),
|
||||
terminal_id bigint NOT NULL REFERENCES terminals(id),
|
||||
terminal_source_key text NOT NULL,
|
||||
profile_id text NOT NULL REFERENCES growth_coach_profiles(id),
|
||||
parent_id text REFERENCES growth_coach_plans(id),
|
||||
cadence text NOT NULL CHECK (cadence IN ('DAILY','WEEKLY','MONTHLY')),
|
||||
status text NOT NULL CHECK (status IN ('GENERATING','READY','PARTIAL','FAILED','CLOSED')),
|
||||
period_start date NOT NULL,
|
||||
period_end date NOT NULL,
|
||||
daily_brief text,
|
||||
difficulty smallint NOT NULL CHECK (difficulty BETWEEN 1 AND 5),
|
||||
prompt_version text NOT NULL,
|
||||
schema_version text NOT NULL,
|
||||
model text NOT NULL,
|
||||
provider_request_id text,
|
||||
input_tokens bigint NOT NULL DEFAULT 0,
|
||||
output_tokens bigint NOT NULL DEFAULT 0,
|
||||
latency_ms bigint NOT NULL DEFAULT 0,
|
||||
provider_requests bigint NOT NULL DEFAULT 0,
|
||||
estimated_cost text,
|
||||
failure_code text,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
completed_at timestamptz,
|
||||
CHECK (period_end >= period_start)
|
||||
);
|
||||
-- statement-break
|
||||
CREATE INDEX IF NOT EXISTS growth_coach_plans_scope_idx ON growth_coach_plans(merchant_id,terminal_id,cadence,period_start DESC);
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS growth_coach_recommendations (
|
||||
id text PRIMARY KEY,
|
||||
plan_id text NOT NULL REFERENCES growth_coach_plans(id) ON DELETE CASCADE,
|
||||
merchant_id bigint NOT NULL REFERENCES merchants(id),
|
||||
terminal_id bigint NOT NULL REFERENCES terminals(id),
|
||||
rank smallint NOT NULL CHECK (rank BETWEEN 1 AND 5),
|
||||
status text NOT NULL CHECK (status IN ('OFFERED','ACCEPTED','REJECTED','EXPIRED')),
|
||||
action_type text NOT NULL CHECK (action_type IN ('MANUAL','METRIC')),
|
||||
title text NOT NULL,
|
||||
rationale text NOT NULL,
|
||||
expected_impact text NOT NULL,
|
||||
effort text NOT NULL CHECK (effort IN ('LOW','MEDIUM','HIGH')),
|
||||
evidence_ids jsonb NOT NULL,
|
||||
metric_id text,
|
||||
metric_direction text CHECK (metric_direction IN ('INCREASE','DECREASE')),
|
||||
action_level text CHECK (action_level IN ('SMALL','MEDIUM','STRETCH')),
|
||||
baseline numeric(30,6),
|
||||
target numeric(30,6),
|
||||
sample_size bigint,
|
||||
deadline date NOT NULL,
|
||||
decision_reason text,
|
||||
decision_note text,
|
||||
decided_by bigint REFERENCES merchant_users(id),
|
||||
decided_at timestamptz,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE(plan_id,rank)
|
||||
);
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS golden_steps (
|
||||
id text PRIMARY KEY,
|
||||
recommendation_id text NOT NULL UNIQUE REFERENCES growth_coach_recommendations(id),
|
||||
merchant_id bigint NOT NULL REFERENCES merchants(id),
|
||||
terminal_id bigint NOT NULL REFERENCES terminals(id),
|
||||
terminal_source_key text NOT NULL,
|
||||
action_type text NOT NULL CHECK (action_type IN ('MANUAL','METRIC')),
|
||||
title text NOT NULL,
|
||||
status text NOT NULL CHECK (status IN ('NOT_STARTED','IN_PROGRESS','COMPLETED','ABANDONED','MISSED')),
|
||||
metric_id text,
|
||||
metric_direction text,
|
||||
baseline numeric(30,6),
|
||||
target numeric(30,6),
|
||||
current_value numeric(30,6),
|
||||
sample_size bigint,
|
||||
deadline date NOT NULL,
|
||||
started_at timestamptz,
|
||||
completed_at timestamptz,
|
||||
actor_user_id bigint NOT NULL REFERENCES merchant_users(id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
-- statement-break
|
||||
CREATE INDEX IF NOT EXISTS golden_steps_scope_idx ON golden_steps(merchant_id,terminal_id,status,deadline);
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS growth_coach_jobs (
|
||||
id text PRIMARY KEY,
|
||||
merchant_id bigint NOT NULL REFERENCES merchants(id),
|
||||
terminal_id bigint NOT NULL REFERENCES terminals(id),
|
||||
terminal_source_key text NOT NULL,
|
||||
profile_id text NOT NULL REFERENCES growth_coach_profiles(id),
|
||||
cadence text NOT NULL CHECK (cadence IN ('DAILY','WEEKLY','MONTHLY')),
|
||||
trigger_reason text NOT NULL,
|
||||
period_start date NOT NULL,
|
||||
period_end date NOT NULL,
|
||||
fingerprint text NOT NULL UNIQUE,
|
||||
idempotency_key text,
|
||||
request_hash text,
|
||||
status text NOT NULL CHECK (status IN ('QUEUED','RUNNING','COMPLETED','FAILED')),
|
||||
attempts integer NOT NULL DEFAULT 0,
|
||||
scheduled_at timestamptz NOT NULL DEFAULT now(),
|
||||
lease_token text,
|
||||
lease_expires_at timestamptz,
|
||||
failure_code text,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
completed_at timestamptz,
|
||||
UNIQUE(merchant_id,idempotency_key)
|
||||
);
|
||||
-- statement-break
|
||||
CREATE INDEX IF NOT EXISTS growth_coach_jobs_pending_idx ON growth_coach_jobs(scheduled_at,created_at) WHERE status IN ('QUEUED','RUNNING');
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS growth_coach_events (
|
||||
id bigserial PRIMARY KEY,
|
||||
merchant_id bigint NOT NULL REFERENCES merchants(id),
|
||||
terminal_id bigint NOT NULL REFERENCES terminals(id),
|
||||
actor_user_id bigint REFERENCES merchant_users(id),
|
||||
entity_type text NOT NULL,
|
||||
entity_id text NOT NULL,
|
||||
event_type text NOT NULL,
|
||||
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
|
||||
occurred_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS growth_coach_progress_snapshots (
|
||||
id bigserial PRIMARY KEY,
|
||||
action_id text NOT NULL REFERENCES golden_steps(id) ON DELETE CASCADE,
|
||||
watermark text NOT NULL,
|
||||
value numeric(30,6) NOT NULL,
|
||||
sample_size bigint NOT NULL,
|
||||
evaluated_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE(action_id,watermark)
|
||||
);
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS growth_coach_difficulty_proposals (
|
||||
id text PRIMARY KEY,
|
||||
merchant_id bigint NOT NULL REFERENCES merchants(id),
|
||||
terminal_id bigint NOT NULL REFERENCES terminals(id),
|
||||
from_level smallint NOT NULL CHECK (from_level BETWEEN 1 AND 5),
|
||||
to_level smallint NOT NULL CHECK (to_level BETWEEN 1 AND 5),
|
||||
reason text NOT NULL,
|
||||
status text NOT NULL CHECK (status IN ('OFFERED','ACCEPTED','REJECTED','EXPIRED')),
|
||||
decided_by bigint REFERENCES merchant_users(id),
|
||||
decided_at timestamptz,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS growth_coach_outcome_reviews (
|
||||
id text PRIMARY KEY,
|
||||
plan_id text NOT NULL UNIQUE REFERENCES growth_coach_plans(id),
|
||||
merchant_id bigint NOT NULL REFERENCES merchants(id),
|
||||
terminal_id bigint NOT NULL REFERENCES terminals(id),
|
||||
summary text NOT NULL,
|
||||
completed_on_time integer NOT NULL DEFAULT 0,
|
||||
missed integer NOT NULL DEFAULT 0,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
-- statement-break
|
||||
CREATE TABLE IF NOT EXISTS growth_coach_idempotency (
|
||||
merchant_id bigint NOT NULL REFERENCES merchants(id),
|
||||
idempotency_key text NOT NULL,
|
||||
request_hash text NOT NULL,
|
||||
response jsonb NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY(merchant_id,idempotency_key)
|
||||
);
|
||||
-- statement-break
|
||||
INSERT INTO schema_migrations(version) VALUES (5) ON CONFLICT DO NOTHING;
|
||||
Reference in New Issue
Block a user