정식 승격 상태판과 증거를 동기화

This commit is contained in:
Yun Chan 2026-08-30 00:06:45 +09:00
parent 34aff65cb0
commit be08c0b573
16 changed files with 1160 additions and 63 deletions

View file

@ -15,12 +15,12 @@ Vignette 저장소의 모든 검증 수단(백엔드 단위 테스트, 웹 타
| 검증 | 작업 디렉터리 | 명령 | DB | API(8000) | 웹(5173) | 엔진GW(9099) | 브라우저 | 현재 통과 |
|---|---|---|---|---|---|---|---|---|
| 백엔드 단위 테스트 | `apps/api` | `python -m pytest app/ -q` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | 2026-08-28 전체 실행 1002 passed |
| 백엔드 단위 테스트 | `apps/api` | `python -m pytest app/ -q` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | 2026-08-29 전체 실행 1074 passed / 1 skipped |
| 엔진 게이트웨이 테스트 | `apps/api` | `python -m pytest engine_gateway/ -q` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | 2026-08-28 전체 실행 68 passed |
| API 타입 생성 체크 | `apps/web` | `npm run check:api-types` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
| 웹 타입체크 | `apps/web` | `npm run typecheck` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
| 웹 빌드 | `apps/web` | `npm run build` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
| Playwright E2E(전체) | `apps/web` | `npm run e2e` | **필요(+시드)** | **필요** | 자동기동 | 일부만 | **필요** | 현재 수집 1138 tests / 62 files · 현 작업트리 전체 GREEN 미검증 |
| Playwright E2E(전체) | `apps/web` | `npm run e2e` | **필요(+시드)** | **필요** | 자동기동 | 일부만 | **필요** | 현재 수집 1158 tests / 64 files · 현 작업트리 전체 GREEN 미검증 |
핵심 원칙: **단위 테스트(pytest)와 타입체크/빌드는 외부 서비스 없이 단독 실행된다.**
**E2E만 풀스택(DB+API+웹+브라우저)을 요구한다.** 아래 각 절에서 근거와 절차를 설명한다.
@ -42,7 +42,7 @@ Vignette 저장소의 모든 검증 수단(백엔드 단위 테스트, 웹 타
```sh
# apps/api
python -m pytest app/ -q # 앱 단위 테스트: 2026-08-28 전체 실행 1002 passed
python -m pytest app/ -q # 앱 단위 테스트: 2026-08-29 전체 실행 1074 passed / 1 skipped
python -m pytest engine_gateway/ -q # 게이트웨이 단위 테스트: 2026-08-28 전체 실행 68 passed
```
@ -80,12 +80,12 @@ python -m pytest engine_gateway/ --collect-only -q
#### 개선관리 워크북 focused 증거 (2026-08-27~28)
아래는 전체 1002/68과 별도로 해당 계약을 좁혀 실행한 확정 증거다. 같은 묶음의 테스트가 여러 요구사항
아래는 전체 API 1074 passed/1 skipped·gateway 68 passed와 별도로 해당 계약을 좁혀 실행한 확정 증거다. 같은 묶음의 테스트가 여러 요구사항
경계를 함께 검증할 수 있으므로 숫자를 요구사항별 전체 합계로 더하지 않는다.
| 항목 | focused 명령/파일 | 확인 결과 |
|---|---|---|
| C-001 기술 사전검증 | `scripts/check-clinical-crisis-review.py`, `run-clinical-crisis-technical-observations.py`, checker/client reply/source pack/state machine/session focused pytest | 90 passed + Ruff PASS(Starlette 제3자 경고 1건) — P1 생성·스트림 실제 경로 6/6, v2 사례 6건과 런타임 패키지 8개가 HEAD·작업트리 SHA에 일치한다. DB direct load·baseline·carry-over·observed 값을 `1..3`으로 정규화하고 과상한 출력은 재생성하며, 수련생 실제 위기는 엔진 전 중단·109, 내담자 수단 상세는 차단한다. 6시트 개선관리 완료본은 수식 오류 0, 10 완료/1 검토/총 11, canonical gate `pending-valid`다. 사례 30필드·청소년 6답변·자격 포함 승인 10필드·서명 증거가 비어 있어 `review_complete=false`; 외부 임상 승인을 대체하지 않음 |
| C-001 기술 사전검증 | `scripts/check-clinical-crisis-review.py`, `run-clinical-crisis-technical-observations.py`, checker/client reply/source pack/state machine/session focused pytest | 90 passed + Ruff PASS(Starlette 제3자 경고 1건) — P1 생성·스트림 실제 경로 6/6, v2 사례 6건과 런타임 패키지 8개가 HEAD·작업트리 SHA에 일치한다. DB direct load·baseline·carry-over·observed 값을 `1..3`으로 정규화하고 과상한 출력은 재생성하며, 수련생 실제 위기는 엔진 전 중단·109, 내담자 수단 상세는 차단한다. 6시트 개선관리 완료본은 수식 38개·수식 오류 0, 9 완료/2 검토/총 11, canonical gate `pending-valid`다. REQ-001은 기존 Gmail 인증 세션·관리자 권한·복구 데이터 가시성 수락으로 완료했고, REQ-008 실제 Windows 재부팅 smoke와 C-001 사례 30필드·청소년 6답변·자격 포함 승인 10필드·서명 증거만 남아 있으며 외부 임상 승인을 대체하지 않음 |
| C-002 | `app/test_first_session_checklist.py` | 3 passed — 첫 회기 라포 반영·한 초점 열린 질문, evidence turn, 이후 회기 not-applicable |
| C-003 | `app/test_learner_dashboard.py` | 11 passed — 종료 회기 4건 전에는 insufficient, 이후 dominant share 0.75 경계 |
| REQ-001 | `app/test_auth_providers.py`, `app/test_access_logging.py`, OAuth UI focused E2E, `e2e/admin.spec.ts`/`e2e/uc-admin-console.spec.ts` | auth 41 + access-log redaction 6 pytest, OAuth UI desktop/mobile 4, route-mock browser 2, 실제 DB/browser 1 passed — 모든 provider-verified Google 이메일을 도메인·사전등록 없이 learner·approved로 허용하고 suspended는 보존. callback query는 Uvicorn access log에서 제거. 관리자 사전등록 create는 pending 고정이며 승인 전 `/personas` 403·`/learn→/pending`, 별도 PATCH 승인 뒤 같은 세션 `/personas` 200, `learner_feedback_enabled=false` 영속 재조회, 비활성화·활성 세션 0 |
@ -99,10 +99,12 @@ python -m pytest engine_gateway/ --collect-only -q
| REQ-008 | `app/test_engine_health_contract.py`와 session stream 회귀 | health contract 1 passed; incomplete EOF는 live/session 50 묶음에 포함 |
| 학습자 성장 패널 헤더 | `uc-learner-home-dashboard.spec.ts --grep "성장 지표 라포 헤더"` + `check:design-ssot` + `check:cosmetic-filter-safety` + typecheck | chromium desktop/mobile 2 passed — 선택된 라포 문구의 부모는 plain `div`, 배경 transparent·border/radius/padding 0이며 주변 panel 구조는 유지 |
| 교수자 검토·요약 카드 간격 | `full-sweep-professor.spec.ts --grep "renders six KPI cards"` + `layout-visual-gate.spec.ts` + `session-layout.spec.ts` | 기존 0px에서 회귀 게이트 RED를 확인하고 `--sp-3`=12px로 교정했다. focused 1 passed, 7폭 교수자 gate 1 passed, 전체 layout visual 15 passed, session 무회귀 8 passed. Pages `0c60261e…` 승격 뒤 실제 `/teach`는 computed/실측 12px, overflow 0, console warning/error 0이다. |
| REQ-001 공개 운영 | `https://vignette.chanpaca.net/login` + Google account chooser/callback | `allowed_email_domains=[]`이며 기존 일반 Google 계정 callback→`/admin` 성공으로 가입 범위는 확인했다. Pages production `ef48c0ae…` 승격 뒤 custom domain은 단일 `Google 계정으로 로그인` CTA 1개·구 계정 선택 버튼 0·dev login 0·console warning/error 0이고 격리 Playwright OAuth 시작 검증 **1 passed**다. 실제 Gmail callback과 복구 계정의 `/admin`·기존 데이터 확인 전에는 검토를 유지한다. |
| Google 계정 데이터 복구 | migration 19, alias/auth focused, 전체 API·gateway, 백업 복제 DB, 활성 DB·공개 API | Gmail의 빈 중복 사용자와 canonical 관리자 계정을 이메일 자동병합 없이 명시적 Google subject 별칭 2개로 연결했다. 정식 계정 21회기·64턴·평가 6건과 Gmail 원본 설정 1행을 보존하고 source-only 알림 키를 canonical 설정에 무손실 병합했다. Gmail 옛 세션 1개 철회, 중복 계정 suspended, 감사로그 1건, 사용자 1175·회기 469·평가 65와 고아 0을 확인했다. API+gateway **1111 passed/1 skipped**, release agent **31 passed**, 백업 복제 DB 트랜잭션·cleanup 및 런타임 DB 역할 alias SELECT를 통과했다. 실제 Gmail OAuth callback은 사용자 계정 선택 후 최종 확인한다. |
| REQ-001 공개 운영 | `https://vignette.chanpaca.net/login`, `/settings`, `/learn/history`, `/admin/continuous-improvement` | 2026-08-28 증거에서 `allowed_email_domains=[]`, Pages `0c60261e…`의 단일 Google CTA·공개 Playwright 2 passed, 기존 인증 세션의 Gmail 로그인 이메일·관리자 콘솔·온보딩 비전환·복구 이력 20건/리뷰 필요 6건을 확인했고 소유자가 데이터 가시성을 수락해 REQ-001을 완료했다. 2026-08-29 public API 530은 별도 현재 장애이며, 이 과거 수락 증거를 current health GREEN으로 재사용하지 않는다. |
| Google 계정 데이터 복구 | migration 19, alias/auth focused, 전체 API·gateway, 백업 복제 DB, 활성 DB·공개 API·기존 인증 브라우저 세션 | Gmail의 빈 중복 사용자와 canonical 관리자 계정을 이메일 자동병합 없이 명시적 Google subject 별칭 2개로 연결했다. 정식 계정 21회기·64턴·평가 6건과 Gmail 원본 설정 1행을 보존하고 source-only 알림 키를 canonical 설정에 무손실 병합했다. Gmail 옛 세션 1개 철회, 중복 계정 suspended, 감사로그 1건, 사용자 1175·회기 469·평가 65와 고아 0을 확인했다. API+gateway **1111 passed/1 skipped**, release agent **31 passed**, 백업 복제 DB 트랜잭션·cleanup 및 런타임 DB 역할 alias SELECT를 통과했다. 후속 기존 인증 브라우저 세션에서 Gmail 로그인 이메일·관리자 권한·복구 데이터 가시성을 확인했다. |
| REQ-005·007·008 공개 운영 | P20 회기 `03dddadd-adf3-4922-acbf-31002470da52` | 생성→자기점검 원장 잠금→실제 학습자/AI 내담자 2턴→종료→AI 평가 완료→P20 1회 기록 영속→다음 회기 버튼 재노출 |
| 운영 배포·복구 | live API clean `dba9b75a…`·tree `14cd4607…`, Pages `ef48c0ae-374e-41fc-b6fd-03f69b26e946`; boot/watchdog 동일 pin | fresh API+cloudflared provenance receipt SHA `186529be…f7a4`, 실제 API cwd가 새 detached root이며 local/public prod·db·engine·voice, OpenAPI 129, auth 401이 정상이다. boot/watchdog 실제 실행은 `LastTaskResult=0`·failcount 0이고 custom domain Pages entry SHA는 clean build와 일치한다. 배포별 preview origin은 정적 자산만 일치하고 연결 진단을 표시하므로 runtime GREEN이 아니며, 실제 Windows 재부팅 smoke는 미실행이다. |
| 아바타 decode/fallback 후보 | `app/test_upload_storage_contract.py`, `scripts/test_initialize_public_runtime_upload_root.py`, `initialize-public-runtime-upload-root.py probe` | source union 실측은 93 objects·52,973 bytes·inventory SHA-256 `9d703126…e6aa`, decode 정상 3/실패 90이며 현 DB 참조는 정상 2/실패 6/missing 0이다. 후보는 manifest v3 decode 상태·합계·digest를 fail-closed로 결속하고 invalid static 응답을 404/fallback으로 보낸다. focused 단위/probe 증거와 전체 API/Web/PowerShell/E2E·정식 배포 증거를 분리한다. |
| G8 사람 게이트 현재 상태 | `e2e/continuous-improvement-admin.spec.ts`, `e2e/continuous-improvement-live.spec.ts`, Codex 내장 브라우저 | typecheck와 route-fixture desktop/mobile **10/10** 통과, 3.5초/8초 watchdog overlay 0이다. 격리 local stateful in-app browser에서도 8.5초 뒤 overlay 0·heading 정상, content `keep_quarantine` 뒤 effect 0, 증거 없는 release 승인 disabled→`reject` 뒤 effect 0, 증거 4종 promote 승인 뒤 lifecycle effect 정확히 1을 확인했다. 이 local proof는 GREEN이지만 실DB append-only 결정과 public proof는 아직 남아 있다. |
| 운영 배포·복구 | deployed API/task baseline `dba9b75a…`·tree `14cd4607…`, Pages `0c60261e-cb37-482d-ba42-d91586194c48` | 2026-08-29 최신 read-only 상태는 local 8001 연결 거부·public API 530·Docker daemon 부재다. 정적 public Web 200과 2026-08-28 green receipt는 현재 API/DB GREEN 증거가 아니다. avatar 후보는 미배포이며, 전체 통합 GREEN·후보 SHA·사용자 승인 전에는 runtime/task/DB/upload-root를 바꾸지 않는다. 실제 Windows 재부팅 smoke도 미실행이다. |
로컬 Playwright는 기존 포트의 서버를 기본 재사용하지 않는다. 공개 preview나 다른 checkout의 stale bundle을
현 작업트리 증거로 오인하지 않도록 충돌 시 fail-closed하며, 동일 dev server를 의도적으로 공유할 때만
@ -307,7 +309,7 @@ VITE_API_BASE=http://127.0.0.1:8000 npm run e2e # 프록시 대신 API
### 3.6 실측 테스트 개수 (현재)
2026-08-28 `npx playwright test --list` 기준 **현재 수집 1138 tests / 62 files**다
2026-08-29 `npx playwright test --list` 기준 **현재 수집 1158 tests / 64 files**다
(유스케이스 16테마 `uc-*.spec.ts` 239 시나리오 포함).
이 숫자는 수집량이지 통과량이 아니다. 현 작업트리 전체 1109개 완주는 아직 증거가 없으며,
과거 전체 GREEN 기록과 이번 focused/release gate 결과를 구분해 적는다.
@ -553,7 +555,9 @@ hourly heartbeat는 최신 GREEN이 6시간 이상 오래됐거나 material mile
- 2026-07-15 보태니컬 글래스 UI·SSOT 검증: `npm run check:design-ssot` + `npm run typecheck` + `npm run build` 통과, `npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --reporter=line` 시각 게이트 **15/15**, `npx playwright test e2e/auth-visual.spec.ts --project=chromium-single-run --reporter=line` **1/1**, `npx playwright test e2e/session-layout.spec.ts e2e/learner.spec.ts e2e/session-review.spec.ts --project=chromium-desktop --project=chromium-mobile` **46/46**, 로그인→온보딩 focused desktop/mobile **2/2**. 공통 AppShell GNB, Theme store, Surface variant의 소유권과 전 폭 대시보드/리뷰 탭, 로그인·온보딩 라이트/다크 390/1280px, 고해상도 보태니컬 자산을 함께 고정한다. 라이트 테마도 패널당 복수 굴절 그라데이션, backdrop blur, 헤어라인, 그림자를 computed style로 단언하며 모바일 셸이 보태니컬 배경을 제거하지 않는지 검사한다. 관리자 사용자 표는 semantic table, 정렬 헤더, 1440px 최소 폭과 표 전용 가로 스크롤을 desktop/mobile에서 검증한다.
- 2026-07-31 활성 세션 보태니컬 워크스페이스 검증: `full-sweep-session.spec.ts`의 1536×1024 계약이 좌 326px·우 357px 레일, 1408px 상단/본문, 1468px 하단 제어바, 좌우/스테이지 보태니컬 WebP 연결과 라이트 테마를 실측한다. 세션 전수 desktop/mobile **32 passed / 2 skipped**, `session-layout` **8/8**, 7폭 `layout-visual-gate` **15/15**, `check:design-ssot`·typecheck·build를 통과했다. 1366×768 이하는 스테이지보다 자막이 작아지지 않게 별도 압축 계약을 적용한다.
- 2026-07-31 관리자 Provider·모델별 비용 원장 검증: `python -X utf8 -m pytest -p no:cacheprovider app/test_llm_pricing.py app/test_admin_ops.py app/test_usage_report.py engine_gateway/test_provider_registry.py engine_gateway/test_gateway_model.py -q` **66 passed**. Claude CLI SDK 비용 추정값과 Agy/Gemini·Codex·Claude API 공식 참조단가 추정을 분리하고, 기존 DB의 0달러 Agy 행을 조회 시 재산정하며, 단가 미등록 모델은 `미산정`으로 남기는 계약을 고정했다. `npm run generate:api-types`·`check:api-types`·typecheck·build를 통과했고, 관리자 AI desktop/mobile focused E2E **2 passed**와 7폭 focused layout visual gate **1 passed**에서 Gemini 원장 `$0.06`·`참조단가` 표시와 레이아웃 containment를 확인했다. 공개 API 프로세스를 재시작하고 Cloudflare Pages production `11b3e11f`에 배포했다. 인증된 공개 `/admin/usage?window_days=30``source=database`, `durable=true`, `gemini-3.6-flash-high` 5호출·입력 35,703·출력 1,129·참조단가 `$0.062022`를 반환했고, 검증용 인증 세션은 즉시 삭제했다. 커스텀 도메인의 `AdminAi-BfxMUlzj.js``AdminAi-XHkda_d4.css`는 올바른 JavaScript/CSS MIME으로 200을 반환한다.
- 2026-08-28 Gemini 3.7 비용 미산정 회귀 검증: screenshot과 같은 `gemini-3.7-flash-high` 21회·입력 115,950·출력 4,407의 기존 0달러 원장을 2026 프로모션 기준 `≤$0.103489`(`reference_upper_bound`)로 보정한다. Gemini 3.6/3.7 출시·프로모션·2027 경계, 요청별 200K 단가 구간, 일부 미산정 `$…+`, 상한+미산정 `partial_upper_bound``미산정`, 저장된 과거 비용의 라벨 불변, 정확한 `(e2e, fake-client, input=1, output=1, cost=0)` 제외를 회귀화했다. 전체·일별·예산에 비용 확실성을 전파하고 불확실한 평균·비중은 숨긴다. 동일 계량 시그니처 SQL 집계와 migration 18의 client-turn 기간 인덱스를 고정했으며, release agent는 이 파일만 `CREATE INDEX CONCURRENTLY`로 트랜잭션 밖에서 적용한다. focused **105 passed / 1 skipped**, `app engine_gateway` 전체 **1102 passed / 1 skipped**, 새 빈 PostgreSQL에서 온라인 마이그레이션+통합 **1 passed**, release agent **31 passed**, API type generation/check·typecheck·build, 관리자 AI desktop/mobile **6 passed**를 확인했다. 통합 컨테이너와 테스트 DB는 `--rm`으로 정리했으며, 공개 배포·운영 DB 데이터 삭제는 하지 않았다.
- 2026-08-29 Google 계정 데이터 복구 검증: migration 19가 owner-managed `auth_identity_alias`와 세션의 별도 `login_email`을 추가한다. Gmail 중복 계정은 학습 데이터 0건이지만 기본 설정 1행이 있어 최초 가드가 rollback했고, 전체 app_user FK를 전수 스캔한 뒤 canonical 설정과 겹치는 값이 동일함을 확인했다. 개정 트랜잭션은 source-only `account_approval` 알림 키를 canonical 설정에 합치고 원본 설정 행은 보존한다. 백업 복제 DB에서 alias 2·세션 21·턴 64·평가 6·활성 source session 0·고아 0으로 통과 후 복제 DB를 삭제했고, 활성 DB도 사용자 1175·회기 469·평가 65·고아 0을 유지했다. 공개 API fresh provenance는 commit `dba9b75a…`·tree `14cd4607…`, health DB/engine true, voice exact, OpenAPI 129, auth 401이며 boot/watchdog 실제 실행 결과는 0/0이다. Pages production `ef48c0ae…`의 custom domain은 clean build entry·login chunk SHA와 일치하고 단일 Google CTA E2E 1건을 통과했다. 실제 Gmail callback은 브라우저 계정 선택을 기다린다.
- 2026-08-29 REQ-001 완료·개선관리 재생성 검증: 기존 실서비스 인증 세션의 `/settings`에서 `yunchan8804@gmail.com`, 관리자 메뉴와 `/admin/continuous-improvement` 접근, 온보딩 비전환을 확인했고 `/learn/history`는 회기 20건·리뷰 필요 6건을 표시했다. 소유자가 데이터 가시성을 수락했으며, 최신 Pages `0c60261e…`의 단일 Google CTA·무제한 도메인 계약·공개 Playwright 2 passed와 결합해 REQ-001을 완료로 승격했다. 완료본은 artifact-tool import→export→reimport로 6시트·11개 고유 요구·38수식·수식 오류 0·완료 9/검토 2를 확인했다. C-001은 패키지 8/8·기술 사례 6/6·canonical checker 정상의 `pending-valid`지만 외부 입력 46칸을 공란으로 보존했고, REQ-008은 실제 Windows 재부팅 smoke 전까지 검토다. 완료본 SHA는 `c832547f…d8bd`, sidecar SHA는 `8c9618f0…a610`이다.
- 2026-07-31 Claude CLI 토큰 원장 focused 검증: result `modelUsage`의 전체 agent tree를 합산하고 최상위 `usage` 폴백, cache read/create 입력 포함, generate·SSE done 전파를 회귀화했다. 전체 backend **436 passed**, gateway **45 passed**, API type generation/check·typecheck·build, 관리자 AI desktop/mobile **2 passed**. 신규 live Opus generate는 입력 **31,918**·출력 **4**·비용 추정 `$0.124862`, Haiku SSE done은 입력 **30,450**·출력 **159**·비용 추정 `$0.061685`를 반환했다. 운영 DB 30일 원장의 과거 Claude 행은 역산하지 않고 `tokens=미계량`, `token_unmetered_turns=194`, `cost_basis=provider_estimate`로 분리됨을 임시 인증 세션으로 확인하고 세션을 즉시 폐기했다.
- 2026-07-31 Claude CLI 과거 토큰 백필 검증: 로컬 Claude JSONL **3,908개**의 assistant usage record **3,848개**를 읽되 본문을 출력하지 않고, 정규화 응답 SHA-256과 DB 생성 시각 창(-30초~+180초)이 모두 일치하며 후보가 정확히 1개인 턴만 복구했다. 운영 DB의 과거 0/0 Claude 턴 **442건****169건**을 실제 입력 **5,179,999**·출력 **46,910** 토큰으로 갱신했고, 불일치 **273건**은 계속 `미계량`, 모호한 후보는 **0건**이었다. apply는 `--expected-matches 169` 가드와 행별 `UPDATE 1` 확인을 통과했고, 적용 후 dry-run 재실행에서 추가 exact match **0건**을 확인했다. 전체 backend **439 passed**, gateway **45 passed**이며, 공개 관리자 브라우저 **1 passed**에서 Claude 행의 실제 토큰과 부분 계량 `125/183회` 표시를 확인하고 임시 세션을 폐기했다.