G0~G8 성과·동맹 측정 OS 작업 일괄 고정

8월 7일까지 워킹트리에만 남아 있던 미커밋 작업을 커밋한다. 여러 사본
폴더(worktree·clone)에 흩어져 있던 중간 스냅샷을 정리하기 전에 원본을
git 이력으로 고정하는 것이 목적이다.

- contracts/routes/services: measurement, outcome_trajectory, rupture_repair,
  deliberate_practice, calibration_transfer, supervision_research,
  multimodal_alliance, continuous_improvement 계열 신규 모듈과 테스트
- infra/db/init: 07~16 마이그레이션(측정 기반~calibration transfer 실행)
- apps/web: 세션 리뷰 카드·관리 화면·E2E 스펙 추가
- docs/ops: G0~G8 라이브 통합·배포·롤백 증거 문서와 evidence JSON/PNG
- scripts: smoke·ledger·릴리스 에이전트·NAS 프리뷰 운영 스크립트

engine.public 로그 .bak과 apps/web/test-results 산출물은 커밋에서 제외했다.
This commit is contained in:
Yun Chan 2026-08-08 01:30:53 +09:00
parent 93dd8f82d7
commit 16e791e044
390 changed files with 243188 additions and 499 deletions

View file

@ -0,0 +1,404 @@
\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
('41000000-0000-0000-0000-000000000001', 'g4-smoke-learner', 'learner', 'g4-smoke', 'approved', now()),
('41000000-0000-0000-0000-000000000002', 'g4-smoke-teacher', 'instructor', 'g4-smoke', 'approved', now()),
('41000000-0000-0000-0000-000000000003', 'g4-smoke-other', 'learner', 'g4-other', 'approved', now());
INSERT INTO app.sessions (id, learner_id, session_no)
VALUES ('42000000-0000-0000-0000-000000000001', '41000000-0000-0000-0000-000000000001', 1);
INSERT INTO app.turns (id, session_id, seq, speaker, text_masked, actor_kind)
VALUES (
'43000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
1,
'client',
'[G4 smoke masked scene]',
'client_ai'
);
SET LOCAL ROLE :"app_role";
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);
SELECT set_config('app.current_uid', '41000000-0000-0000-0000-000000000002', true);
INSERT INTO app.practice_prescription_submission (
submission_id, session_id, learner_id, content_hash, created_by_role
) VALUES (
'44000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
repeat('a', 64),
'agent'
);
INSERT INTO app.practice_coaching_card (
coaching_card_record_id, submission_id, session_id, learner_id,
card_key, scene_id, coach_claim, card_payload, evidence_turn_ids,
source_refs, uncertainty, counterevidence
) VALUES (
'45000000-0000-0000-0000-000000000001',
'44000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
'oas-g4-card-smoke',
'scene-smoke',
'내담자 반응을 확인하는 재연습 장면을 실행한다.',
'{"card_id":"oas-g4-card-smoke"}',
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[],
ARRAY['source-pack:g4-smoke:v1'],
0.2,
ARRAY[]::TEXT[]
);
INSERT INTO app.practice_prescription (
prescription_record_id, prescription_key, submission_id,
coaching_card_record_id, session_id, learner_id, competency_id,
criterion_id, observable_behavior, activity_mode, scenario_variant_id,
scenario_novelty, difficulty_level, prescription_payload,
evidence_turn_ids, uncertainty, counterevidence
) VALUES (
'46000000-0000-0000-0000-000000000001',
'oas-g4-practice-smoke',
'44000000-0000-0000-0000-000000000001',
'45000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
'competency.empathy.smoke',
'criterion.reflect-and-check',
'정서를 반영한 뒤 내담자에게 맞는지 확인한다.',
'replay',
'variant-smoke-familiar',
'familiar',
1,
'{
"schema_version":"vignette.practice-prescription.v1",
"event_name":"practice.prescribed",
"prescription_id":"oas-g4-practice-smoke",
"coaching_card_id":"oas-g4-card-smoke",
"scene_id":"scene-smoke",
"competency_id":"competency.empathy.smoke",
"criterion_id":"criterion.reflect-and-check",
"observable_behavior":"정서를 반영한 뒤 내담자에게 맞는지 확인한다.",
"activity":{
"mode":"replay",
"launch_intent":"practice.replay.launch",
"scenario_variant_id":"variant-smoke-familiar",
"scenario_novelty":"familiar",
"difficulty_level":1,
"pause_at_evidence_ref":"43000000-0000-0000-0000-000000000001"
},
"can_launch":true,
"evidence_refs":[{
"ref_id":"43000000-0000-0000-0000-000000000001",
"scene_id":"scene-smoke","turn_index":1,"actor":"client","kind":"scene_context"
}],
"source_refs":["source-pack:g4-smoke:v1"],
"uncertainty":0.2,
"counterevidence":[]
}'::JSONB,
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[],
0.2,
ARRAY[]::TEXT[]
);
INSERT INTO app.competency_graph_snapshot (
snapshot_id, learner_id, session_id, snapshot_no, content_hash,
source_prescription_submission_id, graph_payload, evidence_turn_ids,
created_by_role
) VALUES (
'47000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
1,
repeat('b', 64),
'44000000-0000-0000-0000-000000000001',
'{
"schema_version":"vignette.competency-graph.v1",
"definitions":[{
"competency_id":"competency.empathy.smoke",
"label_ko":"스모크 공감",
"description":"스모크 검증을 위한 합성 공감 역량 정의다.",
"prerequisite_ids":[]
}],
"states":[{
"competency_id":"competency.empathy.smoke",
"band":"unassessed","forgetting_risk":0.7,"uncertainty":1.0,
"attempt_count":0,"familiar_demonstrations":0,
"unseen_transfer_demonstrations":0,"highest_familiar_difficulty":0,
"evidence_refs":[],"counterevidence":[]
}]
}'::JSONB,
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[],
'agent'
);
INSERT INTO app.practice_curriculum_decision_event (
decision_id, source_snapshot_id, selected_prescription_record_id,
learner_id, session_id, content_hash, decision_payload, created_by_role
) VALUES (
'48000000-0000-0000-0000-000000000001',
'47000000-0000-0000-0000-000000000001',
'46000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
repeat('c', 64),
'{
"schema_version":"vignette.curriculum-decision.v1",
"selected_prescription_id":"oas-g4-practice-smoke",
"competency_id":"competency.empathy.smoke",
"competency_band":"unassessed","forgetting_risk":0.7,
"mode":"replay",
"selection_basis":["weakest_available_band:unassessed","forgetting_risk:0.700"],
"deferred_prescription_ids":[],"blocked_prescription_reasons":[]
}'::JSONB,
'agent'
);
-- A client-model database context must not inherit the human admin GUC or see
-- evaluator-owned prescription/curriculum state.
SELECT set_config('app.current_ai_view', 'client', true);
DO $$ BEGIN
IF (
(SELECT count(*) FROM app.practice_prescription_submission)
+ (SELECT count(*) FROM app.practice_coaching_card)
+ (SELECT count(*) FROM app.practice_prescription)
+ (SELECT count(*) FROM app.competency_graph_snapshot)
+ (SELECT count(*) FROM app.practice_curriculum_decision_event)
) <> 0 THEN
RAISE EXCEPTION 'client AI view leaked evaluator practice data';
END IF;
END $$;
SELECT set_config('app.current_ai_view', 'evaluator', true);
INSERT INTO app.practice_episode_submission (
episode_submission_id, episode_key, prescription_record_id,
session_id, learner_id, content_hash, assessment_payload, progress,
mastery_allowed, mastery_blockers, uncertainty, evidence_turn_ids,
counterevidence, created_by_role
) VALUES (
'49000000-0000-0000-0000-000000000001',
'oas-g4-episode-smoke-valid',
'46000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
repeat('d', 64),
'{
"progress":"mastered","mastery_allowed":true,"competency_id":"competency.empathy.smoke",
"attempts":[
{"outcome":"needs_retry","scenario_novelty":"familiar","scenario_variant_id":"variant-smoke-familiar","utterance_template_id":"template-baseline"},
{"outcome":"passed","scenario_novelty":"familiar","scenario_variant_id":"variant-smoke-familiar","utterance_template_id":"template-familiar"},
{"outcome":"passed","scenario_novelty":"unseen_transfer","scenario_variant_id":"variant-smoke-unseen","utterance_template_id":"template-adapted"}
]
}'::JSONB,
'mastered',
TRUE,
ARRAY[]::TEXT[],
0.2,
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[],
ARRAY[]::TEXT[],
'learner'
);
INSERT INTO app.practice_attempt_evidence (
attempt_record_id, attempt_key, episode_submission_id, session_id, learner_id,
sequence_no, scenario_variant_id, scenario_novelty, difficulty_level,
criterion_status, client_response, outcome, utterance_template_id,
uncertainty, evidence_turn_ids, counterevidence, attempt_payload
) VALUES
(
'4a000000-0000-0000-0000-000000000001','oas-g4-attempt-smoke-1',
'49000000-0000-0000-0000-000000000001','42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',1,'variant-smoke-familiar','familiar',1,
'not_observed','rejecting','needs_retry','template-baseline',0.2,
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[],ARRAY['baseline_miss'],
'{"kind":"baseline"}'::JSONB
),
(
'4a000000-0000-0000-0000-000000000002','oas-g4-attempt-smoke-2',
'49000000-0000-0000-0000-000000000001','42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',2,'variant-smoke-familiar','familiar',1,
'observed','engaged','passed','template-familiar',0.2,
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[],ARRAY[]::TEXT[],
'{"kind":"familiar-pass"}'::JSONB
),
(
'4a000000-0000-0000-0000-000000000003','oas-g4-attempt-smoke-3',
'49000000-0000-0000-0000-000000000001','42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',3,'variant-smoke-unseen','unseen_transfer',4,
'observed','explicit_alignment','passed','template-adapted',0.2,
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[],ARRAY[]::TEXT[],
'{"kind":"unseen-pass"}'::JSONB
);
INSERT INTO app.competency_graph_snapshot (
snapshot_id, learner_id, session_id, snapshot_no, content_hash,
supersedes_snapshot_id, source_episode_submission_id, graph_payload,
evidence_turn_ids, created_by_role
) VALUES (
'4b000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
2,
repeat('e', 64),
'47000000-0000-0000-0000-000000000001',
'49000000-0000-0000-0000-000000000001',
'{
"schema_version":"vignette.competency-graph.v1",
"definitions":[{
"competency_id":"competency.empathy.smoke","label_ko":"스모크 공감",
"description":"스모크 검증을 위한 합성 공감 역량 정의다.","prerequisite_ids":[]
}],
"states":[{
"competency_id":"competency.empathy.smoke","band":"transfer_verified",
"forgetting_risk":0.45,"uncertainty":0.2,"attempt_count":3,
"familiar_demonstrations":1,"unseen_transfer_demonstrations":1,
"highest_familiar_difficulty":1,"evidence_refs":[],"counterevidence":[]
}]
}'::JSONB,
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[],
'learner'
);
INSERT INTO app.practice_curriculum_decision_event (
decision_id, source_snapshot_id, selected_prescription_record_id,
learner_id, session_id, content_hash, decision_payload, created_by_role
) VALUES (
'4b000000-0000-0000-0000-000000000002',
'4b000000-0000-0000-0000-000000000001',
'46000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
repeat('f', 64),
'{"selected_prescription_id":"oas-g4-practice-smoke"}'::JSONB,
'learner'
);
-- Learner-self, teacher cohort and unrelated-user 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', '41000000-0000-0000-0000-000000000001', true);
DO $$ BEGIN
IF (SELECT count(*) FROM app.practice_prescription) <> 1 THEN
RAISE EXCEPTION 'learner self RLS smoke failed';
END IF;
END $$;
SELECT set_config('app.current_uid', '41000000-0000-0000-0000-000000000003', true);
DO $$ BEGIN
IF (SELECT count(*) FROM app.practice_prescription) <> 0 THEN
RAISE EXCEPTION 'unrelated learner RLS leaked practice data';
END IF;
END $$;
SELECT set_config('app.current_role', 'instructor', true);
SELECT set_config('app.current_uid', '41000000-0000-0000-0000-000000000002', true);
SELECT set_config('app.current_cohort', 'g4-smoke', true);
DO $$ BEGIN
IF (SELECT count(*) FROM app.practice_prescription) <> 1 THEN
RAISE EXCEPTION 'teacher cohort RLS smoke failed';
END IF;
END $$;
SELECT set_config('app.current_cohort', 'g4-other', true);
DO $$ BEGIN
IF (SELECT count(*) FROM app.practice_prescription) <> 0 THEN
RAISE EXCEPTION 'teacher cross-cohort RLS leaked practice data';
END IF;
END $$;
-- Teacher correction remains append-only and cannot mutate the original attempt.
SELECT set_config('app.current_cohort', 'g4-smoke', true);
INSERT INTO app.practice_teacher_correction (
correction_id, submission_id, content_hash, attempt_record_id,
episode_submission_id, session_id, learner_id, correction_no,
corrected_outcome, correction_reason, evidence_turn_ids, counterevidence,
created_by_uid, created_by_role
) VALUES (
'4c000000-0000-0000-0000-000000000001',
'4c000000-0000-0000-0000-000000000002',
repeat('1', 64),
'4a000000-0000-0000-0000-000000000001',
'49000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
1,
'needs_retry',
'내담자 후속 반응을 근거로 재시도가 필요하다고 확인했다.',
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[],
ARRAY['client_response_not_engaged'],
'41000000-0000-0000-0000-000000000002',
'instructor'
);
RESET ROLE;
-- Same submission id with changed content is rejected.
DO $$ BEGIN
BEGIN
INSERT INTO app.practice_prescription_submission (
submission_id, session_id, learner_id, content_hash, created_by_role
) VALUES (
'44000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001', repeat('9', 64), 'agent'
);
RAISE EXCEPTION 'changed idempotent submission was accepted';
EXCEPTION WHEN unique_violation THEN NULL;
END;
END $$;
-- Memorized phrase cannot be promoted to mastery.
DO $$ BEGIN
BEGIN
INSERT INTO app.practice_episode_submission (
episode_submission_id, episode_key, prescription_record_id,
session_id, learner_id, content_hash, assessment_payload, progress,
mastery_allowed, mastery_blockers, uncertainty, evidence_turn_ids,
counterevidence, created_by_role
) VALUES (
'49000000-0000-0000-0000-000000000009',
'oas-g4-episode-smoke-hack',
'46000000-0000-0000-0000-000000000001',
'42000000-0000-0000-0000-000000000001',
'41000000-0000-0000-0000-000000000001',
repeat('8', 64),
'{
"progress":"mastered","mastery_allowed":true,"competency_id":"competency.empathy.smoke",
"attempts":[
{"outcome":"passed","scenario_novelty":"familiar","scenario_variant_id":"v1","utterance_template_id":"memorized"},
{"outcome":"passed","scenario_novelty":"unseen_transfer","scenario_variant_id":"v2","utterance_template_id":"memorized"}
]
}'::JSONB,
'mastered', TRUE, ARRAY[]::TEXT[], 0.2,
ARRAY['43000000-0000-0000-0000-000000000001']::UUID[], ARRAY[]::TEXT[], 'learner'
);
RAISE EXCEPTION 'memorized phrase mastery was accepted';
EXCEPTION WHEN check_violation THEN NULL;
END;
END $$;
DO $$ BEGIN
BEGIN
UPDATE app.practice_attempt_evidence
SET outcome = 'passed'
WHERE attempt_record_id = '4a000000-0000-0000-0000-000000000001';
RAISE EXCEPTION 'append-only practice attempt update was accepted';
EXCEPTION WHEN object_not_in_prerequisite_state THEN NULL;
END;
END $$;
ROLLBACK;
SELECT 'g4_smoke_rollback_residue' AS check_name,
(
(SELECT count(*) FROM app.practice_prescription_submission
WHERE submission_id = '44000000-0000-0000-0000-000000000001')
+ (SELECT count(*) FROM app.practice_episode_submission
WHERE episode_submission_id = '49000000-0000-0000-0000-000000000001')
+ (SELECT count(*) FROM app.practice_teacher_correction
WHERE correction_id = '4c000000-0000-0000-0000-000000000001')
+ (SELECT count(*) FROM app.app_user
WHERE user_id = '41000000-0000-0000-0000-000000000001')
) AS residue_count;