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 산출물은 커밋에서 제외했다.
169 lines
6.5 KiB
Python
169 lines
6.5 KiB
Python
from __future__ import annotations
|
|
|
|
import copy
|
|
import unittest
|
|
from unittest.mock import AsyncMock, patch
|
|
from uuid import UUID, uuid5
|
|
|
|
from .services import supervision_research_version_evaluator as evaluator
|
|
|
|
|
|
ROW_NAMESPACE = UUID("68f117f9-4b63-4f2d-a3b6-57f558497f45")
|
|
LEARNER_ID = UUID("00000000-0000-0000-0000-0000000000a1")
|
|
SESSION_ID = UUID("10000000-0000-0000-0000-0000000000a1")
|
|
|
|
|
|
def _complete_rows() -> list[dict[str, object]]:
|
|
benchmark = evaluator.load_validated_repository_benchmark()
|
|
rows: list[dict[str, object]] = []
|
|
for batch in (
|
|
benchmark.pack.baseline_batch,
|
|
benchmark.pack.candidate_batch,
|
|
):
|
|
for observation in batch.observations:
|
|
rows.append(
|
|
{
|
|
"measurement_id": uuid5(
|
|
ROW_NAMESPACE, observation.evidence_event_id
|
|
),
|
|
"session_id": SESSION_ID,
|
|
"learner_id": LEARNER_ID,
|
|
"cohort_id": "g6-repo-benchmark-v1",
|
|
"metadata": evaluator.benchmark_anchor_metadata(
|
|
benchmark, batch, observation
|
|
),
|
|
}
|
|
)
|
|
return rows
|
|
|
|
|
|
class RepositoryBenchmarkValidationTests(unittest.TestCase):
|
|
def test_repo_pack_preserves_gold_and_version_provenance(self) -> None:
|
|
benchmark = evaluator.load_validated_repository_benchmark()
|
|
report = evaluator.build_repository_comparison_input(
|
|
_complete_rows(), benchmark=benchmark
|
|
)
|
|
assert report is not None
|
|
self.assertEqual(report.cohort_id, "g6-repo-benchmark-v1")
|
|
self.assertEqual(report.baseline.model, "evaluator-v1")
|
|
self.assertEqual(report.baseline.prompt_version, "1.0.0")
|
|
self.assertEqual(report.baseline.instrument_version, "1.0.0")
|
|
self.assertEqual(report.candidate.model, "evaluator-v2")
|
|
self.assertEqual(report.candidate.prompt_version, "2.0.0")
|
|
self.assertEqual(report.candidate.instrument_version, "1.1.0")
|
|
baseline_gold = {
|
|
(item.case_ref, item.competency_id): item.gold_label
|
|
for item in report.baseline.observations
|
|
}
|
|
candidate_gold = {
|
|
(item.case_ref, item.competency_id): item.gold_label
|
|
for item in report.candidate.observations
|
|
}
|
|
self.assertEqual(baseline_gold, candidate_gold)
|
|
self.assertNotIn("transcript", str(report).lower())
|
|
|
|
def test_incomplete_runtime_evidence_skips_without_inventing_candidate(
|
|
self,
|
|
) -> None:
|
|
rows = _complete_rows()[:-1]
|
|
self.assertIsNone(evaluator.build_repository_comparison_input(rows))
|
|
|
|
def test_runtime_anchor_cannot_change_repo_gold_or_model_provenance(self) -> None:
|
|
rows = copy.deepcopy(_complete_rows())
|
|
metadata = rows[0]["metadata"]
|
|
assert isinstance(metadata, dict)
|
|
metadata["gold_label"] = "invented-gold"
|
|
with self.assertRaisesRegex(ValueError, "differs from repository gold"):
|
|
evaluator.build_repository_comparison_input(rows)
|
|
|
|
def test_runtime_anchor_forbids_transcript_fields(self) -> None:
|
|
rows = copy.deepcopy(_complete_rows())
|
|
metadata = rows[0]["metadata"]
|
|
assert isinstance(metadata, dict)
|
|
metadata["raw_transcript"] = "forbidden"
|
|
with self.assertRaisesRegex(ValueError, "forbidden source text"):
|
|
evaluator.build_repository_comparison_input(rows)
|
|
|
|
|
|
class RepositoryBenchmarkProducerTests(unittest.IsolatedAsyncioTestCase):
|
|
async def test_first_append_and_replay_use_stable_ids(self) -> None:
|
|
conn = AsyncMock()
|
|
append = AsyncMock(
|
|
side_effect=[
|
|
{
|
|
"submission_id": UUID(int=1),
|
|
"drift_report_id": UUID(int=2),
|
|
"status": "drift_flagged",
|
|
"matched_count": 6,
|
|
"idempotent_replay": False,
|
|
"clinical_claim_allowed": False,
|
|
},
|
|
{
|
|
"submission_id": UUID(int=1),
|
|
"drift_report_id": UUID(int=2),
|
|
"status": "drift_flagged",
|
|
"matched_count": 6,
|
|
"idempotent_replay": True,
|
|
"clinical_claim_allowed": False,
|
|
},
|
|
]
|
|
)
|
|
with (
|
|
patch.object(
|
|
evaluator,
|
|
"_load_repository_benchmark_anchors",
|
|
AsyncMock(return_value=_complete_rows()),
|
|
),
|
|
patch.object(
|
|
evaluator.supervision_research_store,
|
|
"append_evaluation_comparison",
|
|
append,
|
|
),
|
|
):
|
|
first = await evaluator.produce_repository_version_comparison(conn)
|
|
replay = await evaluator.produce_repository_version_comparison(conn)
|
|
|
|
self.assertFalse(first["idempotent_replay"])
|
|
self.assertTrue(replay["idempotent_replay"])
|
|
self.assertEqual(first["status"], "drift_flagged")
|
|
self.assertFalse(first["raw_transcript_included"])
|
|
self.assertFalse(first["clinical_claim_allowed"])
|
|
self.assertEqual(append.await_count, 2)
|
|
first_call, second_call = append.await_args_list
|
|
for key in (
|
|
"submission_id",
|
|
"drift_report_id",
|
|
"baseline_submission_id",
|
|
"baseline_batch_record_id",
|
|
"candidate_submission_id",
|
|
"candidate_batch_record_id",
|
|
):
|
|
self.assertEqual(first_call.kwargs[key], second_call.kwargs[key])
|
|
self.assertEqual(first_call.kwargs["baseline"].model, "evaluator-v1")
|
|
self.assertEqual(first_call.kwargs["candidate"].model, "evaluator-v2")
|
|
|
|
async def test_missing_approved_anchors_is_safe_skip(self) -> None:
|
|
conn = AsyncMock()
|
|
append = AsyncMock()
|
|
with (
|
|
patch.object(
|
|
evaluator,
|
|
"_load_repository_benchmark_anchors",
|
|
AsyncMock(return_value=[]),
|
|
),
|
|
patch.object(
|
|
evaluator.supervision_research_store,
|
|
"append_evaluation_comparison",
|
|
append,
|
|
),
|
|
):
|
|
result = await evaluator.produce_repository_version_comparison(conn)
|
|
self.assertEqual(result["status"], "skipped")
|
|
self.assertEqual(
|
|
result["reason"], "repo_approved_synthetic_evidence_incomplete"
|
|
)
|
|
append.assert_not_awaited()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|