\set ON_ERROR_STOP on \if :{?app_role} \else \set app_role vignette \endif BEGIN; INSERT INTO app.app_user ( user_id, external_id, role, cohort, account_status, consent_at ) VALUES ('51000000-0000-0000-0000-000000000001', 'g5-smoke-learner', 'learner', 'g5-smoke', 'approved', now()), ('51000000-0000-0000-0000-000000000002', 'g5-smoke-teacher', 'instructor', 'g5-smoke', 'approved', now()), ('51000000-0000-0000-0000-000000000003', 'g5-smoke-other', 'learner', 'g5-other', 'approved', now()); INSERT INTO app.sessions (id, learner_id, session_no) VALUES ('52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 1); INSERT INTO app.turns (id, session_id, seq, speaker, text_masked, actor_kind) VALUES ('53000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', 1, 'counselor', '[G5 masked evidence 1]', 'human_learner'), ('53000000-0000-0000-0000-000000000002', '52000000-0000-0000-0000-000000000001', 2, 'client', '[G5 masked evidence 2]', 'client_ai'); INSERT INTO app.measurement_instrument ( instrument_id, instrument_version, name_ko, instrument_kind, construct, validation_basis, scoring_schema, metadata ) VALUES ( 'vignette-g5-calibration', '1.0.0', 'G5 역량별 자기보정', 'training_metric', 'self_calibration', '잠긴 자기예측과 독립 관찰의 교육용 역량별 대조.', '{"per_competency":true}'::JSONB, '{"clinical_claim_allowed":false}'::JSONB ), ( 'vignette-g5-transfer', '1.0.0', 'G5 미지 사례 전이', 'training_metric', 'transfer', '교육용 합성 미지 사례의 역량별 전이 관찰.', '{"per_competency":true,"scenario_novelty":"unseen_transfer"}'::JSONB, '{"clinical_claim_allowed":false,"data_classification":"synthetic_educational"}'::JSONB ) ON CONFLICT (instrument_id, instrument_version) DO NOTHING; INSERT INTO audit.model_run ( model_run_id, session_id, agent_role, provider, model, prompt_bundle_id, prompt_bundle_version, prompt_bundle_hash, structured_schema_version, input_evidence_hash, status ) VALUES ( '54000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', 'evaluator', 'smoke', 'g5-deterministic', 'g5-smoke', '1.0.0', repeat('a', 64), 'vignette.calibration-transfer.v1', repeat('b', 64), 'ready' ); SET LOCAL ROLE :"app_role"; SELECT set_config('app.ai_context', '', true); SELECT set_config('app.current_ai_view', '', true); SELECT set_config('app.current_role', 'learner', true); SELECT set_config('app.current_uid', '51000000-0000-0000-0000-000000000001', true); SELECT set_config('app.current_cohort', 'g5-smoke', true); INSERT INTO app.calibration_prediction_history ( history_id, session_id, learner_id, competency_id, practice_block_id, scenario_variant_id, phrase_family_id, created_by_role ) VALUES ( '55000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 'competency.empathic-check', 'oas-g5-block-smoke', 'variant-smoke-familiar', 'phrase-smoke-familiar', 'learner' ); INSERT INTO app.calibration_prediction_revision ( prediction_revision_id, submission_id, content_hash, history_id, session_id, learner_id, revision_no, predicted_success_probability, confidence, recorded_sequence, revision_reason, instrument_id, instrument_version, evidence_turn_ids, created_by_role ) VALUES ( '56000000-0000-0000-0000-000000000001', '56100000-0000-0000-0000-000000000001', repeat('1', 64), '55000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 1, 0.70, 0.60, 1, '첫 자기예측을 기록했다.', 'vignette-g5-calibration', '1.0.0', ARRAY['53000000-0000-0000-0000-000000000001']::UUID[], 'learner' ); INSERT INTO app.calibration_prediction_revision ( prediction_revision_id, submission_id, content_hash, history_id, session_id, learner_id, revision_no, supersedes_prediction_revision_id, predicted_success_probability, confidence, recorded_sequence, revision_reason, instrument_id, instrument_version, evidence_turn_ids, created_by_role ) VALUES ( '56000000-0000-0000-0000-000000000002', '56100000-0000-0000-0000-000000000002', repeat('2', 64), '55000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 2, '56000000-0000-0000-0000-000000000001', 0.55, 0.70, 2, '반대 근거를 보고 공개 전에 예측을 낮췄다.', 'vignette-g5-calibration', '1.0.0', ARRAY['53000000-0000-0000-0000-000000000001']::UUID[], 'learner' ); INSERT INTO app.calibration_prediction_lock ( lock_id, submission_id, content_hash, history_id, prediction_revision_id, session_id, learner_id, locked_sequence, created_by_role ) VALUES ( '57000000-0000-0000-0000-000000000001', '57100000-0000-0000-0000-000000000001', repeat('3', 64), '55000000-0000-0000-0000-000000000001', '56000000-0000-0000-0000-000000000002', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 2, 'learner' ); DO $$ BEGIN BEGIN INSERT INTO app.calibration_prediction_revision ( prediction_revision_id, submission_id, content_hash, history_id, session_id, learner_id, revision_no, supersedes_prediction_revision_id, predicted_success_probability, confidence, recorded_sequence, revision_reason, instrument_id, instrument_version, created_by_role ) VALUES ( '56000000-0000-0000-0000-000000000003', '56100000-0000-0000-0000-000000000003', repeat('4', 64), '55000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 3, '56000000-0000-0000-0000-000000000002', 0.90, 0.90, 3, '공개 후 오염 시도', 'vignette-g5-calibration', '1.0.0', 'learner' ); RAISE EXCEPTION 'post-lock prediction revision was accepted'; EXCEPTION WHEN object_not_in_prerequisite_state THEN NULL; END; END $$; SELECT set_config('app.ai_context', '1', true); SELECT set_config('app.current_ai_view', 'evaluator', true); SELECT set_config('app.current_role', 'admin', true); INSERT INTO app.calibration_performance_observation ( observation_id, submission_id, content_hash, history_id, prediction_lock_id, session_id, learner_id, competency_id, practice_block_id, scenario_variant_id, phrase_family_id, status, source_kind, perspective, model_run_id, instrument_id, instrument_version, uncertainty, evidence_turn_ids, counterevidence, revealed_sequence ) VALUES ( '58000000-0000-0000-0000-000000000001', '58100000-0000-0000-0000-000000000001', repeat('5', 64), '55000000-0000-0000-0000-000000000001', '57000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 'competency.empathic-check', 'oas-g5-block-smoke', 'variant-smoke-familiar', 'phrase-smoke-familiar', 'passed', 'model_inferred', 'independent_observer', '54000000-0000-0000-0000-000000000001', 'vignette-g5-calibration', '1.0.0', 0.20, ARRAY['53000000-0000-0000-0000-000000000002']::UUID[], ARRAY[]::TEXT[], 3 ); INSERT INTO app.calibration_assessment_snapshot ( assessment_snapshot_id, submission_id, content_hash, session_id, learner_id, competency_id, snapshot_no, source_observation_ids, assessment_payload, model_run_id, instrument_id, instrument_version, evidence_turn_ids ) VALUES ( '59000000-0000-0000-0000-000000000001', '59100000-0000-0000-0000-000000000001', repeat('6', 64), '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 'competency.empathic-check', 1, ARRAY['58000000-0000-0000-0000-000000000001']::UUID[], '{"competency_id":"competency.empathic-check","pair_count":1,"bias":"insufficient_evidence","improvement":"insufficient_evidence","pairs":[],"excluded_block_ids":[],"counterevidence":["below-minimum"]}'::JSONB, '54000000-0000-0000-0000-000000000001', 'vignette-g5-calibration', '1.0.0', ARRAY['53000000-0000-0000-0000-000000000002']::UUID[] ); INSERT INTO app.calibration_metacognitive_prescription ( prescription_id, assessment_snapshot_id, session_id, learner_id, competency_id, prescription_payload, model_run_id, instrument_id, instrument_version, evidence_turn_ids ) VALUES ( '5a000000-0000-0000-0000-000000000001', '59000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 'competency.empathic-check', '{"competency_id":"competency.empathic-check","bias":"insufficient_evidence","practice_mode":"collect_more_evidence","instruction_ko":"같은 역량의 새 장면을 세 번 수행하고 외부평가 전에 예측을 잠근다.","completion_evidence":["three_locked_predictions"]}'::JSONB, '54000000-0000-0000-0000-000000000001', 'vignette-g5-calibration', '1.0.0', ARRAY['53000000-0000-0000-0000-000000000002']::UUID[] ); INSERT INTO app.calibration_transfer_suite ( transfer_suite_record_id, submission_id, content_hash, suite_key, session_id, learner_id, training_phrase_family_ids, model_run_id, instrument_id, instrument_version ) VALUES ( '5b000000-0000-0000-0000-000000000001', '5b100000-0000-0000-0000-000000000001', repeat('7', 64), 'oas-g5-suite-smoke', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', ARRAY['phrase-smoke-familiar'], '54000000-0000-0000-0000-000000000001', 'vignette-g5-transfer', '1.0.0' ); INSERT INTO app.calibration_transfer_trial ( transfer_trial_record_id, transfer_suite_record_id, session_id, learner_id, trial_key, competency_id, scenario_variant_id, context_variant, relationship_style, difficulty_level, expression_variant, synthetic_subgroup, scenario_family_id, phrase_family_id, status, uncertainty, evidence_turn_ids, counterevidence, model_run_id, instrument_id, instrument_version ) VALUES ( '5c000000-0000-0000-0000-000000000001', '5b000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 'oas-g5-transfer-smoke-a', 'competency.empathic-check', 'variant-smoke-unseen-a', 'school', 'withdrawn', 3, 'indirect', 'synthetic-a', 'family-unseen-a', 'phrase-adapted-a', 'passed', 0.20, ARRAY['53000000-0000-0000-0000-000000000001']::UUID[], ARRAY[]::TEXT[], '54000000-0000-0000-0000-000000000001', 'vignette-g5-transfer', '1.0.0' ), ( '5c000000-0000-0000-0000-000000000002', '5b000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 'oas-g5-transfer-smoke-b', 'competency.empathic-check', 'variant-smoke-unseen-b', 'home', 'ambivalent', 4, 'metaphoric', 'synthetic-b', 'family-unseen-b', 'phrase-adapted-b', 'failed', 0.30, ARRAY['53000000-0000-0000-0000-000000000002']::UUID[], ARRAY['client_response_not_engaged'], '54000000-0000-0000-0000-000000000001', 'vignette-g5-transfer', '1.0.0' ); INSERT INTO app.calibration_transfer_assessment ( transfer_assessment_id, transfer_suite_record_id, session_id, learner_id, competency_id, source_trial_ids, assessment_payload, evidence_turn_ids, model_run_id, instrument_id, instrument_version ) VALUES ( '5d000000-0000-0000-0000-000000000001', '5b000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 'competency.empathic-check', ARRAY['5c000000-0000-0000-0000-000000000001','5c000000-0000-0000-0000-000000000002']::UUID[], '{"competency_id":"competency.empathic-check","trial_count":2,"observed_trial_count":2,"success_rate":0.5,"coverage":{"contexts":2},"eligible":false,"transfer_verified":false,"blockers":["below-minimum"],"evidence_refs":[],"counterevidence":["client_response_not_engaged"]}'::JSONB, ARRAY['53000000-0000-0000-0000-000000000001','53000000-0000-0000-0000-000000000002']::UUID[], '54000000-0000-0000-0000-000000000001', 'vignette-g5-transfer', '1.0.0' ); INSERT INTO app.calibration_subgroup_drift_report ( drift_report_id, transfer_suite_record_id, session_id, learner_id, competency_id, source_trial_ids, report_payload, model_run_id, instrument_id, instrument_version ) VALUES ( '5e000000-0000-0000-0000-000000000001', '5b000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 'competency.empathic-check', ARRAY['5c000000-0000-0000-0000-000000000001','5c000000-0000-0000-0000-000000000002']::UUID[], '{"competency_id":"competency.empathic-check","status":"insufficient_evidence","compared_subgroups":[],"subgroup_results":[],"threshold":0.2,"notice_ko":"교육용 합성 subgroup 관측이 부족해 드리프트를 판정하지 않는다."}'::JSONB, '54000000-0000-0000-0000-000000000001', 'vignette-g5-transfer', '1.0.0' ); -- Learner-self, unrelated learner, teacher cohort and cross-cohort isolation. SELECT set_config('app.ai_context', '', true); SELECT set_config('app.current_ai_view', '', true); SELECT set_config('app.current_role', 'learner', true); SELECT set_config('app.current_uid', '51000000-0000-0000-0000-000000000001', true); DO $$ BEGIN IF (SELECT count(*) FROM app.calibration_prediction_history) <> 1 THEN RAISE EXCEPTION 'learner self G5 RLS smoke failed'; END IF; END $$; SELECT set_config('app.current_uid', '51000000-0000-0000-0000-000000000003', true); DO $$ BEGIN IF (SELECT count(*) FROM app.calibration_prediction_history) <> 0 THEN RAISE EXCEPTION 'unrelated learner G5 RLS leaked data'; END IF; END $$; SELECT set_config('app.current_role', 'instructor', true); SELECT set_config('app.current_uid', '51000000-0000-0000-0000-000000000002', true); SELECT set_config('app.current_cohort', 'g5-smoke', true); DO $$ BEGIN IF (SELECT count(*) FROM app.calibration_transfer_suite) <> 1 THEN RAISE EXCEPTION 'teacher cohort G5 RLS smoke failed'; END IF; END $$; SELECT set_config('app.current_cohort', 'g5-other', true); DO $$ BEGIN IF (SELECT count(*) FROM app.calibration_transfer_suite) <> 0 THEN RAISE EXCEPTION 'teacher cross-cohort G5 RLS leaked data'; END IF; END $$; SELECT set_config('app.current_cohort', 'g5-smoke', true); INSERT INTO app.calibration_teacher_review_event ( review_id, submission_id, content_hash, target_kind, target_id, session_id, learner_id, review_no, disposition, correction_payload, review_reason, evidence_turn_ids, counterevidence, created_by_uid, created_by_role ) VALUES ( '5f000000-0000-0000-0000-000000000001', '5f100000-0000-0000-0000-000000000001', repeat('8', 64), 'calibration_assessment', '59000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 1, 'confirmed', '{}'::JSONB, '역량별 근거와 불확실성을 확인했다.', ARRAY['53000000-0000-0000-0000-000000000002']::UUID[], ARRAY[]::TEXT[], '51000000-0000-0000-0000-000000000002', 'instructor' ); DO $$ BEGIN BEGIN INSERT INTO app.calibration_teacher_review_event ( review_id, submission_id, content_hash, target_kind, target_id, session_id, learner_id, review_no, supersedes_review_id, disposition, correction_payload, review_reason, created_by_uid, created_by_role ) VALUES ( '5f000000-0000-0000-0000-000000000002', '5f100000-0000-0000-0000-000000000002', repeat('9', 64), 'calibration_assessment', '59000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 2, '5f000000-0000-0000-0000-000000000001', 'corrected', '{"raw_transcript":"forbidden"}'::JSONB, '금지된 본문 복제 시도', '51000000-0000-0000-0000-000000000002', 'instructor' ); RAISE EXCEPTION 'raw transcript payload was accepted'; EXCEPTION WHEN check_violation THEN NULL; END; BEGIN INSERT INTO app.calibration_teacher_review_event ( review_id, submission_id, content_hash, target_kind, target_id, session_id, learner_id, review_no, supersedes_review_id, disposition, correction_payload, review_reason, created_by_uid, created_by_role ) VALUES ( '5f000000-0000-0000-0000-000000000003', '5f100000-0000-0000-0000-000000000003', repeat('a', 64), 'calibration_assessment', '59000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 2, '5f000000-0000-0000-0000-000000000001', 'corrected', '{"total_score":0.9}'::JSONB, '금지된 합산 점수 시도', '51000000-0000-0000-0000-000000000002', 'instructor' ); RAISE EXCEPTION 'aggregate score payload was accepted'; EXCEPTION WHEN check_violation THEN NULL; END; END $$; RESET ROLE; DO $$ BEGIN BEGIN UPDATE app.calibration_prediction_revision SET predicted_success_probability = 1.0 WHERE prediction_revision_id = '56000000-0000-0000-0000-000000000001'; RAISE EXCEPTION 'append-only calibration revision update was accepted'; EXCEPTION WHEN object_not_in_prerequisite_state THEN NULL; END; BEGIN INSERT INTO app.calibration_performance_observation ( observation_id, submission_id, content_hash, history_id, prediction_lock_id, session_id, learner_id, competency_id, practice_block_id, scenario_variant_id, phrase_family_id, status, source_kind, perspective, model_run_id, instrument_id, instrument_version, uncertainty, evidence_turn_ids, revealed_sequence ) VALUES ( '58000000-0000-0000-0000-000000000009', '58100000-0000-0000-0000-000000000001', repeat('f', 64), '55000000-0000-0000-0000-000000000001', '57000000-0000-0000-0000-000000000001', '52000000-0000-0000-0000-000000000001', '51000000-0000-0000-0000-000000000001', 'competency.empathic-check', 'oas-g5-block-smoke', 'variant-smoke-familiar', 'phrase-smoke-familiar', 'passed', 'model_inferred', 'independent_observer', '54000000-0000-0000-0000-000000000001', 'vignette-g5-calibration', '1.0.0', 0.2, ARRAY['53000000-0000-0000-0000-000000000002']::UUID[], 4 ); RAISE EXCEPTION 'changed idempotent observation was accepted'; EXCEPTION WHEN unique_violation THEN NULL; END; END $$; ROLLBACK; SELECT 'g5_calibration_transfer_rollback_residue' AS check_name, ( (SELECT count(*) FROM app.calibration_prediction_history WHERE history_id = '55000000-0000-0000-0000-000000000001') + (SELECT count(*) FROM app.calibration_performance_observation WHERE observation_id = '58000000-0000-0000-0000-000000000001') + (SELECT count(*) FROM app.calibration_transfer_suite WHERE transfer_suite_record_id = '5b000000-0000-0000-0000-000000000001') + (SELECT count(*) FROM app.calibration_teacher_review_event WHERE review_id = '5f000000-0000-0000-0000-000000000001') + (SELECT count(*) FROM app.app_user WHERE user_id = '51000000-0000-0000-0000-000000000001') ) AS residue_count;