NAS 정식 배포 완료 상태를 SSOT에 반영

This commit is contained in:
Yun Chan 2026-09-01 12:25:59 +09:00
parent dce8562089
commit 34ac1c282a
4 changed files with 121 additions and 23 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="brand"><span class="mark" aria-hidden="true"></span><span>Vignette 커맨드센터<small>팀장 리뷰 · dev status</small></span></div>
<div class="head-right"> <div class="head-right">
<a class="owner-pill" href="#owner"><span class="d" aria-hidden="true"></span>내 차례 <b id="pill-owner">0</b></a> <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-31 KST</span> <span class="stamp">2026-09-01 KST</span>
</div> </div>
</header> </header>
@ -300,9 +300,9 @@
<section class="pulse"> <section class="pulse">
<div class="pulse-main"> <div class="pulse-main">
<span class="eyebrow">project pulse</span> <span class="eyebrow">project pulse</span>
<h1>지금 위치: <em>public 도메인은 별도 호스트(Cloudflare)에서 정상 서빙 중</em>(이 PC와 분리 운영), NAS preview는 degraded. 이미지 fail-closed 후보·E2E와 C-001·REQ-008 사람 게이트가 남았다.</h1> <h1>지금 위치: <em>NAS <code>vignette-prod</code> 정식 배포 완료(SHA <code>dce85620</code>)</em> — public API/웹 모두 새 빌드 서빙, 데이터 보존 확인. 실계정 로그인 폐루프와 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>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> <p class="pulse-state"><b>현재(2026-09-01 A 경로 실배포):</b> Forgejo master <code>dce85620</code>를 NAS에서 직접 clone·빌드해 <code>vignette-prod</code> api/engine/web을 교체했고, Cloudflare Pages production도 새 빌드(<code>index-BFKGXJQi.js</code>, 이전 세대 자산 보존)로 배포했다. 사전 dump <code>cab6826b…</code>(11.0MB·TOC 1783)·env 백업·구 이미지 보존으로 롤백 경로를 고정했다. 중복 활성 회기 14쌍 30행은 최신 유지 정책으로 종료(영수증 보존·삭제 0)하고 마이그레이션 20/21/22를 온라인 적용했다. 이 과정에서 mig22의 <code>name[]=text[]</code> 캐스트 결함과 master의 게이트웨이 openai provider 누락 회귀를 발견해 즉시 교정했다(테스트 71 passed). 배포 후 public health <code>ok·db/engine true</code>, OpenAPI 200, <code>/auth/me</code> 401, dev-login 404, 데이터 집계 전후 동일(app_user 1380/sessions 618/turns 1743). 증거: <a href="./ops/evidence/nas-prod-deploy-2026-09-01.json">nas-prod-deploy-2026-09-01.json</a>. <b>다음:</b> headful 실계정 로그인→회기 폐루프(소유자 창내 로그인), C-001 외부 임상 검수, NAS 재부팅 자동복구 smoke.</p>
<details class="pulse-details"> <details class="pulse-details">
<summary>운영 복구·watchdog·배포 상세 보기</summary> <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-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>

View file

