diff --git a/.github/workflows/api-contract.yml b/.github/workflows/api-contract.yml new file mode 100644 index 0000000..856aede --- /dev/null +++ b/.github/workflows/api-contract.yml @@ -0,0 +1,58 @@ +name: API contract + +on: + pull_request: + paths: + - "apps/api/**" + - "apps/web/src/lib/api.gen.ts" + - "apps/web/scripts/generate-api-types.mjs" + - "apps/web/package.json" + - "apps/web/package-lock.json" + - ".github/workflows/api-contract.yml" + push: + branches: + - master + paths: + - "apps/api/**" + - "apps/web/src/lib/api.gen.ts" + - "apps/web/scripts/generate-api-types.mjs" + - "apps/web/package.json" + - "apps/web/package-lock.json" + - ".github/workflows/api-contract.yml" + +permissions: + contents: read + +jobs: + api-types: + name: OpenAPI type drift + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install API dependencies + working-directory: apps/api + run: python -m pip install -r requirements.txt + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + cache-dependency-path: apps/web/package-lock.json + + - name: Install web dependencies + working-directory: apps/web + run: npm ci + + - name: Check generated API types + working-directory: apps/web + run: npm run check:api-types diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 04c8759..fa2d437 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -1,6 +1,6 @@ # Vignette Handoff -> Updated: 2026-06-27 18:26 KST. 새 세션은 이 문서와 `docs/DESIGN_CONCEPT.md`를 먼저 읽고 이어가면 된다. +> Updated: 2026-06-27 18:39 KST. 새 세션은 이 문서와 `docs/DESIGN_CONCEPT.md`를 먼저 읽고 이어가면 된다. ## 현재 상태 @@ -21,7 +21,7 @@ - X2 예산 경고/저비용 평가 라우팅: `ADMIN_USAGE_BUDGET_USD`가 0보다 크면 `/admin/usage`가 budget 상태(ok/warn/exceeded)를 반환하고 `/admin`이 예산 배너를 표시한다. `EVALUATOR_FAST_MODEL`/`EVALUATOR_DEEP_MODEL`을 설정하면 fast/deep 평가 호출만 해당 모델 override로 gateway에 전달한다. 비우면 기존 gateway default 라우팅을 유지한다. - H4 LLM call audit + 동의 게이트: 상담 생성(generate/stream)과 fast/deep 평가의 외부 LLM 호출 직후 `audit.llm_call_log`에 provider/model/token/cost/inference_geo/latency metadata만 적재한다. prompt/completion 본문은 저장하지 않는다. 로컬 dev-login 실제 `/turn` smoke에서 `audit.llm_call_log`가 8→11로 3행 증가했다. 이번 패스에서 `app_user.consent_at` 기반 learner 동의 수락/철회 API와 세션 시작/voice dev persona 시작 하드게이트를 추가했다. 한국어 이름/기관 NER와 guardian/legal 서명 evidence는 후속. - RAG warm 동시성: E2E가 여러 세션을 빠르게 만들 때 BGE-M3 embedder가 동시에 지연 로드되어 `tqdm` lock 예외와 API health/dev-login timeout이 반복됐다. `rag.py` embedder load/encode를 process-wide `RLock`으로 직렬화하고, `sessions.py` warm task를 semaphore 1개로 제한했다. 최신 검증: `layout-visual-gate + session-layout` 11 passed. -- 계약 SSOT 3차: `apps/web/src/lib/api.ts`의 수기 DTO 중 auth, persona, admin/user/engine, review leaf, teacher safety/growth leaf, learner sessions, session review/worksheet aggregate, teacher dashboard aggregate 타입을 `apps/web/src/lib/api.gen.ts`의 `ApiSchema` alias로 전환했다. generated optional/default 차이는 화면 form state, notification default helper, 배열 렌더링 fallback으로 흡수한다. CI 연결과 SessionDetail/SessionStart 계열 stage enum 정리는 후속. 검증: `npm run check:api-types`, `npm run typecheck`, `npm run build`, learner/session-review/teacher/layout focused E2E 32 passed. +- 계약 SSOT 3차: `apps/web/src/lib/api.ts`의 수기 DTO 중 auth, persona, admin/user/engine, review leaf, teacher safety/growth leaf, learner sessions, session review/worksheet aggregate, teacher dashboard aggregate 타입을 `apps/web/src/lib/api.gen.ts`의 `ApiSchema` alias로 전환했다. generated optional/default 차이는 화면 form state, notification default helper, 배열 렌더링 fallback으로 흡수한다. 이번 패스에서 `.github/workflows/api-contract.yml`을 추가해 PR/master push에서 `npm run check:api-types`가 FastAPI OpenAPI와 generated type drift를 막는다. SessionDetail/SessionStart 계열 stage enum 정리는 후속. 검증: `npm run check:api-types`, `npm run typecheck`, `npm run build`, learner/session-review/teacher/layout focused E2E 32 passed. - 턴 런타임 리팩터 2차: `turn_runtime.finalize_completed_turn`으로 REST submit/stream/voice WS의 `record_completed_turn` + `record_safety_event` 호출쌍을 공통화했다. gateway `/v1/stream` route 레벨에서 `token/done/error` SSE 프레임 contract tests를 추가했다. 검증: focused backend 40 passed. - Phase3 artifact checker 강화: `scripts/check-phase3-artifacts.py`가 CSV enum, KPI report 필수 field, approved export의 PII pass·κ/ICC·withdrawn exclusion·consent scope·file sha256을 검증한다. `app/test_phase3_artifact_checker.py` 5 tests 추가. 실제 파일럿 evidence와 steward/legal/IAA gate는 그대로 외부 의존이다. - 세션 종료 UX/dark theme SSOT 정리: 드래그형 `SlideToEnd`를 명시 확인 다이얼로그로 교체하고, Topbar/Settings theme 토글을 `lib/theme.ts` 단일 경로로 통합했다. 저장값이 없으면 dark를 기본으로 두고 앱 부팅 시 theme를 먼저 적용해 초기 flash를 줄인다. API 기본 preference `system`은 Settings에서 light로 오해하지 않고 현재 초기 테마를 따른다. 회기 리뷰·설정 포함 주요 페이지의 흰 섹션 잔재도 dark 작업면으로 맞췄다. 검증: `npm run typecheck`, `npm run build`, `npx playwright test e2e/layout-visual-gate.spec.ts e2e/settings.spec.ts e2e/session-review.spec.ts --project=chromium-desktop --project=chromium-mobile --project=chromium-single-run --workers=1` 22 passed. diff --git a/docs/dev_dashboard.html b/docs/dev_dashboard.html index eaf0932..ed9c6e2 100644 --- a/docs/dev_dashboard.html +++ b/docs/dev_dashboard.html @@ -637,7 +637,7 @@
-
현재

