G7 증명과 G8 clean-head 승격 준비
This commit is contained in:
parent
94c681d450
commit
5221f79e3f
52 changed files with 6876 additions and 506 deletions
|
|
@ -13,7 +13,7 @@
|
|||
> 선제 구축했고, 임상 문안·평가기준·골든셋 콘텐츠는 임상팀(구훈정·어유경) 외부 정의로 받는다.
|
||||
>
|
||||
> **Outcome & Alliance OS** — 2026-08-06 정식 전략 실행 트랙으로 승격했다. G0 Measurement Truth, G1
|
||||
> 현재 소스·실행 증거 재감사에서는 G0~G6이 internal DONE이다. G1 승격 prompt 1.2+read-skew/JSON 복구는 24/24 ready·방향 9/9·오류 0을 재확인했고, G0 census 29/29·위반 0, G4/G5 실제 API/DB/브라우저 폐루프, G6 safety metadata-only 최우선 runtime을 disposable clone에서 확인했다. G8은 2026-08-07 격리 NAS에서 실제 receipt-bound image rollback 2회(`nas-g8-723eeef2…`/`nas-g8-2738846c…`)를 executed lifecycle receipt로 실행해 DONE이다. 과거 clean release/NAS의 route·asset·auth·browser 증거는 배포 존재 이력으로 보존하지만 현재 소스의 완료 증거로 재사용하지 않는다. G7 Multimodal Alliance는 내부 구현 DONE과 외부 proof GATE를 분리하며 Deepgram key·quota live, expected provider/model authenticated public WSS, 명시 동의 물리 마이크·50분 soak, 독립 라벨 voice-gain benchmark와 운영 topology resource high-water를 추적한다. 운영 TTS는 OpenAI API `gpt-4o-mini-tts`로 고정하고 공식 앱 통합·출력 소유 근거와 AI 생성 음성 고지를 반영했다. G0~G8과
|
||||
> 현재 소스·실행 증거 재감사에서는 G0~G6이 internal DONE이다. G1 승격 prompt 1.2+read-skew/JSON 복구는 24/24 ready·방향 9/9·오류 0을 재확인했고, G0 census 29/29·위반 0, G4/G5 실제 API/DB/브라우저 폐루프, G6 safety metadata-only 최우선 runtime을 disposable clone에서 확인했다. G8의 실제 receipt-bound image rollback 2회(`nas-g8-723eeef2…`/`nas-g8-2738846c…`)는 executed lifecycle receipt로 완료됐지만, 같은 NAS SHA의 평문 origin 회기 E2E 24건 실패 때문에 UUID 수정 배포·NAS-origin 재검증 전까지 전체 상태는 runtime REVALIDATION이다. 과거 clean release/NAS의 route·asset·auth·browser 증거는 배포 존재 이력으로 보존하지만 현재 소스의 완료 증거로 재사용하지 않는다. G7 Multimodal Alliance는 내부 구현 DONE과 외부 proof GATE를 분리하며 current source 공개 배포, 운영 기본 `local_whisper`/`melotts` ready, authenticated public WSS, 명시 동의 물리 마이크·50분 soak, 독립 라벨 voice-gain benchmark와 운영 topology resource high-water를 추적한다. 외부 Deepgram/OpenAI adapter는 fallback으로 보존한다. G0~G8과
|
||||
> AOS-001~012는 `docs/TODO.md` I절에서 전건 추적하고, 상태는 SSOT 대시보드의 9개 계획 카드가 소유한다.
|
||||
> 이 얇은 백로그에는 그중 외부·환경 증거가 필요한 항목만 기존 B2/B4/Phase 3 게이트와 합쳐 유지한다.
|
||||
|
||||
|
|
@ -24,6 +24,17 @@
|
|||
- [ ] **운영 티켓 자동 분류·처리 후속** — Claude Recipe headless 자동 수정 후보, 관리자 승인 후 이슈 등록·PR/작업
|
||||
스레드 생성, 처리 결과 audit trail 확장은 아직 설계/승인 필요. 담당 그룹 자동 배정·우선순위 escalation·raw/rollup
|
||||
보존기간 같은 운영 정책은 B3에서 이미 결정됨(수동 승인·미도입 고정). 자동 수정은 운영자 승인 전까지 실행하지 않는다.
|
||||
- [ ] **G8 clean-head 릴리스와 NAS-origin 재검증** — explicit clean-head mode와 source-only UUID gate는
|
||||
unit 30/30·UUID 6/6으로 구현됐다. 2026-08-09 execute 두 번은 각각 archive CRLF와 session E2E에서
|
||||
fail-closed되어 NAS mutation 0이었다. timezone 없는 `started_at`의 약 9시간 오차는 UTC/KST 계약, API 전체
|
||||
921 passed, KST focused DB/browser 1/1로 수정했다. 사용자 승인 아래 새 clean commit의 HEAD/tree/archive를
|
||||
결속한 뒤 격리 NAS preview에 승격해 실제 평문 origin의 전체 회기 E2E·SSE→DB review·student returned-practice를
|
||||
0 failure로 재검증한다. fixture-only desktop/mobile 묶음은 16:49 KST 단일 120/120 뒤 dashboard 변경분 10/10을
|
||||
따로 재검증해 current tree는 불변 110 + 최신 dashboard 10의 분할 GREEN이며, clean HEAD에서 exact 120을 다시
|
||||
실행한다. 어느 쪽도 NAS runtime 증거를 대신하지 않는다. release agent는 DB dump/restore를 지원하지 않으므로 매 execute 직전 fresh custom dump의
|
||||
SHA/size/TOC와 DB identity를 별도 증거로 결속한다. agent 자동 rollback은 image/Compose/active-state 범위뿐이며,
|
||||
적용 migration/data restore는 별도 owner 승인을 받는다. remote release root/Compose mode `0777` P1도 최소권한
|
||||
preflight로 닫는다. 이때만 G8 전체 DONE이 가능하다.
|
||||
|
||||
> B1 완료 항목(학생 리뷰 1~5 척도 320px 반응형 재배치, 셸 구분선, 세션 종료 UX/다크테마, 아바타 SVG 리그 복귀와 래스터 비활성화, SEO/공유 카드, 레이아웃 정렬, 권한 위임,
|
||||
> 메일링, 아카이브 API, TTS voice map, 빈상태 레이아웃, 평가 실패 복구 UX, SSE 저장, live-coach 표면화,
|
||||
|
|
@ -34,10 +45,18 @@
|
|||
## B2. 환경 제약 — 이 워크스테이션에서 증거 생산 불가
|
||||
|
||||
- [ ] **공개 DB 계정·회기 복구 안정화** — 2026-08-07 18:26 KST owner 승인으로 recovered named volume을
|
||||
`vignette-dev-db:55432`에 전환했다. public owner 집계 users 84, sessions 30, turns 705, Google 계정 16,
|
||||
`vignette-dev-db:55432`에 전환했다. cutover owner 집계 users 84, sessions 30, turns 705, Google 계정 16,
|
||||
Google 소유 회기 30, orphan 0이며 health·engine·OAuth·watchdog가 정상이다. 기존 인증 세션은 복사하지
|
||||
않아 사용자는 Google 재로그인이 필요하다. current dump·old-original dump·전환 직전 rollback container를
|
||||
모두 보존하며, 실제 사용자의 소유 회기 확인과 자동 백업 운영화 전에는 제거하지 않는다.
|
||||
2026-08-09 Docker Desktop 중단과 18:02:55 KST Docker Desktop UI의 active DB 직접 stop으로 `db=false`가
|
||||
재발했지만 동일 recovered container/volume만 재기동했다. 현재 owner read-only 집계 users 356/sessions 193/
|
||||
turns 726, Google users 16/Google-owned sessions 31이고 public db/engine true다. watchdog·로그온 boot의
|
||||
detached-clean commit/tree/script SHA pin은 provenance 11개를 포함한 combined 22/22로 완료했지만 실제 task action은 아직
|
||||
shared worktree다. watchdog 반복 실행은 필수 pin 인자 부재로 mutation 전에 exit해
|
||||
`LastTaskResult=1`이며, 18:05 KST 복구 뒤 local/public health는 `status=ok·db=true·engine=true`, 공개 OpenAPI 119 paths,
|
||||
`/voice/health` OpenAI STT/TTS, local voice sidecar listener 9882/9883은 0이다.
|
||||
clean commit 뒤 두 task를 같은 stable release root로 재등록하기 전까지 자동복구는 fail-closed 상태다.
|
||||
상세: `docs/ops/public-db-recovery-rehearsal-2026-08-07.md`.
|
||||
- [ ] **DB 백업 운영화** — 검증형 one-shot custom dump와 실제 일회용 restore drill은 통과했다. 예약 실행,
|
||||
실패 알림, NAS/off-host 암호화 복제, restore drill 주기와 보존 정책을 운영 게이트로 남긴다.
|
||||
|
|
@ -53,7 +72,7 @@
|
|||
- [ ] **공개 Google OAuth 실제 `/turn` proof** — 로그인 가능한 계정으로 `storageState` 캡처 후 `E2E_PUBLIC_AUTH=1`
|
||||
+ `chromium-public-auth` 1회 통과 필요. (소유자 지시로 보류 중.) 실행 명령은 대시보드 "다음 실행 명령" 참조.
|
||||
- [ ] **음성 캐스케이드 live** — 공개 G7 API route와 TLS 1.3 WSS handshake, 비인증 1008 차단, 로컬
|
||||
synthetic short soak는 통과했다. Deepgram streaming adapter/interim/final/word timestamp, HMAC word pseudonym,
|
||||
synthetic short soak는 통과했다. Deepgram/OpenAI fallback adapter와 운영 기본 `local_whisper`/`melotts`의 interim/final/word timestamp, HMAC word pseudonym,
|
||||
streaming 중 1초 동의 재검사, 공통 consent transaction lock, bounded queue/10MiB cap, 텍스트 보존·음성 재연결
|
||||
UX, `ready` provider/model 계약은 code/internal 완료다. actual-Postgres 동시 철회는 writer 대기→커밋 뒤 차단과
|
||||
timeline row 0, synthetic gold benchmark는 text-only 1-MAE `0.9533`→multimodal `0.9673`을 확인했다.
|
||||
|
|
@ -61,21 +80,42 @@
|
|||
마이크 열거·캡처 없이 검사한다. full soak는 `--confirm-physical-capture` 없이는 장치를 열기 전에 exit 3이다.
|
||||
Session 첫 음성 사용은 30일·원음 미보존 서버 동의 원장을 먼저 기록하며, 성공 전 `getUserMedia`와 voice
|
||||
WebSocket은 0회다. public runner v4의 turn별 interim/speech-final/latency, 관리자 single-worker runtime
|
||||
high-water, exact image/container/cgroup/proc/Docker/TCP topology sampler, 30명/50회기/150축 독립 blind
|
||||
human-held-out voice-gain evaluator와 이를 같은 public host·동시 50분 시간창으로 묶는 canonical checker까지
|
||||
high-water, exact image/container/cgroup/proc/Docker/TCP topology sampler, held-out 30명 외 calibration split
|
||||
참가자를 포함한 총 최소 31명/held-out 50회기/150축 독립 blind human-held-out voice-gain evaluator와 이를
|
||||
같은 public host·동시 50분 시간창으로 묶는 canonical checker까지
|
||||
code/internal 준비를 마쳤다.
|
||||
운영 TTS는 OpenAI API `gpt-4o-mini-tts`와 공식 Services Agreement 근거, 회기 전·중 AI 생성 음성 고지로
|
||||
닫았다. 남은 범위: 실제 Deepgram 운영 key·quota의 live interim/final, 기대값과 일치하는 authenticated public
|
||||
운영 TTS는 MeloTTS Korean(MIT) loopback으로 전환했고 외부 OpenAI adapter와 회기 전·중 AI 생성 음성 고지는
|
||||
보존했다. 남은 범위: current source 공개 배포, `local_whisper`/`melotts`의 live interim/final과 기대값이 일치하는 authenticated public
|
||||
ready, 실행 직전 명시 동의 물리 마이크와 50분 양방향 soak, 독립 라벨 held-out/pilot voice-gain benchmark,
|
||||
같은 실행의 실제 worker·Uvicorn·OS·Cloudflare edge RSS/CPU/queue high-water. 네 artifact가
|
||||
`scripts/check-g7-external-proof.py` exit 0을 만들기 전에는 닫지 않는다.
|
||||
2026-08-09 공개 재감사에서는 `/voice/health`가 OpenAI STT/TTS이고 9882/9883 listener가 0임을 확인했다.
|
||||
current source의 `start-public-runtime.ps1`는 sidecar-before-API lifecycle, exact
|
||||
`local_whisper/small/cpu-int8`·`melotts/melotts-korean` readiness, provider env, Uvicorn
|
||||
`--ws websockets --ws-max-queue 4`를 fail-closed로 연결했다(launcher/sidecar 80/80, API voice 71/71).
|
||||
공개 topology용 Windows host mode도 PID/start/exe·command SHA/cwd, RSS/CPU/handles/threads,
|
||||
listener owner/conflict와 TCP high-water를 검증한다. 다만 아직 공개에
|
||||
실행하지 않았고 현재 API는 Git metadata가 없는 냉동 release cwd라 provenance gate를 통과할 수 없다. current
|
||||
committed repo 기반 public runtime 정렬을 먼저 닫기 전에는 물리 마이크 50분 창을 열지 않는다.
|
||||
코드 P0는 완료됐다. runner exit는 canonical checker `exit 0`/`gate_closed=true`에 결속되고, browser Origin은
|
||||
API/WSS/admin/topology host·scheme과 분리 검증되며, 실제 capture 최소 3,120초와 세 artifact 공통 overlap
|
||||
`≥3000s`를 강제한다. Windows topology는 detached-clean HEAD/tree, runner/collector/checker SHA와 exact
|
||||
`psutil==6.1.1`까지 pin한다. fresh launcher는 legacy API/exact-config cloudflared를 bounded 교체해 새
|
||||
PID/start/exe/command SHA/cwd safe receipt를 만든다. runner/checker/topology 86/86, launcher/sidecar 80/80,
|
||||
G7 통합 166/166을 통과했다. 이제 clean source 공개 배포·rehearse 전에는 물리 마이크를 열지 않는다.
|
||||
격리 NAS 프리뷰 `http://100.116.83.60:8088`은 전용 Compose 프로젝트·포트·네트워크·볼륨에 배포했고, 실제 브라우저 회기와
|
||||
review API 저장 축어록 2턴을 확인했다. 기존 프로젝트 중단·재생성 명령은 실행하지 않았다. 매일 04:30 KST `Vignette 회기 E2E 정기 검증`
|
||||
(automation id `vignette-e2e`)은 ACTIVE이며 material milestone+release-only patch 결정성·manifest·clean-index·NAS preflight+배포 SHA 변경 때만 프리뷰를 갱신한다. current exact SHA `6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611`은 API/Web 새 이미지, 전체 browser E2E 108/108, OpenAPI 126, auth 401, G0~G8, postdeploy browser SSE→DB review와 health 3/3을 통과했다. 실제 receipt-bound rollback은 같은 프리뷰에서 별도 helper로 두 번 실행해 종료했다([런북](./nas-preview-g8-rollback-proof-runbook.md), [기계 판독 증거](./evidence/nas-preview-g8-actual-rollback-2026-08-07.json)). 첫 예약 실행 이력은 대기 상태다. 증거: [배포 증거](./nas-preview-deployment-evidence-2026-08-07.md),
|
||||
[브라우저 증거](./evidence/nas-preview-live-turn-2026-08-07.png). 물리 마이크 캡처 미승인과 Deepgram 운영 키 부재는 해소되지 않았으므로 G7은 BUILD로 유지한다.
|
||||
(automation id `vignette-e2e`)은 ACTIVE이며 material milestone+release gate·NAS preflight+배포 SHA 변경 때만 프리뷰를 갱신한다. 2026-08-07 SHA `6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611`은 API/Web 새 이미지, localhost candidate E2E 108/108, OpenAPI 126, auth 401, G0~G8, postdeploy browser SSE→DB review와 health 3/3을 통과했다. 같은 NAS 평문 origin에서는 UUID 결함으로 24건 실패했으므로 current source 배포 증거로 재사용하지 않는다. 실제 receipt-bound rollback은 같은 프리뷰에서 별도 helper로 두 번 실행해 종료했다([런북](./nas-preview-g8-rollback-proof-runbook.md), [기계 판독 증거](./evidence/nas-preview-g8-actual-rollback-2026-08-07.json)). 첫 예약 실행 이력은 대기 상태다. 증거: [배포 증거](./nas-preview-deployment-evidence-2026-08-07.md),
|
||||
2026-08-09 17:07 KST current baseline은 exact API/Web `52e0…8b2d`/`6fdb…f215`, previous
|
||||
`79ec…4450`/`c530…2f28` 보존, `api/web/db/proxy` 4개 running + `unless-stopped`, DB volume
|
||||
`vignette-preview-20260807_pgdata`, health 3/3·db/engine true·auth 401·OpenAPI 126 + G1~G8 routes·root/JS/CSS
|
||||
200, helper/listener 0이다. dump `92ed…1570f` 960,970 bytes와 `726982…5d41c` 1,015,222 bytes도 존재한다.
|
||||
[브라우저 증거](./evidence/nas-preview-live-turn-2026-08-07.png). 명시 동의 물리 마이크와 독립 human voice-gain 증거는 아직 없으므로 G7은 external GATE로 유지한다.
|
||||
- [ ] **claude_cli ↔ Anthropic API live 동일성** — provider 라우팅·Anthropic `/v1/models` 탐색·지원 추론 강도·관리자 fail-closed 저장 경로는 구현 완료. 남은 범위는 연구팀/기관 `ANTHROPIC_API_KEY`를 게이트웨이 호스트에 주입한 live 응답·계량·오류 표면화 비교다. Claude CLI·Codex CLI(Terra/Medium)·Agy CLI(Gemini 3.6 Flash/High)는 로컬 live probe를 통과했다.
|
||||
- [ ] **재부팅 후 watchdog smoke** — `watch-public-runtime.ps1` + Scheduled Task가 재부팅 후 엔진/API/터널을 복구하고
|
||||
public `/turn`이 통과하는지 실측. 재부팅 불가로 미실행(parser/check-only 경로는 확인). DNS 개통 후
|
||||
- [ ] **stable-source task 재등록 + 재부팅 후 watchdog smoke** — 사용자 승인 clean commit의 detached release
|
||||
root에 watchdog·로그온 boot를 함께 재등록하고 action의 commit/tree/script SHA pin과 watchdog
|
||||
`LastTaskResult=0`을 확인한 뒤, 실제 Windows 재부팅 후 엔진/API/터널 복구와 public `/turn`을 실측한다.
|
||||
재부팅 불가로 미실행(parser·provenance 회귀는 확인). DNS 개통 후
|
||||
`api-vnet.18ka.net`은 `-AdditionalPublicHealthUrls`로 명시 추가. 상세: `docs/ops/public-runtime-watchdog.md`.
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"schema": "vignette.material-milestone.v1",
|
||||
"milestone_id": "g8-clean-head-insecure-origin-runtime-2026-08-09",
|
||||
"material": true,
|
||||
"status": "ready_for_approved_clean_commit",
|
||||
"goals": [
|
||||
"G8"
|
||||
],
|
||||
"evidence_refs": [
|
||||
"git-head:94c681d450d2f3b4df55c036e333cd817db3f7fb",
|
||||
"git-tree:d8d7f93fa0626e0dda475f0cc5b36734eaacceba",
|
||||
"git-archive-sha256:6b1b15bd9a1ff2bdc6f0cfcd45a754688577c703c7a4a2e676bb9bad56cca3f7",
|
||||
"tests:release-agent-clean-head-30",
|
||||
"tests:insecure-context-uuid-6",
|
||||
"tests:api-full-920",
|
||||
"tests:session-timestamp-kst-db-browser-1",
|
||||
"nas-predeploy-dump-sha256:72698232d132a8f848d81ff9d8602430e7e623c7646cc236cb39f978aab5d41c",
|
||||
"nas-active-before:6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611"
|
||||
],
|
||||
"release_gate": {
|
||||
"dry_run": "passed_no_mutation",
|
||||
"execute_attempts": 2,
|
||||
"deployment_mutated": false,
|
||||
"latest_candidate_e2e": {
|
||||
"passed": 109,
|
||||
"failed": 5
|
||||
},
|
||||
"source_only_gate_repair": "insecure-context UUID moved to isolated Vite gate; 6 passed",
|
||||
"timestamp_fix": "UTC offset transport and KST review calendar date passed focused disposable DB and browser regression",
|
||||
"remaining_blocker": "the verified dirty worktree changes are not yet represented by an approved clean Git commit",
|
||||
"next_gate": "create an approved clean commit, recompute HEAD tree and archive identity, then rerun clean-head dry-run and execute"
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ sha256:52e0e816…8b2d → com.docker.compose.project=vignette-preview-2026080
|
|||
| 5 | `docker run` argv에 target project/service label을 넣지 않는다 | `assert_argv_isolation` + fake-runner 테스트 |
|
||||
| 6 | exact container name·ID와 `docker inspect` 증거를 남긴다 | `verify` 산출 evidence(`vignette.nas-preview-g8-helper-isolation.v1`) |
|
||||
|
||||
테스트: `scripts/test_launch_nas_preview_g8_helpers.py` (31/31). 실제 실행 없이 argv 계약이
|
||||
테스트: `scripts/test_launch_nas_preview_g8_helpers.py` (37/37). 실제 실행 없이 argv 계약이
|
||||
고정되므로, 누가 나중에 target label을 주입하면 테스트가 먼저 깨진다.
|
||||
|
||||
### helper 하드닝 (argv에 고정)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## 판정
|
||||
|
||||
G7은 계속 **BUILD**다. 이번 변경은 물리 마이크나 운영 Deepgram을 사용한 실증이 아니라,
|
||||
G7은 **내부 구현 DONE · external GATE**다. 이번 변경은 물리 마이크나 운영 provider를 사용한 실증이 아니라,
|
||||
외부 증거가 들어왔을 때 synthetic·preflight·짧은 probe로 잘못 닫히지 않도록 종료 게이트를
|
||||
실행 가능한 네 artifact 계약으로 만든 것이다.
|
||||
|
||||
|
|
@ -23,9 +23,10 @@ G7은 계속 **BUILD**다. 이번 변경은 물리 마이크나 운영 Deepgram
|
|||
- Linux topology sampler는 exact Compose project/service, container ID, image digest, init PID,
|
||||
start/restart, cgroup path를 매 sample 전후 재검증한다. cgroup v2, `/proc`, parsed Docker stats,
|
||||
endpoint를 폐기한 host TCP queue/retransmit의 50분 high-water를 만들 수 있다.
|
||||
- 누적 high-water를 이번 실행의 부하로 오인하지 않도록 API worker와 대상 container는 증거 시작
|
||||
120초 이내에 새로 시작된 동일 인스턴스여야 한다. checker가 시작 시각, PID/container ID와
|
||||
네 artifact의 겹치는 시간창을 함께 묶어 이 조건을 fail-closed로 검증한다.
|
||||
- 누적 high-water를 이번 실행의 부하로 오인하지 않도록 Linux Compose API/container는 증거 시작
|
||||
120초 이내의 동일 인스턴스여야 한다. Windows host mode는 detached-clean commit/tree, runner/collector/checker
|
||||
SHA, `psutil==6.1.1`, 새 API/cloudflared PID/start/exe·command SHA/실제 cwd를 매 sample 전후 검증한다.
|
||||
checker는 voice/runtime/topology 세 artifact의 **공통 시간창이 3,000초 이상**인지 재계산한다.
|
||||
- 독립 human-labeled held-out voice-gain 계약은 raw audio·transcript·synthetic pack을 거부하고,
|
||||
사전등록·동의·participant split·model artifact·blind independent labeler provenance를 요구한다.
|
||||
production gate는 30명/50회기/150 paired axis, ICC(A,1) 0.75, 선택적 κ 0.70, gain 0.01,
|
||||
|
|
@ -37,8 +38,9 @@ G7은 계속 **BUILD**다. 이번 변경은 물리 마이크나 운영 Deepgram
|
|||
|
||||
## 검증
|
||||
|
||||
- API voice/G7 focused: `92 passed`.
|
||||
- external evidence runners/checkers와 release gate contract: `31 passed`; Ruff와 `py_compile` 통과.
|
||||
- API 전체 `921 passed`, gateway `58 passed`, API voice 통합 `71 passed`.
|
||||
- runner/checker/topology `86 passed`, public launcher/sidecar `80 passed`, G7 통합 `166 passed`;
|
||||
Ruff·`py_compile`·PowerShell 5.1 parser·`git diff --check` 통과.
|
||||
- Web typecheck와 API type contract 통과.
|
||||
- `voice-success.spec.ts` chromium single-run `2/2` 통과. 동의 원장 응답을 의도적으로 지연한
|
||||
동안 `getUserMedia=0`, voice WebSocket `0`을 확인했고, 성공 뒤 각각 정확히 1회였다.
|
||||
|
|
@ -51,7 +53,8 @@ G7은 계속 **BUILD**다. 이번 변경은 물리 마이크나 운영 Deepgram
|
|||
|
||||
## 실제 종료 때 필요한 네 artifact
|
||||
|
||||
1. `soak-public-voice-websocket.py` v4의 운영 Deepgram/OpenAI TTS·authenticated public WSS·
|
||||
1. `soak-public-voice-websocket.py` v4의 운영 기본값 `local_whisper`/`melotts`와 정확히 일치하는 ready metadata,
|
||||
authenticated public WSS·
|
||||
명시 동의 물리 마이크 50분 양방향 `passed` evidence.
|
||||
2. 같은 시간창에 `capture-g7-runtime-evidence.py`로 얻은 관리자 endpoint worker/Uvicorn queue
|
||||
high-water evidence.
|
||||
|
|
@ -65,7 +68,7 @@ DONE으로 표시하지 않는다.
|
|||
|
||||
## 실행 오케스트레이터 (2026-08-08 추가)
|
||||
|
||||
앞의 1~3번은 **같은 public host의 겹치는 시간창**이어야 하는데, 지금까지는 운영자가 세 명령을 따로
|
||||
앞의 1~3번은 **같은 public transport host의 공통 3,000초 시간창**이어야 하는데, 지금까지는 운영자가 세 명령을 따로
|
||||
띄우고 시계를 손으로 맞춰야 했다. 50분짜리 실행에서 한 번 어긋나면 처음부터 다시 해야 한다.
|
||||
`scripts/run-g7-external-proof-window.py`가 세 캡처를 **동시에** 시작하고, 전부 끝나면 human pack을
|
||||
더해 checker까지 그대로 돌린다.
|
||||
|
|
@ -74,22 +77,30 @@ DONE으로 표시하지 않는다.
|
|||
# 실제 실행 (물리 마이크 50분 + 사람 pack 필요)
|
||||
& $py -X utf8 -B scripts/run-g7-external-proof-window.py `
|
||||
--wss-url wss://api-vignette.chanpaca.net/voice/ws `
|
||||
--origin https://api-vignette.chanpaca.net `
|
||||
--origin https://vignette.chanpaca.net `
|
||||
--admin-runtime-url https://api-vignette.chanpaca.net/admin/voice-runtime `
|
||||
--compose-project <project> --api-container <c> --api-image-digest sha256:... `
|
||||
--caddy-container <c> --caddy-image-digest sha256:... `
|
||||
--topology-mode windows-host --repo-root <detached-clean-root> `
|
||||
--git-sha <commit> --git-tree-sha <tree> `
|
||||
--runner-script-sha256 <sha> --collector-script-sha256 <sha> `
|
||||
--checker-script-sha256 <sha> --psutil-version 6.1.1 `
|
||||
--api-pid <pid> --api-executable-name python.exe --api-executable-sha256 <sha> `
|
||||
--api-cwd <root\apps\api> --api-listen-port 8001 `
|
||||
--cloudflared-pid <pid> --cloudflared-executable-name cloudflared.exe `
|
||||
--cloudflared-executable-sha256 <sha> --cloudflared-cwd <root> `
|
||||
--microphone-device "<장치명>" --confirm-physical-capture `
|
||||
--duration-seconds 3120 `
|
||||
--human-voice-gain <pack.json> --out-dir <증거디렉터리>
|
||||
```
|
||||
|
||||
게이트를 **약화시키지 않는다.** CLI로 실증한 fail-closed 경계 네 가지:
|
||||
게이트를 **약화시키지 않는다.** CLI로 실증한 주요 fail-closed 경계:
|
||||
|
||||
| 시도 | 결과 |
|
||||
|---|---|
|
||||
| 동의·장치 없이 운영 실행 | `physical_microphone_consent_required`, exit 2 |
|
||||
| human pack 없이 운영 실행 | `human_voice_gain_pack_required`, exit 2 |
|
||||
| 50분 미만 시간창 | `production_window_too_short`, exit 2 |
|
||||
| 세 캡처의 host 불일치 | `hosts_must_match:[...]`, exit 2 |
|
||||
| 3,120초 미만 시간창 | `production_window_too_short`, exit 2 |
|
||||
| transport host/scheme 불일치 | `hosts_must_match` 또는 scheme 오류, exit 2 |
|
||||
| 허용되지 않은 browser Origin | query/원문을 반사하지 않는 Origin 오류, exit 2 |
|
||||
|
||||
`--rehearse`는 마이크·사람 없이 **배관만** 확인하는 모드다. soak을 `--preflight-only`로 돌려 장치를
|
||||
열지 않고, 인자·경로·산출 파일까지 실제로 검증한다. 보고서의 `gate_closed`는 rehearse에서 **항상
|
||||
|
|
@ -122,5 +133,6 @@ has /admin/voice-runtime → False
|
|||
2. `--rehearse`로 배관을 확인한다.
|
||||
3. 동의 하 물리 마이크 50분 + human pack으로 실제 실행한다.
|
||||
|
||||
기대 provider 기본값은 2026-08-08 결정에 맞춰 `local_whisper` / `melotts`다
|
||||
(근거: `../decisions/local-voice-stack.md`). 회귀는 `scripts/test_run_g7_external_proof_window.py` 20/20.
|
||||
기대 provider 기본값은 2026-08-08 결정에 맞춰 `local_whisper/small` / `melotts/melotts-korean`이다
|
||||
(근거: `../decisions/local-voice-stack.md`). runner 회귀는 37/37, G7 통합은 166/166이다. production exit 0은
|
||||
`checker_returncode == 0 && gate_closed is true`에 결속되며 rehearse는 성공해도 gate를 닫지 않는다.
|
||||
|
|
|
|||
|
|
@ -1,134 +1,233 @@
|
|||
# Public Runtime Watchdog
|
||||
|
||||
This runbook keeps the public Vignette runtime recoverable after Windows
|
||||
reboot, update, or process crash. It covers:
|
||||
이 runbook은 Windows 재부팅, 업데이트, 프로세스 장애 뒤 Vignette 공개 런타임을 복구한다.
|
||||
|
||||
- engine gateway: `http://127.0.0.1:9099`
|
||||
- prod API: `http://127.0.0.1:8001`
|
||||
- Cloudflare tunnel for `https://api-vignette.chanpaca.net`
|
||||
- engine gateway: http://127.0.0.1:9099
|
||||
- prod API: http://127.0.0.1:8001
|
||||
- web preview: http://127.0.0.1:5174
|
||||
- Cloudflare tunnel: https://api-vignette.chanpaca.net
|
||||
|
||||
## Secret Handling
|
||||
## 복구 소스 신뢰 계약
|
||||
|
||||
Do not put secrets in scheduled task arguments.
|
||||
운영 task는 개발 중인 shared branch worktree를 실행하지 않는다. 아래 조건을 모두 만족하는 별도 release
|
||||
worktree만 허용한다.
|
||||
|
||||
The scripts use the existing runtime locations:
|
||||
- 승인된 commit을 가리키는 detached HEAD
|
||||
- tracked/untracked non-ignored 변경 0
|
||||
- 설치 시 기록한 Git commit SHA와 tree SHA 일치
|
||||
- watchdog 또는 boot script SHA-256과 start-public-runtime.ps1 SHA-256 일치
|
||||
- task action의 working directory와 실행 script가 동일 release root
|
||||
|
||||
- API secrets stay in `apps/api/.env`.
|
||||
- Cloudflared credentials stay under the current user's `.cloudflared` config.
|
||||
- Claude CLI OAuth/config stays in the current Windows user profile.
|
||||
watchdog은 이 증거를 health probe와 failcount 기록보다 먼저 다시 확인한다. boot recovery는 Docker, DB,
|
||||
프로세스 mutation보다 먼저 확인한다. 하나라도 달라지면 현재 운영 프로세스를 유지하고 nonzero로 종료한다.
|
||||
|
||||
The installer creates a per-user interactive scheduled task. It starts at user
|
||||
logon and repeats as a watchdog. Fully unattended boot before any user logs in
|
||||
requires an operator-managed service account or Task Scheduler credential; do
|
||||
that in Windows, not by adding secrets to these scripts.
|
||||
API secret은 release root의 apps/api/.env에 두되 task 인자에는 넣지 않는다. 이 파일과 web node_modules,
|
||||
runtime log는 Git ignore 대상이다. Cloudflared와 Claude CLI credential은 현재 Windows 사용자 profile에 둔다.
|
||||
|
||||
## Install Or Update
|
||||
## Stable Release 준비
|
||||
|
||||
From the repo root:
|
||||
아래 작업은 승인된 clean commit이 생긴 뒤 단일 public mutation owner가 수행한다. 기존 release root를
|
||||
덮어쓰지 않는다.
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install-public-runtime-task.ps1 -RunNow
|
||||
```
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repoRoot = 'D:\workspace\vignette'
|
||||
$commit = (& git.exe -C $repoRoot rev-parse --verify HEAD).Trim()
|
||||
if ($LASTEXITCODE -ne 0) { throw 'HEAD 조회 실패' }
|
||||
$releaseRoot = "D:\workspace\vignette-public-runtime-$($commit.Substring(0, 12))"
|
||||
if (Test-Path -LiteralPath $releaseRoot) { throw "release root already exists: $releaseRoot" }
|
||||
|
||||
The installer is idempotent. Re-running it updates the same task:
|
||||
& git.exe -C $repoRoot worktree add --detach $releaseRoot $commit
|
||||
if ($LASTEXITCODE -ne 0) { throw 'detached release worktree 생성 실패' }
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install-public-runtime-task.ps1 -IntervalMinutes 5
|
||||
```
|
||||
Copy-Item -LiteralPath (Join-Path $repoRoot 'apps\api\.env') `
|
||||
-Destination (Join-Path $releaseRoot 'apps\api\.env')
|
||||
Push-Location (Join-Path $releaseRoot 'apps\web')
|
||||
& npm.cmd ci
|
||||
if ($LASTEXITCODE -ne 0) { throw 'release web npm ci 실패' }
|
||||
Pop-Location
|
||||
|
||||
Do not add future domains to the default watchdog until DNS and Cloudflare
|
||||
routing are live. For example, `api-vnet.18ka.net` is intentionally excluded
|
||||
from the default checks while that domain has no DNS. After a public domain is
|
||||
actually reachable, add it explicitly:
|
||||
$dirty = @(& git.exe -C $releaseRoot status --porcelain=v1 --untracked-files=normal)
|
||||
if ($LASTEXITCODE -ne 0 -or $dirty.Count -ne 0) {
|
||||
throw "release source가 clean하지 않음: $($dirty -join '; ')"
|
||||
}
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install-public-runtime-task.ps1 `
|
||||
-AdditionalPublicHealthUrls https://api-vnet.18ka.net/health
|
||||
```
|
||||
apps/api/.env의 내용을 console이나 evidence에 출력하지 않는다. 새 root에 node_modules와 .env를 준비한 뒤에도
|
||||
위 Git status 결과는 빈 값이어야 한다.
|
||||
|
||||
Task name:
|
||||
## Task 설치 또는 승격
|
||||
|
||||
```powershell
|
||||
VignettePublicRuntimeWatchdog
|
||||
```
|
||||
두 registrar 자체도 동일 stable release root에서 실행해야 한다. 다른 worktree의 registrar로 target만
|
||||
바꾸는 호출은 거부된다.
|
||||
|
||||
## Manual Start
|
||||
$bootRegistrar = Join-Path $releaseRoot 'scripts\register-boot-task.ps1'
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $bootRegistrar `
|
||||
-StableSourceRoot $releaseRoot
|
||||
if ($LASTEXITCODE -ne 0) { throw 'boot task 등록 실패' }
|
||||
|
||||
Use this when you want to force a runtime restore immediately:
|
||||
$watchdogInstaller = Join-Path $releaseRoot 'scripts\install-public-runtime-task.ps1'
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $watchdogInstaller `
|
||||
-StableSourceRoot $releaseRoot `
|
||||
-IntervalMinutes 5
|
||||
if ($LASTEXITCODE -ne 0) { throw 'watchdog task 등록 실패' }
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\start-public-runtime.ps1
|
||||
```
|
||||
task 이름과 역할:
|
||||
|
||||
`start-public-runtime.ps1`는 관리자·인증 제어면과 엔진을 분리한다. 이미
|
||||
`environment=prod`, `db=true`인 API와 정상 웹·터널은 유지하고, 엔진만 실패한 경우
|
||||
엔진만 복구한다. 엔진이 늦게 준비돼도 관리자·인증 API 기동을 막지 않는다.
|
||||
- VignettePublicRuntime: 사용자 로그온 시 Docker, PostgreSQL, runtime 복구
|
||||
- VignettePublicRuntimeWatchdog: 사용자 로그온 및 5분 반복 health/recovery
|
||||
|
||||
API 코드 변경을 운영 프로세스에 반영할 때는 다른 표면을 유지한 채 API만 명시적으로 교체한다.
|
||||
둘 다 현재 사용자의 Interactive/Limited task다. 사용자 로그인 전 headless boot가 필요하면 별도
|
||||
operator-managed service account가 필요하며 credential을 script나 task arguments에 넣지 않는다.
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\start-public-runtime.ps1 `
|
||||
-ForceApiRestart `
|
||||
-SkipEngineRestart `
|
||||
-SkipWebRestart `
|
||||
-SkipCloudflaredRestart
|
||||
```
|
||||
### 등록 직후 source pin 검증
|
||||
|
||||
## Health Checks
|
||||
RunNow 전에 action을 읽어 두 task가 같은 release root와 commit을 가리키는지 확인한다.
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\watch-public-runtime.ps1 -CheckOnly
|
||||
Invoke-RestMethod http://127.0.0.1:9099/health
|
||||
Invoke-RestMethod http://127.0.0.1:8001/health
|
||||
Invoke-RestMethod https://api-vignette.chanpaca.net/health
|
||||
```
|
||||
$requirements = @{
|
||||
VignettePublicRuntime = @(
|
||||
'-StableSourceRoot',
|
||||
'-ExpectedSourceCommit',
|
||||
'-ExpectedSourceTree',
|
||||
'-ExpectedBootScriptSha256',
|
||||
'-ExpectedStartScriptSha256'
|
||||
)
|
||||
VignettePublicRuntimeWatchdog = @(
|
||||
'-StableSourceRoot',
|
||||
'-ExpectedSourceCommit',
|
||||
'-ExpectedSourceTree',
|
||||
'-ExpectedWatchdogSha256',
|
||||
'-ExpectedStartScriptSha256'
|
||||
)
|
||||
}
|
||||
|
||||
Optional extra public host check, only after the host resolves:
|
||||
foreach ($taskName in $requirements.Keys) {
|
||||
$task = Get-ScheduledTask -TaskName $taskName -ErrorAction Stop
|
||||
$action = @($task.Actions)[0]
|
||||
if ($action.WorkingDirectory -ne $releaseRoot) {
|
||||
throw "$taskName working directory drift: $($action.WorkingDirectory)"
|
||||
}
|
||||
if ($action.Arguments.IndexOf($releaseRoot, [StringComparison]::OrdinalIgnoreCase) -lt 0) {
|
||||
throw "$taskName release root pin 누락"
|
||||
}
|
||||
if ($action.Arguments.IndexOf($commit, [StringComparison]::OrdinalIgnoreCase) -lt 0) {
|
||||
throw "$taskName commit pin 누락"
|
||||
}
|
||||
foreach ($marker in $requirements[$taskName]) {
|
||||
if ($action.Arguments.IndexOf($marker, [StringComparison]::Ordinal) -lt 0) {
|
||||
throw "$taskName action pin 누락: $marker"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\watch-public-runtime.ps1 `
|
||||
-CheckOnly `
|
||||
-AdditionalPublicHealthUrls https://api-vnet.18ka.net/health
|
||||
```
|
||||
검증 뒤 watchdog만 명시적으로 실행하고 완료 상태를 확인한다.
|
||||
|
||||
Optional real Claude CLI readiness smoke:
|
||||
Start-ScheduledTask -TaskName VignettePublicRuntimeWatchdog
|
||||
Get-ScheduledTaskInfo -TaskName VignettePublicRuntimeWatchdog
|
||||
|
||||
```powershell
|
||||
Invoke-RestMethod http://127.0.0.1:9099/ready
|
||||
```
|
||||
LastTaskResult=0과 stable release root의 public-runtime-watchdog.failcount=0을 확인한다. health만 정상이라고
|
||||
새 source 배포가 완료된 것은 아니다. 공개 API process cwd, Git commit, OpenAPI, auth, voice provider/model,
|
||||
실제 session smoke까지 별도 배포 gate에서 확인한다.
|
||||
|
||||
`/ready` can consume a small Claude budget because it performs a real generation.
|
||||
## 숨김 수동 Trigger
|
||||
|
||||
## Logs And Task State
|
||||
watch-public-runtime-hidden.vbs는 source script를 직접 실행하지 않는다. 등록된 watchdog task action에
|
||||
StableSourceRoot, commit, tree, watchdog SHA, start SHA marker가 모두 있고 legacy Workspace action이 아님을
|
||||
검사한 뒤 Start-ScheduledTask만 호출한다.
|
||||
|
||||
```powershell
|
||||
Get-ScheduledTask -TaskName VignettePublicRuntimeWatchdog
|
||||
Get-ScheduledTaskInfo -TaskName VignettePublicRuntimeWatchdog
|
||||
Get-Content .\public-runtime-watchdog.log -Tail 50
|
||||
Get-Content .\apps\api\engine.public.err.log -Tail 50
|
||||
Get-Content .\apps\api\api.public.err.log -Tail 50
|
||||
Get-Content .\cloudflared.public.err.log -Tail 50
|
||||
```
|
||||
cscript.exe //nologo scripts\watch-public-runtime-hidden.vbs
|
||||
|
||||
## Remove
|
||||
task가 아직 legacy shared-worktree action이면 VBS도 fail-closed한다.
|
||||
|
||||
```powershell
|
||||
Unregister-ScheduledTask -TaskName VignettePublicRuntimeWatchdog -Confirm:$false
|
||||
```
|
||||
## Manual Source Recovery
|
||||
|
||||
## Recovery Notes
|
||||
운영 code를 강제로 교체해야 할 때도 shared worktree의 start-public-runtime.ps1을 실행하지 않는다.
|
||||
위 pin 검증을 끝낸 release root의 script만 사용한다.
|
||||
|
||||
If local health is good but public health fails, inspect the cloudflared process
|
||||
and `C:\Users\<user>\.cloudflared\vignette-config.yml`.
|
||||
$startScript = Join-Path $releaseRoot 'scripts\start-public-runtime.ps1'
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $startScript `
|
||||
-Workspace $releaseRoot `
|
||||
-ForceApiRestart `
|
||||
-SkipEngineRestart `
|
||||
-SkipWebRestart `
|
||||
-SkipCloudflaredRestart
|
||||
if ($LASTEXITCODE -ne 0) { throw 'public API 교체 실패' }
|
||||
|
||||
If engine health fails, verify that `claude` runs for the same Windows user that
|
||||
owns the scheduled task and that the user has completed Claude CLI login.
|
||||
start-public-runtime.ps1은 engine, API, web, tunnel을 분리해 이미 healthy인 표면을 유지한다. local Whisper와
|
||||
MeloTTS exact readiness가 닫히기 전에는 API restart gate를 통과하지 않는다.
|
||||
|
||||
엔진 장애 중에도 `http://127.0.0.1:8001/health`의 `environment=prod`, `db=true`가
|
||||
유지되면 관리자·인증 제어면은 정상이다. 이때 watchdog은 API·웹·터널을 재시작하지
|
||||
않는다. 예약 작업 확인 기준은 `VignettePublicRuntime`의 `LastTaskResult=0`과
|
||||
`public-runtime-watchdog.failcount=0`이다.
|
||||
위 명령은 routine API-only 복구라 cloudflared를 유지하며 **G7 fresh topology 증거를 만들지 않는다**. G7 공개
|
||||
승격에서는 기존 API/cloudflared PID를 재사용하지 않고 아래 opt-in 계약을 사용한다. 실행 전에 detached-clean
|
||||
commit/tree와 Python/cloudflared/config SHA를 read-only로 고정하고, config ingress가 이미 exact public topology인지
|
||||
확인한다. 이 모드는 `-ForceApiRestart`가 필수이고 `-SkipCloudflaredRestart`를 허용하지 않는다.
|
||||
|
||||
If API health fails with `environment`, `db`, or auth configuration errors,
|
||||
inspect `apps/api/.env`; do not copy secret values into scripts or task
|
||||
arguments.
|
||||
$python = 'C:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe'
|
||||
$cloudflared = 'C:\Users\encep\AppData\Local\Microsoft\WinGet\Links\cloudflared.exe'
|
||||
$cloudflaredConfig = 'C:\Users\encep\.cloudflared\vignette-config.yml'
|
||||
$commit = (& git.exe -C $releaseRoot rev-parse --verify HEAD).Trim()
|
||||
$tree = (& git.exe -C $releaseRoot rev-parse --verify 'HEAD^{tree}').Trim()
|
||||
$pythonSha = (Get-FileHash -LiteralPath $python -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$cloudflaredSha = (Get-FileHash -LiteralPath $cloudflared -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$configSha = (Get-FileHash -LiteralPath $cloudflaredConfig -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$receipt = Join-Path (Join-Path 'D:\workspace\vignette-runtime-evidence' $commit) 'public-runtime-launch-provenance.json'
|
||||
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $startScript `
|
||||
-Workspace $releaseRoot `
|
||||
-ForceApiRestart `
|
||||
-SkipEngineRestart `
|
||||
-SkipWebRestart `
|
||||
-RequireFreshPublicProvenance `
|
||||
-ExpectedSourceCommit $commit `
|
||||
-ExpectedSourceTree $tree `
|
||||
-ExpectedPythonSha256 $pythonSha `
|
||||
-ExpectedCloudflaredSha256 $cloudflaredSha `
|
||||
-ExpectedCloudflaredConfigSha256 $configSha `
|
||||
-RuntimeProvenancePath $receipt
|
||||
if ($LASTEXITCODE -ne 0) { throw 'fresh public provenance 승격 실패' }
|
||||
|
||||
receipt에는 raw command line·config contents를 넣지 않고 PID/start/executable·command SHA/실제 cwd와 topology 입력만
|
||||
남긴다. 이 receipt의 PID와 pin을 `run-g7-external-proof-window.py --topology-mode windows-host`에 그대로 전달하고,
|
||||
공개 health·auth·OpenAPI·local provider ready를 확인하기 전에는 task action을 새 root로 재등록하지 않는다.
|
||||
|
||||
## Read-only CheckOnly
|
||||
|
||||
watchdog script를 직접 CheckOnly로 실행할 때도 task와 같은 pin을 모두 전달해야 한다.
|
||||
|
||||
$watchScript = Join-Path $releaseRoot 'scripts\watch-public-runtime.ps1'
|
||||
$startScript = Join-Path $releaseRoot 'scripts\start-public-runtime.ps1'
|
||||
$commit = (& git.exe -C $releaseRoot rev-parse --verify HEAD).Trim()
|
||||
$tree = (& git.exe -C $releaseRoot rev-parse --verify 'HEAD^{tree}').Trim()
|
||||
$watchSha = (Get-FileHash -LiteralPath $watchScript -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$startSha = (Get-FileHash -LiteralPath $startScript -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $watchScript `
|
||||
-StableSourceRoot $releaseRoot `
|
||||
-ExpectedSourceCommit $commit `
|
||||
-ExpectedSourceTree $tree `
|
||||
-ExpectedWatchdogSha256 $watchSha `
|
||||
-ExpectedStartScriptSha256 $startSha `
|
||||
-CheckOnly
|
||||
|
||||
추가 public host는 DNS와 routing이 실제로 열린 뒤 installer의
|
||||
AdditionalPublicHealthUrls에 명시한다. 아직 열리지 않은 future host를 기본 probe에 넣어 restart loop를 만들지 않는다.
|
||||
|
||||
## Health와 로그
|
||||
|
||||
Invoke-RestMethod http://127.0.0.1:9099/health
|
||||
Invoke-RestMethod http://127.0.0.1:8001/health
|
||||
Invoke-RestMethod https://api-vignette.chanpaca.net/health
|
||||
Get-ScheduledTaskInfo -TaskName VignettePublicRuntimeWatchdog
|
||||
Get-Content -LiteralPath (Join-Path $releaseRoot 'public-runtime-watchdog.log') -Tail 50
|
||||
Get-Content -LiteralPath (Join-Path $releaseRoot 'apps\api\engine.public.err.log') -Tail 50
|
||||
Get-Content -LiteralPath (Join-Path $releaseRoot 'apps\api\api.public.err.log') -Tail 50
|
||||
|
||||
engine /ready는 실제 Claude generation을 수행할 수 있어 소량의 budget을 사용한다. shared secret으로 기동한
|
||||
gateway는 token header가 필요하다.
|
||||
|
||||
엔진 장애 중에도 API health가 environment=prod, db=true이면 관리자와 인증 제어면은 유지한다. source
|
||||
provenance failure는 재시작으로 우회하지 말고 task action과 stable release를 다시 승격한다.
|
||||
|
||||
## Task 제거
|
||||
|
||||
제거는 명시적 운영 결정으로만 수행한다.
|
||||
|
||||
Unregister-ScheduledTask -TaskName VignettePublicRuntimeWatchdog -Confirm:$false
|
||||
Unregister-ScheduledTask -TaskName VignettePublicRuntime -Confirm:$false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue