회기 무발화 0턴 분리, 자기예측 락 불변식 및 TDD 회귀 검증 완료
Some checks failed
API contract / OpenAPI type drift (push) Failing after 3m27s

This commit is contained in:
Yun Chan 2026-09-08 23:28:06 +09:00
parent a479db7a5a
commit a0311c5957
100 changed files with 4884 additions and 11210 deletions

View file

@ -2,7 +2,6 @@
from __future__ import annotations
import hashlib
import json
from collections.abc import Mapping, Sequence
from datetime import UTC, datetime
@ -26,6 +25,13 @@ from .calibration_transfer import (
assess_synthetic_subgroup_drift,
assess_transfer,
)
from .outcome_repository_values import (
canonical_hash as _canonical_hash,
created_role as _created_role,
public_row as _public_row,
value as _value,
)
from .practice_competency import target_techniques
class CalibrationTransferNotFoundError(LookupError):
@ -64,36 +70,6 @@ _POSITIVE_CLIENT_STATES = frozenset(
)
def _value(row: Mapping[str, Any], key: str, default: Any = None) -> Any:
try:
return row[key]
except (KeyError, TypeError):
return default
def _public_row(row: Mapping[str, Any]) -> dict[str, Any]:
"""API 응답에서 학습자 피드백 정책 판정 전용 열을 제거한다."""
payload = dict(row)
payload.pop("source_learner_feedback_enabled", None)
return payload
def _canonical_hash(payload: Mapping[str, Any]) -> str:
serialized = json.dumps(
payload,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
default=str,
)
return hashlib.sha256(serialized.encode("utf-8")).hexdigest()
def _created_role(principal: Principal) -> str:
return "instructor" if principal.role == Role.TEACHER else principal.role.value
def _ensure_unique_evidence(
evidence_turn_ids: Sequence[UUID], *, required: bool = False
) -> tuple[UUID, ...]:
@ -182,6 +158,16 @@ async def append_prediction_revision(
reason = revision_reason.strip()
if not reason:
raise CalibrationTransferStateError("revision_reason must not be blank")
if not (0.0 <= predicted_success_probability <= 1.0):
raise CalibrationTransferStateError(
"predicted_success_probability must be between 0.0 and 1.0"
)
if not (0.0 <= confidence <= 1.0):
raise CalibrationTransferStateError(
"confidence must be between 0.0 and 1.0"
)
if instrument_id == "vignette.calibration-self-prediction":
instrument_id = "calibration-mirror-g5"
payload = {
"prediction_revision_id": str(prediction_revision_id),
"history_id": str(history_id),
@ -318,6 +304,11 @@ async def append_prediction_revision(
asyncpg.ForeignKeyViolationError,
asyncpg.ObjectNotInPrerequisiteStateError,
) as exc:
message = str(exc)
if "lock" in message.lower() or "reveal" in message.lower():
raise CalibrationTransferStateError(
f"self-prediction history is locked or revealed: {message}"
) from exc
raise CalibrationTransferStateError(
"prediction revision violated provenance or history invariants"
) from exc
@ -880,21 +871,9 @@ async def append_transfer_suite(
def _actual_target_techniques(competency_id: str) -> frozenset[str]:
key = competency_id.lower()
if any(token in key for token in ("empathy", "empathic", "reflection")):
return frozenset({"empathy", "reflection", "validation", "restatement"})
if any(token in key for token in ("open_question", "open-question")):
return frozenset({"facilitative_question", "exploration", "clarification"})
if any(token in key for token in ("rupture", "repair", "impact")):
return frozenset(
{"opinion_check", "validation", "reflection", "here_and_now_focus"}
)
if any(token in key for token in ("goal", "collaborative", "reagreement")):
return frozenset(
{"consent_motivation_check", "opinion_check", "restatement"}
)
if any(token in key for token in ("presence", "response-space")):
return frozenset({"holding", "reflection", "here_and_now_focus"})
targets = target_techniques(competency_id)
if targets is not None:
return targets
raise CalibrationTransferStateError(
f"unsupported actual transfer competency: {competency_id}"
)