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:
parent
93dd8f82d7
commit
16e791e044
390 changed files with 243188 additions and 499 deletions
189
infra/db/init/15_self_directed_practice_runtime.sql
Normal file
189
infra/db/init/15_self_directed_practice_runtime.sql
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
-- Outcome & Alliance OS G4/G5: cross-session self-directed practice runtime.
|
||||
-- A prescription is authored in one session; its observed attempt evidence belongs
|
||||
-- to a later completed session owned by the same learner.
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_practice_prescription_record_learner
|
||||
ON app.practice_prescription(prescription_record_id, learner_id);
|
||||
|
||||
ALTER TABLE app.practice_episode_submission
|
||||
DROP CONSTRAINT IF EXISTS practice_episode_prescription_fkey;
|
||||
ALTER TABLE app.practice_episode_submission
|
||||
ADD CONSTRAINT practice_episode_prescription_fkey
|
||||
FOREIGN KEY (prescription_record_id, learner_id)
|
||||
REFERENCES app.practice_prescription(prescription_record_id, learner_id)
|
||||
ON DELETE RESTRICT;
|
||||
|
||||
ALTER TABLE app.practice_curriculum_decision_event
|
||||
DROP CONSTRAINT IF EXISTS practice_decision_prescription_fkey;
|
||||
ALTER TABLE app.practice_curriculum_decision_event
|
||||
ADD CONSTRAINT practice_decision_prescription_fkey
|
||||
FOREIGN KEY (selected_prescription_record_id, learner_id)
|
||||
REFERENCES app.practice_prescription(prescription_record_id, learner_id)
|
||||
ON DELETE RESTRICT;
|
||||
|
||||
CREATE OR REPLACE FUNCTION audit.enforce_practice_episode_transfer_gate()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
attempts JSONB;
|
||||
competency_key TEXT;
|
||||
has_familiar_pass BOOLEAN;
|
||||
has_unseen_pass BOOLEAN;
|
||||
has_transfer_collision BOOLEAN;
|
||||
claimed_prior_familiar INT;
|
||||
durable_prior_familiar INT;
|
||||
BEGIN
|
||||
IF NEW.assessment_payload->>'progress' IS DISTINCT FROM NEW.progress
|
||||
OR COALESCE((NEW.assessment_payload->>'mastery_allowed')::BOOLEAN, FALSE)
|
||||
IS DISTINCT FROM NEW.mastery_allowed THEN
|
||||
RAISE EXCEPTION 'practice episode columns must match assessment payload'
|
||||
USING ERRCODE = '23514';
|
||||
END IF;
|
||||
attempts := COALESCE(NEW.assessment_payload->'attempts', '[]'::JSONB);
|
||||
IF jsonb_typeof(attempts) <> 'array' OR jsonb_array_length(attempts) = 0 THEN
|
||||
RAISE EXCEPTION 'practice episode requires attempt evidence'
|
||||
USING ERRCODE = '23514';
|
||||
END IF;
|
||||
|
||||
competency_key := NEW.assessment_payload->>'competency_id';
|
||||
claimed_prior_familiar := COALESCE(
|
||||
(NEW.assessment_payload->>'prior_familiar_demonstrations')::INT,
|
||||
0
|
||||
);
|
||||
SELECT COALESCE((state->>'familiar_demonstrations')::INT, 0)
|
||||
INTO durable_prior_familiar
|
||||
FROM app.competency_graph_snapshot snapshot,
|
||||
jsonb_array_elements(COALESCE(snapshot.graph_payload->'states', '[]'::JSONB)) state
|
||||
WHERE snapshot.learner_id = NEW.learner_id
|
||||
AND state->>'competency_id' = competency_key
|
||||
ORDER BY snapshot.snapshot_no DESC
|
||||
LIMIT 1;
|
||||
durable_prior_familiar := COALESCE(durable_prior_familiar, 0);
|
||||
IF claimed_prior_familiar > durable_prior_familiar THEN
|
||||
RAISE EXCEPTION 'practice episode prior familiar claim exceeds durable graph'
|
||||
USING ERRCODE = '23514';
|
||||
END IF;
|
||||
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM jsonb_array_elements(attempts) item
|
||||
WHERE item->>'outcome' = 'passed'
|
||||
AND item->>'scenario_novelty' = 'familiar'
|
||||
) INTO has_familiar_pass;
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM jsonb_array_elements(attempts) item
|
||||
WHERE item->>'outcome' = 'passed'
|
||||
AND item->>'scenario_novelty' = 'unseen_transfer'
|
||||
AND length(btrim(COALESCE(item->>'utterance_template_id', ''))) > 0
|
||||
) INTO has_unseen_pass;
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM jsonb_array_elements(attempts) unseen,
|
||||
jsonb_array_elements(attempts) familiar
|
||||
WHERE unseen->>'outcome' = 'passed'
|
||||
AND unseen->>'scenario_novelty' = 'unseen_transfer'
|
||||
AND familiar->>'outcome' = 'passed'
|
||||
AND familiar->>'scenario_novelty' = 'familiar'
|
||||
AND (
|
||||
unseen->>'scenario_variant_id' = familiar->>'scenario_variant_id'
|
||||
OR unseen->>'utterance_template_id' = familiar->>'utterance_template_id'
|
||||
)
|
||||
) INTO has_transfer_collision;
|
||||
|
||||
IF NEW.progress = 'mastered'
|
||||
AND (
|
||||
(NOT has_familiar_pass AND claimed_prior_familiar < 1)
|
||||
OR NOT has_unseen_pass
|
||||
OR has_transfer_collision
|
||||
) THEN
|
||||
RAISE EXCEPTION 'practice mastery requires durable familiar success and novel unseen transfer evidence'
|
||||
USING ERRCODE = '23514';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION audit.enforce_practice_attempt_sequence_and_transfer()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
latest_sequence INT;
|
||||
prescription_record UUID;
|
||||
competency_key TEXT;
|
||||
familiar_pass_exists BOOLEAN;
|
||||
durable_prior_familiar INT;
|
||||
repeated_template_exists BOOLEAN;
|
||||
repeated_variant_exists BOOLEAN;
|
||||
BEGIN
|
||||
PERFORM pg_advisory_xact_lock(hashtextextended(NEW.episode_submission_id::TEXT, 0));
|
||||
SELECT max(attempt.sequence_no) INTO latest_sequence
|
||||
FROM app.practice_attempt_evidence attempt
|
||||
WHERE attempt.episode_submission_id = NEW.episode_submission_id;
|
||||
IF NEW.sequence_no <> COALESCE(latest_sequence, 0) + 1 THEN
|
||||
RAISE EXCEPTION 'practice attempt sequence must be contiguous'
|
||||
USING ERRCODE = '23514';
|
||||
END IF;
|
||||
|
||||
IF NEW.outcome = 'passed' AND NEW.scenario_novelty = 'unseen_transfer' THEN
|
||||
IF length(btrim(COALESCE(NEW.utterance_template_id, ''))) = 0 THEN
|
||||
RAISE EXCEPTION 'unseen transfer pass requires a nonblank utterance template id'
|
||||
USING ERRCODE = '23514';
|
||||
END IF;
|
||||
SELECT episode.prescription_record_id, prescription.competency_id
|
||||
INTO prescription_record, competency_key
|
||||
FROM app.practice_episode_submission episode
|
||||
JOIN app.practice_prescription prescription
|
||||
ON prescription.prescription_record_id = episode.prescription_record_id
|
||||
AND prescription.learner_id = episode.learner_id
|
||||
WHERE episode.episode_submission_id = NEW.episode_submission_id
|
||||
AND episode.learner_id = NEW.learner_id;
|
||||
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM app.practice_attempt_evidence prior
|
||||
JOIN app.practice_episode_submission episode
|
||||
ON episode.episode_submission_id = prior.episode_submission_id
|
||||
WHERE episode.prescription_record_id = prescription_record
|
||||
AND prior.learner_id = NEW.learner_id
|
||||
AND prior.scenario_novelty = 'familiar'
|
||||
AND prior.outcome = 'passed'
|
||||
) INTO familiar_pass_exists;
|
||||
SELECT COALESCE((state->>'familiar_demonstrations')::INT, 0)
|
||||
INTO durable_prior_familiar
|
||||
FROM app.competency_graph_snapshot snapshot,
|
||||
jsonb_array_elements(COALESCE(snapshot.graph_payload->'states', '[]'::JSONB)) state
|
||||
WHERE snapshot.learner_id = NEW.learner_id
|
||||
AND state->>'competency_id' = competency_key
|
||||
ORDER BY snapshot.snapshot_no DESC
|
||||
LIMIT 1;
|
||||
familiar_pass_exists := familiar_pass_exists
|
||||
OR COALESCE(durable_prior_familiar, 0) > 0;
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM app.practice_attempt_evidence prior
|
||||
JOIN app.practice_episode_submission episode
|
||||
ON episode.episode_submission_id = prior.episode_submission_id
|
||||
WHERE episode.prescription_record_id = prescription_record
|
||||
AND prior.learner_id = NEW.learner_id
|
||||
AND prior.scenario_novelty = 'familiar'
|
||||
AND prior.utterance_template_id = NEW.utterance_template_id
|
||||
) INTO repeated_template_exists;
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM app.practice_attempt_evidence prior
|
||||
JOIN app.practice_episode_submission episode
|
||||
ON episode.episode_submission_id = prior.episode_submission_id
|
||||
WHERE episode.prescription_record_id = prescription_record
|
||||
AND prior.learner_id = NEW.learner_id
|
||||
AND prior.scenario_novelty = 'familiar'
|
||||
AND prior.scenario_variant_id = NEW.scenario_variant_id
|
||||
) INTO repeated_variant_exists;
|
||||
IF NOT familiar_pass_exists OR repeated_template_exists OR repeated_variant_exists THEN
|
||||
RAISE EXCEPTION 'unseen transfer pass cannot reuse familiar variant or memorized phrase'
|
||||
USING ERRCODE = '23514';
|
||||
END IF;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
Loading…
Add table
Add a link
Reference in a new issue