@ -212,25 +212,25 @@
> 이 PC는 개발 전용, Cloudflare는 서빙용만·tunnel 제거. **A 경로로 진행**: 실제 prod 배포 준비 → 실배포 → 로그인·작동 E2E 확인까지. > 이 PC는 개발 전용, Cloudflare는 서빙용만·tunnel 제거. **A 경로로 진행**: 실제 prod 배포 준비 → 실배포 → 로그인·작동 E2E 확인까지.
> 2026-09-01 재부팅 후 아래를 순서대로 진행. 지침: `docs/ops/deployment-pipeline.md` · 핸드오프 `docs/ops/handoff-goal-production-2026-08-29.md`. > 2026-09-01 재부팅 후 아래를 순서대로 진행. 지침: `docs/ops/deployment-pipeline.md` · 핸드오프 `docs/ops/handoff-goal-production-2026-08-29.md`.
### 재부팅 직후 (truth 재확인) ### 재부팅 직후 (truth 재확인) — **2026-09-01 완료**
- [ ] OS/셸/경로/도구 확정(AGENTS.md §0), git HEAD·worktree·status 확인 (재부팅 전 스냅샷: HEAD `be08c0b5`, Forgejo master == 로컬, 미커밋 98/untracked 27) - [x] OS/셸/경로/도구 확정, git HEAD `be08c0b5`·Forgejo master 일치·미커밋 71/untracked 27 확인
- [ ] live public API health·NAS prod(`vignette-prod`, `yunchan-nas`)·Forgejo(토큰) read-only 재확인 — `scripts/vignette-pipeline.py --check` - [x] live public API health 200(`prod`·db/engine true)·NAS `vignette-prod` 5컨테이너 확인 — `scripts/vignette-pipeline.py --check` ALL OK
- [ ] NAS prod 배포 기계장치 확정: 현재 preview용 SSH target(`100.116.83.60:8088`)을 prod(192.168.0.38)용 경로로 분리/고정 - [x] NAS prod 배포 기계장치 확정: `192.168.0.38:/volume1/docker/vignette-prod`(compose+`private/env.nas` immutable image pin), Forgejo http clone(git 컨테이너)→NAS 로컬 docker build로 고정. cloudflared는 NAS 호스트 상주(`private/cloudflared-host.yml`, api-vignette만 ingress) — public 도메인은 이 PC와 완전 분리 확인
### A-1. 배포 후보 GREEN 정리 ### A-1. 배포 후보 GREEN 정리 — **2026-09-01 완료**
- [ ] 이 PC 98미커밋/27untracked를 검토해 **기능 코드 diff만 좁게 stage**(dirty tree 전체 commit 금지 — 핸드오프 §11) - [x] 미커밋 diff를 4개 논리 커밋으로 좁게 stage(회기 연속성 API·케이스 UI·NAS infra·docs/증거), tmp/outputs 제외
- [ ] `apps/web`: `npm run typecheck`·`npm run build` / `apps/api`: 관련 test suite / SSOT checker PASS - [x] web typecheck·build PASS / API 1090 passed·gateway 71 passed / SSOT checker·design SSOT·api-types PASS
- [ ] Forgejo master로 배포 후보(hunk) 커밋을 좁게 push, 후보 SHA·tree 고정(레포: `yunchan/vignette`) - [x] Forgejo master push, 최종 배포 SHA `dce85620` 고정(github 미러 동기화 포함)
### A-2. NAS prod 실배포 (승인·백업 후) ### A-2. NAS prod 실배포 — **2026-09-01 완료**
- [ ] NAS prod 이미지 빌드·업로드→compose 교체 절차를 **prod 전용으로 설계**(영구 볼륨 `pgdata`/`apiuploads` 보존, 다운타임 허용 창, backup/rollback) - [x] prod 전용 절차 실행: pre-deploy dump `cab6826b…`(11,036,928B·TOC 1783)+env 백업+구 이미지 보존(롤백 경로), 외부 볼륨 `pgdata`/`apiuploads` 무접촉
- [ ] 소유자 실배포 승인 문구 제시 → 승인 후 NAS prod `docker-compose.nas.yml` 기준 이미지 재빌드·교체 - [x] 중복 활성 회기 14쌍·30행을 최신 유지 정책으로 종료(영수증 `receipts/stale-active-cleanup-20260901.csv`, 삭제 0) 후 마이그레이션 20/21/22 온라인 적용. mig22의 `name[]=text[]` 캐스트 결함 발견→`5a74302e` 교정. master에 게이트웨이 openai provider가 누락된 회귀 발견→구 운영 이미지에서 이식(`dce85620`, 테스트 71 passed)
- [ ] 배포 후 readiness: public API `status=ok·db/engine true`, NAS 5개 컨테이너 healthy, OpenAPI/auth 계약 확인 - [x] readiness: public health `ok·db/engine true·openai OK`, OpenAPI 200, `/auth/me` 401, dev-login 404, 컨테이너 healthy, 데이터 보존(app_user 1380/sessions 618/turns 1743/case_profile 565 전후 동일)
### A-3. 로그인·작동 E2E 검증 (실브라우저) ### A-3. 로그인·작동 E2E 검증 (실브라우저) — 진행 중
- [ ] public 도메인 로그인 화면 렌더 + Google OAuth redirect_uri 검증 (`auth.spec.ts` public) — 읽기 전용 - [x] public 로그인 화면 렌더(단일 Google CTA·콘솔 오류는 예상된 401 1건) + OAuth redirect_uri `https://api-vignette.chanpaca.net/auth/callback`·PKCE S256 검증 — headful 브라우저
- [ ] 실제 Google 계정 로그인→callback→관리자/P20 실회기·턴 생성(production DB mutation) — 소유자 승인 + 계정 세션(storageState) 필요 - [ ] 실제 Google 계정 로그인→callback→회기·턴 생성(production DB mutation) — **소유자 창내 로그인 대기**(자격증명은 소유자 직접 입력)
- [ ] 배포된 WEB 200·새 asset hash·MIME, DB/이미지 fallback 확인 → **로그인·정상 작동까지 전건 GREEN일 때만 완료** - [x] 배포 WEB: Pages production `9224a64b`, 새 entry `index-BFKGXJQi.js` 200·정상 MIME, 직전 entry `index-Bm30Ozeb.js` 200 보존(이전 세대 자산 175개 보존)
### 잔여(재부팅 외) — 그대로 유지 ### 잔여(재부팅 외) — 그대로 유지
- [ ] 실배포 완료 전까지는 아래가 열린 게이트 유지: `public API/DB·아바타 정식 승격`, `G8 실DB/public 사람 게이트`, - [ ] 실배포 완료 전까지는 아래가 열린 게이트 유지: `public API/DB·아바타 정식 승격`, `G8 실DB/public 사람 게이트`,

