배포 검증 문서 갱신

This commit is contained in:
Yun Chan 2026-06-27 17:30:14 +09:00
parent 0ec266a761
commit 94cc56592f
2 changed files with 13 additions and 13 deletions

View file

@ -1,6 +1,6 @@
# Vignette Handoff # Vignette Handoff
> Updated: 2026-06-27 17:06 KST. 새 세션은 이 문서와 `docs/DESIGN_CONCEPT.md`를 먼저 읽고 이어가면 된다. > Updated: 2026-06-27 17:25 KST. 새 세션은 이 문서와 `docs/DESIGN_CONCEPT.md`를 먼저 읽고 이어가면 된다.
## 현재 상태 ## 현재 상태
@ -10,8 +10,8 @@
- 로컬 engine gateway 기본 포트: `http://127.0.0.1:9099` - 로컬 engine gateway 기본 포트: `http://127.0.0.1:9099`
- 공개 웹: `https://vignette.chanpaca.net` - 공개 웹: `https://vignette.chanpaca.net`
- 공개 API: `https://api-vignette.chanpaca.net` - 공개 API: `https://api-vignette.chanpaca.net`
- 직전 기록된 앱 배포 소스 commit: `6bd91b0d` (`현재 작업 상태 저장`) - 최신 앱 배포 소스 commit: `0ec266a` (`동의 게이트와 런타임 안정화`)
- 직전 기록된 Cloudflare Pages production deploy: `2a58ca49`, source `6bd91b0d` + dirty worktree, branch `main`. 최신 배포 ID는 `wrangler pages deploy` 실행 로그와 최종 작업 보고를 기준으로 확인한다. - 최신 Cloudflare Pages production deploy: `12da49f3`, source `0ec266a`, branch `main`.
- Google OAuth 허용 이메일 도메인: `hs.ac.kr`, `twentyoz.kr` - Google OAuth 허용 이메일 도메인: `hs.ac.kr`, `twentyoz.kr`
- 최신 백엔드 회귀: `python -m pytest app/ -q``125 passed` - 최신 백엔드 회귀: `python -m pytest app/ -q``125 passed`
- X1 재귀학습 export 1차: `scripts/export-recursive-dataset.py` 기본 read-only dry-run, `--write-dataset` 명시 시에만 `ds.*` write, approved export는 steward/legal/IAA gate 없으면 거부. - X1 재귀학습 export 1차: `scripts/export-recursive-dataset.py` 기본 read-only dry-run, `--write-dataset` 명시 시에만 `ds.*` write, approved export는 steward/legal/IAA gate 없으면 거부.
@ -26,10 +26,10 @@
- `frontenddesign` 스킬은 현재 세션의 사용 가능 스킬 목록에 없었다. 대신 `docs/DESIGN_CONCEPT.md`를 SSOT로 사용했다. - `frontenddesign` 스킬은 현재 세션의 사용 가능 스킬 목록에 없었다. 대신 `docs/DESIGN_CONCEPT.md`를 SSOT로 사용했다.
- 공개 API 터널은 현재 `C:\Users\encep\.cloudflared\vignette-config.yml`에서 `http://127.0.0.1:8001`을 본다. - 공개 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`로 떠 있다. - 공개용 API 프로세스는 `127.0.0.1:8001`에서 `ENVIRONMENT=prod`로 떠 있다. 로컬 개발 API `127.0.0.1:8000`과 Tailnet 개발 API `127.0.0.1:8010``ENVIRONMENT=dev`로 떠 있다.
- 2026-06-27 05:34:32 UTC 모바일 502는 cloudflared 로그상 `127.0.0.1:8001` origin connection refused와 일치한다. 현재 public API는 복구됐고, watchdog 기본 검사에서 아직 DNS가 없는 `api-vnet.18ka.net`을 제외해 향후 설치 시 불필요한 restart loop를 막았다. 2026-06-27 17:00 KST 재확인: `https://vignette.chanpaca.net/login` 200, `https://api-vignette.chanpaca.net/health` prod/db/engine true. - 2026-06-27 05:34:32 UTC 모바일 502는 cloudflared 로그상 `127.0.0.1:8001` origin connection refused와 일치한다. 현재 public API는 복구됐고, watchdog 기본 검사에서 아직 DNS가 없는 `api-vnet.18ka.net`을 제외해 향후 설치 시 불필요한 restart loop를 막았다. 2026-06-27 17:25 KST 재확인: `https://vignette.chanpaca.net/login` 200, `https://api-vignette.chanpaca.net/health` prod/db/engine true, 공개 로그인 버튼 클릭 시 `accounts.google.com` OAuth URL로 이동.
- `GET /personas`는 이제 인증 필요다. 검증 당시 로컬 `http://127.0.0.1:8000/personas`와 현재 공개 `https://api-vignette.chanpaca.net/personas` 모두 비로그인 401 확인 완료. - `GET /personas`는 이제 인증 필요다. 검증 당시 로컬 `http://127.0.0.1:8000/personas`와 현재 공개 `https://api-vignette.chanpaca.net/personas` 모두 비로그인 401 확인 완료.
- 로컬 웹이 `http://127.0.0.1:5175`처럼 다른 Vite 포트로 떠도 로그인은 `로컬 테스트 계정으로 계속` 버튼을 사용한다. Google OAuth 버튼은 로컬에서는 disabled로 둔다. 현재 OAuth callback이 공개 API로 돌아가기 때문에 로컬 Google OAuth는 로컬 세션에 붙을 수 없다. - 로컬 웹이 `http://127.0.0.1:5175`처럼 다른 Vite 포트로 떠도 로그인은 `로컬 테스트 계정으로 계속` 버튼을 사용한다. Google OAuth 버튼은 로컬에서는 disabled로 둔다. 현재 OAuth callback이 공개 API로 돌아가기 때문에 로컬 Google OAuth는 로컬 세션에 붙을 수 없다.
- 현재 확인용 프로세스는 `127.0.0.1:9099` engine gateway, `127.0.0.1:8001` prod public API, `127.0.0.1:8000` dev API, `127.0.0.1:8010` Tailnet dev API, `127.0.0.1:5173` Vite web, `cloudflared` tunnel 1개다. Tailnet URL은 `https://alpaca-home.taile93291.ts.net/login`이다. stale Vite allowedHosts로 403이 났던 상태는 `scripts/start-tailscale-runtime.ps1` 재기동 후 `login` 200, `/api/health` dev/db/engine true, direct Google start → `local_oauth_unavailable`로 재검증됐다. - 현재 확인용 프로세스는 `127.0.0.1:9099` engine gateway, `127.0.0.1:8001` prod public API, `127.0.0.1:8000` dev API, `127.0.0.1:8010` Tailnet dev API, `127.0.0.1:5173` Vite web, `cloudflared` tunnel 1개다. Tailnet URL은 `https://alpaca-home.taile93291.ts.net/login`이다. stale Vite allowedHosts로 403이 났던 상태는 `scripts/start-tailscale-runtime.ps1` 재기동 후 `login` 200, `/api/health` dev/db/engine true, direct Google start → `local_oauth_unavailable`, dev-login → `/learn` + `/api/auth/me` 200으로 재검증됐다.
- `scripts/dev-up.ps1 -NoGateway -NoWeb`는 이제 gateway/web stale 정리를 건너뛰고 지정 `-ApiPort`의 API만 재기동한다. 검증 당시 8000 API-only 재기동 후 8001/8010/5173/9099/20241 listener가 보존됐다. - `scripts/dev-up.ps1 -NoGateway -NoWeb`는 이제 gateway/web stale 정리를 건너뛰고 지정 `-ApiPort`의 API만 재기동한다. 검증 당시 8000 API-only 재기동 후 8001/8010/5173/9099/20241 listener가 보존됐다.
- Docker `vignette-dev-db`는 실행 중이고 DB는 accepting connections다. 단, 기존 컨테이너라 healthcheck가 없고 `POSTGRES_USER=vignette` 기반이다. `vignette_app` role은 NOBYPASSRLS로 존재하지만 현재 API startup DDL이 owner 권한을 요구하므로 런타임 app-role 전환은 마이그레이션 owner/런타임 role 분리 후 진행한다. - Docker `vignette-dev-db`는 실행 중이고 DB는 accepting connections다. 단, 기존 컨테이너라 healthcheck가 없고 `POSTGRES_USER=vignette` 기반이다. `vignette_app` role은 NOBYPASSRLS로 존재하지만 현재 API startup DDL이 owner 권한을 요구하므로 런타임 app-role 전환은 마이그레이션 owner/런타임 role 분리 후 진행한다.
- 공개 런타임 재기동 스크립트: - 공개 런타임 재기동 스크립트:
@ -157,8 +157,8 @@
- API 8001 health and public `https://api-vignette.chanpaca.net/health` both returned `environment:"prod"`, `db:true`, `engine:true` - API 8001 health and public `https://api-vignette.chanpaca.net/health` both returned `environment:"prod"`, `db:true`, `engine:true`
- Public web deployment: - Public web deployment:
- `wrangler pages deploy dist --project-name vignette --branch main --commit-dirty=true` - `wrangler pages deploy dist --project-name vignette --branch main --commit-dirty=true`
- Production deployment `2a58ca49` - Production deployment `12da49f3`
- `https://vignette.chanpaca.net/` serves `assets/index-BzQmv-jU.js` and `assets/index-CCZ1bDA9.css` - `https://vignette.chanpaca.net/` serves `assets/index-DEgzFAUm.js` and `assets/index-BGRtWhu_.css`
- `https://vignette.chanpaca.net/live2d/mao/Mao.model3.json`, Haru model, and Cubism core routes return 404 - `https://vignette.chanpaca.net/live2d/mao/Mao.model3.json`, Haru model, and Cubism core routes return 404
- Public login screen: - Public login screen:
- `npx playwright test e2e/auth.spec.ts --project=chromium-desktop --grep "public login"` - `npx playwright test e2e/auth.spec.ts --project=chromium-desktop --grep "public login"`

