diff --git a/apps/web/e2e/session-persistence.spec.ts b/apps/web/e2e/session-persistence.spec.ts index e6fe9d6..68a1000 100644 --- a/apps/web/e2e/session-persistence.spec.ts +++ b/apps/web/e2e/session-persistence.spec.ts @@ -61,6 +61,14 @@ interface LiveCoachEvent { suggestion: { title: string; message: string; + sources?: Array<{ + source_id: string; + title: string; + locator?: string | null; + kb_kind?: string | null; + version?: string | null; + citation?: string | null; + }>; }; } @@ -619,30 +627,59 @@ test.describe("session persistence", () => { const coachResponse = await coachResponsePromise; await expectResponseOk(coachResponse); const coachSuggestion = (await coachResponse.json()) as LiveCoachEvent["suggestion"]; + const workbookSource = coachSuggestion.sources?.find( + (source) => source.source_id === "workbook_0615_case_conceptualization", + ); + expect(workbookSource, "live coach response should include the licensed workbook source pack").toBeTruthy(); + expect(workbookSource?.version).toBe("2026-06-15"); + expect(workbookSource?.citation ?? "").toContain("0615"); await expect(page.locator(".sx-coach-card").getByText(coachSuggestion.title)).toBeVisible({ timeout: 30_000, }); + await page.locator(".sx-coach-card").getByRole("button", { name: "근거 보기" }).click(); + const evidenceDialog = page.locator(".sx-coach-modal [role='dialog']"); + await expect(evidenceDialog).toBeVisible({ timeout: 15_000 }); + await expect(evidenceDialog).toContainText(workbookSource!.title); + await expect(evidenceDialog).toContainText("2026-06-15"); + await expect(evidenceDialog).toContainText("0615"); + await evidenceDialog.getByRole("button", { name: "닫기" }).click(); + await expect(evidenceDialog).toHaveCount(0); const historyResponse = await page.request.get(`/api/sessions/${sessionId}/live-coach`); await expectResponseOk(historyResponse); const history = (await historyResponse.json()) as LiveCoachHistoryResponse; expect(history.source).toBe("database"); - expect( - history.events.some( + const persistedCoachEvent = history.events.find( (event) => event.turn_seq === 1 && event.learner_text_excerpt?.includes("가장 버거운 감정") && event.suggestion.title === coachSuggestion.title, + ); + expect(persistedCoachEvent, "DB-backed live coach history should include the delivered coaching event").toBeTruthy(); + expect( + persistedCoachEvent?.suggestion.sources?.some( + (source) => + source.source_id === workbookSource!.source_id && + source.version === workbookSource!.version && + (source.citation ?? "").includes("0615"), ), + "DB-backed live coach history should preserve source pack metadata", ).toBe(true); await page.goto(`/learn/session/${sessionId}`); await expect(page.locator(".sx-utt.is-learner").filter({ hasText: learnerText })).toBeVisible({ timeout: 15_000, }); - await expect(page.locator(".sx-utt.is-learner .sx-utt__coach-mark")).toBeVisible({ + const coachMark = page.locator(".sx-utt.is-learner .sx-utt__coach-mark"); + await expect(coachMark).toBeVisible({ timeout: 15_000, }); + await coachMark.click(); + const historyDialog = page.locator(".sx-coach-history [role='dialog']"); + await expect(historyDialog).toBeVisible({ timeout: 15_000 }); + await expect(historyDialog).toContainText(workbookSource!.title); + await expect(historyDialog).toContainText("2026-06-15"); + await expect(historyDialog).toContainText("0615"); }); test("persists voice nonverbal metadata into DB-backed review @single-run", async ({ page }) => { diff --git a/docs/dev_dashboard.html b/docs/dev_dashboard.html index ebd3395..b57e930 100644 --- a/docs/dev_dashboard.html +++ b/docs/dev_dashboard.html @@ -547,7 +547,7 @@

112차 적용(2026-07-01): 교수자 회기 리뷰 데스크톱에서 overview/transcript가 여러 CSS Grid row를 가로질러 오른쪽 검토 rail 높이를 나눠 먹으며 중앙에 큰 빈 row가 생기던 문제를 제거했다. 1181px 이상 교수자 리뷰는 main column + review rail 2열 wrapper로 분리하고, main 내부는 요약 전체폭, 차트/회기 흐름 2열, 축어록 전체폭으로 배치해 서로 다른 rail 높이가 빈공간을 만들지 않게 했다. 회귀 방지로 layout-visual-gate.spec.ts에 교수자 리뷰 폭별 dead vertical gap 검증을 추가했다. 검증: npm run typecheck, npm run build, session-review desktop 8 passed, layout-visual-gate 10 passed, session-layout desktop/mobile 8 passed.

113차 적용(2026-07-01): 학습자 /learn/practice 페르소나 리스트가 긴 한글 주호소 요약에서 카드 내부를 클립하던 문제를 수정했다. .lh-list-pane의 viewport 고정 높이와 내부 스크롤을 풀고, .lh-personas/.lh-persona가 콘텐츠 높이를 그대로 만들게 했으며, .lh-persona__summary 3줄 clamp를 제거했다. learner.spec.ts에는 각 페르소나 카드의 summary/body/card scroll overflow가 0인지 재는 regression guard를 추가했다. 검증: npm run typecheck, npm run build, 브라우저 rect smoke 1095px/1440px에서 카드 overflow 0 및 문서 가로 overflow 0. 현재 로컬 127.0.0.1:8000/health가 timeout이라 real API 기반 전체 learner.spec.ts는 이번 패스에서 미실행.

114차 적용(2026-07-01): 회기 종료 후 background deep 평가가 실제 durable DB row로 저장되는지 fixture 없이 검증했다. _schedule_session_evaluation()은 생성한 task를 반환하고 done callback에서 crash/cancel을 로그로 관찰하며, 평가 결과·timeout/error 저장이 durable store에 닿지 않으면 명시 error 로그를 남긴다. 교수자 명시 재평가 API도 평가 산출물이 생성됐지만 저장 실패하면 503으로 표면화한다. GET /eval/sessions/{id}/evaluationstatus/error/durable을 노출해 캐시 착시와 DB 저장 증거를 구분한다. 새 DB-backed E2E는 학습자 세션 생성→턴→종료→teacher 로그인→/eval/.../evaluation polling→교수자 리뷰 UI 평가 완료까지 실제 API/DB/엔진으로 확인한다. 검증: API 전체 309 passed, npm run check:api-types, npm run typecheck, PLAYWRIGHT_PORT=5205 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 7 passed, PLAYWRIGHT_PORT=5203 npx playwright test e2e/session-review.spec.ts --project=chromium-desktop --project=chromium-mobile --workers=1 16 passed, 현재 Playwright 수집 기준 162 tests in 18 files.

-

115차 적용(2026-07-01): 교수자 학생 분석을 /teach 대시보드에서 분리해 좌측 메뉴의 /teach/analysis 전용 작업면으로 이동했다. 교수 콘솔은 검토 큐·위기 알림·페르소나 검수·최근 회기 triage만 맡고, 학생 분석은 학습자 목록 레일, 사용자별 추이, 단계 분포, 전체 회기 타임라인, 회기 리뷰 드릴다운 구조를 맡는다. 회귀 방지로 teacher.spec.ts는 콘솔에 분석 패널이 없고 메뉴를 통해 학생 분석 페이지로 이동하는지 확인하며, layout-visual-gate.spec.ts는 학생 분석 페이지를 7개 폭에서 별도 캡처·검사한다. 검증: npm run typecheck, npm run build, npm run check:api-types, PLAYWRIGHT_PORT=5211 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1 9 passed, PLAYWRIGHT_PORT=5214 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=1 11 passed, SSOT checker PASS, 현재 Playwright 수집 기준 163 tests / 18 files.

+

115차 적용(2026-07-01): 교수자 학생 분석을 /teach 대시보드에서 분리해 좌측 메뉴의 /teach/analysis 전용 작업면으로 이동했다. 교수 콘솔은 검토 큐·위기 알림·페르소나 검수·최근 회기 triage만 맡고, 학생 분석은 전체 학습자 검색 테이블을 먼저 보여준다. 각 학습자는 한 줄 행으로 표시하고, 우측 펼침 버튼은 행 아래에 미니 추이·기법 태그·최근 기록을 확장한다. 학습자 이름 또는 상세 보기 버튼은 ?learner= 상세 드릴다운으로 이동하며, 상세 화면은 추이·전체 회기·단계 분석 탭으로 선택 학습자의 전체 회기 이력을 나눈다. 회귀 방지로 teacher.spec.ts는 콘솔에 분석 패널이 없고 메뉴를 통해 학생 분석 페이지로 이동한 뒤 검색·행 펼침·상세 진입·전체 회기 탭을 확인하며, layout-visual-gate.spec.ts는 학생 분석 목록/상세 화면을 7개 폭에서 별도 캡처·검사한다. 검증: npm run typecheck, npm run build, npm run check:api-types, PLAYWRIGHT_PORT=5215 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1 9 passed, PLAYWRIGHT_PORT=5217 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=1 12 passed, 현재 Playwright 수집 기준 166 tests / 18 files.

116차 적용(2026-07-01): 원천문서 갭 E2E 목표에 맞춰 C1/C2 silent failure를 추가로 닫았다. C1은 학습자가 저장한 사례개념화 워크시트가 app.case_worksheet를 거쳐 교수자 검토 화면 read-only 값으로 복원되고, 교수자 수정요청 검수 메모가 teacherReview.worksheetStatus/worksheetNote로 재조회되는지 실제 브라우저/API/DB E2E로 고정했다. C2는 app.safety_events가 runtime bootstrap/healthcheck에서 빠져 DB 정상처럼 보일 수 있던 구멍과, jsonb codec 위에 이미 직렬화한 문자열을 넘겨 detail이 JSON string으로 저장되던 구멍을 수정했다. record_safety_event()는 RLS용 ai_context=true로 저장하고, alert list 실패는 non-dev에서 fail-closed한다. 검증: python -X utf8 -m pytest -p no:cacheprovider app/test_session_turn_persistence.py app/test_teacher_dashboard.py app/test_runtime_policy.py -q 59 passed, PLAYWRIGHT_PORT=5228 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "case worksheet|crisis safety" 2 passed, 현재 Playwright 수집 기준 166 tests in 18 files.

20차 적용(2026-06-28): 신규 Google/SAML 사용자는 account_status=pending으로 시작하고 승인 전에는 /pending 안내 화면만 본다. yunchan@twentyoz.kr는 슈퍼 관리자 allowlist로 admin+approved를 받으며, /admin/users는 가입 승인 탭에서 pending 계정을 승인 또는 보류 처리한다.

21차 적용(2026-06-28): 관리자 페이지 진입권을 기본 역할과 분리해 app_user.admin_access로 저장한다. AUTH_SUPER_ADMIN_EMAILS 기본값은 yunchan@twentyoz.kr, hoonjungkoo@hs.ac.kr이며, 슈퍼 관리자는 학습자·교수자·관리자 공간 전환과 관리자 권한 부여/회수를 할 수 있다. 학생·교수 계정도 admin_access=true면 우측 상단 관리자 진입이 노출된다. 구성 슈퍼 관리자의 권한 회수와 계정 비활성화는 차단한다.

@@ -670,8 +670,8 @@
현재

PDF(9·16·18쪽): 자해·자살 감지 시 대화 중단 + 자살예방상담 109 안내 + 지도교수 자동 알림, 안전은 LLM 밖 별도 CrisisGate. 구현: CRISIS_HOTLINE_NUMBER=109, TurnResponse.crisis_resource/conversation_stopped, REST·SSE·voice 경로의 엔진 호출 전 중단, app.safety_events detail 적재, 교수자 대시보드 안전 알림 큐.

검증

pytest app/test_session_turn_persistence.py app/test_voice_ws.py -q 20 passed, 안전 이벤트 DB insert payload 테스트 포함. pytest app/test_teacher_dashboard.py app/test_session_turn_persistence.py app/test_voice_ws.py -q 21 passed.

잔여

비밀보장 예외고지·생명유지서약 등 임상 문안, 실시간 push/메일 알림, 지역 자원 확장, 운영 정책 문구는 소유자·임상팀 확정 필요. 현재 교수자 알림은 safety_events 기반 대시보드 큐다.

- -
현재

TeacherDashboardResponse.learner_growthGET /teacher/learners/{learner_id}/analysis가 턴별 fast-loop 평가의 appropriateness, rapport_signal, 기법 태그를 학습자별로 집계한다. /teach는 검토 큐·위기 알림·페르소나 검수·최근 회기 triage만 맡고, /teach/analysis는 학습자 목록 레일, 회기별 막대 추이, 평균 라포, 변화폭, 단계 분포, 전체 회기 타임라인을 표시한다. 평가가 없는 회기는 "평가 부족"으로 남겨 가짜 곡선을 만들지 않는다. 회기 행은 /teach/session/{id}/review로 진입해 같은 회기 리뷰를 교수자 읽기 전용으로 열고, 워크시트는 학습자 제출물 수정 없이 검토 전용으로 표시한다.

검증

pytest app/test_teacher_dashboard.py app/test_session_turn_persistence.py app/test_voice_ws.py -q 21 passed, pytest app/test_rbac_idor.py app/test_teacher_dashboard.py -q 9 passed, npm run check:api-types, npm run typecheck, npm run build passed. npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1 4 passed.

잔여

자기효능감·기술숙련도·수련만족도 사전사후(H1)와 교수자 코멘트/검수 워크플로 확장은 별도 평가설계·임상팀 문항 확정이 필요하다.

+ +
현재

TeacherDashboardResponse.learner_growthGET /teacher/learners/{learner_id}/analysis가 턴별 fast-loop 평가의 appropriateness, rapport_signal, 기법 태그를 학습자별로 집계한다. /teach는 검토 큐·위기 알림·페르소나 검수·최근 회기 triage만 맡고, /teach/analysis는 검색 가능한 1행 학습자 테이블을 먼저 보여준다. 우측 펼침 버튼은 행 아래에 회기별 미니 추이, 평균 라포, 변화폭, 기법 태그, 최근 기록을 확장하고, 이름 또는 상세 보기 버튼은 ?learner= 상세 화면으로 들어간다. 상세 화면은 추이·전체 회기·단계 분석 탭으로 나뉘며, 회기 행은 /teach/session/{id}/review로 진입해 같은 회기 리뷰를 교수자 읽기 전용으로 연다. 평가가 없는 회기는 "평가 부족"으로 남겨 가짜 곡선을 만들지 않고, 워크시트는 학습자 제출물 수정 없이 검토 전용으로 표시한다.

검증

python -X utf8 -m pytest -p no:cacheprovider app/test_teacher_dashboard.py -q 6 passed, npm run check:api-types, npm run typecheck, npm run build passed. PLAYWRIGHT_PORT=5215 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1 9 passed, PLAYWRIGHT_PORT=5217 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=1 12 passed.

잔여

자기효능감·기술숙련도·수련만족도 사전사후(H1)와 교수자 코멘트/검수 워크플로 확장은 별도 평가설계·임상팀 문항 확정이 필요하다.

@@ -936,7 +936,7 @@ M3 auth claim mappingpython -B -m pytest -p no:cacheprovider app/test_auth_providers.py -q30 passed; Google/SAML cohort maps, provider external_id, provider error reason, PKCE/state, HttpOnly cookie, SAML fixture ACS covered Auth managed-user upsert boundarypy_compile / py -3.11 -X utf8 -B -m pytest -p no:cacheprovider app/test_auth_providers.py app/test_admin_ops.py app/test_rbac_idor.py app/test_runtime_policy.py app/test_session_turn_persistence.py app/test_learner_dashboard.py app/test_teacher_dashboard.py -q / npm run check:api-typesManagedUserUpsertInput owns managed-user create/reactivate inputs; create_session(), admin user create, and direct auth regression calls no longer pass long keyword bags. ManagedUserMemoryInput remains DB→memory fallback/store sync, and ManagedUserPatch remains partial profile/admin update. Focused backend 103 passed; API type drift check passed. CrisisGate 109python -m pytest app/test_session_turn_persistence.py app/test_voice_ws.py -q / PLAYWRIGHT_PORT=5228 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "crisis safety"real crisis stops before engine, returns 109 resource, and DB insert uses ai_context=true. db.healthcheck() now requires app.safety_events; alert detail handles legacy double-encoded JSON string rows; full browser+API+DB E2E verifies safety_events reaches the DB-backed teacher safety queue with resource 109. - Professor review workflowpython -X utf8 -m pytest -p no:cacheprovider app/test_teacher_dashboard.py -q / PLAYWRIGHT_PORT=5211 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1 / PLAYWRIGHT_PORT=5214 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=1 / npm run check:api-types / npm run typecheck / npm run build5 backend passed + 9 E2E passed + strict visual gate 11 passed; /teach now keeps only triage queues, and /teach/analysis returns a selected learner's full session timeline, unrestricted trend points, stage breakdown, and review counts in a separate student analysis page. + Professor review workflowpython -X utf8 -m pytest -p no:cacheprovider app/test_teacher_dashboard.py -q / PLAYWRIGHT_PORT=5215 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1 / PLAYWRIGHT_PORT=5217 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=1 / npm run check:api-types / npm run typecheck / npm run build6 backend passed + 9 E2E passed + strict visual gate 12 passed; /teach now keeps only triage queues, and /teach/analysis starts with a searchable one-row-per-learner table, right-side row expansion, and ?learner= detail tabs for full session timeline, unrestricted trend points, stage breakdown, and review counts. Learner/teacher growth metricspy -3.11 -X utf8 -B -m pytest -p no:cacheprovider app/test_evaluation_persistence.py app/test_evaluator_model_routing.py app/test_session_turn_persistence.py app/test_rbac_idor.py app/test_teacher_dashboard.py app/test_learner_dashboard.py -q59 backend passed; shared session_metrics now prefers rehydrated label_ko for technique labels while preserving label/name/id/code fallback. alternative_utterances string and text/suggestion dict inputs are pinned before any future evaluator adapter extraction. Teacher review statuspytest app/test_teacher_dashboard.py app/test_rbac_idor.py app/test_learner_dashboard.py -q / npm run typecheck / npm run build / teacher.spec.ts12 backend passed + 4 E2E passed; app.session_review_status, teacher dashboard review_status/review_note/reviewed_at, worksheet worksheet_status/worksheet_note/worksheet_reviewed_at, and PUT /teacher/sessions/{session_id}/review-status are documented. 검토 큐 visible 회귀와 교수자 전용 review grid 영역도 테스트로 고정했다. Persona authoring + generation contractpy -3.11 -X utf8 -B -m pytest -p no:cacheprovider app/test_persona_generation_contract.py app/test_persona_review.py -q39 passed; teacher/admin draft create/read/update/review submit, approved persona revision clone, code-family archive delete, learner blocked, approved-only catalog maintained. app/persona_generation_contract.py owns draft structured schema, prompt bundle id/version/hash, legacy GenerateResponse payload extraction, and generated draft defaults/coercion. @@ -966,7 +966,7 @@ Browser SSE stream + AI tutor persistencePLAYWRIGHT_PORT=5205 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=17 passed; 기존 direct /turn real API 저장 검증에 더해 실제 Session UI가 openSessionStream()으로 POST /sessions/{id}/stream을 호출하고, stream 완료 후 DB-backed /review가 learner/client 축어록을 반환하는지 확인한다. 추가로 코칭 모드에서 POST /live-coach 성공 뒤 DB-backed GET /live-coach 이력과 재로딩 후 학습자 발화의 C 마커가 유지되는지 확인하고, voice WS stt_result 및 실제 Session 마이크 UI 생성 비언어 메타가 DB-backed review nonverbal로 파생되는지도 고정한다. 같은 spec은 Phase 3 pre/post 저장과 세션 종료 background deep 평가의 durable DB row까지 검증한다. db/engine health가 false면 skip하고, stream/live-coach/evaluation 503/error는 실패로 취급한다. C3 theory-mode reevaluationC:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe -X utf8 -m pytest -p no:cacheprovider app/test_eval_routes.py -q / app/test_eval_routes.py app/test_notifications.py app/test_session_turn_persistence.pyeval route 5 passed, focused backend 39 passed. 수동 session 재평가는 persona 기본 theory_target보다 학습자가 선택한 session theory_mode를 우선하고, turn 재평가도 TurnContext.theory_mode에 같은 값을 전달한다. Crisis safety gate UIPLAYWRIGHT_PORT=5193 npx playwright test e2e/session-mvp.spec.ts --project=chromium-single-run --workers=12 passed; mock SSE safety + done.conversation_stopped에서 안전 자원 109와 입력 disabled가 유지되고, 빈 client reply를 내담자 응답 없음으로 덮지 않으며 live-coach를 호출하지 않는지 검증한다. - Persona/session review UI E2EPLAYWRIGHT_PORT=5211 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=19 passed; teacher approves pending persona, opens pending/recent review detail, and reaches the separate student analysis page from the menu before switching full learner timelines. + Persona/session review UI E2EPLAYWRIGHT_PORT=5215 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=19 passed; teacher approves pending persona, opens pending/recent review detail, reaches the separate student analysis page from the menu, searches the learner table, expands a learner row, and drills into the full learner timeline tab. Voice UI synthetic E2Enpx playwright test e2e/voice-success.spec.ts --project=chromium-single-run2 passed; direct WS cascade plus Session mic button path with synthetic browser audio and Web Audio playback under blocked media-element autoplay Voice s2s decision memodocs/decisions/voice-s2s-poc.mdcriteria recorded; keep/drop decision remains owner DECIDE Hanshin data/SSO gatedocs/ops/hanshin-data-governance-gate.mdartifact created; written external evidence still required @@ -1003,10 +1003,10 @@ Voice/session focusedvoice + voice-success + session-layout15 passed Layout redesign handoffdocs/ops/layout-redesign-handoff-2026-06-26.mdsubagent scopes, files, verification, remaining visual review recorded Layout redesign focused E2Enpx playwright test e2e/learner.spec.ts e2e/session-layout.spec.ts e2e/session-review.spec.ts e2e/admin.spec.ts e2e/settings.spec.ts e2e/teacher.spec.ts --project=chromium-desktop --project=chromium-mobile54 passed after isolated Admin tablet flake rerun - Strict layout visual gatePLAYWRIGHT_PORT=5214 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=111 passed; 11 screens × 7 widths(390/720/861/900/1024/1280/1440), horizontal overflow 0 + control clip 0, 77 full-page screenshots. 학생 분석 전용 화면과 빈 회기리뷰 fixture 포함. + Strict layout visual gatePLAYWRIGHT_PORT=5217 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=112 passed; 12 screens × 7 widths(390/720/861/900/1024/1280/1440), horizontal overflow 0 + control clip 0, 84 full-page screenshots. 학생 분석 목록 테이블/상세 화면과 빈 회기리뷰 fixture 포함. Parallel visual quality reviewworkflow layout-visual-quality-review (7 agents)49 screenshots read; per-screen findings(critical 1, major 8+, minor) → fix verdicts Parallel layout fixes6 agent team: learner-home/session/review/professor/admin/settings (disjoint files)all fixes applied; npm run typecheck OK; no cross-file conflicts - Strict gate re-run (post-fix)PLAYWRIGHT_PORT=5214 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=111 passed; re-screenshotted 77 + Strict gate re-run (post-fix)PLAYWRIGHT_PORT=5217 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=112 passed; re-screenshotted 84 Layout focused E2E re-run (post-fix)learner/session-layout/session-review/admin/settings/teacher × desktop+mobile54 passed; no regression Adversarial visual verifyworkflow layout-visual-verify (7 agents)7/7 accept; critical/major resolved, regression 0; minor polish backlog recorded Engine config 운영값GET /admin/engine-configclaude_cli / http://127.0.0.1:9099 / gateway-default, durable, source=database (의도값 일치) diff --git a/docs/guides/architecture.md b/docs/guides/architecture.md index a8bb0ca..c762cff 100644 --- a/docs/guides/architecture.md +++ b/docs/guides/architecture.md @@ -381,8 +381,10 @@ session lifecycle을 유지하며, future Node read API는 이 read-model contra - `PUT /teacher/sessions/{session_id}/review-status` — 교수자/관리자가 회기 검토 메모와 상태를 저장한다. 저장 대상은 `app.session_review_status`이며, 검토 완료된 회기는 pending queue에서 제외된다. - `/teach` 교수 콘솔은 검토 큐·위기 알림·페르소나 검수·최근 회기 triage만 맡는다. - `/teach/analysis` 학생 분석은 별도 메뉴/라우트로 분리되어 담당 학습자 목록, 선택 학습자의 - 전체 회기·단계 분포·회기별 추이를 보여준다. `/teach/session/:sessionId/review` 화면은 같은 + `/teach/analysis` 학생 분석은 별도 메뉴/라우트로 분리되어 전체 담당 학습자를 검색 가능한 + 1행 테이블로 먼저 보여주고, 우측 펼침으로 행 아래 미니 추이·기법·최근 기록을 확장한다. + 학습자 이름 또는 상세 보기 버튼은 `?learner=` 상세 드릴다운으로 이동하며, 상세 화면은 + 추이·전체 회기·단계 분석 탭으로 선택 학습자의 전체 이력을 보여준다. `/teach/session/:sessionId/review` 화면은 같은 `GET /sessions/{id}/review` 자료를 교수자 읽기 전용으로 표시하고, 검토 메모 저장은 위 teacher endpoint로 분리한다. ### 2.9.2 운영 메일 알림 — `app/services/notifications.py` diff --git a/docs/guides/testing.md b/docs/guides/testing.md index 83dd5ed..d76f1a8 100644 --- a/docs/guides/testing.md +++ b/docs/guides/testing.md @@ -268,17 +268,17 @@ VITE_API_BASE=http://127.0.0.1:8000 npm run e2e # 프록시 대신 API - 2026-07-01 focused 검증: `PLAYWRIGHT_PORT=5205 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1` **7 passed**. 실제 브라우저 `openSessionStream()` → `/sessions/{id}/stream` → DB-backed `/review` 축어록 저장 경로, AI 튜터 코칭 이력 저장/재로딩, WebSocket `stt_result` 음성 비언어 메타데이터, Session 마이크 UI가 생성한 voice activity/silence 메타, Phase 3 pre/post 점수의 DB-backed 저장/재조회, 그리고 세션 종료 background deep 평가가 durable DB row로 저장되어 교수자 리뷰가 `평가 완료`로 전환되는지 검증한다. - 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 focused 검증: `PLAYWRIGHT_PORT=5193 npx playwright test e2e/session-mvp.spec.ts --project=chromium-single-run --workers=1` **2 passed**. MVP 종료/리뷰 흐름과 위기 안전 게이트가 빈 내담자 응답 신호에 덮이지 않는지 검증한다. -- 2026-07-01 focused 검증: `PLAYWRIGHT_PORT=5211 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1` **9 passed**. 교수자 콘솔의 페르소나 검수, 검토 큐, 최근 회기 진입과 별도 `/teach/analysis` 학생 분석 메뉴, 전체 회기 타임라인 전환을 검증한다. +- 2026-07-01 focused 검증: `PLAYWRIGHT_PORT=5215 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1` **9 passed**. 교수자 콘솔의 페르소나 검수, 검토 큐, 최근 회기 진입과 별도 `/teach/analysis` 학생 분석 메뉴, 학습자 검색 테이블, 행 펼침, 상세 드릴다운, 전체 회기 탭 전환을 검증한다. 레이아웃·시각 회귀 게이트(핵심 합격선): | 게이트 | 스펙 | 구성 | 개수 | |---|---|---|---| | 세션 레이아웃 | `e2e/session-layout.spec.ts` | 4 테스트 × (desktop+mobile) | **8 / 8** | -| 시각 레이아웃 게이트 | `e2e/layout-visual-gate.spec.ts` | `@single-run`, 11개 화면 × 7개 폭 검사 + 다크 테마 assertion | **11 / 11** | +| 시각 레이아웃 게이트 | `e2e/layout-visual-gate.spec.ts` | `@single-run`, 12개 화면 × 7개 폭 검사 + 다크 테마 assertion | **12 / 12** | | 레이아웃 포커스(재설계 화면) | `session-layout`·`session-review`·`admin`·`learner`·`settings`·`teacher`, `@single-run` 제외 | desktop+mobile 병렬 | **54** | -> `layout-visual-gate`는 7개 폭(390/720/861/900/1024/1280/1440)에서 11개 핵심 화면의 가로 +> `layout-visual-gate`는 7개 폭(390/720/861/900/1024/1280/1440)에서 12개 핵심 화면의 가로 > 오버플로·잘린 컨트롤·다크 테마 적용을 검사하고 전체 페이지 스크린샷을 > `node_modules/.tmp/layout-gate/`에 남긴다. > `session-layout`은 회기 전/활성 화면이 뷰포트를 벗어나지 않는지, 우측 패널이 코어 영역을 diff --git a/docs/ops/backlog-2026-06-26.md b/docs/ops/backlog-2026-06-26.md index 81949de..e496761 100644 --- a/docs/ops/backlog-2026-06-26.md +++ b/docs/ops/backlog-2026-06-26.md @@ -24,7 +24,7 @@ 최신 동기화 추가(2026-07-01): 교수자 회기 리뷰 데스크톱에서 `overview`/`transcript`가 여러 CSS Grid row를 가로질러 오른쪽 검토 rail 높이를 나눠 먹으며 중앙에 큰 빈 row가 생기던 문제를 제거했다. 1181px 이상 교수자 리뷰는 main column + review rail 2열 wrapper로 분리하고, main 내부는 요약 전체폭, 차트/회기 흐름 2열, 축어록 전체폭으로 배치한다. 회귀 방지로 `layout-visual-gate.spec.ts`에 교수자 리뷰 폭별 dead vertical gap 검증을 추가했다. 검증: `npm run typecheck`, `npm run build`, `npx playwright test e2e/session-review.spec.ts --project=chromium-desktop --workers=1` 8 passed, `npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=1` 10 passed, `npx playwright test e2e/session-layout.spec.ts --project=chromium-desktop --project=chromium-mobile --workers=1` 8 passed. -최신 동기화 추가(2026-07-01): 교수자 학생 분석을 콘솔에서 분리했다. `GET /teacher/learners/{learner_id}/analysis`는 담당 범위 안의 특정 학습자 전체 회기를 오래된 순서로 반환하고, 제한 없는 회기별 추이 point, 라포·탐색·개입·정리 단계 분포, 검토 대기/완료 카운트를 함께 내려준다. `/teach`는 검토 큐·위기 알림·페르소나 검수·최근 회기 triage만 맡고, 좌측 메뉴의 `/teach/analysis`가 학습자 목록 레일 → 사용자별 분석 → 전체 회기 타임라인 → 회기 리뷰 드릴다운을 맡는다. 검증: `python -X utf8 -m pytest -p no:cacheprovider app/test_teacher_dashboard.py -q` 5 passed, `npm run check:api-types`, `npm run typecheck`, `npm run build`, `PLAYWRIGHT_PORT=5211 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1` 9 passed, `PLAYWRIGHT_PORT=5214 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=1` 11 passed, `python -X utf8 scripts/check-dev-dashboard-ssot.py --json` PASS, `npx playwright test --list` 현재 163 tests / 18 files. +최신 동기화 추가(2026-07-01): 교수자 학생 분석을 콘솔에서 분리했다. `GET /teacher/learners/{learner_id}/analysis`는 담당 범위 안의 특정 학습자 전체 회기를 오래된 순서로 반환하고, 제한 없는 회기별 추이 point, 라포·탐색·개입·정리 단계 분포, 검토 대기/완료 카운트를 함께 내려준다. `/teach`는 검토 큐·위기 알림·페르소나 검수·최근 회기 triage만 맡고, 좌측 메뉴의 `/teach/analysis`는 전체 학습자 검색 테이블 → 우측 행 펼침 요약 → `?learner=` 사용자별 상세 → 추이/전체 회기/단계 분석 탭 → 회기 리뷰 드릴다운을 맡는다. 검증: `python -X utf8 -m pytest -p no:cacheprovider app/test_teacher_dashboard.py -q` 6 passed, `npm run check:api-types`, `npm run typecheck`, `npm run build`, `PLAYWRIGHT_PORT=5215 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1` 9 passed, `PLAYWRIGHT_PORT=5217 npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=1` 12 passed, `npx playwright test --list` 현재 166 tests / 18 files. 최신 동기화 추가(2026-07-01): 원천문서 갭 E2E 탐색을 이어서 H1/C3 silent failure 후보를 좁혔다. `session-persistence.spec.ts`는 Phase 3 pre/post 3척도 입력이 브라우저 리뷰 UI → `PUT /users/me/prepost-measures` → DB-backed `GET /users/me/prepost-measures` → 새로고침 후 UI 복원까지 실제 API/DB로 이어지는지 검증한다. C3 수동 재평가는 persona 기본 `theory_target`이 학습자가 선택한 session `theory_mode`를 덮어쓰던 우선순위 오류를 수정했고, turn 재평가도 `TurnContext.theory_mode`를 채우도록 회귀화했다. 서브에이전트 병렬 조사 결과 남은 큰 공백은 C1 워크시트 learner-save/teacher-review full browser+API+DB E2E, C2 safety_events→교수자 대시보드 E2E, H2 live-coach evidence modal/source-pack actual sync proof였다. 검증: `python -X utf8 -m pytest -p no:cacheprovider app/test_eval_routes.py app/test_notifications.py app/test_session_turn_persistence.py -q` 39 passed, `PLAYWRIGHT_PORT=5205 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1` 7 passed, `npx playwright test --list` 현재 162 tests / 18 files. @@ -63,7 +63,7 @@ - [ ] **운영 티켓 자동 분류·처리 후속** — Claude Recipe headless 자동 수정 후보, 관리자 승인 후 이슈 등록·PR/작업 스레드 생성, 처리 결과 audit trail 확장은 아직 설계/승인 필요. 담당 그룹 자동 배정·우선순위 escalation·raw/rollup 보존기간 같은 운영 정책은 B3 결정 항목에서 먼저 닫아야 한다. 자동 수정은 운영자 승인 전까지 실행하지 않는다. - [x] **learner-home 로딩 스켈레톤 밀도(390)** — (2026-06-26 처리) '연습 대상' 로딩 스켈레톤을 빈 div 단일 셰이머에서 실제 카드 구조(마크 박스 + 이름/메타/요약 2줄 플레이스홀더)를 모사하도록 밀도 보강. `LearnerHome.tsx` (`.lh-skel__box`/`.lh-skel__line*`), reduced-motion 분기 동반 갱신. **검증: `npm run typecheck` PASS + `vite build` PASS.** -검증 기준: 변경 후 `npm run typecheck`, `e2e/layout-visual-gate.spec.ts`(현재 9/9), 레이아웃 포커스 E2E(54), `e2e/session-layout.spec.ts`(8/8) 무회귀. 이전 후속 라운드는 빈상태 레이아웃(`typecheck`, `build`, layout gate 9/9, session-layout+session-review desktop 6/6), 사용자별 티켓 조회 UI(backend 29, settings 7, admin ticket 1), synthetic health sampler(backend 31, one-shot recorded_count 5), health retention/rollup(backend 36, dry-run OK), 티켓 중복 저장·수동 연결(backend 38, API types/typecheck/build OK, admin tickets E2E 2), M2 route-level seed recall 주입(py_compile, M2 focused 68)을 확인했다. 최신 다크 UI v2 라운드는 `learner.spec.ts`+`session-review.spec.ts` 9 passed, `session-layout.spec.ts` 8 passed, `layout-visual-gate.spec.ts` 9 passed로 별도 기록한다. +검증 기준: 변경 후 `npm run typecheck`, `e2e/layout-visual-gate.spec.ts`(현재 12/12), 레이아웃 포커스 E2E(54), `e2e/session-layout.spec.ts`(8/8) 무회귀. 이전 후속 라운드는 빈상태 레이아웃(`typecheck`, `build`, layout gate 9/9, session-layout+session-review desktop 6/6), 사용자별 티켓 조회 UI(backend 29, settings 7, admin ticket 1), synthetic health sampler(backend 31, one-shot recorded_count 5), health retention/rollup(backend 36, dry-run OK), 티켓 중복 저장·수동 연결(backend 38, API types/typecheck/build OK, admin tickets E2E 2), M2 route-level seed recall 주입(py_compile, M2 focused 68)을 확인했다. 최신 다크 UI v2 라운드는 `learner.spec.ts`+`session-review.spec.ts` 9 passed, `session-layout.spec.ts` 8 passed, `layout-visual-gate.spec.ts` 9 passed로 별도 기록한다. --- @@ -112,7 +112,7 @@ ## 이번 세션에 닫은 것(참고) -- 레이아웃 시각 수용: 기본 게이트 7/7 + 적대적 재검수 7/7 accept에 더해 빈 회기리뷰 전용 gate를 추가한 현재 9/9 — DONE. +- 레이아웃 시각 수용: 기본 게이트 7/7 + 적대적 재검수 7/7 accept에 더해 빈 회기리뷰, 교수자 리뷰, 학생 분석 목록/상세 gate를 추가한 현재 12/12 — DONE. - live 운영 증거: engine config 운영값, 상주 엔진풀 probe(TTFT/cost/세션재사용), 상주 엔진풀 RSS 실측, Postgres RLS/audit smoke 5 checks PASS, turn cost telemetry(app.turns 13행) — DONE. - 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 키 필요). 프로덕션 탑재는 라이선스/권리 결정 전 금지. - Session.tsx 음성 guard/view-model P2 리팩터 + capture/EOT 계약 보강: `isVoiceStatusBusy()`와 `sessionVoiceStatusView()`가 마이크 busy/disabled, aria label, transcript/response/status label, 텍스트 입력 차단 계산을 소유한다. Session mic capture는 `AudioWorklet`-first PCM16 + `MediaRecorder` fallback이고, `/voice/ws`는 `stt_result` control로 EOT ready/pending을 route에서 판단한다. 검증은 web typecheck/build, `session-mvp`, `voice-success` synthetic UI, `session-layout`, `test_voice_ws.py/test_voice_service.py` 범위만 인정한다. 실제 Deepgram WSS, 물리 마이크·공개 WSS·50분 장시간 실측은 B2 음성 캐스케이드 live 항목에 계속 남긴다.