회기 무발화 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

@ -447,6 +447,8 @@ session lifecycle을 유지하며, future Node read API는 이 read-model contra
제외한다. `growth.training_exposure`는 종료 회기만 집계하고 4회 미만이면 `insufficient`, 4회 이상에서
최다 페르소나 비중이 0.75 이상이면 `훈련 집중 주의`, 그 밖에는 `balanced`로 표시한다. 투명한 노출 비중이지
공정성·임상 진단이 아니다. 성취는 공식 등급/수료가 아니라 실제 연습 milestone만 표시한다.
- 대시보드 DTO와 builder는 `app/session_dashboard_projection.py`가 소유한다. 공통 세션 projection은
`app/session_projection.py`가 소유하며, 라우트는 조회·권한 경계만 조합한다.
- `GET /sessions/cases?persona_code=...` — 같은 NPC의 사례별 누적 회기·상담자에게 보인 턴·누적
회기 시간과 진행 중 회기를 DB 전체에서 집계한다. 런타임 추정값은 반환하지 않으며 DB read가 실패하면
503으로 닫아 UI가 이어가기를 열지 않는다. `GET /sessions/cases/{case_id}/memory`는 foldout을 연
@ -581,8 +583,16 @@ GET {ENGINE_URL}/ready|/health — readiness/liveness
- 영속/폴백 분기는 `app/runtime_policy.py`(`runtime_fallback_allowed` / `require_runtime_fallback_allowed`)가
게이트. 라우트는 `session_persistence`(DB) → 실패 시 store(in-proc) 순으로 시도한다.
- 회기 평가 저장은 `SessionEvaluationWrite``status/source/scope/stage/payload/error` write packet을
소유한다. `routes/sessions.py``routes/eval.py`는 같은 named packet을 만들어 저장하고, DB SQL과
fallback cache record는 `session_persistence.py`가 유지한다.
소유한다. `routes/sessions.py``routes/eval.py`는 같은 named packet을 만들어
`app/session_evaluation_repository.py`의 DB SQL·cache 경계로 넘긴다. 공통 정규화·마스킹 값은
`app/session_persistence_values.py`가 소유한다.
- outcome 계열 저장소의 canonical hash/value/public row/created role은
`app/services/outcome_repository_values.py`가 단일 소유한다. `practice_competency.py`는 공통 역량
분류만 소유하고 각 저장소의 기존 예외를 바꾸지 않는다. 인증 projection·admin 일별 mapper와 voice metadata
builder도 각 route/service 경계에 남긴다.
- text/voice 경로가 공유하는 `TurnMemory` 조립은 `app/session_turn_memory.py`의 순수
`build_turn_memory()`가 맡는다. recall summary·pinned facts·client-visible recent turns·KB behavior cues의
네 필드만 조립하며, time/cache/scenario fetch와 session lifecycle은 기존 경계에 유지한다.
### 2.12 페르소나 카탈로그 — `app/persona_repository.py`
@ -735,6 +745,13 @@ React 19 + Vite. 라우팅은 `apps/web/src/App.tsx`(react-router-dom).
- `App.tsx`는 모든 역할 페이지를 `lazy()`로 로드하고 공통 `Suspense` 부트 경계를 사용한다. 페이지별 순수 표시 계산은
`pages/*/model.ts`, 브라우저 음성 캡처는 `pages/session/voiceCapture.ts`, 페르소나 이름·난도·아바타 팔레트는
`lib/personaViewModel.ts`가 소유해 라우트 컴포넌트의 API/상태/렌더 책임과 분리한다.
- `pages/Session.tsx`는 회기 lifecycle과 API/상태 전이를 유지한다. 표시 타입·파생 계산은
`pages/session/sessionViewModel.ts`, 브라우저 캡처와 status 표시는 `pages/session/voiceCapture.ts`
소유한다.
- `lib/focusTrap.ts`는 Tab 순환과 DOM 조회만 소유하며 initial focus·Escape·lifecycle은 `Session`에 남긴다.
공통 동맹 scale은 `pages/session-review/allianceScale.ts`가 소유하고 `AllianceCheckpointPrompt`
`AlliancePulseCard`가 사용한다. 잠긴 자기점수 표시는 `AlliancePulseCard``LockedScores`, 관리자 승인
제출 제어는 `ContinuousImprovementCockpit``ApprovalSubmitControls`가 소유한다.
- Pages 배포 전환 중 열린 탭이 삭제된 lazy 청크를 요청하면 `lib/chunkRecovery.ts`가 Vite
`vite:preloadError`를 받아 현재 경로에서 문서 재로드를 1회만 수행한다. 정상 라우트 렌더 뒤 재시도 표식을
지우고, 같은 경로의 연속 실패는 무한 재로드하지 않고 `RouteErrorBoundary`의 수동 복구 액션으로 넘긴다.