데이터 복구 배포 증거를 동기화
This commit is contained in:
parent
dba9b75a38
commit
ffadc8bd49
7 changed files with 134 additions and 14 deletions
|
|
@ -80,6 +80,7 @@
|
|||
| [`ops/public-runtime-watchdog.md`](./ops/public-runtime-watchdog.md) | 공개 런타임 워치독 설치·복구 절차(런북) |
|
||||
| [`ops/nas-preview-g8-rollback-proof-runbook.md`](./ops/nas-preview-g8-rollback-proof-runbook.md) | G8 실제 receipt-bound image rollback 런북 — Gate6 helper 격리 계약 정정(소속 기준), 실행 순서, 금지 사항, 실패 복구 |
|
||||
| [`ops/evidence/nas-preview-g8-actual-rollback-2026-08-07.json`](./ops/evidence/nas-preview-g8-actual-rollback-2026-08-07.json) | G8 실제 rollback/restore 기계 판독 증거 — helper 격리, 두 executed receipt, HMAC journal 체인, DB lifecycle 집계, 정리 상태 |
|
||||
| [`ops/evidence/google-account-data-recovery-2026-08-29.json`](./ops/evidence/google-account-data-recovery-2026-08-29.json) | Google identity alias 기반 관리자 데이터 복구의 백업·가드 트랜잭션·운영 DB·공개 런타임·Pages 승격 증거(실제 Gmail callback 미완료) |
|
||||
| [`ops/public-db-recovery-rehearsal-2026-08-07.md`](./ops/public-db-recovery-rehearsal-2026-08-07.md) | 공개 DB 데이터 교체 사고의 RLS/volume 포렌식, 이중 dump, 격리 복구·최신 migration/API 리허설과 승인형 cutover/rollback 게이트 |
|
||||
| [`ops/server-migration-runbook-draft.md`](./ops/server-migration-runbook-draft.md) | 서버 이관 런북 초안(자택→학교 정보처) — 이식성 이슈·DB dump/restore·엔진 모드 전환·검증 (초안) |
|
||||
| [`ops/pilot-usage-guide-draft.md`](./ops/pilot-usage-guide-draft.md) | 파일럿 사용 지침 초안(대학원생 ~20명) — 접속·시간 기반 회기·라이브 코칭·유의사항 (초안) |
|
||||
|
|
|
|||
10
docs/TODO.md
10
docs/TODO.md
|
|
@ -21,7 +21,7 @@
|
|||
| C-001 | **OPEN · 외부 GATE** | v2 P1 합성 사례 6건·상태별 canonical 판정기와 109·수단 상세 차단·런타임/carry-over/output `ideation_stage <= 3`·4개 공식 source 기술 사전검증은 완료. 사례별 5필드 30개, 청소년 답변 6개, 검토자·소속·자격·검토일·결정 근거·서면 증거/해시·검토 버전/사례 해시를 포함한 승인 10필드가 있어야 닫는다. 현재 `review_complete=false`; F절/B4에서 추적한다. |
|
||||
| C-002 | **DONE · 내부 기술** | 첫 회기 라포 반영·한 초점 열린 질문 체크리스트, 근거 turn 연결, 이후 회기 not-applicable |
|
||||
| C-003 | **DONE · 내부 기술** | 종료 회기 4건 미만 insufficient, 이후 최다 페르소나 비중 0.75 이상 훈련 집중 주의; 임상·공정성 판정 아님 |
|
||||
| REQ-001 | **READY · 공개 UI 승격 GATE** | provider가 이메일을 검증한 모든 Google 계정은 도메인·사전등록 없이 learner·approved로 즉시 로그인하며 원본 기능 범위는 공개 실증했다. 후속 수락기준인 단일 `Google 계정으로 로그인` CTA는 Google 계정 선택기(`prompt=select_account`)를 유지하고 `hd`를 보내지 않는다. 로컬 desktop/mobile 직접 영향 32/32, 인증 시각·경계폭 2/2, 전체 7폭 15/15, 세션 8/8, API auth 41/41을 통과했지만 현재 공개 Pages는 구 UI다. 새 Pages 승격 뒤 public 단일 CTA DOM과 실제 Gmail callback을 확인하기 전에는 개선관리 상태를 `검토`로 유지한다. |
|
||||
| REQ-001 | **READY · 실제 Gmail callback GATE** | provider가 이메일을 검증한 모든 Google 계정은 도메인·사전등록 없이 learner·approved로 즉시 로그인하며 Google 계정 선택기(`prompt=select_account`)를 유지하고 `hd`를 보내지 않는다. Pages production `ef48c0ae…` 승격 뒤 public custom domain은 단일 `Google 계정으로 로그인` CTA 1개·구 선택 버튼 0·dev login 0·console warning/error 0이고 공개 OAuth 시작 E2E 1/1을 통과했다. 실제 Gmail callback과 복구 계정의 관리자 진입·기존 21회기/64턴/평가 6건 확인 전에는 개선관리 상태를 `검토`로 유지한다. |
|
||||
| REQ-002 | **DONE · 내부 기술** | admin protocol draft→active→retired, 라이선스 C/D 외부 LLM 금지, evaluator-only RAG·rollback; migration 17 owner-run/readiness-only |
|
||||
| REQ-003 | **DONE · 내부 기술** | 페르소나 주호소가 JSON 키 순서나 surface 상태보다 우선 |
|
||||
| REQ-004 | **DONE · 내부 기술** | learner 현재 설정 AND 세션 snapshot. OFF는 AI 파생 API 403/UI 무요청·혼합 결과 redaction, 사용자 입력·privacy 보존; teacher/admin 유지 |
|
||||
|
|
@ -30,13 +30,13 @@
|
|||
| REQ-007 | **DONE · 내부 기술** | 회기 종료를 평가보다 먼저 영속화해 옛 평가 실패 뒤에도 새 회기 생성·턴 진행 가능 |
|
||||
| REQ-008 | **READY · 실제 재부팅 GATE** | 원본 응답 생성 실패는 기본 엔진+live-client readiness, 구조화 done 없는 SSE EOF의 `client_stream_incomplete` 복원과 공개 P20 실제 응답으로 닫았다. 후속 재부팅 복구는 전역 mutex·단계별 상한·프로세스 트리 종료·전체 다운 즉시복구와 PowerShell 5.1 timed `WaitForExit`의 process handle 선확보를 적용했다. 실제 PS5.1 계약 32 passed, detached-clean `44b7835c…`·tree `06133249…`, 5174 자동복구·HTTP 200, watchdog result 0/failcount 0까지 확인했지만 실제 Windows 재부팅 smoke 전에는 개선관리 상태를 `검토`로 유지한다. PC 비종속 상시 호스트 이전도 별도 운영 게이트다. |
|
||||
|
||||
현재 공개 API/engine/web/STT/TTS/cloudflared와 Pages production
|
||||
`1f1ddf18-9d5c-4d83-95a7-8cd71245e646`의 서비스 소스는 clean `2a396361…89c`다. 재부팅 복구용
|
||||
로그온·watchdog 작업은 detached-clean `44b7835c…`·tree `06133249…`로 별도 승격했고,
|
||||
현재 공개 API/engine/STT/TTS/cloudflared와 Pages production
|
||||
`ef48c0ae-374e-41fc-b6fd-03f69b26e946`의 서비스 소스는 detached-clean `dba9b75a…`·tree `14cd4607…`다. 재부팅 복구용
|
||||
로그온·watchdog 작업도 같은 root·commit·tree·script SHA로 재등록했고,
|
||||
60분 상한·`IgnoreNew`·복구 mutex·전체 다운 즉시복구·PowerShell 5.1 build 종료코드 판정을 고정했다.
|
||||
watchdog의 명시 복구 실행 뒤 웹 5174 HTTP 200, `LastTaskResult=0`, failcount 0과 local/public
|
||||
`status=ok·db=true·engine=true`를 확인했다.
|
||||
새 학습자 라포 헤더 번들은 준비됐지만 Pages 승격은 명시 승인 전이라 아직 공개되지 않았다.
|
||||
새 학습자 라포 헤더와 단일 Google 로그인 번들은 custom domain에 공개됐다. 실제 Gmail callback과 Windows 재부팅 smoke는 별도 게이트다.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -696,6 +696,8 @@
|
|||
<p class="dg-note">213차 복구·전환(2026-08-18 심야): 대청소 사건(node_modules·구 런타임 워크트리·Temp 소실)과 Docker Desktop 사망·whisper 9882 WinNAT 포트 예약·DHCP LAN IP 회수(223→224)가 연쇄 발생했다. 릴레이를 현행 루트 <code>bf5f7352</code>에 같은 4종 핀으로 재기동(r3 영수증)하고 <code>192.168.0.224:9100</code>으로 바인딩(방화벽 규칙 재발행), NAS는 실행 컨테이너 env 전량을 <code>.env(0600)</code>로 재구성해 ENGINE_URL 224 적용 후 api만 재생성했다. 클로드 예산 한도(Reached maximum budget $0.5)가 재발해 NAS ENGINE_MODE를 <code>agy_cli</code>(gemini)로 전환 — 공개는 소유자가 이미 관리자 UI로 agy 전환 완료. 결과: NAS origin health <b>3/3 status=ok·db·engine true</b>, auth 401, OpenAPI 126, <b>actual-origin 실회기 turn→review smoke 통과</b>(agy 턴 2개→종료 200→리뷰 200) — 2026-08-12 이후 처음으로 201차 current GREEN 조건을 모두 충족했다. 회기 종료 503 핫픽스(<code>bf5f7352</code>)도 소유자 실세션 종료 성공으로 운영 검증. 권고: 공유기 DHCP 예약(호스트 MAC), Docker VM 클럭 동기화(호스트 대비 ~4일 선행 — DB 타임스탬프 오염), NAS 로컬 agy 이전. 증거는 <code>docs/ops/evidence/nas-relay-recovery-2026-08-18.json</code>이다.</p>
|
||||
<p class="dg-note">214차 개선관리 워크북 동기화(2026-08-27): <b>C-002·C-003과 REQ-001~008은 내부 기술 구현 DONE</b>이다. 첫 회기 체크리스트와 종료 회기 기반 훈련 노출 지표를 추가했고, 외부 연구참여자 사전등록은 create에서 <code>pending</code> 고정 뒤 별도 PATCH 승인으로 분리했다. 프로토콜 레지스트리는 <code>draft→active→retired</code>, 라이선스 C/D의 외부 LLM 금지, evaluator-only RAG 색인·rollback을 강제하며 migration 17은 owner-run이고 startup은 readiness-only라 runtime DDL을 하지 않는다. 주호소는 surface보다 우선하고, 새 회기는 <code>persona_id/version</code>을 고정한다. 학습자 AI 피드백은 <b>현재 계정 설정 AND 세션 snapshot</b>일 때만 노출하며 OFF에서는 순수 파생 API 403·기존 share 404·혼합 응답 AI 필드 redaction·UI 파생 endpoint별 요청 0회를 적용한다. 축어록·저장 워크시트·outcome/calibration 입력·multimodal 동의/철회/삭제/raw audio/privacy 원장은 보존하고 alliance는 self-scores-only로 보여주며, privacy 삭제 조작면은 44px 이상·가로 overflow 0을 유지한다. teacher/admin 감독 뷰는 유지한다. 옛 회기 평가 실패는 degraded 리뷰로 남아도 새 회기를 막지 않는다. 헬스는 기본 엔진과 live-client를 함께 확인하고 구조화 <code>done</code> 없는 EOF는 <code>client_stream_incomplete</code>다. 상담자/내담자 이름은 prompt와 구조화 결과에서 <code>[COUNSELOR]</code>/<code>[CLIENT]</code>로 역할 마스킹한다. <b>C-001은 내부 안전 사전검증만 완료</b>했으며 임상 검토자 이름·소속 기관·검토일·서면 결정이 없어 외부 GATE로 계속 연다.</p>
|
||||
<p class="dg-note">216차 REQ-001 로그인 단순화(2026-08-29): 실제로 같은 <code>startOAuth</code>를 호출하던 학교용·다른 계정용 중복 버튼을 단일 <b>Google 계정으로 로그인</b> CTA로 통합했다. 보조문구는 <b>모든 Google 계정</b>이며 Google authorize의 <code>prompt=select_account</code>는 남겨 공유 기기에서도 계정을 고를 수 있게 하고 특정 Workspace 도메인을 강제하는 <code>hd</code>는 보내지 않는다. Google이 검증한 이메일은 도메인·사전등록 제약 없이 즉시 승인되는 기존 서버 계약을 유지한다. <b>검증</b>: auth API <b>41 passed</b>, 직접 영향 desktop/mobile <b>32 passed</b>, 인증 시각·경계폭 <b>2 passed</b>, 7폭 전체 레이아웃 <b>15 passed</b>, 세션 무회귀 <b>8 passed</b>, typecheck·lint·build 통과. 이 단일 CTA 번들은 아직 공개 미배포라 현재 공개 Pages의 기존 2버튼 DOM을 새 증거로 대체하지 않는다.</p>
|
||||
<p class="dg-note">218차 Google 계정 데이터 복구·API 승격(2026-08-29): Gmail 로그인이 새 learner로 갈라져 정식 관리자 데이터가 사라져 보인 원인은 Google subject별 <code>app_user</code>가 둘로 생성된 것이었다. 이메일 자동병합 대신 owner-managed <code>auth_identity_alias</code>를 추가해 canonical 관리자에 Google subject 2개를 명시적으로 연결하고, 세션에는 실제 로그인 이메일을 별도 기록한다. 원본 백업 SHA <code>c4725500…f2e9</code>를 <code>pg_restore --list</code>로 확인한 뒤 복제 DB에서 복구 트랜잭션을 먼저 통과시켰다. Gmail 중복 계정의 상담 데이터는 0건이지만 설정 1행이 있어 첫 시도는 가드가 전부 rollback했고, 전체 FK 스캔 뒤 겹치는 설정값이 동일함을 확인해 source-only 알림 키만 canonical에 합치고 원본 설정 행은 보존했다. 활성 DB 결과는 canonical <b>21회기·64턴·평가 6건</b>, 별칭 2·옛 Gmail 세션 0·감사로그 1·고아 0이며 사용자 1175·회기 469·평가 65를 유지한다. API+gateway <b>1111 passed/1 skipped</b>, release agent <b>31 passed</b>. detached-clean <code>dba9b75a…</code>·tree <code>14cd4607…</code>에서 API/cloudflared fresh provenance를 발행하고 public health·DB·engine·voice·OpenAPI 129·auth 401을 확인했으며 boot/watchdog을 같은 핀으로 재등록해 <code>LastTaskResult=0</code>·failcount 0이다. 브라우저의 옛 Gmail 세션은 reload 후 <code>/login</code>으로 철회됐고, 실제 Gmail callback은 사용자 계정 선택 후 최종 확인한다.</p>
|
||||
<p class="dg-note">219차 단일 Google 로그인 Pages 승격(2026-08-29): detached-clean <code>dba9b75a…</code>에서 typecheck·lint·cosmetic safety와 2회 byte-identical production build를 통과하고 최근 production 5개의 lazy asset 115개를 보존해 714파일을 Pages production <code>ef48c0ae-374e-41fc-b6fd-03f69b26e946</code>로 승격했다. custom domain은 새 entry <code>index-B1rSWL_W.js</code> SHA <code>f8411694…b5bf</code>, login chunk <code>Login-DNNQ624T.js</code> SHA <code>f010f764…9c1d</code>가 clean build와 일치하며, 단일 <b>Google 계정으로 로그인</b> CTA 1개·구 계정 선택 버튼 0·dev login 0·console warning/error 0이다. 격리 Playwright의 공개 OAuth 시작 계약도 <b>1 passed</b>이고 이전 entry는 JavaScript MIME 200으로 보존된다. 배포별 preview origin은 같은 정적 자산을 받지만 서버 연결 진단을 표시하므로 runtime GREEN으로 기록하지 않는다. <b>REQ-001은 실제 Gmail callback과 복구 계정의 관리자·21회기·64턴·평가 6건 확인 전까지 검토</b>다.</p>
|
||||
<div class="dg-principles" aria-label="디자인 생성 가드레일">
|
||||
<div><b>래스터만 사용</b><span>이미지 생성 도구 산출물은 PNG 기반 시안이다. SVG·벡터·와이어프레임·로고 시트로 해석하지 않는다.</span></div>
|
||||
<div><b>기능 우선</b><span>메인 라우트의 실제 액션과 정보 구조를 먼저 반영한다. 장식은 기능을 가리지 않는 수준에서만 쓴다.</span></div>
|
||||
|
|
|
|||
|
|
@ -983,6 +983,12 @@ DB 레벨 이중강제(`04_audit_eval_rls.sql` §5, `app/db.py` `acquire()`):
|
|||
`AUTH_EMAIL_COHORT_MAP`과 `AUTH_DOMAIN_COHORT_MAP` 설정, SAML fixture의 `cohort` claim을 합쳐
|
||||
`cohort_ids`로 세션에 저장한다. 관리 사용자 `app_user.external_id`는 provider subject 기반
|
||||
(`google:`/`saml:`/`dev:`)으로 저장해 email 변경 리스크를 줄인다.
|
||||
- 한 사람이 복수 Google subject를 가져야 할 때는 소유자가 검증한 `app.auth_identity_alias`만
|
||||
provider subject를 canonical `app_user`에 연결한다. callback은 이메일 조회보다 이 별칭을 먼저 해석하고,
|
||||
역할·코호트·온보딩·관리자 권한과 학습 데이터는 canonical 사용자에서 읽되 `auth_session.login_email`에는
|
||||
실제 로그인에 사용한 검증 이메일을 별도로 남긴다. 같은 이메일이라는 이유만으로 계정을 자동 병합하지
|
||||
않으며 inactive/suspended canonical 대상은 fail-closed한다. 별칭 테이블은 RLS SELECT 정책만 두어 런타임
|
||||
역할의 쓰기를 막고, 연결·해제는 owner-run migration/복구 절차와 감사 로그로만 수행한다.
|
||||
- 관리자 외부 연구참여자 사전등록 `POST /admin/users`는 요청값과 무관하게 항상
|
||||
`account_status=pending`으로 정확한 이메일 계정을 만든다. 승인·정지는 별도
|
||||
`PATCH /admin/users/{user_id}`에서만 수행해 create와 승인 권한 효과를 분리한다. 이는 공개 무제한 가입
|
||||
|
|
|
|||
|
|
@ -98,9 +98,10 @@ python -m pytest engine_gateway/ --collect-only -q
|
|||
| 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 구조는 유지 |
|
||||
| REQ-001 공개 운영 | `https://vignette.chanpaca.net/login` + Google account chooser/callback | 로그인 DOM ‘모든 Google 계정’, `allowed_email_domains=[]`, twentyoz.kr/Gmail 동시 노출, 기존 Google 계정 callback→`/admin` 성공. 신규 Gmail 선택은 계정 생성 행동시점 확인 뒤 별도 실행 |
|
||||
| REQ-001 공개 운영 | `https://vignette.chanpaca.net/login` + Google account chooser/callback | `allowed_email_domains=[]`이며 기존 일반 Google 계정 callback→`/admin` 성공으로 가입 범위는 확인했다. Pages production `ef48c0ae…` 승격 뒤 custom domain은 단일 `Google 계정으로 로그인` CTA 1개·구 계정 선택 버튼 0·dev login 0·console warning/error 0이고 격리 Playwright OAuth 시작 검증 **1 passed**다. 실제 Gmail callback과 복구 계정의 `/admin`·기존 데이터 확인 전에는 검토를 유지한다. |
|
||||
| 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 OAuth callback은 사용자 계정 선택 후 최종 확인한다. |
|
||||
| REQ-005·007·008 공개 운영 | P20 회기 `03dddadd-adf3-4922-acbf-31002470da52` | 생성→자기점검 원장 잠금→실제 학습자/AI 내담자 2턴→종료→AI 평가 완료→P20 1회 기록 영속→다음 회기 버튼 재노출 |
|
||||
| 운영 배포·복구 | live clean `2a396361…89c`, Pages `1f1ddf18-9d5c-4d83-95a7-8cd71245e646`; recovery task `99779a6a…fbc8` | local/public prod·db·engine·voice·web 정상. recovery root/tree/script pin, 60분·IgnoreNew·mutex·hard-down 즉시복구 계약 50 passed; watchdog·boot 수동 실행 결과 0. 실제 Windows 재부팅 smoke와 새 Pages 승격은 미실행 |
|
||||
| 운영 배포·복구 | live API clean `dba9b75a…`·tree `14cd4607…`, Pages `ef48c0ae-374e-41fc-b6fd-03f69b26e946`; boot/watchdog 동일 pin | fresh API+cloudflared provenance receipt SHA `186529be…f7a4`, 실제 API cwd가 새 detached root이며 local/public prod·db·engine·voice, OpenAPI 129, auth 401이 정상이다. boot/watchdog 실제 실행은 `LastTaskResult=0`·failcount 0이고 custom domain Pages entry SHA는 clean build와 일치한다. 배포별 preview origin은 정적 자산만 일치하고 연결 진단을 표시하므로 runtime GREEN이 아니며, 실제 Windows 재부팅 smoke는 미실행이다. |
|
||||
|
||||
로컬 Playwright는 기존 포트의 서버를 기본 재사용하지 않는다. 공개 preview나 다른 checkout의 stale bundle을
|
||||
현 작업트리 증거로 오인하지 않도록 충돌 시 fail-closed하며, 동일 dev server를 의도적으로 공유할 때만
|
||||
|
|
@ -551,6 +552,7 @@ hourly heartbeat는 최신 GREEN이 6시간 이상 오래됐거나 material mile
|
|||
- 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=line` **1/1**, `npx playwright test e2e/session-layout.spec.ts e2e/learner.spec.ts e2e/session-review.spec.ts --project=chromium-desktop --project=chromium-mobile` **46/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-layout` **8/8**, 7폭 `layout-visual-gate` **15/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 -q` **66 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 production `11b3e11f`에 배포했다. 인증된 공개 `/admin/usage?window_days=30`은 `source=database`, `durable=true`, `gemini-3.6-flash-high` 5호출·입력 35,703·출력 1,129·참조단가 `$0.062022`를 반환했고, 검증용 인증 세션은 즉시 삭제했다. 커스텀 도메인의 `AdminAi-BfxMUlzj.js`와 `AdminAi-XHkda_d4.css`는 올바른 JavaScript/CSS MIME으로 200을 반환한다.
|
||||
- 2026-08-29 Google 계정 데이터 복구 검증: migration 19가 owner-managed `auth_identity_alias`와 세션의 별도 `login_email`을 추가한다. Gmail 중복 계정은 학습 데이터 0건이지만 기본 설정 1행이 있어 최초 가드가 rollback했고, 전체 app_user FK를 전수 스캔한 뒤 canonical 설정과 겹치는 값이 동일함을 확인했다. 개정 트랜잭션은 source-only `account_approval` 알림 키를 canonical 설정에 합치고 원본 설정 행은 보존한다. 백업 복제 DB에서 alias 2·세션 21·턴 64·평가 6·활성 source session 0·고아 0으로 통과 후 복제 DB를 삭제했고, 활성 DB도 사용자 1175·회기 469·평가 65·고아 0을 유지했다. 공개 API fresh provenance는 commit `dba9b75a…`·tree `14cd4607…`, health DB/engine true, voice exact, OpenAPI 129, auth 401이며 boot/watchdog 실제 실행 결과는 0/0이다. Pages production `ef48c0ae…`의 custom domain은 clean build entry·login chunk SHA와 일치하고 단일 Google CTA E2E 1건을 통과했다. 실제 Gmail callback은 브라우저 계정 선택을 기다린다.
|
||||
- 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회` 표시를 확인하고 임시 세션을 폐기했다.
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
> 선제 구축했고, 임상 문안·평가기준·골든셋 콘텐츠는 임상팀(구훈정·어유경) 외부 정의로 받는다.
|
||||
>
|
||||
> **개선관리 워크북(2026-08-29)** — C-002·C-003과 REQ-002~007은 내부 기술·요구 실증 DONE이다. REQ-001은
|
||||
> generic Google callback까지 완료했지만 단일 CTA Pages 승격, REQ-008은 recovery task `44b7835c…`·tree
|
||||
> `06133249…`와 5174 자동복구까지 완료했지만 실제 Windows 재부팅 smoke가 남아 둘 다 `검토`다. C-001은
|
||||
> single Google CTA를 Pages production `ef48c0ae…`에 승격했지만 실제 Gmail callback, REQ-008은 recovery task
|
||||
> `dba9b75a…`·tree `14cd4607…`와 5174 자동복구까지 완료했지만 실제 Windows 재부팅 smoke가 남아 둘 다 `검토`다. C-001은
|
||||
> v2 합성 사례 6건·상태별 canonical 판정기와 자동
|
||||
> 안전 게이트의 기술 사전검증까지 완료됐다. 사례별 5필드 30개·청소년 답변 6개·검토자 이름/소속/자격을 포함한
|
||||
> 승인 10필드와 서명 증거가 없어 B4 외부 GATE로 유지하며, `review_complete=true` 전에는 완료로 닫지 않는다.
|
||||
|
|
@ -136,11 +136,6 @@
|
|||
|
||||
## B3. 소유자(윤찬) 결정 — 전건 확정(2026-06-30)
|
||||
|
||||
- [ ] **공개 Web 후보 Pages 승격 승인** — `99779a6a…fbc8`·tree `57da86aa…6b91`의 production build와
|
||||
학습자 라포 헤더 desktop/mobile focused E2E 2/2, 현행 포함 최근 production 5세대 자산 보존(최종 dist
|
||||
714파일·assets 164개)은 완료됐다. 실제 Pages upload는 코드 외부 전송 명시 승인이 없어 중단했다.
|
||||
`Cloudflare Pages production 배포 승인`을 받으면 Pages Web만 승격하고 공개 API/DB·task·NAS runtime은 건드리지 않는다.
|
||||
|
||||
> 7건(평가설계 H1 κ/ICC·환각률, s2s 2차 PoC 채택, 페르소나 음성 모델·권리, fast-loop 외부 API 허용,
|
||||
> 재귀학습 fine-tuning 범위, 운영 보존기간·에스컬레이션, 추가 축어록 수급 20건/1개월)은 **전건 확정**됐다.
|
||||
> 결정 상세는 대시보드 "윤찬 결정 상세" 탭, ADR은 `docs/decisions/`(`voice-s2s-poc.md`,
|
||||
|
|
|
|||
114
docs/ops/evidence/google-account-data-recovery-2026-08-29.json
Normal file
114
docs/ops/evidence/google-account-data-recovery-2026-08-29.json
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
{
|
||||
"schema_version": 1,
|
||||
"status": "awaiting_live_oauth_callback",
|
||||
"captured_at_utc": "2026-08-29T07:05:31.681Z",
|
||||
"scope": "Google identity alias recovery for the canonical administrator account",
|
||||
"safety": {
|
||||
"raw_google_subjects_in_artifact": false,
|
||||
"email_auto_merge": false,
|
||||
"user_rows_deleted": 0,
|
||||
"learning_rows_deleted": 0,
|
||||
"source_preferences_deleted": 0,
|
||||
"manual_email_identity_untouched": true
|
||||
},
|
||||
"backup": {
|
||||
"path": "D:\\workspace\\vignette-backups\\vignette-dev-db-vignette-20260829-062958Z.dump",
|
||||
"sha256": "c472550091075e4b4e0cc52f7f45720fbcf2ccc11cacbebe4b61f0a7d2cef2e9",
|
||||
"pg_restore_list_verified": true
|
||||
},
|
||||
"implementation": {
|
||||
"git_commit": "dba9b75a388783b11d821257e2f91fbcc30bbe1a",
|
||||
"git_tree": "14cd4607b07f93e06cef8c912694ae1da8517b8c",
|
||||
"migration": "infra/db/init/19_auth_identity_alias.sql",
|
||||
"migration_sha256": "e61299acde3967ea8a0d0552bd08ea53821ff74477ad7fd2461af05c7ae4cae7",
|
||||
"alias_rows_for_canonical_user": 2,
|
||||
"runtime_role_alias_select_verified": true,
|
||||
"runtime_role_alias_write_policy_present": false
|
||||
},
|
||||
"transaction": {
|
||||
"isolation": "serializable",
|
||||
"first_backup_clone_attempt": "rolled_back_on_unexpected_source_user_preferences_reference",
|
||||
"foreign_key_references_scanned": 67,
|
||||
"allowed_source_references": [
|
||||
"app.auth_session.user_id",
|
||||
"app.user_preferences.user_id"
|
||||
],
|
||||
"preference_merge": "source-only notification keys merged; canonical conflicts fail closed; source row preserved",
|
||||
"backup_clone_recovery_passed": true,
|
||||
"backup_clone_removed": true,
|
||||
"active_recovery_committed": true,
|
||||
"source_sessions_revoked": 1,
|
||||
"recovery_audit_events": 1
|
||||
},
|
||||
"postconditions": {
|
||||
"canonical_sessions": 21,
|
||||
"canonical_turns": 64,
|
||||
"canonical_evaluations": 6,
|
||||
"canonical_role": "admin",
|
||||
"canonical_admin_access": true,
|
||||
"source_sessions": 0,
|
||||
"source_turns": 0,
|
||||
"source_evaluations": 0,
|
||||
"source_active_auth_sessions": 0,
|
||||
"source_account_status": "suspended",
|
||||
"source_preferences_preserved": 1,
|
||||
"users": 1175,
|
||||
"sessions": 469,
|
||||
"evaluations": 65,
|
||||
"orphan_sessions": 0,
|
||||
"orphan_turns": 0,
|
||||
"orphan_evaluations": 0,
|
||||
"duplicate_external_ids": 0
|
||||
},
|
||||
"tests": {
|
||||
"ruff": "passed",
|
||||
"focused_auth_runtime": "78 passed, 1 warning",
|
||||
"api_and_gateway": "1111 passed, 1 skipped, 1 warning",
|
||||
"release_agent": "31 passed",
|
||||
"disposable_postgresql_migration": "passed including idempotent reapply and RLS write denial"
|
||||
},
|
||||
"public_runtime": {
|
||||
"release_root": "D:\\workspace\\vignette-public-runtime-dba9b75a3887",
|
||||
"clean_detached_head": true,
|
||||
"provenance_receipt": "D:\\workspace\\vignette-runtime-evidence\\dba9b75a388783b11d821257e2f91fbcc30bbe1a\\public-runtime-launch-provenance.json",
|
||||
"provenance_receipt_sha256": "186529bed76c96f2fd9a7ebcde7d81584253d130812e875782c3017bda23f7a4",
|
||||
"api_cwd_verified": true,
|
||||
"cloudflared_cwd_verified": true,
|
||||
"health": "ok",
|
||||
"db": true,
|
||||
"engine": true,
|
||||
"voice": true,
|
||||
"openapi_paths": 129,
|
||||
"unauthenticated_auth_status": 401,
|
||||
"boot_task_pinned": true,
|
||||
"watchdog_task_pinned": true,
|
||||
"watchdog_last_task_result": 0,
|
||||
"watchdog_failcount": 0
|
||||
},
|
||||
"public_web": {
|
||||
"cloudflare_pages_deployment_id": "ef48c0ae-374e-41fc-b6fd-03f69b26e946",
|
||||
"environment": "production",
|
||||
"source_commit": "dba9b75a388783b11d821257e2f91fbcc30bbe1a",
|
||||
"deployment_url": "https://ef48c0ae.vignette-b1q.pages.dev",
|
||||
"custom_domain_login_status": 200,
|
||||
"custom_domain_entry": "assets/index-B1rSWL_W.js",
|
||||
"custom_domain_entry_sha256": "f8411694ec12101b38aaef40c7804ba36a84030fc1818efc60e6f4db40a6b5bf",
|
||||
"login_chunk": "assets/Login-DNNQ624T.js",
|
||||
"login_chunk_sha256": "f010f764d64f4e4e2afdde637bbaffccdd630f9158657e5d6217bd3df47e9c1d",
|
||||
"single_google_login_cta": true,
|
||||
"legacy_google_account_selector_absent": true,
|
||||
"dev_login_absent": true,
|
||||
"custom_domain_console_warnings_or_errors": 0,
|
||||
"public_auth_e2e": "1 passed",
|
||||
"preserved_previous_entry": "assets/index-DJPgoeJy.js",
|
||||
"preserved_previous_entry_status": 200,
|
||||
"immutable_preview_static_assets_match": true,
|
||||
"immutable_preview_runtime_green": false,
|
||||
"immutable_preview_runtime_note": "The deployment-specific preview origin shows the fail-closed server connection diagnostic; the production custom domain is the accepted runtime origin"
|
||||
},
|
||||
"browser": {
|
||||
"revoked_source_session_redirected_to_login": true,
|
||||
"live_gmail_oauth_callback_verified": false,
|
||||
"remaining_gate": "User confirmation and Google account selection are required before transmitting the Gmail identity through OAuth"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue