vignette/infra/db/init/23_client_affect_trace.sql

39 lines
1.7 KiB
SQL

-- =============================================================================
-- 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()
)
);