View file

@ -61,10 +61,38 @@ NAS 배포는 Forgejo에서 직접 pull하는 방식으로 전환하고, cloudfl
- Forgejo master == 로컬 master (배포 무결성) - Forgejo master == 로컬 master (배포 무결성)
- NAS: `docker compose ps` healthy, `/api/health` ok·db/engine true - NAS: `docker compose ps` healthy, `/api/health` ok·db/engine true
## 5. 남은 구성 작업 (TODO) ## 5. 실증된 배포 절차 (2026-09-01 1차 실배포 완료, SHA `dce85620`)
- [ ] Forgejo master를 기준으로 NAS 자동배포 스크립트/후크 구성 (또는 NAS에서 git pull) NAS 호스트에는 git이 없으므로 git 컨테이너로 Forgejo에서 clone한다.
- [ ] `origin`(github) private 유지하나 Forgejo가 SSOT임을 문서·SSOT 대시보드에 반영
- [ ] cloudflare tunnel 제거 (서빙용만 유지) ```sh
# NAS에서 (ssh yunchan@192.168.0.38)
cd /volume1/docker/vignette-prod
# 0) 백업: pg_dump custom + env.nas 사본 + 구 이미지 SHA 기록
docker exec vignette-prod-db-1 pg_dump -U vignette_owner -d vignette -Fc > backups/pre-deploy-<sha>-<ts>.dump
cp private/env.nas private/env.nas.before-deploy-<sha>
# 1) Forgejo master clone (git 컨테이너) + HEAD 검증
docker run --rm -v $PWD:/work alpine/git clone --depth 1 --branch master \
http://192.168.0.38:3000/yunchan/vignette.git /work/src-<sha>
# 2) 이미지 빌드 (API 컨텍스트는 저장소 루트)
cd src-<sha> && docker build -f apps/api/Dockerfile -t vignette-nas-api:<sha> .
docker build --build-arg VITE_API_BASE=/api -t vignette-nas-web:<sha> apps/web
# 3) 새 마이그레이션은 psql로 온라인 적용(initdb 스크립트는 재실행되지 않음),
# 구 API가 의존하는 제약을 바꾸는 마이그레이션은 stop api 후 적용
# 4) private/env.nas의 VIGNETTE_NAS_API_IMAGE/WEB_IMAGE를 새 이미지 ID로 교체
docker compose -f docker-compose.nas.yml --env-file private/env.nas config --quiet
docker compose -f docker-compose.nas.yml --env-file private/env.nas up -d
# 5) readiness: public health ok·db/engine true, OpenAPI 200, /auth/me 401, 데이터 집계 전후 동일
```
공개 웹(Cloudflare Pages)은 별도 배포한다: `apps/web`에서 `npm run build`
`node ../../scripts/preserve-assets.mjs`(이전 세대 자산 보존) →
`npx wrangler pages deploy dist --project-name vignette --branch main`.
## 6. 남은 구성 작업 (TODO)
- [ ] Forgejo master 기준 NAS 자동배포 후크(수동 절차는 §5로 실증 완료)
- [x] Forgejo가 SSOT임을 문서에 반영, github은 private 미러 유지
- [ ] cloudflare tunnel 제거 검토 — 현재 api-vignette 서빙이 NAS 호스트 상주 cloudflared 터널 경유라 제거 시 대체 ingress 필요(소유자 결정)
- [ ] 배포 시 NAS 접근 자격(SSH 키/배포 계정)을 `.env.deploy`/시크릿으로 관리 - [ ] 배포 시 NAS 접근 자격(SSH 키/배포 계정)을 `.env.deploy`/시크릿으로 관리
``` ```

