-- ============================================================================= -- Vignette · migration 23 — 관리자 감정 관측용 Jev 전이 trace -- ============================================================================= -- 기존 turns/session_state 행은 수정하지 않는다. trace는 성공한 client 턴에만 -- 연결되며, 앱 런타임은 client turn INSERT와 state UPSERT를 같은 트랜잭션에 둔다. CREATE TABLE IF NOT EXISTS app.client_affect_trace ( turn_id UUID PRIMARY KEY REFERENCES app.turns(id) ON DELETE CASCADE, session_id UUID NOT NULL REFERENCES app.sessions(id) ON DELETE CASCADE, trace JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_client_affect_trace_session_created ON app.client_affect_trace (session_id, created_at DESC); ALTER TABLE app.client_affect_trace ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS p_client_affect_trace_select_admin ON app.client_affect_trace; DROP POLICY IF EXISTS p_client_affect_trace_insert_learner ON app.client_affect_trace; CREATE POLICY p_client_affect_trace_select_admin ON app.client_affect_trace FOR SELECT USING (app.current_role_name() = 'admin'); CREATE POLICY p_client_affect_trace_insert_learner ON app.client_affect_trace FOR INSERT WITH CHECK ( app.current_role_name() = 'learner' AND EXISTS ( SELECT 1 FROM app.turns AS turn_row JOIN app.sessions AS session_row ON session_row.id = turn_row.session_id WHERE turn_row.id = app.client_affect_trace.turn_id AND turn_row.session_id = app.client_affect_trace.session_id AND turn_row.speaker = 'client' AND session_row.learner_id = app.current_uid() ) );