diff --git a/apps/web/index.html b/apps/web/index.html index b1016c7..d83c78d 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -63,6 +63,131 @@ rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/variable/pretendardvariable.min.css" /> +
diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 2037f78..19460c3 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -30,7 +30,7 @@ - M3 인증 claim 1차: Google/SAML/dev-login이 설정 기반 cohort map과 SAML cohort claim을 `cohort_ids`로 넘기고, 관리 사용자 `external_id`는 provider subject 기반(`google:`/`saml:`/`dev:`)으로 저장한다. 운영 SAML 서명검증·기관 claim schema·deprovisioning audit은 후속. - 페르소나 저작/P4~P7 적재 2차: `data/personas/P4.json`~`P7.json`을 저장소 관리 `PersonaCard`로 읽어 `materialize_seed_personas()`와 seed fallback catalog에 포함했다. 검증: `python -m pytest app/test_persona_review.py -q` 22 passed, 로컬 dev DB materialize 결과 `approved_codes=P1,P2,P3,P4,P5,P6,P7`, 인증된 로컬 `GET /personas`도 `P1..P7` 반환. - Google OAuth 진단 1차: provider callback error는 `access_denied`/`provider_error`로 분리하고, 로그인 화면은 실패 reason code를 함께 표시한다. 실제 Google 계정 완료 proof는 아직 owner 로그인/storageState가 필요하다. -- 관리자 기본 진입 경로와 blank diagnostics: `initialPathForUser()`가 pending/onboarding/admin-entitled landing을 소유한다. 관리자 콘솔 접근권이 있는 계정은 primary role이 learner/teacher여도 `/` 또는 로그인 완료 뒤 `/admin`으로 들어간다. `App.tsx`는 pathname 변경마다 document scroll을 0으로 복원해 긴 관리자 화면에서 짧은 `/admin/access`로 이동해도 sticky shell만 보이는 빈 화면을 만들지 않는다. 관리자 화면은 `/admin/users`와 `/admin/tickets` payload 계약 위반을 안전하게 정규화하고 `관리자 데이터 진단`으로 깨진 필드/path/asset을 표시한다. 앱 route-level error boundary도 렌더 예외를 full white page 대신 진단 화면으로 대체하며, 0-height main도 진단 패널로 노출한다. 검증: `npm run typecheck`, `npm run build`, `npx playwright test e2e/admin.spec.ts --project=chromium-desktop --workers=1 --grep "admin route guards"` 5 passed, custom domain 새 asset 확인. +- 관리자 기본 진입 경로와 blank diagnostics: `initialPathForUser()`가 pending/onboarding/admin-entitled landing을 소유한다. 관리자 콘솔 접근권이 있는 계정은 primary role이 learner/teacher여도 `/` 또는 로그인 완료 뒤 `/admin`으로 들어간다. `App.tsx`는 pathname 변경마다 document scroll을 0으로 복원해 긴 관리자 화면에서 짧은 `/admin/access`로 이동해도 sticky shell만 보이는 빈 화면을 만들지 않는다. 관리자 화면은 `/admin/users`와 `/admin/tickets` payload 계약 위반을 안전하게 정규화하고 `관리자 데이터 진단`으로 깨진 필드/path/asset을 표시한다. 앱 route-level error boundary도 렌더 예외를 full white page 대신 진단 화면으로 대체하며, 0-height main도 진단 패널로 노출한다. `index.html`에는 React/module 실행 자체가 실패해도 3.5초 뒤 `Vignette 화면 진단`을 body에 직접 띄우는 boot watchdog이 있다. 검증: `npm run typecheck`, `npm run build`, `npx playwright test e2e/admin.spec.ts --project=chromium-desktop --workers=1 --grep "admin route guards"` 5 passed, dist file JS-missing watchdog smoke passed, custom domain 새 asset 확인. - `frontenddesign` 스킬은 현재 세션의 사용 가능 스킬 목록에 없었다. 대신 `docs/DESIGN_CONCEPT.md`를 SSOT로 사용했다. - 공개 API 터널은 현재 `C:\Users\encep\.cloudflared\vignette-config.yml`에서 `http://127.0.0.1:8001`을 본다. - 공개용 API 프로세스는 `127.0.0.1:8001`에서 `ENVIRONMENT=prod`로 떠 있다. 로컬 개발 API `127.0.0.1:8000`과 Tailnet 개발 API `127.0.0.1:8010`도 `ENVIRONMENT=dev`로 떠 있다. diff --git a/docs/dev_dashboard.html b/docs/dev_dashboard.html index 2b95a6c..2363293 100644 --- a/docs/dev_dashboard.html +++ b/docs/dev_dashboard.html @@ -963,7 +963,7 @@ P2a RBAC/audit/visibilityapp.test_rbac_idor11 tests OK; other learner 403, read_session audit, evaluator-only hidden, teacher session review read allowed, super_admin learner review principal is promoted to admin for evaluation/worksheet/review-status loaders, admin_access-only learner remains blocked, and learner worksheet write remains 403 Admin access delegationC:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe -X utf8 -m pytest -p no:cacheprovider app/test_admin_ops.py app/test_auth_providers.py::AuthProviderScaffoldTest::test_admin_role_can_enter_admin_console_even_with_stale_access_flag app/test_auth_providers.py::AuthProviderScaffoldTest::test_admin_role_can_enter_all_role_spaces_without_super_admin app/test_auth_providers.py::AuthProviderScaffoldTest::test_admin_access_flag_without_admin_role_does_not_enter_learner_space -q / npx playwright test e2e/admin.spec.ts --project=chromium-desktop --project=chromium-mobile --workers=1 --grep "approved admin without onboarding" / npx playwright test e2e/admin.spec.ts --project=chromium-desktop --workers=1 --grep "admin route guards" / npm run typecheck13 backend focused passed + admin onboarding guard E2E 2 passed + latest admin route guards 5 passed + web typecheck/build passed. 실제 admin role은 admin_access=false 세션이어도 can_access_role(Role.ADMIN)으로 관리자 API를 열고, 승인된 관리자 또는 admin_access 계정은 /admin*에서 학습자 온보딩으로 우회하지 않는다. 최초 진입 경로도 initialPathForUser가 소유해 primary role이 learner/teacher인 관리자 권한 계정이 / 또는 로그인 완료 후 /admin으로 들어간다. 관리자 sidebar와 기존 workspace 전환 계약은 유지된다. SPA route scroll resetnpm run typecheck / npx playwright test e2e/admin.spec.ts --project=chromium-desktop --workers=1 --grep "resets scroll" / Pages deployTypecheck passed and focused E2E 1 passed. App.tsx resets document scroll to top on pathname changes, so moving from a long admin page to short /admin/access no longer preserves stale scrollY and shows only the sticky topbar/sidebar over an empty main area. Probe proof: previous scrollY=1000, after clicking 권한 scrollY=0, heading 역할, 그룹, 접근 범위 in viewport. Live custom domain now serves assets/index-R5KK7hZI.js/assets/index-DzdAkRsn.css. - Admin silent-blank diagnosticsnpm run typecheck / npx playwright test e2e/admin.spec.ts --project=chromium-desktop --workers=1 --grep "admin route guards"Typecheck passed and route-guard E2E 5 passed. Admin now normalizes malformed /admin/users payload fields such as cohort_ids=null, active_sessions=null, and created_at=null, plus malformed /admin/tickets payloads such as summary=null, instead of throwing during render. The screen shows 관리자 데이터 진단 with section/path/asset and broken field keys. App-level route render errors now fall back to a full-page diagnostic instead of a white screen, and zero-height main content still falls back to the admin diagnostic panel. + Admin silent-blank diagnosticsnpm run typecheck / npx playwright test e2e/admin.spec.ts --project=chromium-desktop --workers=1 --grep "admin route guards" / dist watchdog smokeTypecheck passed and route-guard E2E 5 passed. Admin now normalizes malformed /admin/users payload fields such as cohort_ids=null, active_sessions=null, and created_at=null, plus malformed /admin/tickets payloads such as summary=null, instead of throwing during render. The screen shows 관리자 데이터 진단 with section/path/asset and broken field keys. App-level route render errors now fall back to a full-page diagnostic instead of a white screen, and zero-height main content still falls back to the admin diagnostic panel. If the React module itself fails before mount, index.html boot watchdog appends Vignette 화면 진단 directly to body; file-based JS-missing smoke confirmed the panel appears with path/asset/bodyText/visibleNodes. OAuth/SAML admin landingC:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe -X utf8 -m pytest -p no:cacheprovider app/test_auth_providers.py -q / public runtime restart36 passed; OAuth/SAML callback now normalizes generic saved next paths such as /learn and /teach to /admin for admin-entitled users while preserving deep links such as /learn/session/.... Live proof after commits 998acb44/6b6241f4 and latest route-boundary deploy 287029f2: local/public /health returned prod, db=true, engine=true; custom domain now serves assets/index-R5KK7hZI.js/assets/index-DzdAkRsn.css; unauthenticated /personas remains 401. Session read-model DB readinesspython scripts\check-deploy-preflight.py --skip-db --env-file infra\.env.example --allow-placeholder-secrets / https://api-vignette.chanpaca.net/health2026-06-29 prod 503 원인은 운영 DB의 app.turns.provider_events 컬럼 누락이었다. 운영 DB hotfix 후 /teacher/dashboard code path는 source=database로 복구됐다. 현재 db.healthcheck(), runtime table readiness, deploy preflight DB mode는 app.turns 음성 메타 컬럼 5개(audio_ref, silence_ms, speech_rate, barge_in, provider_events), app.session_review_status worksheet 컬럼, app.safety_events 필수 컬럼을 함께 검증한다. 2026-06-30에는 Docker Desktop/DB 중단으로 public API 530/error code 1033이 재발했지만, Docker Desktop/DB 재기동 뒤 public health가 environment=prod, db=true, engine=true로 복구됐다. Admin usage persistencepython -m pytest app/test_admin_ops.py app/test_runtime_policy.py -q / authenticated local public-API smoke26 passed; /admin/usage returns 200 with source=database, durable=true. Cloudflare blocked raw Python public smoke with 1010, so app-level HTTP was verified against 127.0.0.1:8001 using the same prod process. diff --git a/docs/guides/architecture.md b/docs/guides/architecture.md index a2d529e..b42a19b 100644 --- a/docs/guides/architecture.md +++ b/docs/guides/architecture.md @@ -732,6 +732,9 @@ DB 레벨 이중강제(`04_audit_eval_rls.sql` §5, `app/db.py` `acquire()`): 패널에 깨진 필드와 asset/path를 표시한다. 앱 route-level error boundary는 관리자 컴포넌트 바깥에서 터진 렌더 예외도 full white page 대신 진단 화면으로 노출하고, 본문 0-height 상태도 route-scoped diagnostic panel로 표시해 운영자가 원인 없이 빈칸만 보지 않게 한다. +- `index.html`은 React module 실행 자체가 실패하는 경우도 full white page로 두지 않는다. 부트스트랩 watchdog은 + 3.5초/8초 시점에 body/root visible content와 관리자 본문 marker를 검사하고, 실패 시 `Vignette 화면 진단` + 패널을 React root 바깥 body에 직접 추가해 path, asset, bodyText, visibleNodes, global error를 표시한다. - OAuth/SAML callback은 저장된 `next`가 일반 진입 경로(`/`, `/learn`, `/teach`, `/login`, `/onboarding`)이고 로그인 사용자가 관리자 콘솔 접근권을 가지면 `/admin`으로 정규화한다. 단, `/learn/session/...` 같은 깊은 링크는 사용자가 의도적으로 연 URL일 수 있으므로 보존한다.