View file

@ -0,0 +1,70 @@
{
"title": "NAS vignette-prod 정식 배포 (A 경로)",
"date": "2026-09-01",
"source": {
"forgejo_master": "dce85620",
"deploy_commits": [
"72353ecd 회기 연속성과 멀티 케이스 계약을 영속화",
"f1b80676 케이스 이어하기 UI와 공통 탭·이미지 복구를 반영",
"d6d9dc5f NAS 정식 스택과 배포 파이프라인 도구를 고정",
"ed3252ed 상태판·가이드와 검증 증거를 동기화",
"8931e64b NAS compose를 운영 실물 정의로 동기화",
"5a74302e 마이그레이션 22의 name 배열 비교 캐스트를 교정",
"dce85620 게이트웨이 openai provider 경로를 정식 소스로 복원"
],
"build_host": "yunchan-nas (Forgejo http clone, git container)",
"clone_verification": "HEAD dce85620 == Forgejo master"
},
"pre_deploy_backup": {
"db_dump": "/volume1/docker/vignette-prod/backups/pre-deploy-8931e64b-20260901-114915.dump",
"dump_bytes": 11036928,
"dump_sha256": "cab6826b6f4b09d7dc87c68103310daa38d367db4e05791fd925d79c0c1e53e2",
"dump_toc_entries": 1783,
"env_backup": "private/env.nas.before-deploy-8931e64b-20260901",
"rollback_images": {
"api": "vignette-nas-api:goal-nas-ssot-20260830 (sha256:7d39d211...)",
"web": "vignette-nas-web:goal-nas-ssot-20260830 (sha256:3998d90b...)"
}
},
"db_migrations": {
"duplicate_active_cleanup": {
"closed_rows": 30,
"policy": "learner-persona 쌍마다 최신 활성 회기만 유지, 이전 활성은 ended_at=now()로 종료 (데이터 삭제 없음)",
"receipt": "/volume1/docker/vignette-prod/receipts/stale-active-cleanup-20260901.csv",
"remaining_duplicate_pairs": 0
},
"applied": ["20_public_bootstrap_ticket_events.sql", "21_single_active_session.sql", "22_case_profile_multi_case.sql"],
"mig21_index_valid": true,
"mig22_fix": "attname name[] vs text[] 비교 캐스트 결함을 발견·교정 (5a74302e)"
},
"deployed_runtime": {
"api_engine_image": "vignette-nas-api:dce85620 (sha256:b0ea1ce8a047d36b63b45b1bca795d1f5687e7c9fc0deafea0827684d1de6ffc)",
"web_image": "vignette-nas-web:8931e64b (sha256:8040a6541285a95e180d81c9f06c5b22711849fa02029e824104ac7fac057f5c)",
"db_container": "미교체 (Up 45h 연속, pgdata 외부 볼륨 보존)",
"gateway_regression_found": "master에 openai provider 구현이 없어 첫 컷오버가 degraded — 구 운영 이미지에서 코드 이식·테스트 71 passed 후 재배포로 해소"
},
"post_deploy_readiness": {
"public_health": "status=ok, db=true, engine=true, engine_mode=openai, provider OK",
"openapi": 200,
"auth_me_unauthenticated": 401,
"dev_login": 404,
"internal_loopback_18080": 200,
"containers": "api/engine/db healthy, web/proxy up"
},
"data_preservation": {
"before": {"app_user": 1380, "sessions": 618, "turns": 1743, "case_profile": 565, "active_sessions": 437},
"after": {"app_user": 1380, "sessions": 618, "turns": 1743, "case_profile": 565},
"note": "active_sessions는 중복 정리 30행 종료로 437→407 (행 삭제 0)"
},
"public_web_pages": {
"deployment": "9224a64b.vignette-b1q.pages.dev (production, branch main)",
"new_entry": "index-BFKGXJQi.js / index-DT8YbZqu.css (200, application/javascript)",
"previous_entry_preserved": "index-Bm30Ozeb.js = 200",
"preserved_prior_assets": 175
},
"browser_e2e_readonly": {
"login_screen": "렌더 정상 (단일 Google CTA)",
"console_errors": "비로그인 /auth/me 401 1건뿐 (예상 동작)",
"oauth_redirect": "accounts.google.com, redirect_uri=https://api-vignette.chanpaca.net/auth/callback, PKCE S256, scope openid email profile"
}
}