-- 023_workflows.sql -- Workflow definitions, stages, and version snapshots (v0.26.1) -- Depends on: teams, personas, users -- ── Workflow Definitions ──────────────────── CREATE TABLE IF NOT EXISTS workflows ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), team_id UUID REFERENCES teams(id) ON DELETE CASCADE, -- NULL = global name TEXT NOT NULL, slug TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', branding JSONB NOT NULL DEFAULT '{}', entry_mode TEXT NOT NULL DEFAULT 'public_link' CHECK (entry_mode IN ('public_link', 'team_only')), is_active BOOLEAN NOT NULL DEFAULT false, version INTEGER NOT NULL DEFAULT 1, on_complete JSONB, -- v0.27.0 chaining hook, NULL for now retention JSONB NOT NULL DEFAULT '{"mode": "archive"}', created_by UUID NOT NULL REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Slug unique within scope: team-scoped workflows + global workflows CREATE UNIQUE INDEX IF NOT EXISTS idx_workflows_team_slug ON workflows(team_id, slug) WHERE team_id IS NOT NULL; CREATE UNIQUE INDEX IF NOT EXISTS idx_workflows_global_slug ON workflows(slug) WHERE team_id IS NULL; CREATE INDEX IF NOT EXISTS idx_workflows_active ON workflows(is_active) WHERE is_active = true; COMMENT ON TABLE workflows IS 'Team-owned or global workflow definitions with staged processes.'; COMMENT ON COLUMN workflows.slug IS 'URL-safe identifier, unique within scope (team or global).'; COMMENT ON COLUMN workflows.branding IS '{"accent_color": "#hex", "logo_url": "...", "tagline": "..."}'; COMMENT ON COLUMN workflows.on_complete IS 'v0.27.0: chaining hook — triggers another workflow on completion.'; COMMENT ON COLUMN workflows.retention IS '{"mode": "archive"|"delete", "delete_after_days": N}'; -- ── Workflow Stages ───────────────────────── CREATE TABLE IF NOT EXISTS workflow_stages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), workflow_id UUID NOT NULL REFERENCES workflows(id) ON DELETE CASCADE, ordinal INTEGER NOT NULL, name TEXT NOT NULL, persona_id UUID REFERENCES personas(id) ON DELETE SET NULL, assignment_team_id UUID REFERENCES teams(id) ON DELETE SET NULL, form_template JSONB NOT NULL DEFAULT '{}', history_mode TEXT NOT NULL DEFAULT 'full' CHECK (history_mode IN ('full', 'summary', 'fresh')), auto_transition BOOLEAN NOT NULL DEFAULT false, transition_rules JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_workflow_stages_workflow ON workflow_stages(workflow_id, ordinal); COMMENT ON TABLE workflow_stages IS 'Ordered stages within a workflow. Each stage has a driving persona and optional human assignment.'; COMMENT ON COLUMN workflow_stages.history_mode IS 'full=complete history, summary=utility-role summary, fresh=clean slate'; COMMENT ON COLUMN workflow_stages.form_template IS 'JSON schema of fields the persona should collect from the visitor.'; -- ── Workflow Versions (immutable snapshots) ─ CREATE TABLE IF NOT EXISTS workflow_versions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), workflow_id UUID NOT NULL REFERENCES workflows(id) ON DELETE CASCADE, version_number INTEGER NOT NULL, snapshot JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (workflow_id, version_number) ); CREATE INDEX IF NOT EXISTS idx_workflow_versions_workflow ON workflow_versions(workflow_id, version_number DESC); COMMENT ON TABLE workflow_versions IS 'Immutable snapshots of workflow definitions. Active instances run against a pinned version.'; COMMENT ON COLUMN workflow_versions.snapshot IS 'Full JSON: definition + stages + persona tool grants at publish time.'; -- ── updated_at trigger ────────────────────── CREATE TRIGGER workflows_updated_at BEFORE UPDATE ON workflows FOR EACH ROW EXECUTE FUNCTION update_updated_at();