79 KiB
테스트·검증 가이드
Vignette 저장소의 모든 검증 수단(백엔드 단위 테스트, 웹 타입체크/빌드, Playwright E2E)을
"무엇을 어떻게 실행하고, 어떤 의존이 필요한가" 기준으로 정리한다. 이 문서는 실제
apps/web/package.json, apps/web/playwright.config.ts, apps/web/e2e/,
apps/api/app/test_*.py, apps/api/engine_gateway/test_*.py,
infra/docker-compose.yml을 읽고 작성했으며, 명령·경로는 그대로 따라 할 수 있다.
주 환경은 Windows 11 + PowerShell이다. 아래 명령은 셸 공통(python -m ..., npm run ...)
형태로 적었고, 환경변수 지정은 PowerShell/Bash 양쪽 예시를 병기한다.
0. 한눈에 보는 검증 매트릭스
| 검증 | 작업 디렉터리 | 명령 | DB | API(8000) | 웹(5173) | 엔진GW(9099) | 브라우저 | 현재 통과 |
|---|---|---|---|---|---|---|---|---|
| 백엔드 단위 테스트 | apps/api |
py -3.11 -m pytest app/ -q |
불필요 | 불필요 | 불필요 | 불필요 | 불필요 | 2026-09-07 전체 실행 1090 passed / 1 skipped / 1 warning |
| 엔진 게이트웨이 테스트 | apps/api |
py -3.11 -m pytest engine_gateway/ -q |
불필요 | 불필요 | 불필요 | 불필요 | 불필요 | 2026-09-07 전체 실행 71 passed / 1 warning |
| 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 |
필요(+시드) | 필요 | 자동기동 | 일부만 | 필요 | 현재 수집 1188 tests / 66 files · 현 작업트리 전체 GREEN 미검증 |
핵심 원칙: 단위 테스트(pytest)와 타입체크/빌드는 외부 서비스 없이 단독 실행된다. E2E만 풀스택(DB+API+웹+브라우저)을 요구한다. 아래 각 절에서 근거와 절차를 설명한다.
2026-09-07 앱 전체 실행의 1 skipped는 기존 선택형 격리 DB 테스트
test_admin_usage_postgres.py가 VIGNETTE_USAGE_TEST_DATABASE_URL 미설정일 때 건너뛰는 경우다. 새 skip은
아니며, warning 1건은 통과 수와 분리해 기록한다.
1. 백엔드 단위 테스트 (pytest)
Windows에서 기본 python이 Python 3.11이 아니거나 pytest를 갖지 않을 수 있다. 이 저장소의 기준은 py -3.11이며, 새 가상환경은 py -3.11 -m venv .venv로 만든 뒤 활성화한다. 특정 사용자 절대경로를 문서나 명령에 넣지 않는다.
1.1 대상과 구성
- 작업 디렉터리:
apps/api - 테스트는 FastAPI
TestClient와 인메모리store.py폴백으로 돌기 때문에 Postgres/엔진 게이트웨이가 없어도 통과한다. 별도pytest.ini/pyproject.toml설정은 없고 기본 수집 규칙(test_*.py)을 그대로 쓴다. pydantic-settings가apps/api/.env를 자동 로드한다(있으면). 단위 테스트는.env없이도 돈다.
1.2 실행
# apps/api
py -3.11 -m pytest app/ -q # 앱 단위 테스트: 2026-09-07 전체 실행 1090 passed / 1 skipped / 1 warning
py -3.11 -m pytest engine_gateway/ -q # 게이트웨이 단위 테스트: 2026-09-07 전체 실행 71 passed / 1 warning
수집만 빠르게 확인하려면:
py -3.11 -m pytest app/ --collect-only -q
py -3.11 -m pytest engine_gateway/ --collect-only -q
수집량은 위 명령의 현재 출력으로 확인한다. --collect-only 숫자는 통과 수가 아니므로 실행 결과와 섞어
기록하지 않는다.
참고: 실행 중
PendingDeprecationWarning: Please use 'import python_multipart'경고가 보일 수 있으나 무해하며 통과 결과에 영향을 주지 않는다.
1.3 app/ 테스트 파일 (보안·상태머신·평가 중심)
| 파일 | 검증 영역 |
|---|---|
app/test_runtime_policy.py |
런타임 정책(공개/dev 모드, dev-login 가드) |
app/test_session_turn_persistence.py |
세션 턴 영속화(DB/인메모리 양쪽) |
app/test_session_share.py |
회기 리뷰 공유 URL, sanitized 공개 payload, 토큰 폐기 |
app/test_evaluation_persistence.py |
fast-loop 평가 정규화 적재/복원 매핑 |
app/test_orchestrator_masking.py |
오케스트레이터 PII 마스킹 게이트 |
app/test_state_machine_resistance.py |
저항엔진 상태머신(openness 전이) |
app/test_rbac_idor.py |
RBAC / IDOR 권한 경계 |
app/test_auth_providers.py |
인증 프로바이더(SSO/allowlist) |
app/test_admin_ops.py |
관리자 콘솔 운영 저장 모델(헬스·티켓) |
app/test_persona_review.py |
페르소나 리뷰 워크플로 |
app/test_voice_service.py |
음성 캐스케이드 서비스(STT/TTS) |
app/test_voice_ws.py |
음성 WebSocket 경계 |
app/test_session_digest_worker.py |
M2 session digest worker 요청 계약, accepted-only 적용, raw text 차단, 재실행 방지 |
scripts/check-dev-dashboard-ssot.py --json |
docs/dev_dashboard.html 상태 카운트·M2 검증 수치·DONE/GATE stale 문구 guard |
개선관리 워크북 focused 증거 (2026-08-27~28)
2026-09-09 최신 원본 워크북(다운로드 SHA-256 f64bbc859354e8bfdd62bb3393cffd841ca3a0d865983967a149aea7cf569773)은 14개 항목 중 완료 13·검토 1(C-001)이며 REQ-008~011도 완료다. py -3.11 -m pytest app/ -q는 1108 passed/기존 skip 1/1 warning, uc-session-conversation·calibration-transfer route fixture browser는 44 passed/0 failed/0 skipped다. 이는 local proof이며 REQ-010 추가 수정은 미배포다. 이 워크북 감사 단계에서는 build·새 public live 검증을 수행하지 않았다. 상세 매핑은 spreadsheet-implementation-audit-2026-09-09.md를 따른다.
실배포 CUA 128은 전체 완료가 아니다. H09의 /learn/history 검색·필터 복귀는 uc-learner-practice-history local 16 passed, T18의 감독·연구 canonical/legacy drilldown은 API 12 passed와 uc-teacher-console local 15 passed로 수정 검증했다. 두 RED는 배포 재검증 전까지 열린 상태이며 실제 배포 관찰은 CUA 보고와 ledger를 따른다.
아래는 2026-09-07 전체 API 1090 passed/1 skipped/1 warning·gateway 71 passed/1 warning와 별도로 해당 계약을 좁혀 실행한 확정 증거다. 같은 묶음의 테스트가 여러 요구사항 경계를 함께 검증할 수 있으므로 숫자를 요구사항별 전체 합계로 더하지 않는다.
| 항목 | 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시트 개선관리 완료본은 수식 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 |
| REQ-002·005 | app/test_protocol_registry.py app/test_persona_session_contract.py |
26 passed(경고 1); persona pin·ideation runtime clamp 계약 단독 4 passed(경고 1) |
| REQ-002 실제 DB | e2e/persona-db-lifecycle.spec.ts의 protocol lifecycle |
1 passed(10.1s) — draft 생성→RAG 활성화→폐기와 DB 정리 |
| REQ-003·005 실제 DB | e2e/persona-db-lifecycle.spec.ts의 persona lifecycle |
1 passed(14.4s), mock/skip 0 — 교수자 작성→검수 승인→catalog v1, complaint 우선 주호소·surface 미노출, 학습자 prestart, exact persona ID/version session pin, controlled 실제 SSE, UI exact reply, DB `2 |
| REQ-003 | app/test_persona_review.py |
34 passed — complaint 우선 read model과 surface 오인 방지 포함 |
| REQ-004 | app/test_feedback_policy.py 포함 직접 정책 subset, 관련 route/read-model, e2e/session-review.spec.ts desktop·mobile |
85 + 163 pytest, browser 2 passed — learner OFF 직접 API 403, 과거 OFF source가 섞인 calibration 집계의 learner-input-only redaction, deliberate-practice 원천 snapshot OFF 집계·제출 403, alliance self-scores-only, 파생 endpoint별 요청 0회, 입력·privacy 보존, 삭제 조작면 44px·overflow 0 |
| REQ-006 | app/test_pii_masking_eval.py app/test_live_coach_sources.py app/test_client_reply_quality.py |
24 passed — raw prompt와 구조화 결과의 counselor/client role masking |
| REQ-007 | session persistence/evaluation 관련 API 9 files, live/session 묶음, 실제 DB persistence E2E | 163 + 50 pytest, DB E2E 1 passed — 실패한 옛 평가 뒤 새 회기 가능 |
| 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, /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회 기록 영속→다음 회기 버튼 재노출 |
| 아바타 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를 의도적으로 공유할 때만
PLAYWRIGHT_REUSE_EXISTING_SERVER=1을 명시한다. Windows/npm 11에서는 Vite host/port를 등호형 인자로 전달한다.
REQ-005의 persona_id/persona_version API 계약과 영속 pin은 focused 테스트와 실제 DB 브라우저 lifecycle에서
모두 확인했다. 통합 실행은 승인 catalog의 exact ID/version, controlled 실제 SSE, UI 응답, DB 2턴과 cleanup을 함께 고정한다.
1.4 engine_gateway/ 테스트
| 파일 | 검증 영역 |
|---|---|
engine_gateway/test_gateway_model.py |
게이트웨이 모델 선택·공유 engine contract·SSE 요청/응답 계약(ENGINE_MODE별) |
engine_gateway/test_provider_registry.py |
Claude/Anthropic/Codex/Agy capability 정규화, 기본값, CLI 인자, 잘못된 모델·추론 강도 차단 |
1.5 로컬 DB smoke — 저항엔진 openness 곡선
저항엔진은 단위 테스트와 별도로 실제 API 경로와 Postgres 저장 상태를 함께 확인할 수 있다.
이 smoke는 dev-login으로 학습자 세션을 만들고, P1 상담 2개에 공감 발화/조언점프 발화를 각각
5턴씩 넣은 뒤 app.session_state, app.turns, app.turn_client_state를 직접 조회한다.
사전 조건:
- API가 DB 연결 상태로 떠 있어야 한다.
apps/api/.env또는 환경변수에DATABASE_URL이 있어야 한다.- API는 dev-login과 onboarding 저장이 가능한 dev 런타임이어야 한다.
# repo root
python scripts/smoke-resistance-openness-db.py \
--api-base-url http://127.0.0.1:8000 \
--timeout 240 \
--out docs/ops/resistance-openness-db-smoke-YYYY-MM-DD.json
통과 기준:
- 공감 세션은 5턴 안에
stage=탐색으로 열리고effective_openness가 0보다 커진다. - 조언점프 세션은
stage=라포,effective_openness=0.0을 유지한다. - DB의
turn_seq와app.turns저장 행 수가 API 턴 수와 일치한다.
2. 웹 타입체크 / 빌드
2.1 대상과 구성
- 작업 디렉터리:
apps/web package.json스크립트(실측):generate:api-types→ FastAPI OpenAPI export 후src/lib/api.gen.ts재생성check:api-types→ FastAPI OpenAPI export 후 생성 타입 stale 여부 확인typecheck→tsc -blint→tsc -b(현재 lint는 타입체크와 동일)build→tsc -b && vite builddev→vitee2e→playwright test
- 타입체크/빌드는 순수 정적 검사라 DB·API·브라우저가 필요 없다.
2.2 실행
# apps/web
npm install # 최초 1회 (devDependencies: typescript, vite, @playwright/test 등)
npm run check:api-types # FastAPI OpenAPI ↔ src/lib/api.gen.ts 동기화 확인
npm run typecheck # tsc -b — 타입 오류 0 확인
npm run build # cosmetic-filter namespace gate → tsc -b → vite build
API DTO를 바꿨다면 먼저 생성 산출물을 갱신한다.
# apps/web
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 (풀스택)
3.1 의존성 — 왜 풀스택인가
Playwright suite에는 Vite /api 프록시를 통해 실제 로컬 FastAPI/DB를 호출하는 full-stack
E2E와, 특정 UI/error 상태를 고정하는 route fixture UI 회귀 테스트가 함께 있다
(apps/web/e2e/README.md). DB-backed/real-API 증거는 해당 spec이 검증 대상 endpoint를
page.route().fulfill()로 대체하지 않고 실제 API 응답 또는 persisted read-model을 확인한
경우로 한정한다. 특히 공용 헬퍼 apps/web/e2e/support.ts의 fetchAvailablePersonas()는
source === "database" && !degraded 페르소나만 사용 가능으로 간주한다.
// support.ts
const usable = personas.filter((p) => p.source === "database" && !p.degraded);
expect(usable.length, ...).toBeGreaterThan(0);
즉 인메모리 degraded 페르소나로는 대부분의 E2E가 통과하지 못한다. 따라서 E2E는:
- Postgres(pgvector pg16) — 실제 DB가 떠 있어야 함
- 시드 페르소나 —
AUTO_SEED_PERSONAS=true(필요 시ALLOW_SEED_PERSONA_FALLBACK=true) - FastAPI —
127.0.0.1:8000에서 수동 기동 - Vite 웹 서버 — Playwright가 자동 기동(아래 3.3)
- Chromium —
npx playwright install chromium로 1회 설치 - 엔진 게이트웨이(9099) — 일부 테스트만 필요(3.5 참고). 레이아웃/시각 게이트는 불필요.
3.2 사전 준비
# (1) 브라우저 바이너리 설치 — 최초 1회
cd apps/web
npx playwright install chromium
# (2) DB 기동 — Docker Desktop 필요. infra/docker-compose.yml의 db 서비스
# (pgvector/pgvector:pg16). 전체 스택을 띄우려면:
# docker compose -f infra/docker-compose.yml up -d db
3.3 API 서버 기동 (시드 포함)
PowerShell:
# apps/api
$env:AUTH_DEV_LOGIN_ENABLED = "true" # dev-login 허용 (support.ts가 사용)
$env:ENVIRONMENT = "dev"
$env:AUTO_SEED_PERSONAS = "true" # DB에 SEED P1~P3 적재
$env:ALLOW_SEED_PERSONA_FALLBACK = "true"
python -m uvicorn app.main:app --host 127.0.0.1 --port 8000
Bash:
cd apps/api
AUTH_DEV_LOGIN_ENABLED=true ENVIRONMENT=dev AUTO_SEED_PERSONAS=true \
ALLOW_SEED_PERSONA_FALLBACK=true \
python -m uvicorn app.main:app --host 127.0.0.1 --port 8000
.env에 동일 값을 넣어두면 매번 환경변수를 줄 필요가 없다. DB 연결이 실패하면 main.py lifespan이 인메모리 degraded로 기동되지만(/health→{"status":"degraded","db":false}), 이 상태에서는 E2E 페르소나 헬퍼가 실패하므로 E2E용으로는 반드시 DB를 붙여야 한다.
3.4 E2E 실행
웹 서버는 Playwright webServer가 자동으로 띄운다(PLAYWRIGHT_BASE_URL 미설정이고
PLAYWRIGHT_SKIP_WEB_SERVER가 없을 때, npm run dev -- --host localhost --port 5173).
# apps/web — API(8000)와 DB가 떠 있는 상태에서
npm run e2e # 전체: fixture 병렬 단계 → DB/engine 직렬 단계
npm run e2e:list # 두 단계의 수집 목록·개수만 확인
npm run e2e:parallel # desktop/mobile route-fixture, workers=4
npm run e2e:single-run # DB/engine @single-run, workers=1
npm run e2e:headed # 브라우저 표시
npm run e2e:ui # Playwright UI 모드
특정 스펙/그룹만:
npx playwright test e2e/session-layout.spec.ts
npx playwright test --project=chromium-single-run --workers=1 --grep "검색어"
npx playwright test --grep-invert "@single-run" # 병렬 시나리오만
npm run e2e:list # 실행하지 않고 단계별 목록·개수만
유용한 오버라이드(playwright.config.ts 실측):
PLAYWRIGHT_PORT=5174 npm run e2e # 웹 포트 변경
PLAYWRIGHT_BASE_URL=http://localhost:5173 npm run e2e # 외부에 이미 뜬 웹 사용(자동기동 끔)
VITE_API_BASE=http://127.0.0.1:8000 npm run e2e # 프록시 대신 API 직접 지정
3.5 프로젝트(브라우저 프로파일) 구성
playwright.config.ts는 testDir: ./e2e, timeout: 30s, expect.timeout: 5s로
다음 프로젝트를 정의한다. fixture 프로젝트는 fullyParallel:true로 실행한다. npm run e2e는
로컬 단일 API/DB/engine 자원을 보호하기 위해 desktop/mobile 단계를 workers=4로 먼저 끝내고,
chromium-single-run을 fullyParallel:false, workers=1로 두 번째 실행한다.
| 프로젝트 | 뷰포트/디바이스 | 대상 grep |
|---|---|---|
chromium-desktop |
1440×900 | @single-run·@public-auth 제외 전부 |
chromium-mobile |
Pixel 5 | @single-run·@public-auth 제외 전부 |
chromium-single-run |
1280×800 | @single-run만(별도 2단계, workers=1 직렬) |
chromium-public-auth |
1440×900 | E2E_PUBLIC_AUTH=1일 때만 활성, 공개 사이트 대상 |
- CI(
process.env.CI)에서는retries: 2,workers: 1,list+html리포터를 쓴다. - 로컬 fixture 단계의 기본 worker는 4다. 전체 러너에서만
E2E_PARALLEL_WORKERS로 조정할 수 있고, 12-worker 전면 병렬은 단일 FastAPI/DB가 포화되어 서로 무관한 요청 timeout을 만들므로 기준선으로 쓰지 않는다. npx playwright test를 직접 실행하면 로컬에서 세 프로젝트가 같은 worker pool에 섞일 수 있다. 전체 게이트는 반드시npm run e2e, focused DB/engine 게이트는 프로젝트와 worker를 명시한npx playwright test --project=chromium-single-run --workers=1 --grep "..."을 사용한다.- 기본 캡처는
testInfo.outputPath(...)로node_modules/.tmp/아래에 남긴다. 실패 시 trace(첫 재시도)·스크린샷·비디오도 같은 임시 결과 경로를 사용한다. - 날짜가 붙은
docs/ops/evidence/증거는 명시적 검토·승격이 있을 때만 이 임시 산출물에서 복사한다. 자동 보존하지 않는다. E2E_EVIDENCE_DIR를 명시하면self-directed-learning-loop캡처만 해당 디렉터리에 현재 파일명으로 export한다. 날짜·승격 의미를 자동 부여하지 않는다.
3.6 실측 테스트 개수 (현재)
2026-09-09 npx playwright test --list 기준 현재 수집 1188 tests / 66 files다
(유스케이스 16테마 uc-*.spec.ts 239 시나리오 포함).
이 숫자는 수집량이지 통과량이 아니다. 현 작업트리 전체 1109개 완주는 아직 증거가 없으며,
과거 전체 GREEN 기록과 이번 focused/release gate 결과를 구분해 적는다.
NAS-origin 112건은 전부 실 API/DB 회기가 아니다. release receipt는 session-layout·session-persistence
22건을 real_api_db_specs, 나머지 route fixture 90건을 route_fixture_specs로 분리한다.
학생 동일 계정의 홈 추천→브라우저 회기 생성→실제 SSE→review ready→G4/G5 POST·reload→0→1→1은
scripts/run-periodic-learner-e2e.py --execute가 전용 disposable engine/DB/API/Web에서만 실행한다.
이 runner는 clean HEAD/tree, 로컬 npipe Docker, loopback 동적 포트, sentinel과 exact cleanup을 강제하며
공개 8001·DB 55432·engine 9099와 NAS를 구조적으로 거부한다. 최초 세 full run은 하네스 결함, 4차는 reload 뒤
persisted episode를 UI에 hydrate하지 못하는 제품 결함을 각각 fail-closed로 검출했다. 5·6차는 핵심 동일 학습자
SSE·review·G4/G5 0→1→1을 연속 통과했지만 fixture 공유와 mobile success-state locator를 각각 검출했다. 보정 후 7차
clean aa81af29…는 same-learner SSE·review·G4/G5 0→1→1, 별도 zero-state returned desktop/mobile 4/4, route mock 0,
exact cleanup 0으로 첫 전체 GREEN receipt를 만들었다. 94666192…와 f97e7fad… run은 disposable DB init에서
멈췄고, 두 번째 receipt의 cleanup 전 로그는 detached
infra/db/init/99_app_role.sh가 CRLF라 /usr/bin/env: ‘bash\r’: No such file or directory로 exit 127이 난 것을
확정했다. Windows core.autocrlf=true에서도 shell entrypoint를 LF로 checkout하도록 .gitattributes에
*.sh text eol=lf를 뒀다. 공통 톱바 desktop/mobile 44px을 포함한 clean b02bee26…·tree 8f677bba…의
single full run은 487.3초에 GREEN이었다. 최신 receipt periodic-learner-e2e-20260812-171547.json·SHA-256
8c11a136…af8e3은 same-learner SSE·review·
G4/G5 0→1→1, returned desktop/mobile 4/4, route mock 0, 보호 runtime 접촉 0과 exact cleanup 0을 증명한다.
hourly heartbeat는 최신 GREEN이 6시간 이상 오래됐거나 material milestone이 바뀔 때만 다시 실행한다.
-
병렬 시나리오: 166 tests (desktop 83 + mobile 83)
-
@single-run직렬 시나리오: 49 tests (DB 영속화·세션 MVP·음성 성공경로·인증 시각 테마·회기말 평가 저장·교수자 명시 재평가 저장·교수자 턴 재평가 저장·교수자 UI 평가 재시도·교수자 사용자별 분석·source pack sync·이론모드 저장·동의 철회 후 voice 차단·브라우저 stream PII 마스킹·음성 transcript 저장 실패 UI 표면화 등) -
2026-08-06 Outcome & Alliance OS G0 검증:
test_measurement_contract.py+ runtime schema SSOT 11 passed, 기존 세션·평가·메모리·RBAC backend 100 passed, auth cohort 계약 39 passed, Python 생성 계약--check와 TypeScript measurement/API 계약·typecheck 통과.scripts/check-measurement-ledger.sql을 실제 PostgreSQL owner 연결에서 실행해 learner/client/evaluator 가시 행이 각각 1/1/2, 교차 view 누수 0, append-only UPDATE/DELETE guard 2건임을 확인했다. DB-backedsession-persistence의 학습자 턴→교수자 대시보드, 워크시트 저장→수정요청 검수, 종료 deep 평가→durable 교수자 리뷰 focused E2E는 각각 1/1 passed. 명시 cohort가 없는 새 DB에서도 이 증거가 재현되도록 dev-login E2E 요청만 공통e2e-hanshincohort를 고정하며 생산 OAuth/SAML·관리 사용자 cohort는 우선 보존한다. -
2026-08-06 Outcome & Alliance OS G1 검증: alliance service/calibration/routes/runtime/contract focused backend 40 passed.
scripts/smoke-alliance-pulse-api.py는 실제 DB/API/엔진에서202 → awaiting_agents → ready, 공개 전 외부 관점 비노출, terminal 3관점×3축 9개, 동일 cohort 교수자 조회, 교수자 재평정 2회의 append-only supersession, 시도별 model-run provenance를 단언한다.e2e/alliance-pulse.spec.ts는 데스크톱· 모바일 6/6, 기존session-review.spec.ts는 같은 두 프로젝트 12/12를 통과했다. prompt1.2.0합성 gold 비교는 방향 정확도 9/9, 최종 실패 0, 근거 recall **88.9%**이며 첫 시도 실패 2건의 오류·재시도 provenance도 보존했다. 비교 산출물은ops/alliance-calibration-report-prompt-1.2.0-2026-08-06.md다. -
2026-08-07 G1 실제 회기 checkpoint 보강: 최신 source API와 dev PostgreSQL에서 첫 발화 전 pre는
degraded/insufficient_transcript로 fail-closed, 첫 실제 왕복의 durable turn UUID 2개를 쓰는 mid와 DB 종료 뒤 post는ready로 통과했다. 같은 응답 안의awaiting_agents/external-event read skew를 차단하고 degraded terminal/audit DB 제약을 교정했다. post는 최초 202, 동일 payload 안정 replay, 변경 payload 409, RLS/cohort와 교수자 3축 supersession을 재확인했다. focused G1 43 passed와 Ruff 통과, 기계 판독 결과는ops/evidence/alliance-pulse-pre-mid-post-live-2026-08-07.json이다. -
2026-08-06 Outcome & Alliance OS G2 검증: contract/store/schema와 G3 통합 focused backend 90 passed, durable review UUID 회귀 37 passed.
scripts/smoke-outcome-trajectory-api.py는 실제 DB/API/엔진에서 한 case의 S1~S5 연속성, 15개 독립 축, total score 부재, 동일 제출 measurement ID 멱등성, 변경 payload 409, 같은 cohort 교수자 조회, 다른 학습자·교차 cohort 차단, 역할별 관계 메모리, 합성·비임상 고지를 단언했다.e2e/outcome-trajectory.spec.tsdesktop/mobile은 체크인 포함 12/12, G1 Alliance Pulse와 기존 Session Review 회귀는 15/15를 통과했다. 상세 metadata는ops/outcome-trajectory-live-integration-evidence-2026-08-06.md가 소유한다. -
2026-08-07 G2 악화/false-alert 실 DB 보강:
scripts/smoke-outcome-trajectory-alerts.py는 합성 교육용 두 case의 실제 10회기와 production observation producer가 쓴 30개measurement_event를 evaluator가 소비하는지 확인한다. 악화 case는 S3off_track·S4S5S5deteriorating, 단발성 noise control은 S3watch뒤 S4on_track이며 alert 승격 0건이다. case별 revision 5개·supersession 4개와 최신 observation 30개→measurement 30개 provenance, learner/teacher projection 일치, 합성·비임상 표시를 단언하고 결과 JSON에는 source score를 기록하지 않는다. G2 focused 40 passed, Ruff·py_compile·실 DB smoke 통과. 동시 G8 변경 수렴 뒤 전체 API도 855 passed를 통과했다. -
2026-08-06 Outcome & Alliance OS G3 검증: 11-case/9-type 합성 benchmark의 rupture type macro-F1 1.0, repair status accuracy 1.0, critical miss·judge gaming·memorized phrase false resolution 0. Scenario Director와 runtime/store/session focused 107 passed, G0
G8 계약·store·runtime 묶음 251 passed. 실제 DB/API 회기 smoke는 durable turn UUID 2개, 독립 내부 토큰 write, 동일 payload 멱등 ID, 변경 payload 409, learner/cohort RLS, 교수자 append-only correction과 최종 resolved를 확인했다. G1G3 및 기존 리뷰 desktop/mobile 회귀는 50/50 passed. 상세는ops/rupture-repair-live-integration-evidence-2026-08-06.md가 소유한다. -
2026-08-07 G3 자동 runtime 보강:
scripts/smoke-rupture-runtime-auto.py는 외부 client/evaluator만 결정론 seam으로 교체하고 실제sessions.submit_turn·공용finalize_completed_turnhook을 호출한다. rupture 저장 endpoint를 호출하지 않은 채 로컬 PostgreSQL에 missed/partial/resolved 3 episode, observation 10개, reconciliation 3개, structured learner evaluation 5개, 세션 밖 evidence 참조 0건, 세션별 fast/deep model-run 2개를 단언하며 learner/teacher read projection도 검증한다. focused G3 runtime/store/core/scenario는 70 passed다. 재현 JSON은ops/evidence/rupture-runtime-auto-live-2026-08-07.json이다. 이 실 DB 실행은 텍스트 learner-turn transport를 증명하며 SSE·음성 개별 transport live smoke까지 증명했다고 확장하지 않는다. -
2026-08-06 Outcome & Alliance OS G4 검증: deliberate-practice core/store/API focused 54 passed, 5개 연습 모드와 약점·망각 우선 선택, 익숙한 성공/자기 성공 주장만으로 mastery를 열지 않는 전이 게이트를 고정했다. 실제 DB/API 회기 smoke는 durable turn UUID 2개를 prescription/attempt evidence로 사용하고 내부 토큰 처방 write, 동일 제출 안정 ID, 변경 payload 409, learner/cohort RLS, 교수자 append-only correction, 총점·XP 없음,
clinical_claim_allowed=false를 확인했다. G4 desktop/mobile은 8/8, G1~G4와 기존 회기 리뷰 통합 회귀는 58/58 passed. 상세는ops/deliberate-practice-live-integration-evidence-2026-08-06.md가 소유한다. -
2026-08-06 Outcome & Alliance OS G5 검증: calibration-transfer core/store/API focused 41 passed, 4-case benchmark expectation accuracy 1.0, 잠금 뒤 오염 거부 1, 반복 보정 MAE 0.667→0.267, unseen varied transfer 4/4, 암기 문구 false verification 0, 합성 subgroup drift gap 0.667을 확인했다. 실제 DB/API smoke는 durable turn prediction revision→불가역 lock→독립 runtime observation reveal, 동일 제출 안정 ID·변경 payload 409, post-lock revision 422, learner/supervisor projection과 교차 역할/cohort 차단을 검증했다. G5 learner/teacher desktop/mobile 4/4, G1~G5+기존 리뷰 62/62, typecheck·build 통과. 상세는
ops/calibration-transfer-live-integration-evidence-2026-08-06.md가 소유한다. -
2026-08-07 G4/G5 production caller 실 DB 검증:
scripts/smoke-session-learning-producer.py는 실제 종료 회기·durable turn UUID 2개·ready session evaluation을 만들고session_learning_producer만으로 G4/G5 파생 원장을 생성한다. 잠금 전 G4 submission/card/prescription/snapshot/decision 각 1개와 G5 observation 0개, prediction lock callback 뒤 failed 독립 observation·turn/session provenance model-run 각 1개를 확인했다. 재실행은 G4 deterministic submission/card replay이며 G5·model-run 중복 0이다. episode/attempt, mastery/pass, calibration assessment, transfer suite/trial/assessment는 모두 0이다. learner/teacher projection 일치와clinical_claim_allowed=false도 단언한다. focused 100 passed, 전체 API 855 passed, Ruff·py_compile·실 PostgreSQL smoke 통과. 상세는ops/outcome-os-g4-g5-production-caller-evidence-2026-08-07.md가 소유한다. -
2026-08-07 학생 자기주도 전체 루프 UI 검증:
self-directed-learning-loop.spec.ts는 실제src화면에서 학습 홈 추천→목표 선택→텍스트/SSE 회기→종료·리뷰→G4 처방 CTA→새 회기 사전 설정·시작을 하나의 상태 흐름으로 묶고 desktop/mobile 2/2를 통과했다. 미소유/api/**는 전부 fixture 404이고auth/me·세션·리뷰·practice read model도 route fixture이므로 실제 로그인/API/DB 증거가 아니다. 390×844·320×568 활성 회기를 포함한 9장 시각 QA, 내부 criterion/counterevidence/UUID 비노출, 홈 CTA 44px·4.5:1 대비와 source API/DB 증거의 분리 기준은ops/outcome-os-self-directed-learning-loop-ui-evidence-2026-08-07.md가 소유한다. -
2026-08-06 Outcome & Alliance OS G6 검증: supervision/research core/store/API/runtime-schema focused 28 passed, 6-signal attention queue 순서 정확, item별 drilldown 1/상한 3, metadata-only calibration dataset raw transcript row 0, version drift
drift_flagged, Phase 3 네 도메인 manifest를 고정했다. 실제 DB/API smoke는 두 개의 durable practice attempt UUID를 원장 pointer로 사용해 내부 토큰, 동일 제출 안정 ID, 변경 payload 409, supervisor/research AI-view 분리, teacher/learner/cross-cohort RLS, teacher audit를 검증했다. role-safe research read model은 model/prompt/instrument version, subgroup metrics, artifact provenance를 hydrate한다. G6 desktop/mobile 6/6, typecheck·build 통과. 상세는ops/supervision-research-live-integration-evidence-2026-08-06.md가 소유한다. -
2026-08-07 G6 scheduled producer 보강: G0~G5 원장 기반 파생과 repo-approved synthetic version evaluator focused 37 passed. 실제 dev DB에서 6 cohort cycle 실패 0, measurement anchor 12개, baseline/candidate batch 2·drift report 1·subgroup 2, replay 중복 0, evaluator/prompt/instrument provenance hydrate를 확인했다. 원문 transcript와 임상 주장은 0이다. 상세는
ops/outcome-os-g6-ledger-producer-evidence-2026-08-07.md와ops/outcome-os-g6-version-comparison-producer-evidence-2026-08-07.md가 소유한다. -
2026-08-06→07 Outcome & Alliance OS G7 검증: 동의→단일 오디오 시계→독립 text/voice/fusion→철회· tombstone의 실제 HTTP/DB 경계와 역할 안전 UI를 통과했다. 공개 배포 뒤 G7 API route, TLS 1.3 WSS handshake와 비인증 1008 차단을 확인했다. Deepgram streaming adapter/interim/final/word timestamp, HMAC word pseudonym, 1초 동의 재검사, 음성 recovery UX와
readyprovider/model 계약은 code/internal 완료다. public preflight는 마이크 없이 authenticated ready/ping과 기대 STT/TTS provider/model 네 값만 검사한다. 실제 Deepgram key·quota live, 명시 동의 물리 마이크, authenticated public ready, 실행된 50분 soak와 운영 TTS 상업 이용권 증거는 외부 게이트이며 synthetic/preflight 증거로 대체하지 않는다. -
2026-08-06→07 Outcome & Alliance OS G8 검증: source→독립 red-team→benchmark→human gate, baseline/threshold/provenance/rollback 4종 증거, lifecycle과 incident DAG를 실제 API/DB에서 통과했다. clean release backend 362 passed, 회기·Outcome desktop/mobile 126 passed / 2 skipped, layout visual gate 15/15, typecheck·API contract·build를 통과한 뒤 API 8001과 Cloudflare Pages production에 배포했다. 공개 OpenAPI 119 paths에서 G1~G8 route, prod/db/engine health, auth 401, Google OAuth 302+state cookie, custom-domain 새 asset과 브라우저 로그인 렌더를 확인했다. 상세는
ops/outcome-os-public-deployment-evidence-2026-08-07.md가 소유한다. -
2026-08-07 Outcome & Alliance OS 배포 프리플라이트: Compose profile과 별도로
direct-runtimeprofile을 추가했다.scripts/provision-outcome-os-runtime-secrets.py는 G3~G8 토큰 6개만 원자적으로 생성·회전하고 값을 출력하지 않는다. 운영apps/api/.env에서 필수키와DATABASE_URL,current_user=vignettenon-owner app-role 검사를 통과했다. 상세는ops/outcome-os-deploy-preflight-evidence-2026-08-07.md가 소유한다. -
2026-08-07 G7 로컬
/voice/wsshort soak: loopback Uvicorn의 실제 FastAPI WebSocket transport에서 8.015초·83/83 연결, 성공 발화 79, 정상/인증 거부/provider degraded close, 같은 socket 철회와 재연결 차단, 4KiB 실행 한정 overflow 뒤 복구를 확인했다. 관련 42 tests와 API/gateway 전체 805 passed. 외부 provider·실DB·물리 마이크·public WSS는 사용하지 않았고 50분 옵션은 제공만 했으므로 B2는 유지한다. 상세는ops/outcome-os-g7-wss-soak-evidence-2026-08-07.md가 소유한다. -
2026-08-07 G7 후속 hardening: voice service/WS/G7 store·API focused 73 passed, public runner 12 passed.
scripts/soak-public-voice-websocket.py --preflight-only는 auth cookie·소유 회기와 아래 네 기대값을 필수로 받고 WSS ready/ping까지만 수행한다. 마이크 장치 열거·캡처가 없으므로 물리 음성 증거로 판정하지 않는다.C:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe -X utf8 ` scripts\soak-public-voice-websocket.py --preflight-only ` --expected-stt-provider local_whisper --expected-stt-model small ` --expected-tts-provider melotts --expected-tts-model melotts-korean50분 유효 공통창 full soak는 같은 기대값에
--duration-seconds 3120,--microphone-device <명시-장치>,--confirm-physical-capture를 추가하고 사용자가 실행 직전에 물리 캡처에 명시 동의한 경우에만 실행한다. -
2026-08-07 G7 external-proof readiness: Session 첫 음성 사용은 서버 30일·원음 미보존 consent ledger가 성공하기 전
getUserMedia와 voice WebSocket을 시작하지 않는다. controlled provider와 synthetic stream을 사용한voice-success.spec.tssingle-run 2/2에서 원장 지연 중 둘 다 0회, 성공 뒤 각각 정확히 1회, 지연 권한 뒤 pause 시 track stop·WS 0을 확인했다. API voice/G7 focused 92 passed, external evidence runner/checker와 release gate contract 31 passed, typecheck·API type contract·Ruff·py_compile을 통과했다.soak-public-voice-websocket.pyv4,/admin/voice-runtime,capture-g7-runtime-evidence.py,capture-g7-topology-evidence.py, independent human-held-out evaluator를check-g7-external-proof.py가 같은 public transport host·겹치는 50분 시간창으로 묶는다. 실제 마이크·운영 local provider·실제 참가자 evidence는 없으므로 G7은 external GATE다. 상세는ops/outcome-os-g7-external-proof-readiness-2026-08-07.md가 소유한다. -
2026-08-09 G7 external-proof P0: production runner exit를 canonical checker 0·
gate_closed=true에 묶고, browser Origin allowlist와 API/WSS/admin/topology host·scheme을 분리했다. capture 최소 3,120초와 세 runtime artifact 공통 3,000초, detached-clean commit/tree·runner/collector/checker SHA·psutil==6.1.1, fresh API/cloudflared PID provenance receipt를 fail-closed로 고정했다. runner/checker/topology 86/86, launcher/sidecar 80/80, G7 통합 166/166, API 921, gateway 58을 통과했다. 이 수치는 실제 물리 마이크·human pack을 대체하지 않으므로 G7은 external GATE다. -
2026-08-09 G7 clean public promotion·rehearsal: detached-clean commit
b34623f3…·tree32cc85c7…에서 API/cloudflared를 fresh 승격하고 receipt9f8d1941…a21bf를 발행했다. 공개 health는status=ok·db=true·engine=true, OpenAPI 126과/admin/voice-runtime,local_whisper/small·melotts/melotts-korean·WS queue 4를 확인했다. watchdog·로그온 task도 같은 stable root/commit/tree/script SHA에 pin해 명시 실행 결과 0·Ready를 확인했다. Python 기본 User-Agent가 Cloudflare에서 403이 되는 경계는 browser-compatible header와 focused 4/4로 고정했다.--rehearse30초는 인증 WSS ready/ping/close1000, runtime 7 samples, Windows topology 7 samples를 모두 통과했고physical_capture=false, UUID/email literal 0이다. current G7 six-suite는 87/87, runtime sampler는 4/4다. 이 리허설은 실제 마이크 3,120초·공통 3,000초 high-water·독립 human pack·canonical checker exit 0을 대체하지 않는다. -
2026-08-09 G7 human pack preflight: production runner는 캡처 전에 category·categorical κ≥0.70, preregistration 선행, 50회기/150축 complete pairing, 양 조건 결측 ITT를 검증한다. 독립 검수는
python -X utf8 -B scripts/check-g7-human-voice-gain.py --input <pack.json>이며--print-schema도 지원한다. 부적합 pack은 마이크를 열기 전에 exit 2다.prepare-g7-human-voice-gain-intake.py는 non-evidence 빈 템플릿과 strict CSV compiler를 제공하며 production gate 통과 전에는 pack을 쓰지 않는다. API evaluator 11/11 + G7 script 86/86 + intake compiler 6/6, 합계 103/103을 통과했다. 실제 사람 데이터가 없으므로 external GATE는 열려 있다. -
2026-08-12 current G7 재검증: runtime/checker 6파일 94/94, intake 6/6, standalone validator 4/4로 총 104/104를 통과했다. 빈 템플릿은
template_is_evidence=false, compile exit 1,pack_written=false이고 임시 intake와 pack 잔여는 0이었다. 마이크·공개 세션·운영 DB는 사용하지 않았다. -
2026-08-07 G7/G8 시각 QA: 데스크톱·Pixel 5에서 focused 16/16, typecheck·build·cosmetic-filter를 통과했다. G8 승인 차단 이유를
title의존에서 상시 문구·aria-describedby·semantic form/Enter 제출로 바꾸고, G7 이벤트 최소 24×24px와 시간축 키보드 포커스를 수치 회귀로 고정했다. 상세는ops/outcome-os-g7-g8-visual-qa-2026-08-07.md가 소유한다. -
2026-08-07 G0~G8 release-only assembly: dirty 257파일을 related 235/mixed 4/excluded 18로 전건 분류했다.
api.gen.ts는 clean HEAD + related API 소스에서 공식 재생성하고 Windows checkout에서도 선언 해시를 다시 확인했다. 최초 공개 승격 patch는 229 files, SHA-256d9ac3c85...34d41로 두 번 동일하게 조립돼 clean HEAD/canonical index 적용 검사를 통과했다. 최종 SSOT 반영 뒤 다시 결정적 조립·검사한다. -
2026-07-15 전체 검증:
npm run e2e:parallel166/166 passed(2.5분),npm run e2e:single-run49/49 passed(23.2분). 합계 215/215 passed. AI 튜터 DB 영속화는 정상 provider 응답이 2분을 넘을 수 있어 해당 테스트만 같은 실엔진 묶음의 장시간 기준인 240초를 사용한다. -
e2e/voice-success.spec.ts는 직접/voice/ws캐스케이드, Session 텍스트 턴의POST /voice/speech, Session 마이크 UI를 함께 검증한다. 브라우저<audio>.play()가 차단된 조건에서도 Web Audio buffer source 재생이 시작되는지와 Session 마이크 UI가audio_end에 browser voice activity/silence 메타를 싣는지 확인한다. -
2026-07-30 세션 음성/지연 focused 검증: 실제 로컬 P1 브라우저에서 응답 생성 중 textarea가 enabled이고 다음 질문 초안이 보존되며, 음성 준비/재생 중에도 보내기가 enabled인 것을 확인했다. 이전 full API 2턴은 fast-loop evaluator를 직렬로 기다려 완료가 24.17/24.57초였지만, 사후 평가 분리 뒤 실제 UI 응답·전송 해제는 첫 턴 9.58초, 상주 세션 연속 턴 6.96초였다.
test_session_turn_persistence.py는 느린 평가 훅이 끝나기 전에 SSEdone이 반환되고 이후 normalized 평가가 learner turn에 붙는 계약을 고정한다. Higgs API smoke는higgs-audio-v3-tts-4b, 24kHz mono WAV 362,924 bytes/7.56초, provider/model 헤더와 synthetic-seed-only reference policy를 확인했다. 검증: backend 432 passed, gateway 44 passed, typecheck/API types/build, session text stream 1 passed, voice skip/draft 1 passed. -
2026-07-13 focused 검증:
PLAYWRIGHT_PORT=5269 npx playwright test e2e/voice-success.spec.ts --project=chromium-single-run --workers=1 --grep "drives one voice turn"1 passed. 같은 로그인에서 텍스트 턴 저장→소유 회기/턴 기반/voice/speech→OpenAI speech 요청→Web Audio buffer 재생 시작을 확인하고, 별도 새 회기에서 마이크 PCM/STT→AI reply→TTS chunk/tts_end경로가 유지되는지 검증한다. 백엔드 voice focused는 32 passed이며, 운영 키 직접 smoke는gpt-4o-mini-tts가 98,133-byte MP3(11.68초)를 반환했다. -
2026-07-01 focused 검증:
PLAYWRIGHT_PORT=5205 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=17 passed. 실제 브라우저openSessionStream()→/sessions/{id}/stream→ DB-backed/review축어록 저장 경로, AI 튜터 코칭 이력 저장/재로딩, WebSocketstt_result음성 비언어 메타데이터, Session 마이크 UI가 생성한 voice activity/silence 메타, Phase 3 pre/post 점수의 DB-backed 저장/재조회, 그리고 세션 종료 background deep 평가가 durable DB row로 저장되어 교수자 리뷰가평가 완료로 전환되는지 검증한다. AI 튜터 코칭 이력 검증은POST /live-coach응답과 DB-backed history payload의status=ready,latency_ms>0도 확인해 규칙 기반degradedfallback 200 응답이 정상 AI 코칭으로 통과하지 못하게 한다. -
2026-07-01 추가 DB-backed 검증:
PLAYWRIGHT_PORT=5238 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "finishes session end evaluation"1 passed. 같은 자동 종료 평가 row가/teacher/dashboard의recent_sessions에서도evaluation_status=ready,review_ready=true,supervisor_state=평가 완료로 반영되는지 실제 DB/API/엔진으로 검증한다. -
2026-07-01 추가 DB-backed 검증:
PLAYWRIGHT_PORT=5237 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "explicit teacher session reevaluation"1 passed. 실제 DB/API/엔진에서 학습자 세션과 턴을 만든 뒤 교수자POST /eval/sessions/{id}/reevaluate가 durableapp.session_evaluationrow를 저장하고,/eval/.../evaluation및/review가평가 완료로 반영되는지 검증한다. -
2026-07-01 추가 DB-backed 검증:
PLAYWRIGHT_PORT=5246 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "teacher turn reevaluation"1 passed. 실제 DB/API/엔진에서 학습자 세션과 턴을 만든 뒤 교수자POST /eval/sessions/{id}/turn이 기존 turn evaluation normalized row를 durable 교체하고,/review의 learner turntechniques가 재평가 응답과 같은 라벨로 hydrate되는지 검증한다. -
2026-07-01 추가 DB-backed UI 검증:
PLAYWRIGHT_PORT=5245 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "manual AI evaluation retry"1 passed. admin engine config를 잠깐 실패 endpoint로 바꿔 실제app.session_evaluation실패 row를 만든 뒤 원복하고, 교수자 리뷰 UI의AI 평가 재시도버튼 클릭이 실제POST /eval/sessions/{id}/reevaluate→ durable ready row →/review평가 완료→/teacher/dashboardevaluation_status=ready까지 이어지는지 검증한다. -
2026-07-02 focused 검증:
PLAYWRIGHT_PORT=5255 npx playwright test e2e/kb-source-packs.spec.ts --project=chromium-single-run --workers=11 passed. 관리자 전용POST /kb/live-coach/source-packs/sync가 인증 없이 401, learner 403, admin 202로 동작하고, 0615 워크북·DSM·공식 상담 지침·자살위험 지침 source pack을 DB-backed evaluator RAG에 sync한 뒤 source-scoped/kb/eval-grounding이 503 skip 없이 200을 반환하고 같은source_id만 반환하는지 검증한다. -
2026-07-02 focused 검증:
PLAYWRIGHT_PORT=5259 npx playwright test e2e/session-mvp.spec.ts --project=chromium-desktop --workers=1 --grep "AI tutor|stale empty AI tutor quota|quota exhaustion|degraded AI tutor|voice conversation stop"7 passed. route-fixture UI에서 AI 튜터의 stale empty quota가 서버GET /live-coach재조회 뒤 실제 잔여 기회로 복구되는지, 최신 quota exhaustion 오류가 이전 코칭 카드에 가려지지 않는지, 음성reply.conversation_stopped가 빈 client reply 대신 109 안전 게이트와 socket close를 유지하고 live-coach를 호출하지 않는지 고정한다. 이 fixture들은 UI 회귀 증거이며 DB-backed 엔진 성공 증거가 아니다. -
2026-07-03 backend focused 검증:
C:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe -X utf8 -m pytest -p no:cacheprovider apps/api/app/test_session_turn_persistence.py -k "live_coach" -q7 passed. live coach LLM 생성은 성공했지만audit.llm_call_log저장 확인이 실패한 경우status=readyAI 코칭으로 보이지 않고 규칙 기반status=degraded코칭으로 내려가는지 검증한다. 기존 DB-backed event 저장 실패 503 회귀와 source metadata/degraded fallback 회귀도 같은 focused 묶음에서 유지한다. -
2026-07-01 focused 검증:
PLAYWRIGHT_PORT=5251 npx playwright test e2e/session-mvp.spec.ts --project=chromium-desktop --workers=1 --grep "AI tutor|runtime AI tutor"4 passed. mock MVP 코칭 카드에서근거 보기모달이 source title,source_packkind,2026-06-15version, citation을 표시하는지 검증하고,status=degradedroute fixture는 카드·근거 모달·이력 모달·턴C마커가대체 코칭/AI 응답 대체를 노출하는지 고정한다. 추가 route fixture는GET /live-coach실패가 빈 이력으로 보이지 않고 alert로 표시되는지,persistence_source/source=runtime이 임시 저장 경고로 표시되는지도 고정한다. 이 fixture들은 UI 회귀 증거이며 DB-backed 엔진 성공 증거가 아니다. -
2026-07-01 focused 검증:
PLAYWRIGHT_PORT=5252 npx playwright test e2e/session-review.spec.ts --project=chromium-desktop --workers=1 --grep "pre/post"1 passed. 기존 저장 pre/post 점수를 비운 입력이저장된 값 기준으로 오인되지 않고 invalid 상태, alert, 저장 버튼 disabled로 표면화되는지 검증한다. -
2026-07-01 focused 검증:
PLAYWRIGHT_PORT=5248 npx playwright test e2e/session-mvp.spec.ts --project=chromium-single-run --workers=1 --grep "pending voice transcript"1 passed. mock WebSocket이transcript final뒤turn_persistence_unavailableerror를 보내면 Session UI가 임시 학습자 발화를 정상 턴으로 확정하지 않고저장 실패배지와 alert로 표면화하며, 대기 중 내담자 말풍선을 제거하고 텍스트 입력을 복구하는지 검증한다. -
2026-07-01 추가 DB-backed 검증:
PLAYWRIGHT_PORT=5241 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "AI tutor coaching history"1 passed. 실제 DB/API/엔진 경로에서 AI 튜터 응답이status=ready,latency_ms>0로 저장되고, 0615 source pack metadata가근거 보기모달, DB-backed/live-coach이력, reload 후C마커 history dialog에 유지되는지 확인한다. -
2026-07-01 fixture UI focused 검증:
PLAYWRIGHT_PORT=5229 npx playwright test e2e/session-review.spec.ts --project=chromium-desktop --workers=1 --grep "retry a failed AI session evaluation|retry fails"2 passed. 교수자 리뷰의평가 실패상태에서AI 평가 재시도버튼이 보이고, 재시도 성공 시 완료로 바뀌며 재시도 실패 시 기존 실패 리뷰와 새 실패 사유가 유지되는지 검증한다. -
2026-07-01 추가 fixture UI/mixed focused 검증:
PLAYWRIGHT_PORT=5236 npx playwright test e2e/session-review.spec.ts e2e/teacher.spec.ts --project=chromium-desktop --workers=1 --grep "manual AI retry|retry a failed AI session evaluation|retry fails|failed AI session evaluation"4 passed. 평가가 아직평가 대기로 자동 polling 중일 때는 수동AI 평가 재시도버튼을 숨기고,평가 실패일 때만 재시도 버튼을 노출하며, 교수자 pending queue에서도하린/P6종료 회기의평가 실패AI 상태가 수동검토 대기상태에 묻히지 않는지 검증한다. -
2026-07-01 추가 fixture UI focused 검증:
PLAYWRIGHT_PORT=5253 npx playwright test e2e/session-review.spec.ts --project=chromium-desktop --workers=1 --grep "long-running session evaluation"1 passed. 긴 회기 평가가 기존 짧은 polling window를 넘겨 늦게 ready가 되어도 리뷰 화면이평가 대기에 고착되지 않고평가 완료리뷰를 반영하는지 검증한다. -
2026-07-02 추가 focused 검증:
C:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe -X utf8 -m pytest -p no:cacheprovider apps/api/app/test_teacher_dashboard.py apps/api/app/test_session_turn_persistence.py -k "teacher_cannot_close_review_before_ai_session_evaluation_ready or teacher_can_mark_session_review_closed_with_note or end_session_does_not_reschedule_evaluation_for_already_ended_session or session_evaluation or stale_missing" -q8 passed +PLAYWRIGHT_PORT=5254 npx playwright test e2e/session-review.spec.ts --project=chromium-desktop --workers=1 --grep "manual AI retry|long-running session evaluation|retry a failed AI session evaluation|retry fails"4 passed. 오래된 missing session evaluation이 교수자 대시보드에서도평가 실패로 보이고, 이미 종료된 세션의 중복/end가 평가를 재예약하지 않으며, AI 평가가 ready가 아니면 교수자 리뷰를검토 완료로 닫아 큐에서 숨길 수 없도록 검증한다. -
2026-07-01 focused 검증:
PLAYWRIGHT_PORT=5229 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "selected CBT theory mode"1 passed. 실제 Session UI에서CBT이론모드를 선택해 시작한 회기가POST /sessionspayload와 DB-backedGET /sessions/{id}상세의theory_mode=cbt로 보존되는지 검증한다. -
2026-07-01 focused 검증:
PLAYWRIGHT_PORT=5231 npx playwright test e2e/voice.spec.ts --project=chromium-single-run --workers=1 --grep "consent withdrawal"1 passed. 동의 철회 뒤 이미 생성된session_id로/api/voice/ws를 열어도consent_required로 닫히는지 실제 브라우저 WebSocket으로 검증한다. -
2026-07-01 focused 검증:
py -3.11 -X utf8 -m pytest -p no:cacheprovider app/test_dataset_export.py app/test_phase3_artifact_checker.py -q16 passed. X1 exporter가 consented/client-visibletext_masked턴만 고르고 rawsc.text를 선택하지 않는지, supervisor comment raw text를 JSONL에서 제거하는지, approved/dry-run JSONL shape·row count·privacy·PII gate가{}false-positive를 막는지 검증한다. -
2026-07-01 focused 검증:
py -3.11 -X utf8 -m pytest -p no:cacheprovider app/test_evaluation_persistence.py app/test_session_turn_persistence.py app/test_live_coach_privacy.py -q50 passed. H4 live coach prompt가 recent turns와 fast-loop evaluation dict의 문자열 leaf를 다시 마스킹하고, turn evaluation persistence가 rationale/comment/alternative utterance의 raw 이름·기관·전화번호를 DB insert 전 제거하며, legacy DB row의 빈text_maskedfallback과 session-end deep evaluation payload/read-model도 raw PII를 재마스킹하는지 검증한다. 같은 라운드에서test_evaluation_persistence.py를IsolatedAsyncioTestCase로 바꿔 async persistence 테스트가 실제 await되도록 고정했다. -
2026-07-01 추가 backend focused 검증:
py -3.11 -X utf8 -m pytest -p no:cacheprovider app/test_session_turn_persistence.py -k "fast_loop_evaluation" -q2 passed +py -3.11 -X utf8 -m pytest -p no:cacheprovider app/test_learner_dashboard.py -k "failed_fast_loop" -q1 passed. fast-loop 평가 훅 예외는 상담 턴 저장을 막지 않되evaluation.error와 리뷰의턴 평가 실패노트로 표면화하고, evaluator가 반환한 error dict는 성장 점수·최근 피드백에서 제외해 실패가 neutral 0.5로 집계되지 않게 검증한다. -
2026-07-01 추가 backend focused 검증:
py -3.11 -X utf8 -m pytest -p no:cacheprovider app/test_eval_routes.py app/test_evaluation_persistence.py -q29 passed. 단일 턴reevaluate_turn이 결과를 응답 바디에만 두지 않고feedback_scores/turn_technique/turn_client_state/supervisor_comment/alternative_utterancerow를 교체 저장하며, 저장 실패는 503, 평가 error는 저장 후 502/503으로 표면화하는지 검증한다. normalized evaluation table의 delete RLS policy도 함께 고정한다. -
2026-07-01 추가 backend focused 검증:
py -3.11 -X utf8 -m pytest -p no:cacheprovider app/test_session_turn_persistence.py -k "session_evaluation or stale_missing" -q3 passed. 오래된 종료 회기가app.session_evaluationrow 없이 무한평가 대기로 남지 않고, timeout+grace 이후 교수자 read-model에서평가 실패와AI 평가 재시도가 필요합니다사유로 표면화되는지 검증한다. -
2026-07-01 추가 backend focused 검증:
py -3.11 -X utf8 -m pytest -p no:cacheprovider app/test_notifications.py app/test_session_turn_persistence.py app/test_evaluation_persistence.py -k "session_evaluation or missing_session_evaluation or scheduled_session_evaluation" -q16 passed. startup recovery가 종료됐지만 evaluation row가 없는 오래된 DB 세션을 evaluator AI context로 찾아 기존 session-end 평가를 재예약하고, 같은 session_id 중복 background 평가를 process-local in-flight set으로 막는지 검증한다. -
2026-07-01 focused 검증:
PLAYWRIGHT_PORT=5228 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "case worksheet|crisis safety"2 passed. C1 학습자 워크시트 저장→교수자 수정요청 검수와 C2 위기 신호→app.safety_events→DB-backed 교수자 안전 알림 큐를 실제 브라우저/API/DB로 검증한다. -
2026-07-01 추가 DB-backed 검증:
PLAYWRIGHT_PORT=5244 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "Korean PII|crisis safety event"2 passed. H4는 실제 Session UI stream으로 한국어 이름·기관·전화번호가 포함된 학습자 발화를 보내고, DB-backedGET /sessions/{id}상세와/review모두 raw 값 없이[NAME]/[ORG]/[PHONE]으로 마스킹되는지 검증한다. C2는 위기 발화가 교수자 검토용 learner turn으로 저장되지만 client AI 응답 turn은 저장되지 않고, DB-backed 교수자 안전 큐의 109 알림이 유지되는지 확인한다. -
2026-07-01 focused 검증:
PLAYWRIGHT_PORT=5193 npx playwright test e2e/session-mvp.spec.ts --project=chromium-single-run --workers=12 passed. MVP 종료/리뷰 흐름과 위기 안전 게이트가 빈 내담자 응답 신호에 덮이지 않는지 검증한다. -
2026-07-01 focused 검증:
PLAYWRIGHT_PORT=5215 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=19 passed. 교수자 콘솔의 페르소나 검수, 검토 큐, 최근 회기 진입과 별도/teach/analysis학생 분석 메뉴, 학습자 검색 테이블, 행 펼침, 상세 드릴다운, 전체 회기 탭 전환을 검증한다. -
2026-07-01 focused 검증:
PLAYWRIGHT_BASE_URL=http://localhost:5210 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --grep "shows selected learner analysis"1 passed +PLAYWRIGHT_BASE_URL=http://localhost:5210 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --grep "professor (student analysis overview|learner detail analysis)"2 passed. 실제 5210 로컬 스택에서 학생 분석 목록의 사용자 표시명, 상세 기본 페르소나별 회기 탭, 페르소나 행 펼침, 전체 회기 탭 전환, 7개 폭 레이아웃 containment를 검증한다. -
2026-07-01 focused 검증:
C:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe -X utf8 -m pytest -p no:cacheprovider app/test_teacher_dashboard.py -q10 passed. 교수자 대시보드 API는session_persistence.list_all_sessions()로 전체 담당 세션을 읽고, 학습자 일반 목록은list_recent_sessions()로 최근 100개 제한을 명시해 한 학생의 최신 회기가 다른 학생 분석 목록을 밀어내지 않는지 검증한다. 또한 ended session summary가app.session_evaluation의evaluation_status,review_ready,supervisor_state,evaluation_error를 별도로 싣고, 평가 실패가 수동 review status와 섞이지 않는지 검증한다. -
2026-07-01 focused 검증:
npx playwright test e2e/admin.spec.ts --project=chromium-desktop --project=chromium-mobile --workers=1 --grep "approved admin without onboarding"2 passed. 승인된role=admin사용자의 세션에admin_access=false,onboarding_completed_at=null이 남아도/admin이/onboarding으로 우회하지 않고 관리 콘솔의/admin/*API를 호출하는지 fixture로 고정한다. -
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=line1/1,npx playwright test e2e/session-layout.spec.ts e2e/learner.spec.ts e2e/session-review.spec.ts --project=chromium-desktop --project=chromium-mobile46/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-layout8/8, 7폭layout-visual-gate15/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 -q66 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 production11b3e11f에 배포했다. 인증된 공개/admin/usage?window_days=30은source=database,durable=true,gemini-3.6-flash-high5호출·입력 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-high21회·입력 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-onlyaccount_approval알림 키를 canonical 설정에 합치고 원본 설정 행은 보존한다. 백업 복제 DB에서 alias 2·세션 21·턴 64·평가 6·활성 source session 0·고아 0으로 통과 후 복제 DB를 삭제했고, 활성 DB도 사용자 1175·회기 469·평가 65·고아 0을 유지했다. 공개 API fresh provenance는 commitdba9b75a…·tree14cd4607…, health DB/engine true, voice exact, OpenAPI 129, auth 401이며 boot/watchdog 실제 실행 결과는 0/0이다. Pages productionef48c0ae…의 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건을 표시했다. 소유자가 데이터 가시성을 수락했으며, 최신 Pages0c60261e…의 단일 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회표시를 확인하고 임시 세션을 폐기했다.
레이아웃·시각 회귀 게이트(핵심 합격선):
| 게이트 | 스펙 | 구성 | 개수 |
|---|---|---|---|
| 세션 레이아웃 | e2e/session-layout.spec.ts |
4 테스트 × (desktop+mobile) | 8 / 8 |
| 시각 레이아웃 게이트 | e2e/layout-visual-gate.spec.ts |
@single-run, 15개 화면 계약 × 7개 폭 검사 + 다크 테마 assertion + 학습 대시보드 라이트 자산 연결 |
15 / 15 |
| 인증 테마 게이트 | e2e/auth-visual.spec.ts |
로그인·온보딩 × light/dark × 390/1280px, 100vw/100dvh 및 overflow 검사 | 1 / 1 |
| 레이아웃 포커스(재설계 화면) | session-layout·session-review·admin·learner·settings·teacher, @single-run 제외 |
desktop+mobile, 격리 API/controlled gateway | 106 / 106 |
layout-visual-gate는 7개 폭(390/720/861/900/1024/1280/1440)에서 15개 핵심 화면 계약(페르소나 운영·작성 단계 포함)의 가로 오버플로·잘린 컨트롤·다크 테마 적용을 검사하고 전체 페이지 스크린샷을node_modules/.tmp/layout-gate/에 남긴다. 학습 대시보드는 추가로 라이트 테마의 카드·사이드바· 우상단 배경 자산이 실제 computed style에 연결됐는지 확인하고 1200px·390px 라이트 캡처를 남긴 뒤 다크로 복귀한다.session-layout은 회기 전/활성 화면이 뷰포트를 벗어나지 않는지, 우측 패널이 코어 영역을 침범하지 않는지, 시작 후 실제session_idURL에서 새로고침해도 활성 회기 상세가 유지되는지, 스트림 실패 시 미저장 전사가 남지 않는지를 검증한다. 2026-08-28 최종 재실행은layout-visual-gate15/15, 위 레이아웃 포커스 106/106,session-layout8/8을 실패·skip 0으로 통과했다. 모바일 관리자 topbar와 세션 입력/44px 제어, P12 ideation DB 범위 clamp를 실제 회귀로 고정했으며, 격리 포트와 프로세스는 모두 종료했다.
3.7 공개 인증 스모크(선택)
e2e/public-auth-turn.spec.ts와 e2e/public-admin-visual.spec.ts는 공개 사이트
(https://vignette.chanpaca.net)를 직접 타격하는 옵트인 스모크로, 로컬 웹 서버를 띄우지 않는다.
관리자 시각 스모크는 운영 홈·AI 운영·사용자·권한·티켓의 본문 노출, primary learner 화면의
운영 콘솔 복귀, 브라우저 pageshow 탭 복원 뒤 실제 내부 .vg-main.scrollTop=0과 heading 가시성을
함께 검증한다. document window.scrollY만 검사하면 관리자 셸의 실제 스크롤 잔류를 놓치므로 금지한다.
로컬 admin.spec.ts의 EasyList cosmetic filters 회귀는 공식 목록에 있는 옛 .ad-root와
.ad-section 숨김 규칙을 그대로 주입한 뒤에도 중립 [data-vignette-admin-root]와 운영 홈 heading이
보이는지 확인한다. API 200이나 DOM 존재만으로 이 검증을 대신하면 광고 차단기 silent blank를 놓친다.
npm run check:cosmetic-filter-safety는 src/**/*.{ts,tsx,css}와 index.html을 검사해
ad-*, ads-*, advert*, sponsor* class/id namespace가 프로덕션 코드에 다시 들어오면 빌드를
즉시 실패시킨다. 탐지기 자체의 양성·음성 fixture는 매 실행마다 먼저 검증하며,
npm run check:cosmetic-filter-safety:self-test로 따로 실행할 수도 있다. 이 검사는 npm run build와
npm run lint 앞단에 포함되므로 scripts/start-public-runtime.ps1의 실제 웹 빌드도 우회하지 못한다.
공개 public-admin-visual.spec.ts 역시 같은 EasyList 규칙을 첫 paint부터 적용한 상태로 관리자 5경로의
heading/root 실제 rect·computed visibility, legacy class 0, watchdog/console 오류 0을 확인한다.
절차는 apps/web/e2e/README.md 참고
(E2E_PUBLIC_AUTH=1, npx playwright codegen ... --save-storage로 인증 상태 캡처 후
E2E_PUBLIC_STORAGE_STATE 재사용). 캡처한 storage state에는 API 세션 쿠키가 들어 있으니
민감 정보로 취급한다.
3.8 배포 전환 청크 복구 스모크(선택)
e2e/chunk-recovery-preview.spec.ts는 프로덕션 빌드의 로그인 lazy 청크 첫 요청을 강제로 실패시켜
문서가 정확히 한 번만 다시 로드되고 로그인 라우트가 정상 렌더되는지 검증한다. 로컬 vite preview 또는
공개 도메인을 PLAYWRIGHT_BASE_URL로 지정하고 아래처럼 실행한다.
$env:E2E_PREVIEW_BUILD = "1"
$env:PLAYWRIGHT_SKIP_WEB_SERVER = "1"
$env:PLAYWRIGHT_BASE_URL = "http://127.0.0.1:5262" # 또는 https://vignette.chanpaca.net
npx playwright test e2e/chunk-recovery-preview.spec.ts --project=chromium-single-run --workers=1
4. 전체 검증 순서 권장안 (로컬)
# 1) 외부 의존 없는 빠른 검증부터
cd apps/api && python -m pytest app/ -q && python -m pytest engine_gateway/ -q
cd apps/web && npm run typecheck && npm run build
# 2) 풀스택 E2E (DB+API 준비 후)
# 터미널 A: docker compose -f infra/docker-compose.yml up -d db
# 터미널 B: cd apps/api && (3.3의 env) python -m uvicorn app.main:app --host 127.0.0.1 --port 8000
# 터미널 C:
cd apps/web && npm run e2e
엔진 턴 생성이나 관리자 모델 저장까지 보려면(음성/세션 MVP 등 일부 @single-run) 현재 코드의
엔진 게이트웨이를 포트 9099에 띄운다. DB의 admin_engine_config.engine_url이 오래 떠 있던 구형
게이트웨이를 가리키면 /admin/engine-capabilities가 404/503이므로 프로세스를 새 코드로 재기동해야 한다.
게이트웨이가 없으면 /health의 engine:false이고 실제 턴 생성·모델 저장 시나리오는 실패한다.
레이아웃/시각 게이트는 게이트웨이 없이도 통과한다.
공급자 실증은 capability만 확인하지 말고 최소 한 번 실제 고유 응답까지 확인한다. Codex는
gpt-5.6-terra / Medium, Agy는 gemini-3.6-flash-high / High가 목록 기본값과 실행 결과 양쪽에서
일치해야 한다. Anthropic은 실제 키가 없는 환경에서 unavailable·저장 거부가 정상이다.
5. 운영 원칙 — "가짜 DONE" 금지
검증 결과는 실제로 실행해 통과한 명령만 근거로 보고한다.
- "통과했다/완료다"라고 말하려면 그 자리에서 **실행 가능한 검증 명령과 관측된 결과(통과 개수)**를
함께 제시한다. 예:
python -m pytest app/ -q→100 passed. - 의존을 갖춘 검증을 우회하지 않는다. E2E를 DB/API 없이 돌려 놓고 "그린"이라고 보고하지 않는다. 풀스택을 못 띄웠으면 **"E2E 미실행"**이라고 명시한다.
- 인메모리 degraded 기동(
db:false)에서 페르소나 헬퍼가 실패하는 것은 환경 문제이지 코드가 통과한 것이 아니다. 환경을 고친 뒤 재실행한 결과로만 판단한다. - 수집(
--collect-only)·목록(--list)은 "있다"는 근거일 뿐 "통과했다"는 근거가 아니다. 통과 주장은 실제 실행 출력으로 뒷받침한다.