-- Migration 028: IACE Clarifications — Multi-User-Workflow -- ========================================================================== -- Up to Phase 2 the Klaerungen feature persisted answers in -- iace_projects.metadata.clarification_answers (JSONB). That works for a -- single user but cannot model assigned_to, comment threads, status -- history or audit trail. Phase 3 introduces a proper relational table. -- -- The previous JSONB blob remains read by the engine as fallback for any -- project whose clarifications have not yet been migrated, so this is a -- non-breaking add-on. A separate one-shot upcopy script migrates the -- existing JSONB answers into rows. -- ========================================================================== -- 1. Main table: one row per (project, clarification_id) CREATE TABLE IF NOT EXISTS iace_clarifications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, project_id UUID NOT NULL REFERENCES iace_projects(id) ON DELETE CASCADE, -- Deterministic clarification ID generated by the engine -- (e.g. "pattern:HP1640:0", "manuf:fanuc:dual-check-safety-dcs:1"). -- Stable across re-inits so persisted answers survive. clarification_key TEXT NOT NULL, -- The verbatim question + source as known at last engine run. -- Kept in this table so the audit trail does not break if the -- pattern library is later updated. question TEXT NOT NULL, source TEXT NOT NULL, category TEXT NOT NULL, norm_references TEXT[] DEFAULT '{}', -- Lifecycle state status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'in_progress', 'answered', 'not_relevant')), answer TEXT DEFAULT '' CHECK (answer IN ('', 'ja', 'nein', 'teilweise')), reasoning TEXT DEFAULT '', -- Multi-User workflow assigned_to TEXT DEFAULT '', -- user id / kuerzel — free text for now answered_by TEXT DEFAULT '', answered_at TIMESTAMPTZ, -- Common audit created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (project_id, clarification_key) ); CREATE INDEX IF NOT EXISTS idx_iace_clar_project ON iace_clarifications(project_id); CREATE INDEX IF NOT EXISTS idx_iace_clar_tenant ON iace_clarifications(tenant_id); CREATE INDEX IF NOT EXISTS idx_iace_clar_status ON iace_clarifications(project_id, status); CREATE INDEX IF NOT EXISTS idx_iace_clar_assignee ON iace_clarifications(assigned_to) WHERE assigned_to <> ''; -- 2. Comment thread: one row per comment, ordered by created_at CREATE TABLE IF NOT EXISTS iace_clarification_comments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), clarification_id UUID NOT NULL REFERENCES iace_clarifications(id) ON DELETE CASCADE, author TEXT NOT NULL DEFAULT '', body TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_iace_clar_comments_clar ON iace_clarification_comments(clarification_id, created_at); -- 3. Status history — every status / answer change is logged. CREATE TABLE IF NOT EXISTS iace_clarification_history ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), clarification_id UUID NOT NULL REFERENCES iace_clarifications(id) ON DELETE CASCADE, actor TEXT NOT NULL DEFAULT '', from_status TEXT, to_status TEXT, from_answer TEXT, to_answer TEXT, note TEXT DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_iace_clar_history_clar ON iace_clarification_history(clarification_id, created_at); -- 4. Trigger to bump updated_at on the main table CREATE OR REPLACE FUNCTION iace_clarifications_touch_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trg_iace_clarifications_updated_at ON iace_clarifications; CREATE TRIGGER trg_iace_clarifications_updated_at BEFORE UPDATE ON iace_clarifications FOR EACH ROW EXECUTE FUNCTION iace_clarifications_touch_updated_at();