View file

@ -288,7 +288,7 @@
<span class="mchip good">Full Playwright <b>72</b> 통과</span> <span class="mchip good">Full Playwright <b>72</b> 통과</span>
<span class="mchip good">Backend pytest <b>122</b> 통과</span> <span class="mchip good">Backend pytest <b>122</b> 통과</span>
<span class="mchip good">Layout 시각게이트 <b>7/7</b> accept</span> <span class="mchip good">Layout 시각게이트 <b>7/7</b> accept</span>
<span class="mchip">Pages prod <b>2a58ca49</b></span> <span class="mchip">Pages prod <b>12da49f3</b></span>
<span class="mchip good">Public API <b>prod OK</b></span> <span class="mchip good">Public API <b>prod OK</b></span>
<span class="mchip crit">원천문서 갭 <b>critical 3</b></span> <span class="mchip crit">원천문서 갭 <b>critical 3</b></span>
<span class="mchip">Decision <b>P0 조건부</b></span> <span class="mchip">Decision <b>P0 조건부</b></span>
@ -695,7 +695,7 @@
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>프로세스 난립 정리</b><p>운영 확인용 프로세스와 로컬/Tailnet 검증용 dev 프로세스를 의도적으로 분리해 유지한다.</p></div><div><b>산출물</b><p>9099 engine, 8001 prod API, 8000/8010 dev API, 5173 web, cloudflared tunnel 1개</p></div><div><b>검증</b><p><code>Get-NetTCPConnection</code>에서 대상 포트별 listener 확인</p></div></div> <div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>프로세스 난립 정리</b><p>운영 확인용 프로세스와 로컬/Tailnet 검증용 dev 프로세스를 의도적으로 분리해 유지한다.</p></div><div><b>산출물</b><p>9099 engine, 8001 prod API, 8000/8010 dev API, 5173 web, cloudflared tunnel 1개</p></div><div><b>검증</b><p><code>Get-NetTCPConnection</code>에서 대상 포트별 listener 확인</p></div></div>
<div class="task-row"><div><span class="task-status s-plan">GATE</span></div><div><b>한신대 공문/데이터 거버넌스 게이트 정리</b><p>SSO 클레임, 추가 축어록 수급, 미성년 원본 활용동의, 개인정보 처리방침을 P1 진입 전 외부 의존성으로 명확히 둔다. 로컬 문서 골격은 준비됐지만 한신대/데이터 steward의 written evidence는 아직 필요하다.</p></div><div><b>산출물</b><p><code>docs/ops/hanshin-data-governance-gate.md</code>, 공문 질의 항목, 동의 범위 체크리스트, SSO claim mapping 표</p></div><div><b>검증</b><p>문서 artifact 작성 완료; IRB 게이트가 아니라 데이터/SSO 게이트로 외부 증거 필요 상태 유지</p></div></div> <div class="task-row"><div><span class="task-status s-plan">GATE</span></div><div><b>한신대 공문/데이터 거버넌스 게이트 정리</b><p>SSO 클레임, 추가 축어록 수급, 미성년 원본 활용동의, 개인정보 처리방침을 P1 진입 전 외부 의존성으로 명확히 둔다. 로컬 문서 골격은 준비됐지만 한신대/데이터 steward의 written evidence는 아직 필요하다.</p></div><div><b>산출물</b><p><code>docs/ops/hanshin-data-governance-gate.md</code>, 공문 질의 항목, 동의 범위 체크리스트, SSO claim mapping 표</p></div><div><b>검증</b><p>문서 artifact 작성 완료; IRB 게이트가 아니라 데이터/SSO 게이트로 외부 증거 필요 상태 유지</p></div></div>
</div> </div>
<div class="source-note"><b>팀장 판정:</b> 로컬 회귀와 공개 prod-safe 게이트는 통과했다. Cloudflare Pages production <code>2a58ca49</code>이 현재 워킹트리 dist로 배포됐고, 운영 반영 전 남은 핵심 증거는 공개 Google OAuth 실제 <code>/turn</code> smoke다. 런타임 mock/demo 자산은 운영 번들에서 제거하고 테스트 fixture만 남긴다.</div> <div class="source-note"><b>팀장 판정:</b> 로컬 회귀와 공개 prod-safe 게이트는 통과했다. Cloudflare Pages production <code>12da49f3</code>이 commit <code>0ec266a</code> dist로 배포됐고, 운영 반영 전 남은 핵심 증거는 공개 Google OAuth 실제 <code>/turn</code> smoke다. 런타임 mock/demo 자산은 운영 번들에서 제거하고 테스트 fixture만 남긴다.</div>
<div class="tab-subhead"><h4>Phase 0 완료 상세 목록</h4><p>이전 "완료" 카드 내용을 탭 안에서 항목별로 보존한다.</p></div> <div class="tab-subhead"><h4>Phase 0 완료 상세 목록</h4><p>이전 "완료" 카드 내용을 탭 안에서 항목별로 보존한다.</p></div>
<div class="task-list"> <div class="task-list">
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>DB persona 기반 테스트</b><p><code>session-persistence</code>, <code>teacher</code>, <code>voice</code><code>/api/personas</code>에서 실제 DB persona를 가져온다.</p></div><div><b>산출물</b><p>seed persona 직접 결합 제거, 실제 persona catalog 기반 E2E</p></div><div><b>검증</b><p>persona 관련 E2E가 DB-backed persona로 통과</p></div></div> <div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>DB persona 기반 테스트</b><p><code>session-persistence</code>, <code>teacher</code>, <code>voice</code><code>/api/personas</code>에서 실제 DB persona를 가져온다.</p></div><div><b>산출물</b><p>seed persona 직접 결합 제거, 실제 persona catalog 기반 E2E</p></div><div><b>검증</b><p>persona 관련 E2E가 DB-backed persona로 통과</p></div></div>
@ -764,7 +764,7 @@
<div class="tab-panel" id="panel-ops" role="tabpanel" aria-labelledby="tab-ops" hidden> <div class="tab-panel" id="panel-ops" role="tabpanel" aria-labelledby="tab-ops" hidden>
<div class="detail-head"><div><h3>운영 / 검증 상세</h3><p>실제 운영 도메인에 반영됐는지, 서버가 하나로 정리됐는지, 완료라고 말할 증거가 있는지 확인하는 탭이다.</p></div><div class="mini-metrics"><span>웹: 200</span><span>API: prod OK</span><span>Live2D: 404</span></div></div> <div class="detail-head"><div><h3>운영 / 검증 상세</h3><p>실제 운영 도메인에 반영됐는지, 서버가 하나로 정리됐는지, 완료라고 말할 증거가 있는지 확인하는 탭이다.</p></div><div class="mini-metrics"><span>웹: 200</span><span>API: prod OK</span><span>Live2D: 404</span></div></div>
<div class="task-list"> <div class="task-list">
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>운영 웹 asset 확인</b><p>커스텀 도메인이 최신 JS/CSS asset을 서빙한다.</p></div><div><b>산출물</b><p><code>index-BzQmv-jU.js</code>, <code>index-CCZ1bDA9.css</code></p></div><div><b>검증</b><p><code>Invoke-WebRequest https://vignette.chanpaca.net/login?probe=deploy-20260627</code></p></div></div> <div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>운영 웹 asset 확인</b><p>커스텀 도메인이 최신 JS/CSS asset을 서빙한다.</p></div><div><b>산출물</b><p><code>index-DEgzFAUm.js</code>, <code>index-BGRtWhu_.css</code></p></div><div><b>검증</b><p><code>Invoke-WebRequest https://vignette.chanpaca.net/login</code></p></div></div>
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>공개 API prod health</b><p>터널은 prod API 8001을 보고, API는 DB와 engine을 모두 확인한다.</p></div><div><b>산출물</b><p><code>environment:"prod"</code>, <code>db:true</code>, <code>engine:true</code></p></div><div><b>검증</b><p><code>https://api-vignette.chanpaca.net/health</code></p></div></div> <div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>공개 API prod health</b><p>터널은 prod API 8001을 보고, API는 DB와 engine을 모두 확인한다.</p></div><div><b>산출물</b><p><code>environment:"prod"</code>, <code>db:true</code>, <code>engine:true</code></p></div><div><b>검증</b><p><code>https://api-vignette.chanpaca.net/health</code></p></div></div>
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>Tailscale Tailnet 런타임</b><p>로컬 web/api/engine을 Tailscale Serve로 묶어 같은 Tailnet PC·모바일에서 HTTPS로 접근한다. Vite proxy와 API 포트가 어긋나지 않도록 <code>dev-up.ps1</code>에서 <code>VITE_API_PROXY_TARGET</code><code>ApiPort</code>와 동기화한다. Tailnet dev-origin에서는 public OAuth redirect_uri가 로컬/Tailnet 세션이 아니라 공개 API 세션으로 돌아가므로 Google 버튼과 직접 Google 시작 URL을 <code>local_oauth_unavailable</code>로 차단하고 dev-login만 사용한다. Vite proxy는 원래 프론트 host/proto를 <code>X-Forwarded-Host</code>/<code>X-Forwarded-Proto</code>로 넘긴다.</p></div><div><b>산출물</b><p><code>scripts/start-tailscale-runtime.ps1</code>, <code>AUTH_DEV_LOGIN_EXTRA_ORIGINS</code>, <code>VITE_ALLOWED_HOSTS</code>, <code>Login.tsx</code>, <code>vite.config.ts</code>, <code>auth.py</code></p></div><div><b>검증</b><p><code>https://alpaca-home.taile93291.ts.net/login</code> 200, <code>/api/auth/config</code> dev_login true, dev-login 후 <code>/api/auth/me</code> 200, direct Google start → <code>/login?oauth=local_oauth_unavailable</code>, Tailnet auth/dev-login E2E 2 passed.</p></div></div> <div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>Tailscale Tailnet 런타임</b><p>로컬 web/api/engine을 Tailscale Serve로 묶어 같은 Tailnet PC·모바일에서 HTTPS로 접근한다. Vite proxy와 API 포트가 어긋나지 않도록 <code>dev-up.ps1</code>에서 <code>VITE_API_PROXY_TARGET</code><code>ApiPort</code>와 동기화한다. Tailnet dev-origin에서는 public OAuth redirect_uri가 로컬/Tailnet 세션이 아니라 공개 API 세션으로 돌아가므로 Google 버튼과 직접 Google 시작 URL을 <code>local_oauth_unavailable</code>로 차단하고 dev-login만 사용한다. Vite proxy는 원래 프론트 host/proto를 <code>X-Forwarded-Host</code>/<code>X-Forwarded-Proto</code>로 넘긴다.</p></div><div><b>산출물</b><p><code>scripts/start-tailscale-runtime.ps1</code>, <code>AUTH_DEV_LOGIN_EXTRA_ORIGINS</code>, <code>VITE_ALLOWED_HOSTS</code>, <code>Login.tsx</code>, <code>vite.config.ts</code>, <code>auth.py</code></p></div><div><b>검증</b><p><code>https://alpaca-home.taile93291.ts.net/login</code> 200, <code>/api/auth/config</code> dev_login true, dev-login 후 <code>/api/auth/me</code> 200, direct Google start → <code>/login?oauth=local_oauth_unavailable</code>, Tailnet auth/dev-login E2E 2 passed.</p></div></div>
<div class="task-row"><div><span class="task-status s-doing">BLOCKED</span></div><div><b>공개 vnet.18ka.net 런타임</b><p><code>vnet.18ka.net</code><code>api-vnet.18ka.net</code>을 정식 공개 도메인으로 허용하도록 코드 경로는 보강했다.</p></div><div><b>산출물</b><p><code>FRONTEND_ORIGIN_MAP</code>, 공개 host별 API 매핑, <code>scripts/start-public-runtime.ps1</code> vnet ingress 옵션</p></div><div><b>블로커</b><p>현재 DNS 미해결, Cloudflare credential의 18ka.net zone 권한 불일치, Google OAuth redirect URI 등록 필요.</p></div></div> <div class="task-row"><div><span class="task-status s-doing">BLOCKED</span></div><div><b>공개 vnet.18ka.net 런타임</b><p><code>vnet.18ka.net</code><code>api-vnet.18ka.net</code>을 정식 공개 도메인으로 허용하도록 코드 경로는 보강했다.</p></div><div><b>산출물</b><p><code>FRONTEND_ORIGIN_MAP</code>, 공개 host별 API 매핑, <code>scripts/start-public-runtime.ps1</code> vnet ingress 옵션</p></div><div><b>블로커</b><p>현재 DNS 미해결, Cloudflare credential의 18ka.net zone 권한 불일치, Google OAuth redirect URI 등록 필요.</p></div></div>
@ -810,8 +810,8 @@
<tr><td>Python compile</td><td><code>python -m compileall app engine_gateway</code></td><td>Passed</td></tr> <tr><td>Python compile</td><td><code>python -m compileall app engine_gateway</code></td><td>Passed</td></tr>
<tr><td>Phase 3 artifact gates</td><td><code>python scripts\check-phase3-artifacts.py --help</code></td><td>Passed; runbook, KPI plan, dataset manifest, privacy audit checker documented</td></tr> <tr><td>Phase 3 artifact gates</td><td><code>python scripts\check-phase3-artifacts.py --help</code></td><td>Passed; runbook, KPI plan, dataset manifest, privacy audit checker documented</td></tr>
<tr><td>Web build</td><td><code>npm run build</code></td><td>Passed</td></tr> <tr><td>Web build</td><td><code>npm run build</code></td><td>Passed</td></tr>
<tr><td>Pages production deploy</td><td><code>wrangler pages deploy dist --project-name vignette --branch main --commit-dirty=true</code></td><td><code>2a58ca49</code>, source <code>6bd91b0d</code> + dirty worktree</td></tr> <tr><td>Pages production deploy</td><td><code>wrangler pages deploy dist --project-name vignette --branch main --commit-dirty=true</code></td><td><code>12da49f3</code>, source <code>0ec266a</code></td></tr>
<tr><td>Custom domain assets</td><td><code>https://vignette.chanpaca.net/login?probe=deploy-20260627</code></td><td><code>index-BzQmv-jU.js</code>, <code>index-CCZ1bDA9.css</code></td></tr> <tr><td>Custom domain assets</td><td><code>https://vignette.chanpaca.net/login</code></td><td><code>index-DEgzFAUm.js</code>, <code>index-BGRtWhu_.css</code></td></tr>
<tr><td>Legacy Live2D routes</td><td><code>/live2d/mao/* / /live2d/haru/* / /live2d/live2dcubismcore.min.js</code></td><td>404</td></tr> <tr><td>Legacy Live2D routes</td><td><code>/live2d/mao/* / /live2d/haru/* / /live2d/live2dcubismcore.min.js</code></td><td>404</td></tr>
<tr><td>Compose</td><td><code>docker compose -f infra\docker-compose.yml --env-file infra\.env.example config --quiet</code></td><td>Passed with dummy required env</td></tr> <tr><td>Compose</td><td><code>docker compose -f infra\docker-compose.yml --env-file infra\.env.example config --quiet</code></td><td>Passed with dummy required env</td></tr>
<tr><td>Focused E2E</td><td><code>admin + db-persistence + voice-success</code></td><td>10 passed</td></tr> <tr><td>Focused E2E</td><td><code>admin + db-persistence + voice-success</code></td><td>10 passed</td></tr>
@ -823,7 +823,7 @@
<tr><td>Public login</td><td><code>auth.spec.ts --grep public login</code></td><td>1 passed</td></tr> <tr><td>Public login</td><td><code>auth.spec.ts --grep public login</code></td><td>1 passed</td></tr>
<tr><td>Public runtime scripts</td><td><code>start/watch/install-public-runtime*.ps1</code></td><td>parser OK; watchdog check-only healthy. Default public checks exclude not-yet-live <code>api-vnet.18ka.net</code>; add future domains explicitly with <code>-AdditionalPublicHealthUrls</code>.</td></tr> <tr><td>Public runtime scripts</td><td><code>start/watch/install-public-runtime*.ps1</code></td><td>parser OK; watchdog check-only healthy. Default public checks exclude not-yet-live <code>api-vnet.18ka.net</code>; add future domains explicitly with <code>-AdditionalPublicHealthUrls</code>.</td></tr>
<tr><td>Public API health</td><td><code>https://api-vignette.chanpaca.net/health</code></td><td>prod, db true, engine true</td></tr> <tr><td>Public API health</td><td><code>https://api-vignette.chanpaca.net/health</code></td><td>prod, db true, engine true</td></tr>
<tr><td>Public/local/Tailnet login recovery</td><td><code>https://vignette.chanpaca.net/login</code> / <code>http://localhost:5173/login</code> / <code>https://alpaca-home.taile93291.ts.net/login</code></td><td>2026-06-27 재확인: 모바일 502 시각의 cloudflared 로그는 <code>127.0.0.1:8001</code> origin refused였고 현재 public login 200, public API health prod/db/engine true. Tailnet 403은 stale Vite allowedHosts 문제였고 <code>start-tailscale-runtime.ps1</code> 재기동 후 login 200 + `/api/health` dev/db/engine true. public Google auth redirect 302 with HttpOnly state cookie, local/Tailnet Google direct → <code>local_oauth_unavailable</code>.</td></tr> <tr><td>Public/local/Tailnet login recovery</td><td><code>https://vignette.chanpaca.net/login</code> / <code>http://localhost:5173/login</code> / <code>https://alpaca-home.taile93291.ts.net/login</code></td><td>2026-06-27 17:25 KST 재확인: 모바일 502 시각의 cloudflared 로그는 <code>127.0.0.1:8001</code> origin refused였고 현재 public login 200, public API health prod/db/engine true. Public login button click reaches <code>accounts.google.com</code> with HttpOnly state cookie. Tailnet 403은 stale Vite allowedHosts 문제였고 <code>start-tailscale-runtime.ps1</code> 재기동 후 login 200 + <code>/api/health</code> dev/db/engine true + dev-login <code>/learn</code> 진입 + <code>/api/auth/me</code> 200. local/Tailnet Google direct → <code>local_oauth_unavailable</code>.</td></tr>
<tr><td>Local 5175 login</td><td><code>PLAYWRIGHT_BASE_URL=http://127.0.0.1:5175 auth.spec.ts</code></td><td>desktop/mobile passed</td></tr> <tr><td>Local 5175 login</td><td><code>PLAYWRIGHT_BASE_URL=http://127.0.0.1:5175 auth.spec.ts</code></td><td>desktop/mobile passed</td></tr>
<tr><td>Learner/readiness E2E</td><td><code>learner.spec.ts + readiness.spec.ts desktop/mobile</code></td><td>14 passed</td></tr> <tr><td>Learner/readiness E2E</td><td><code>learner.spec.ts + readiness.spec.ts desktop/mobile</code></td><td>14 passed</td></tr>
<tr><td>Learner screenshots</td><td><code>learn-empty-desktop.png / learn-empty-mobile-compact.png</code></td><td>document overflow 0, empty history visible</td></tr> <tr><td>Learner screenshots</td><td><code>learn-empty-desktop.png / learn-empty-mobile-compact.png</code></td><td>document overflow 0, empty history visible</td></tr>