apps/api/scripts/export-openapi.pyapps/web/scripts/generate-api-types.mjs로 FastAPI OpenAPI를 apps/web/src/lib/api.gen.ts에 생성한다. npm run check:api-types가 재생성 누락을 잡는다. MeResponse, AuthConfigResponse, ConsentResponse, PersonaSummary, PersonaReviewSummary, admin/user/engine DTO, review leaf DTO, CrisisResourceResponse, teacher safety/growth leaf DTO에 이어 이번 패스에서 LearnerSessionsResponse, SessionReviewResponse, worksheet aggregate, TeacherDashboardResponse, TeacherSessionSummary, TeacherLearnerGrowthApiSchema<...> alias로 전환했다. generated optional/default 차이는 화면 form state, notification default helper, 배열 렌더링 fallback으로 흡수한다.

검증

npm run check:api-types, npm run typecheck, npm run build passed. 영향 범위 E2E: learner.spec.ts + session-review.spec.ts + teacher.spec.ts + layout-visual-gate.spec.ts 32 passed.

잔여

실제 CI 워크플로에 npm run check:api-types를 연결하는 작업은 남아 있다. SessionDetail/SessionStart 계열은 backend stage enum/guard를 먼저 정리한 뒤 생성 타입 alias로 전환한다.

+
현재

apps/api/scripts/export-openapi.pyapps/web/scripts/generate-api-types.mjs로 FastAPI OpenAPI를 apps/web/src/lib/api.gen.ts에 생성한다. npm run check:api-types가 재생성 누락을 잡는다. MeResponse, AuthConfigResponse, ConsentResponse, PersonaSummary, PersonaReviewSummary, admin/user/engine DTO, review leaf DTO, CrisisResourceResponse, teacher safety/growth leaf DTO에 이어 이번 패스에서 LearnerSessionsResponse, SessionReviewResponse, worksheet aggregate, TeacherDashboardResponse, TeacherSessionSummary, TeacherLearnerGrowthApiSchema<...> alias로 전환했다. generated optional/default 차이는 화면 form state, notification default helper, 배열 렌더링 fallback으로 흡수한다. .github/workflows/api-contract.yml은 PR/master push에서 같은 stale check를 실행해 OpenAPI type drift를 CI에서 막는다.

