-- CreateTable CREATE TABLE "claude_questions" ( "id" TEXT NOT NULL, "story_id" TEXT NOT NULL, "task_id" TEXT, "product_id" TEXT NOT NULL, "asked_by" TEXT NOT NULL, "question" TEXT NOT NULL, "options" JSONB, "status" TEXT NOT NULL, "answer" TEXT, "answered_by" TEXT, "answered_at" TIMESTAMP(3), "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "expires_at" TIMESTAMP(3) NOT NULL, CONSTRAINT "claude_questions_pkey" PRIMARY KEY ("id") ); -- CreateIndex CREATE INDEX "claude_questions_story_id_status_idx" ON "claude_questions"("story_id", "status"); -- CreateIndex CREATE INDEX "claude_questions_product_id_status_idx" ON "claude_questions"("product_id", "status"); -- CreateIndex CREATE INDEX "claude_questions_status_expires_at_idx" ON "claude_questions"("status", "expires_at"); -- AddForeignKey ALTER TABLE "claude_questions" ADD CONSTRAINT "claude_questions_story_id_fkey" FOREIGN KEY ("story_id") REFERENCES "stories"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "claude_questions" ADD CONSTRAINT "claude_questions_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "tasks"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "claude_questions" ADD CONSTRAINT "claude_questions_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "products"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "claude_questions" ADD CONSTRAINT "claude_questions_asked_by_fkey" FOREIGN KEY ("asked_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "claude_questions" ADD CONSTRAINT "claude_questions_answered_by_fkey" FOREIGN KEY ("answered_by") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- ST-1101: Postgres LISTEN/NOTIFY voor het Claude vraag-antwoord-kanaal (M11). -- -- AFTER INSERT/UPDATE-trigger op claude_questions emit een JSON-payload op het -- BESTAANDE `scrum4me_changes`-kanaal (zelfde als ST-801) met `entity: 'question'`. -- -- De solo-realtime SSE-route `/api/realtime/solo` MOET in ST-1104 een -- `if (payload.entity === 'question') return false`-filter krijgen — anders -- ontvangen solo-clients ongewenst question-events. -- -- De nieuwe user-scoped SSE-route `/api/realtime/notifications` (ST-1104) -- abonneert zich op hetzelfde kanaal en filtert op `entity === 'question'` -- + product-toegang van de gebruiker. -- -- DELETE wordt niet ondersteund — questions gaan naar status='answered', -- 'cancelled' of 'expired', niet weg. -- -- Payload shape: -- { op: 'I' | 'U', -- entity: 'question', -- id: text, -- product_id: text, -- story_id: text, -- task_id: text|null, -- assignee_id: text|null, // story.assignee_id, voor "wacht op jou"-emphase -- status: 'open'|'answered'|'cancelled'|'expired' } CREATE OR REPLACE FUNCTION notify_question_change() RETURNS trigger AS $$ DECLARE story_row record; payload jsonb; BEGIN SELECT assignee_id INTO story_row FROM stories WHERE id = NEW.story_id; payload := jsonb_build_object( 'op', CASE TG_OP WHEN 'INSERT' THEN 'I' WHEN 'UPDATE' THEN 'U' END, 'entity', 'question', 'id', NEW.id, 'product_id', NEW.product_id, 'story_id', NEW.story_id, 'task_id', NEW.task_id, 'assignee_id', story_row.assignee_id, 'status', NEW.status ); PERFORM pg_notify('scrum4me_changes', payload::text); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS claude_questions_notify ON claude_questions; CREATE TRIGGER claude_questions_notify AFTER INSERT OR UPDATE ON claude_questions FOR EACH ROW EXECUTE FUNCTION notify_question_change();