상태판·가이드와 검증 증거를 동기화
Some checks failed
API contract / OpenAPI type drift (push) Failing after 1m17s

This commit is contained in:
Yun Chan 2026-09-01 11:45:27 +09:00
parent d6d9dc5f61
commit ed3252ede0
23 changed files with 197 additions and 29 deletions

View file

@ -291,7 +291,7 @@
<div class="brand"><span class="mark" aria-hidden="true"></span><span>Vignette 커맨드센터<small>팀장 리뷰 · dev status</small></span></div>
<div class="head-right">
<a class="owner-pill" href="#owner"><span class="d" aria-hidden="true"></span>내 차례 <b id="pill-owner">0</b></a>
<span class="stamp">2026-08-29 KST</span>
<span class="stamp">2026-08-31 KST</span>
</div>
</header>
@ -300,10 +300,13 @@
<section class="pulse">
<div class="pulse-main">
<span class="eyebrow">project pulse</span>
<h1>지금 위치: <em>공개 정적 Web은 유지</em>, <em>Public API·DB는 OUTAGE</em>. 이미지 fail-closed 후보·E2E와 C-001·REQ-008 사람 게이트가 남았다.</h1>
<p class="pulse-state"><b>현재:</b> 배포된 API/task 기준선은 <code>dba9b75a…</code>, Pages는 <code>0c60261e…</code>(source <code>5bf89ff…</code>)지만 Docker daemon 부재로 local 8001은 연결 거부, public API는 530이다. 아바타 decode fail-closed 변경은 후보 코드일 뿐 미배포라 운영 GREEN이 아니다. G8 차단·보류·반려·승인의 격리 local 내장 브라우저 proof는 GREEN이다. <b>다음:</b> 전체 통합 GREEN과 G8 실DB/public proof를 확인한 뒤 push/Pages/runtime/task 전환 승인을 받고, 실제 재부팅은 별도 승인으로 실행한다.</p>
<h1>지금 위치: <em>public 도메인은 별도 호스트(Cloudflare)에서 정상 서빙 중</em>(이 PC와 분리 운영), NAS preview는 degraded. 이미지 fail-closed 후보·E2E와 C-001·REQ-008 사람 게이트가 남았다.</h1>
<p class="pulse-state"><b>2026-08-31 배포 파이프라인 재정립(소유자 지시):</b> git 관리·배포는 <code>git.chanpaca.net</code>(Forgejo 11.0.16) 중심으로 이관 완료 — <code>yunchan/vignette</code> 레포 생성, <code>master</code>=<code>be08c0b5</code> push, 소스 SSOT 지정. <code>github.com</code>(origin)은 <b>private 백업/미러</b>로 유지(제거 아님). 배포 대상은 <b>NAS Production</b>(<code>docker-compose.nas.yml</code>, <code>vignette-prod</code>)이며 이 PC는 단순 개발용. Cloudflare는 <b>서비스 공개 서빙용만 유지</b>, cloudflare tunnel은 제거 대상. 관리 도구 <code>scripts/vignette-pipeline.py --check</code>로 기준선 무결성 점검(ALL OK): 문서 <a href="./ops/deployment-pipeline.md">deployment-pipeline.md</a>.</p>
<p class="pulse-state"><b>현재:</b> 2026-08-30 NAS <code>vignette-prod</code>의 DB·API·engine은 healthy이고, restart loop이던 web/proxy는 필요한 최소 capability만 추가해 재생성했다. Caddy 내부 경유 web 및 <code>/api/health</code>는 200이지만, compose가 bind하는 Tailnet 주소가 NAS에 없어 외부 private/public GREEN은 주장하지 않는다. 회기 연속성 migration 21은 중복 활성 case 14개(초과 row 28개)를 발견해 보존·종료 기준을 owner가 정할 때까지 적용하지 않는다. 아바타 decode fail-closed 변경은 후보 코드일 뿐 미배포다. <b>다음:</b> 중복 회기 정리 기준·Tailnet 복구·production 전용 clean candidate를 확정한 뒤 DB 적용, 실제 브라우저 E2E, 외부 ingress를 순서대로 검증한다.</p>
<details class="pulse-details">
<summary>운영 복구·watchdog·배포 상세 보기</summary>
<p class="pulse-state"><b>2026-08-31 사례 연속성 후보:</b> source candidate에는 같은 NPC의 <code>완전히 새로 시작</code>(빈 새 case)과 <code>이어서 진행</code>(선택 case의 압축 기억)을 분리했고, 사례별 누적 회기·상담자 가시 턴·누적 시간을 시작 전 표시하며 기억은 lazy foldout으로 숨겼다. migration 22는 legacy case unique만 해제하고 migration 21의 단일 활성 회기 제약을 유지한다. 이는 source/test 후보 상태이며 NAS DB 적용·배포·실제 production 브라우저 GREEN은 아직 하지 않았다.</p>
<p class="pulse-state"><b>2026-08-30 NAS production preflight·ingress 복구:</b> <code>vignette-prod-db-1</code>·<code>vignette-prod-api-1</code>·<code>vignette-prod-engine-1</code>은 healthy였다. <code>vignette-prod-web-1</code>은 Nginx startup에 필요한 최소 capability, <code>vignette-prod-private-proxy-1</code>는 Caddy bind에 필요한 최소 capability가 빠져 restart loop이었고, 두 서비스만 재생성해 restart 0의 running 상태와 Caddy→web/API health 200을 확인했다. DB·upload volume·API·engine·tunnel은 건드리지 않았다. 다만 configured Tailnet bind <code>100.116.83.60</code>는 NAS 인터페이스에 없어 host port가 열리지 않는다. LAN/0.0.0.0 bind로 넓히지 않았으며, Tailnet 자체 복구 또는 owner가 정한 보안 경계를 먼저 적용해야 한다.</p>
<p class="pulse-state"><b>2026-08-29 최신 read-only 상태:</b> <code>VignettePublicRuntime</code>과 watchdog은 여전히 old <code>dba9b75a…</code> root를 가리키며, Docker Desktop Linux engine pipe가 없어 DB/API 복구에 실패한다. 정적 로그인 Web 200은 동적 API 정상 증거가 아니다. 운영 변경 승인 전에는 Docker 시작·API/tunnel 재시작·task 재등록·DB/avatar URL 수정·stable upload root cutover를 하지 않는다.</p>
<p class="pulse-state"><b>2026-08-29 아바타 무결성:</b> 허용 source union은 <code>93 objects · 52,973 bytes · SHA-256 9d703126…e6aa</code>다. 실제 decode는 정상 3·실패 90, 현 DB 참조는 정상 2·실패 6·missing 0이다. 후보는 손상 bytes와 참조를 private forensic으로 보존하되 public serve는 404/fallback으로 막고, decode-valid 객체만 immutable public cache에 싣는다. 이 후보는 전체 API/Web/E2E·내장 브라우저·운영 배포 검증 전까지 GREEN이 아니다.</p>
<p class="pulse-state"><b>2026-08-29 재부팅 복구 hardening의 이전 성공 증거:</b> Windows PowerShell 5.1의 timed <code>WaitForExit()</code>가 process handle 선확보 없이는 성공한 web build의 <code>ExitCode</code>를 null로 남겨 실패로 오판하는 경계를 수정했다. 실제 PS5.1 계약 <code>32 passed</code>, detached-clean <code>44b7835c…</code>·tree <code>06133249…</code>에 로그온·5분 watchdog을 같은 핀으로 재등록했다. 당시 watchdog 3회차 복구로 5174가 자동 기동해 HTTP 200을 반환했고 후속 실행은 <code>LastTaskResult=0</code>·failcount 0, local/public health <code>status=ok·db=true·engine=true</code>였다. 기존 <code>ExecutionTimeLimit=PT1H</code>·<code>MultipleInstances=IgnoreNew</code>·exclusive mutex·bounded wait·process-tree 종료·hard-down 즉시복구 계약도 유지한다. 이것은 로그인 뒤 과거 복구 증거이며 현재 장애나 실제 Windows 재부팅 smoke를 대신하지 않는다.</p>
@ -320,7 +323,7 @@
<span class="mchip good">Outcome clean E2E <b>126</b> · visual <b>15/15</b></span>
<span class="mchip good">Alliance G1 baseline 1.2 <b>9/9</b> · 내부 DONE</span>
<span class="mchip">Tailnet <b>이전 확인 OK</b></span>
<span class="mchip crit">Public API <b>OUTAGE · 530/local refused</b></span>
<span class="mchip good">Public API(정식) <b>200 · prod · db/engine true</b></span>
<span class="mchip">이전 OpenAPI 증거 · Public API <b>126 paths · G1~G8</b></span>
<span class="mchip good">Direct-runtime preflight <b>APP ROLE OK</b></span>
<span class="mchip crit">Avatar decode <b>3 valid · 90 invalid</b></span>
@ -709,6 +712,8 @@
<p class="dg-note">220차 개선관리 완료본 현행 증거 동기화(2026-08-29): <code>Vignette_개선관리_완료.xlsx</code>의 상태·수식·서식은 유지하고 <code>개발·기능 이슈!M5:M12</code><code>임상·교육 이슈!M6:M7</code>의 오래된 API/Pages 배포 근거만 현재 <code>dba9b75a…</code>·tree <code>14cd4607…</code>·Pages <code>ef48c0ae…</code>로 갱신했다. artifact-tool import→values-only edit→export→reimport 뒤 6개 전 시트와 수정 집중 4개 렌더를 직접 확인했고 수식 오류 0, 완료 8·검토 3·총 11, 임상 checker <code>pending_external_review</code>·<code>review_complete=false</code>를 유지한다. 새 완료본 SHA는 <code>aab6ef52…e26a</code>다. 기존 C-001 사전검증 원장은 당시 14개 해시의 역사 증거로 보존하고, 현행 원장에서 기술 산출물 13/13 불변과 새 워크북 해시를 결합해 현재 패키지 14/14 연속성을 다시 확인했다. 실제 Gmail callback·Windows 재부팅 smoke·외부 임상 승인 없이 상태를 완료로 올리지 않았다. 기계 판독 증거는 <code>docs/ops/evidence/workbook-runtime-sync-2026-08-29.json</code>이다.</p>
<p class="dg-note">221차 교수자 콘솔 표면 간격 교정·공개 승격(2026-08-29): <code>/teach</code>의 검토 큐와 교수자 요약이 독립 Surface인데 부모 <code>.pf-signal-strip</code><code>gap:0</code> 때문에 테두리·그림자가 맞붙던 원인을 <code>--sp-3</code>=12px로 교정했다. 수정 전 0px를 다시 주입해 새 회귀 검사가 RED가 되는 것을 확인하고 복원 뒤 focused <b>1 passed</b>, 390~1440 7폭 교수자 gate <b>1 passed</b>, 전체 layout visual <b>15 passed</b>, session 무회귀 <b>8 passed</b>, typecheck·design SSOT·lint·build를 통과했다. tracked-clean <code>5bf89ff4…</code>·tree <code>29f76aeb…</code>에서 599파일 build 2회 byte-identical, 이전 자산 172개 보존 후 Pages production <code>0c60261e-cb37-482d-ba42-d91586194c48</code>로 승격했다. 실서비스 1425×1272 DOM은 computed/실측 간격 12px·overflow 0·console warning/error 0이고 custom/preview의 <code>Professor-BfR874KA.css</code> SHA <code>63dbe2c7…7fff</code>가 clean build와 일치한다. 증거는 <code>docs/ops/evidence/professor-summary-gap-deploy-2026-08-29.json</code>이다.</p>
<p class="dg-note">222차 REQ-001 완료·개선관리 재생성(2026-08-29): 기존 실서비스 인증 세션은 <code>yunchan8804@gmail.com</code>으로 설정·관리자 콘솔에 접근하고 온보딩으로 되돌아가지 않으며, 학습 이력 20건·리뷰 필요 6건을 표시했다. 소유자가 복구 데이터 가시성을 수락했고 최신 Pages <code>0c60261e…</code>의 단일 Google CTA 1개·구 선택 버튼 0·dev login 0·무제한 도메인·공개 Playwright 2 passed를 함께 확인해 REQ-001을 완료로 승격했다. 개선관리 완료본은 artifact-tool 구조 추출로 <b>6시트·11개 고유 요구·38수식·오류 0·완료 9/검토 2</b>를 확인했다. C-001은 패키지 8/8·기술 사례 6/6·canonical checker 정상의 <code>pending-valid</code>이며 외부 입력 46칸은 공란으로 보존했다. 완료본 SHA는 <code>c832547f…d8bd</code>, sidecar SHA는 <code>8c9618f0…a610</code>이고 REQ-008 실제 Windows 재부팅 smoke와 C-001 외부 임상 승인만 검토로 남는다.</p>
<p class="dg-note">223차 REQ-007 회기 연속성 보강(2026-08-30, source-only): 종료 기록의 모호한 <code>다시 연습</code> 행동을 <code>다음 회기 이어가기</code>로 바꾸고, 같은 learner-persona case에 진행 중 회기가 있으면 API가 <code>409 active_session_exists</code>와 기존 session id를 돌려 브라우저가 그 회기로 이동하게 했다. 종료·시작은 동일 case row를 먼저 잠가 S2가 S1의 summary/carry를 빠뜨리지 않으며, migration 21의 online partial unique index와 invalid/wrong-index 전·후 condition이 병렬 active row를 fail-closed로 막는다. 종료 영속 뒤 다음 회기는 평가 완료를 기다리지 않는다. 연속성 대상 API <b>87 passed</b>(현 작업트리의 별도 stale-evaluation timeout 기대치 1건 제외), release agent <b>32 passed</b>, Playwright history <b>14 passed</b>·continuity guard <b>1 passed</b>, typecheck·API type check·production build를 확인했다. 기존 DB migration 21은 owner의 duplicate/target-index read-only audit 및 명시 적용이 필요하며, 공개/NAS/DB에는 적용·배포하지 않았다.</p>
<p class="dg-note">224차 NAS production preflight·ingress 복구(2026-08-30): 운영 DB에는 같은 learner-persona의 중복 활성 case <b>14개</b>(초과 active row <b>28개</b>)가 있어 migration 21의 partial unique index를 적용하지 않았다. 과거 회기 중 turn이 있는 row를 자동 종료하거나 삭제하지 않고 owner의 보존·종료 기준을 기다린다. 운영에는 이미 transactional <code>20_public_bootstrap_ticket_events.sql</code>이 있으므로 새 online index는 <code>21_single_active_session.sql</code>로 번호를 분리했다. NAS web/proxy restart loop은 최소 capability를 추가해 두 서비스만 재생성하고 내부 Caddy→web/API health 200으로 복구했지만, configured Tailnet bind 주소가 NAS에 존재하지 않아 외부 private/public GREEN은 아니다. preview 전용 release agent·기존 8/7 manifest를 production 경로로 재사용하지 않으며, 연속성 hunk와 migration 20/21을 명시한 clean production candidate가 별도로 필요하다.</p>
<div class="dg-principles" aria-label="디자인 생성 가드레일">
<div><b>래스터만 사용</b><span>이미지 생성 도구 산출물은 PNG 기반 시안이다. SVG·벡터·와이어프레임·로고 시트로 해석하지 않는다.</span></div>
<div><b>기능 우선</b><span>메인 라우트의 실제 액션과 정보 구조를 먼저 반영한다. 장식은 기능을 가리지 않는 수준에서만 쓴다.</span></div>
@ -1119,7 +1124,7 @@
<tbody>
<tr><td>Web typecheck</td><td><code>npm run typecheck</code></td><td>Passed</td></tr>
<tr><td>Design SSOT / auth visual</td><td><code>npm run check:design-ssot</code> / <code>npx playwright test e2e/auth-visual.spec.ts --project=chromium-single-run --reporter=line</code> / <code>npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --reporter=line</code></td><td>SSOT checker passed; login/onboarding light-dark desktop-mobile 1 passed; 14 core screens × 7 widths visual gate 14 passed.</td></tr>
<tr><td>Full Playwright E2E baseline</td><td><code>npm run e2e:parallel</code> / <code>npm run e2e:single-run</code> / <code>npm run e2e:list</code></td><td>2026-08-29 현재 수집은 <b>1158 tests / 64 files</b>다. 이 숫자는 수집량이며 현 작업트리 전체 GREEN과 동일하지 않다. G8 clean-head release gate는 candidate 112/112와 실제 NAS-origin 112/112를 통과했다. 이전 단일 120/120과 2026-07-15의 fixture desktop/mobile 166/166 + DB/engine/provider 직렬 49/49 = 215/215는 범위가 다른 역사 기준선으로 보존한다.</td></tr>
<tr><td>Full Playwright E2E baseline</td><td><code>npm run e2e:parallel</code> / <code>npm run e2e:single-run</code> / <code>npm run e2e:list</code></td><td>2026-08-31 현재 수집은 <b>1182 tests / 66 files</b>다. 이 숫자는 수집량이며 현 작업트리 전체 GREEN과 동일하지 않다. G8 clean-head release gate는 candidate 112/112와 실제 NAS-origin 112/112를 통과했다. 이전 단일 120/120과 2026-07-15의 fixture desktop/mobile 166/166 + DB/engine/provider 직렬 49/49 = 215/215는 범위가 다른 역사 기준선으로 보존한다.</td></tr>
<tr><td>Refactor governance P1~P8</td><td><code>ruff check app</code> / <code>pytest -q app</code> / <code>pytest -q engine_gateway</code> / <code>npm run typecheck</code> / <code>npm run check:api-types</code> / <code>npm run check:design-ssot</code> / <code>npm run check:dead-code</code> / <code>npm run check:duplication</code> / <code>npm run build</code> / <code>npm audit --audit-level=high</code> / full Playwright</td><td>Backend 400 passed, gateway 29 passed, web gates/build/audit passed, vulnerabilities 0, production duplication 1 clone/15 lines/0.03%, Playwright 215/215 passed. 상세 근거는 <code>ops/refactor-governance-2026-07-15.md</code>.</td></tr>
<tr><td>API typegen SSOT</td><td><code>npm run check:api-types</code></td><td>Passed; FastAPI OpenAPI → <code>src/lib/api.gen.ts</code> stale check</td></tr>
<tr><td>Outcome &amp; Alliance OS G0</td><td><code>py -3.11 -X utf8 -m pytest -p no:cacheprovider apps/api/app/test_measurement_contract.py apps/api/app/test_runtime_schema_ssot.py -q</code> / <code>scripts/check-measurement-ledger.sql</code> / measurement·API contract checks / web typecheck / DB-backed <code>session-persistence</code> focused E2E 3종</td><td>G0 contract/schema 11 passed, 기존 backend 100 passed, auth 39 passed. Python→JSON Schema→TypeScript→PostgreSQL enum·필수필드 계약이 일치하고 8개 deterministic benchmark가 검증됐다. Live PostgreSQL에서 learner/client/evaluator 가시 행 1/1/2, 교차 누수 0, append-only guard 2를 확인했다. 학습자 턴→교수자 대시보드, 워크시트 검수, 종료 deep 평가→durable 리뷰 E2E는 각각 1 passed. G0/AOS-001~004 완료.</td></tr>
@ -1216,10 +1221,10 @@
<tr><td>Public OAuth start</td><td><code>/auth/config</code> + <code>/auth/login?provider=google</code></td><td>2026-08-28 public auth config 200, Google configured true, <code>allowed_email_domains=[]</code>, redirect URI <code>https://api-vignette.chanpaca.net/auth/callback</code>, dev-login disabled. 로그인 DOM은 모든 Google 계정을 명시하고 실제 Google 선택기에 <code>yunchan@twentyoz.kr</code><code>yunchan8804@gmail.com</code>이 함께 노출됐다. 기존 Google 계정 선택→callback→<code>/admin</code> 성공을 확인했다. 신규 Gmail 선택은 Vignette 계정 생성이므로 사용자 행동시점 확인 뒤 별도 실증한다.</td></tr>
<tr><td>Persona auth boundary</td><td><code>GET /personas</code></td><td>2026-06-30 복구 후 public unauth <code>/personas</code>는 401 <code>not authenticated</code>를 반환한다.</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 · source pin</td><td><code>start/watch/boot-public-runtime*.ps1</code> · <code>install-public-runtime-task.ps1</code> · <code>register-boot-task.ps1</code></td><td>2026-08-29 최신 read-only 확인에서 두 task는 old detached <code>dba9b75a…</code> runtime을 계속 가리킨다. Docker daemon 부재로 runtime task는 실패했고 watchdog도 DB/API를 복구하지 못한다. 새 total-size/decode receipt 후보는 미배포이며, 후보 통합 GREEN·사용자 승인 전 task 재등록이나 runtime 재시작을 하지 않는다. 실제 Windows 재부팅 자동복구 smoke도 별도 운영 gate다.</td></tr>
<tr><td>Public API health</td><td><code>http://127.0.0.1:8001/health</code> / <code>https://api-vignette.chanpaca.net/health</code></td><td><b>2026-08-29 최신 상태는 OUTAGE다.</b> local 8001은 연결 거부, public API는 530이며 Docker Desktop Linux engine pipe가 없다. 정적 public Web 200은 API·DB GREEN을 뜻하지 않는다. 2026-08-28의 <code>status=ok·db=true·engine=true</code>와 voice exact 값은 과거 성공 증거로만 보존한다.</td></tr>
<tr><td>Public runtime current snapshot</td><td><code>health + provenance + Scheduled Tasks + Pages</code></td><td>배포된 동적 런타임/task 기준선은 clean commit <code>dba9b75a…</code>·tree <code>14cd4607…</code>, Cloudflare Pages production <code>0c60261e-cb37-482d-ba42-d91586194c48</code>(source <code>5bf89ff4…</code>)다. 현재 API/DB는 장애이고 fail-closed avatar 후보와 새 upload receipt는 아직 이 기준선에 승격되지 않았다. 후보와 deployed baseline을 분리하며, 승인 전 Docker/API/tunnel/task/DB/upload-root mutation을 하지 않는다.</td></tr>
<tr><td>Public/local/Tailnet login recovery</td><td><code>https://vignette.chanpaca.net/login</code> / <code>https://api-vignette.chanpaca.net/health</code> / <code>https://alpaca-home.taile93291.ts.net/login</code></td><td>2026-08-28 Google callback→관리자·P20 실제 회기 폐루프는 유효한 과거 증거다. 2026-08-29에는 public API 530으로 신규 callback·관리자·G8 실DB E2E를 재검증할 수 없으므로 현재 운영 GREEN으로 재사용하지 않는다. public Web은 정적 로그인 화면만 200이다.</td></tr>
<tr><td>Public runtime scripts · source pin</td><td><code>start/watch/boot-public-runtime*.ps1</code> · <code>install-public-runtime-task.ps1</code> · <code>register-boot-task.ps1</code></td><td>이 행은 <b>이 PC의 Windows 런타임/task 기준</b>이다. 2026-08-31 실측에서 이 PC에는 <code>cloudflared</code> 프로세스가 없고 <code>VignettePublicRuntime</code>/<code>Watchdog</code> 두 Scheduled Task는 Disabled이며 로컬 <code>8001</code> listener도 없다 — 이 PC는 더 이상 public 호스트 역할을 하지 않는다. public 도메인은 별도 호스트에서 정상 서빙 중(health 200). 새 avatar fail-closed receipt 후보는 여전히 미배포이며, 사용자 승인 전 이 PC의 task 재등록·runtime 재시작을 하지 않는다. 실제 Windows 재부팅 자동복구 smoke는 별도 운영 gate다.</td></tr>
<tr><td>Public API health</td><td><code>http://127.0.0.1:8001/health</code> / <code>https://api-vignette.chanpaca.net/health</code></td><td><b>2026-08-31 실측: public 도메인은 200·<code>environment=prod</code>·<code>db=true</code>·<code>engine=true</code>·<code>engine_mode=openai</code>로 정상 응답</b>한다(<code>Server: cloudflare</code>, 이 PC가 아닌 별도 호스트가 서빙). 이 PC 로컬 <code>127.0.0.1:8001</code>은 listener 없음(이 PC가 public 호스트 역할을 안 하므로)이며, 이는 도메인 장애가 아니라 <b>개발 워크스테이션과 정식 운영의 분리</b>를 반영한다. NAS preview 8088은 별개로 <code>degraded</code>다. 2026-08-29 OUTAGE 기록은 그 시점의 이 PC 기준 스냅샷으로, 오늘 실측이 이를 대체한다.</td></tr>
<tr><td>Public runtime current snapshot</td><td><code>health + provenance + Scheduled Tasks + Pages</code></td><td>과거 이 PC 배포 기준선은 clean commit <code>dba9b75a…</code>·tree <code>14cd4607…</code>, Pages production <code>0c60261e</code>(source <code>5bf89ff4…</code>)다. 2026-08-31 실측상 public 도메인은 별도 호스트에서 200·prod·db/engine true로 동작 중이고, 이 PC는 개발 워크스테이션일 뿐이다(과거 이 PC 기준선의 '장애'는 이 PC 미사용에 따른 잔여 스냅샷). fail-closed avatar 후보와 새 upload receipt는 아직 승격되지 않았다. 후보와 deployed baseline을 분리하며, 승인 전 Docker/API/tunnel/task/DB/upload-root mutation을 하지 않는다.</td></tr>
<tr><td>Public/local/Tailnet login recovery</td><td><code>https://vignette.chanpaca.net/login</code> / <code>https://api-vignette.chanpaca.net/health</code> / <code>https://alpaca-home.taile93291.ts.net/login</code></td><td>2026-08-28 Google callback→관리자·P20 실제 회기 폐루프는 유효한 과거 증거다. <b>2026-08-31 실측(Playwright chromium): public 로그인 화면 폐루프 1 passed, 데스크톱/모바일 HTTP 200, Google OAuth redirect_uri=<code>https://api-vignette.chanpaca.net/auth/callback</code> 확인, dev-login 차단, 예상된 <code>/auth/me</code> 401 외 콘솔/페이지 에러 0</b> — 읽기 전용 실증 완료(<a href="./evidence/public-domain-readonly-browser-2026-08-31.json">증거</a>). 실제 Google 계정 로그인→callback→관리자·P20 실회기·턴 생성은 production DB mutation이라 소유자 승인·계정 세션(storageState)이 필요해 여전히 열린 게이트다.</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 growth header</td><td><code>uc-learner-home-dashboard.spec.ts --grep "성장 지표 라포 헤더"</code></td><td>2026-08-28 chromium desktop/mobile 2 passed. 라포 문구 부모를 inset surface에서 plain div로 바꾸고 computed background transparent·border/radius/padding 0을 고정했다. typecheck·design SSOT·cosmetic filter safety도 통과했으며 Pages에는 아직 미배포다.</td></tr>