검증

npm run check:api-types, npm run typecheck, npm run build passed. 영향 범위 E2E: learner.spec.ts + session-review.spec.ts + teacher.spec.ts + layout-visual-gate.spec.ts 32 passed.

잔여

SessionDetail/SessionStart 계열은 backend stage enum/guard를 먼저 정리한 뒤 생성 타입 alias로 전환한다.

@@ -783,7 +783,7 @@ Web typechecknpm run typecheckPassed API typegen SSOTnpm run check:api-typesPassed; FastAPI OpenAPI → src/lib/api.gen.ts stale check - Contract SSOT aggregate DTOnpm run typecheck / npm run build / learner + session-review + teacher + layout gatePassed; learner sessions, session review/worksheet, teacher dashboard aggregates now use generated ApiSchema aliases with UI array fallback. Focused E2E 32 passed. + Contract SSOT aggregate DTOnpm run typecheck / npm run build / learner + session-review + teacher + layout gatePassed; learner sessions, session review/worksheet, teacher dashboard aggregates now use generated ApiSchema aliases with UI array fallback. Focused E2E 32 passed. .github/workflows/api-contract.yml now runs npm run check:api-types on API/Web contract changes. Backend pytestpython -m pytest app/ -q130 passed X2 evaluator model routingpython -m pytest app/test_evaluator_model_routing.py app/test_runtime_policy.py -q23 passed; fast/deep evaluator model override and blank-default routing covered C1 case worksheetpython -m pytest app/test_session_turn_persistence.py -q / session-review.spec.ts14 passed; session review exposes transcript-grounded caseWorksheet and UI evidence jump diff --git a/docs/guides/testing.md b/docs/guides/testing.md index 1acc951..c4a3ef7 100644 --- a/docs/guides/testing.md +++ b/docs/guides/testing.md @@ -111,6 +111,13 @@ API DTO를 바꿨다면 먼저 생성 산출물을 갱신한다. npm run generate:api-types ``` +### 2.3 CI 계약 게이트 + +GitHub Actions `.github/workflows/api-contract.yml`은 API/Web 계약 관련 파일이 바뀌는 +`pull_request`와 `master` push에서 Python 3.11 API 의존성, Node 22 web 의존성을 설치한 뒤 +`apps/web`의 `npm run check:api-types`를 실행한다. 이 게이트는 FastAPI OpenAPI와 +`src/lib/api.gen.ts`의 drift만 막는 좁은 CI이며, DB·API 서버·브라우저는 필요 없다. + --- ## 3. Playwright E2E (풀스택) diff --git a/docs/ops/backlog-2026-06-26.md b/docs/ops/backlog-2026-06-26.md index 3adbb93..57b7853 100644 --- a/docs/ops/backlog-2026-06-26.md +++ b/docs/ops/backlog-2026-06-26.md @@ -65,7 +65,7 @@ - P1 서연 음성 아트 PoC: Higgs v3 무참조 synthetic seed + 5개 정서/속도 변주(mp3/wav) 생성. 실존 reference voice 미사용. `/voice/ws`에는 dev-only sample TTS provider로 연결했고 live text_turn smoke에서 `p1-sample-poc` binary 20 chunks/78,573 bytes 수신 확인. 이 provider는 TTS override만 담당한다(마이크/STT는 OpenAI 키 필요). 프로덕션 탑재는 라이선스/권리 결정 전 금지. - 부수: RLS smoke 스크립트 cleanup RLS 컨텍스트 버그 수정, `vignette_app`(NOBYPASSRLS) dev 역할 프로비저닝. - 평가 SSOP 2차: `TurnRecord.evaluation`을 `app.feedback_scores`/`turn_technique`/`turn_client_state`/`supervisor_comment`/`alternative_utterance`로 정규화 적재·복원하고, submit/stream/voice 저장 경로를 `app.turn_runtime`으로 공통화했다. 원시 평가 테이블은 evaluator/admin RLS로 제한하고 서버가 evaluator 컨텍스트로 적재한다. `app.turns` insert는 `RETURNING id` 확인으로 silent success 제거. **검증: `pytest app/test_evaluation_persistence.py -q` 6 passed, 관련 회귀 45 passed, `pytest app/` 119 passed, `pytest engine_gateway/` 7 passed.** -- 계약 SSOT 3차: FastAPI OpenAPI를 `apps/web/src/lib/api.gen.ts`로 생성하는 `npm run generate:api-types`와 stale 체크용 `npm run check:api-types`를 유지한다. `TurnResponse`/`SessionEndResponse`에 이어 `MeResponse`, `AuthConfigResponse`, `ConsentResponse`, `PersonaSummary`, `PersonaReviewSummary`, admin/user/engine DTO, review leaf DTO, `CrisisResourceResponse`, teacher safety/growth leaf DTO, `LearnerSessionsResponse`, `SessionReviewResponse`, worksheet aggregate, `TeacherDashboardResponse`, `TeacherSessionSummary`, `TeacherLearnerGrowth`를 생성 타입 alias로 전환했다. generated optional/default 차이는 UI form state, notification default helper, 배열 렌더링 fallback으로 흡수한다. CI 연결과 SessionDetail/SessionStart 계열의 stage enum 정리는 후속. **검증: `npm run check:api-types`, `npm run typecheck`, `npm run build`, `learner.spec.ts + session-review.spec.ts + teacher.spec.ts + layout-visual-gate.spec.ts` 32 passed.** +- 계약 SSOT 3차: FastAPI OpenAPI를 `apps/web/src/lib/api.gen.ts`로 생성하는 `npm run generate:api-types`와 stale 체크용 `npm run check:api-types`를 유지한다. `TurnResponse`/`SessionEndResponse`에 이어 `MeResponse`, `AuthConfigResponse`, `ConsentResponse`, `PersonaSummary`, `PersonaReviewSummary`, admin/user/engine DTO, review leaf DTO, `CrisisResourceResponse`, teacher safety/growth leaf DTO, `LearnerSessionsResponse`, `SessionReviewResponse`, worksheet aggregate, `TeacherDashboardResponse`, `TeacherSessionSummary`, `TeacherLearnerGrowth`를 생성 타입 alias로 전환했다. generated optional/default 차이는 UI form state, notification default helper, 배열 렌더링 fallback으로 흡수한다. 이번 패스에서 `.github/workflows/api-contract.yml`을 추가해 PR/master push에서 `npm run check:api-types`가 drift를 막는다. SessionDetail/SessionStart 계열의 stage enum 정리는 후속. **검증: `npm run check:api-types`, `npm run typecheck`, `npm run build`, `learner.spec.ts + session-review.spec.ts + teacher.spec.ts + layout-visual-gate.spec.ts` 32 passed.** - 위기게이트 109 1차: 실제 자해·자살 신호는 LLM/엔진 호출 전 중단하고 `crisis_resource(109)`와 `conversation_stopped`를 REST/SSE/voice 응답에 싣는다. `app.safety_events` detail 적재와 교수자 대시보드 안전 알림 큐까지 연결했다. 안전 이벤트 DB insert payload도 회귀화했다. 임상 문안, 실시간 push/메일, 지역 자원 확장은 후속. **검증: `pytest app/test_session_turn_persistence.py app/test_voice_ws.py -q` 20 passed, `pytest app/test_teacher_dashboard.py app/test_session_turn_persistence.py app/test_voice_ws.py -q` 21 passed.** - 교수자 성장 추적 1차: `teacher_dashboard`가 정규화된 턴 평가를 기반으로 학습자별 적절성·라포·기술 사용 추이를 집계하고, 교수 콘솔에 최근 회기/항목별 성장 카드와 안전 알림 큐를 함께 표시한다. **검증: `pytest app/test_teacher_dashboard.py app/test_session_turn_persistence.py app/test_voice_ws.py -q` 21 passed, `pytest app/` 119 passed, `npm run check:api-types`, `npm run typecheck`, `npm run build`.** - 페르소나 저작 CRUD 1차: teacher/admin 전용 `POST/GET/PUT /personas/drafts` 경로를 추가해 draft 생성·조회·편집·검수요청을 열고, 교수 콘솔에 JSON 초안 작성/불러오기/수정 UI를 연결했다. 승인된 카드만 학습자 catalog에 노출하는 기존 경계는 유지한다. P4~P7 실제 임상 콘텐츠 입력은 후속. **검증: `pytest app/test_persona_review.py -q` 19 passed, `pytest app/` 119 passed, `npm run check:api-types`, `npm run typecheck`, `npm run build`.**