vignette/docs/decisions/outcome-alliance-measurement-ledger.md
Yun Chan 16e791e044 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 산출물은 커밋에서 제외했다.
2026-08-08 01:30:53 +09:00

4.7 KiB

ADR — Outcome & Alliance 측정 원장과 provenance 경계

  • 상태: Accepted
  • 결정일: 2026-08-06
  • 범위: Outcome & Alliance OS G0 / AOS-002
  • 소유 코드: apps/api/app/contracts/measurement.py, infra/db/init/07_measurement_foundation.sql

맥락

Vignette에는 이미 rapport_credit, case_profile.alliance_level, fast/deep LLM 평가, Phase 3 KPI가 있다. 그러나 이 값들은 생성 주체와 관점이 서로 다르다. 특히 상태머신 라포 값은 시뮬레이션 진행 상태이고, LLM 평가는 모델 추론이며, 학습자 자기보고와 교수자 평정은 또 다른 증거층이다. 이를 하나의 alliance score로 합치면 UI는 그럴듯해도 측정 의미와 재현 가능성이 사라진다.

결정

  1. 모든 새 측정은 append-only app.measurement_event로 기록한다. 정정은 UPDATE가 아니라 supersedes_id를 가진 새 이벤트로만 표현한다.

  2. 척도와 훈련지표는 버전 고정 app.measurement_instrument에 등록한다.

  3. 모델 또는 에이전트가 만든 측정은 반드시 append-only audit.model_run을 참조한다. 실행에는 provider, model, prompt bundle id/version/hash, structured schema version, 입력 근거 hash를 남긴다.

  4. source와 perspective를 다음 1:1 경계로 제한한다.

    source perspective
    simulated_state client_simulation
    model_inferred independent_observer
    agent_reported client_agent_report
    learner_reported learner_self_report
    human_rated supervisor_human
    observed_runtime runtime_observation
  5. 서로 다른 source/perspective 층은 평균·총점으로 합치지 않는다. 제품에서는 관점별 series를 나란히 보여주고 차이 자체를 학습 근거로 쓴다.

  6. rapport_credit와 기존 alliance_level은 이름과 무관하게 simulation_progress로만 이관한다. Working Alliance 임상 측정으로 승격하지 않는다.

  7. visible_to[]와 RLS가 client/counselor/evaluator/supervisor/research 정보 경계를 물리적으로 강제한다.

  8. benchmark는 ds schema에 격리하고 운영 회기 행과 결합하지 않는다.

선택하지 않은 대안

  • 기존 alliance_level 컬럼 이름을 그대로 공식 동맹 점수로 사용: 출처와 관점이 없고 임상적 과대주장을 만든다.
  • 현재 행을 UPDATE하는 mutable score table: 모델 교체·교수자 재평정·오류 정정 이력을 재현할 수 없다.
  • 모든 관점을 단일 0~100 점수로 정규화: 숫자 비교는 쉬워지지만 불일치가 숨고 자기보정 학습이 불가능해진다.
  • 모델명만 기록: prompt/schema/input drift를 분리할 수 없어 재현 계약으로 부족하다.

전진 복구와 롤백

이 마이그레이션은 기존 컬럼을 삭제하거나 덮어쓰지 않는 additive 변경이다.

  • 애플리케이션 기능 롤백: 새 measurement read/write 경로의 feature flag를 끄고 기존 세션·리뷰 read model로 즉시 복귀한다. 이미 쌓인 원장 행은 감사 증거로 보존한다.
  • 데이터 정정: 잘못된 측정 행은 삭제·수정하지 않고 status='rejected' 또는 정정 값을 가진 새 이벤트로 supersede한다.
  • schema 전진 복구: 계약 불일치는 다음 버전의 instrument/schema와 새 이벤트로 해결한다. 같은 instrument version 또는 benchmark version의 의미를 바꾸지 않는다.
  • 물리 schema 제거: 운영 데이터가 한 건이라도 있으면 기본 롤백 절차로 허용하지 않는다. 완전 미사용이 DB 쿼리로 증명된 개발 환경에서만 역순으로 ds.benchmark_observation, ds.benchmark_case, app.measurement_event, audit.model_run, app.measurement_instrument를 제거할 수 있다.

검증 계약

  • Python Pydantic 계약, 생성 JSON Schema, TypeScript 계약, PostgreSQL CHECK enum이 일치해야 한다.
  • benchmark 8개 장면은 고정 ID·버전과 근거 turn index를 가져야 한다.
  • 혼합 provenance 집계, source/perspective 불일치, 모델 실행 provenance 없는 모델 측정은 실패해야 한다.
  • measurement/model_run UPDATE·DELETE는 trigger로 실패하고, RLS는 AI view 및 사용자 역할별 가시성을 차단해야 한다.
  • 기존 DB-backed 세션·종료·리뷰 E2E가 무회귀여야 G0를 닫는다.

결과

이 결정은 새 기능의 속도를 조금 늦추는 대신, G1 이후 goal/task/bond, rupture/repair, 장기 성과 궤적이 무엇을 누구의 관점에서 어떻게 측정했는지 잃지 않게 한다. 숫자가 같아도 출처가 다르면 다른 증거라는 원칙이 제품 UI, API, DB, benchmark에 동일하게 유지된다.