G7 증명과 G8 clean-head 승격 준비
This commit is contained in:
parent
94c681d450
commit
5221f79e3f
52 changed files with 6876 additions and 506 deletions
552
docs/HANDOFF.md
552
docs/HANDOFF.md
|
|
@ -1,13 +1,17 @@
|
|||
# Vignette G0~G8 완료 핸드오프
|
||||
# Vignette G0~G8 완료 추진 핸드오프
|
||||
|
||||
> Updated: 2026-08-07 KST (G8 실제 rollback 종료 반영)
|
||||
> Updated: 2026-08-09 KST (다른 에이전트 실행용 최종 인계 · G7 외부-proof P0 코드 완료 · current-source fixture 분할 GREEN/clean HEAD 재실행)
|
||||
> Workspace: `D:\workspace\vignette`
|
||||
> 이 문서는 다음 에이전트가 **G0~G8 아홉 목표 중 마지막으로 남은 G7 외부 게이트를 닫기 위한** 실행 기준이다.
|
||||
> 이 문서는 다음 에이전트가 **G0~G8 아홉 목표 중 남은 G7 외부 게이트와 G8 NAS-origin 재검증을 닫기 위한** 실행 기준이다.
|
||||
|
||||
## 0. 현재 상태 한 줄 요약
|
||||
|
||||
**G0~G6·G8은 internal DONE + 실제 runtime 증거로 종료됐다. 남은 것은 G7 외부 4-artifact 게이트 하나이며,
|
||||
이건 코드로 풀 수 없고 사용자가 제공해야 하는 운영 자원·동의가 있어야 진행된다.**
|
||||
**G0~G6의 내부 구현·증거는 종료됐다. 단, 최종 clean source 전체 회귀는 다시 실행해야 한다. G8 실제 rollback
|
||||
receipt는 완료됐지만 current source clean-head 승격은 아직 0회다.
|
||||
두 execute는 모두 NAS mutation 전에 안전하게 중단됐다. naive `started_at`의 9시간 오차 P0는 UTC/KST 계약과
|
||||
실DB 브라우저 회귀로 수정됐고, 이제 새 clean commit 결속과 NAS-origin 전체 E2E가 남았다. G7 공개 런처의
|
||||
local sidecar lifecycle·provider/model readiness·Uvicorn queue, fresh PID/source provenance, 3,120초 runner와
|
||||
3,000초 공통 시간창·canonical exit 계약은 소스에서 완료됐지만, current source 공개 배포와 외부 4-artifact 게이트는 남아 있다.**
|
||||
|
||||
| Goal | 판정 | 완료 증거 |
|
||||
|---|---|---|
|
||||
|
|
@ -18,28 +22,89 @@
|
|||
| G4 Deliberate Practice | DONE | completed-session observer, actual API/DB attempts, teacher correction, returned-practice browser 폐루프 |
|
||||
| G5 Calibration & Transfer | DONE | actual transfer execution, independent/model-run/instrument/evidence provenance, DB/browser idempotency |
|
||||
| G6 Supervision & Research | DONE | safety event metadata-only projection, safety priority 1 > deterioration 2, raw detail 0 |
|
||||
| G7 Multimodal Alliance | **internal DONE · external GATE** | consent/withdrawal, synthetic soak 59/59, public unauth WSS 1008, focused 119 PASS |
|
||||
| G8 Continuous Improvement | **DONE** | 격리 NAS 실제 image rollback 2회 + executed receipt 2건 (아래 §3) |
|
||||
| G7 Multimodal Alliance | **internal DONE · external GATE** | consent/withdrawal, synthetic soak 59/59, G7 runner/checker/topology 86/86, launcher/sidecar 80/80, API voice 71/71 |
|
||||
| G8 Continuous Improvement | **receipt DONE · runtime REVALIDATION** | 실제 image rollback 2회 + executed receipt 2건. timestamp 수정 실DB GREEN, 새 clean commit·NAS-origin E2E 미완료 |
|
||||
|
||||
대시보드 status count는 **33 done / 2 doing / 0 planned**다.
|
||||
대시보드 status count는 **32 done / 3 doing / 0 planned**다.
|
||||
|
||||
### 완료 조건 판정 (2026-08-08 재검증)
|
||||
Git 기준선은 branch `master`, HEAD `94c681d450d2f3b4df55c036e333cd817db3f7fb`이고 `origin/master`보다 2 commit
|
||||
앞서 있다. 공유 worktree에는
|
||||
문서/대시보드/G7·G8 런북, 완결된 clean-head 릴리스 에이전트·테스트, UTC/KST session timestamp 수정과
|
||||
browser 회귀가 tracked dirty로 남아 있다. Git 작업은 stage/commit/push하지 않았다.
|
||||
|
||||
### 이 문서가 인계된 정확한 중단점
|
||||
|
||||
- 사용자가 **현재 구현을 잠시 멈추고 다른 에이전트로 넘길 핸드오프를 만들라**고 요청했다. 핸드오프 정리 중
|
||||
public/NAS/DB/마이크/프로세스의 수동 mutation은 0회다. 5분 Scheduled Task는 한 번씩 자동 실행됐으나 아래
|
||||
source-pin gate에서 안전하게 exit 1로 중단됐다.
|
||||
- active goal은 계속 `active`다. 목표는 “문서 작성”이 아니라 **G0~G8 아홉 목표를 실제 runtime과 E2E로
|
||||
모두 닫는 것**이다. G7 canonical checker exit 0과 G8 current clean source의 NAS-origin 0-failure가 없으면
|
||||
`goal complete`를 호출하지 않는다.
|
||||
- 마지막 완료 작업은 G7 runner/checker/topology·fresh public launcher provenance P0, session timestamp UTC/KST 계약,
|
||||
API full 921/921, gateway 58/58, Web API types·typecheck·build, 자기주도 route fixture desktop/mobile 6/6이다.
|
||||
fixture desktop/mobile은 마지막 단일
|
||||
120/120 뒤 dashboard spec/HTML이 다시 바뀌었고 그 변경의 dashboard 10/10을 별도로 확인했으므로,
|
||||
**현재 트리는 불변 110 + 최신 dashboard 10의 분할 GREEN**이다. 새 clean HEAD에서 exact 120을 다시 실행한다.
|
||||
- G7 Windows topology mode, 3,000초 공통 시간창, canonical checker-bound exit와 공개 런처의 local
|
||||
Whisper/MeloTTS lifecycle·exact readiness·Uvicorn queue·fresh API/cloudflared provenance 계약은 코드와 회귀가
|
||||
끝났지만 public launcher 실행과 capture는 수행하지 않았다.
|
||||
현재 public API가 Git metadata가 없는 냉동 release 디렉터리에서 실행되므로 provenance gate가 의도적으로
|
||||
닫혀 있다. 이 검증을 약화해서 통과시키면 안 된다.
|
||||
- **public task source-pin P0:** watchdog·로그온 boot는 이제 detached clean release만 허용하고 Git
|
||||
commit/tree와 watchdog|boot/start script SHA를 health·Docker·process mutation 전에 검증한다. hidden VBS도
|
||||
shared source를 직접 실행하지 않고 구조적으로 pin된 task만 trigger한다. 회귀는 provenance 11개를 포함한
|
||||
watchdog/start/cutover combined 22/22, PowerShell 5.1 parser·WSH syntax PASS다. 그러나 **실제 두 Scheduled Task action은
|
||||
아직 shared worktree를 가리키며 pin 인자가 없다.** 5분 watchdog은 pin 부재로 반복 실행마다
|
||||
`LastTaskResult=1`이고, 새 필수 pin 인자 부재 때문에 운영 프로세스를 건드리기 전에 의도대로 fail-closed했다.
|
||||
18:02:55 KST에는 Docker Desktop UI가 active DB container에 직접 stop을 보내 public `db=false`가 재발했다.
|
||||
같은 container ID와 recovered named volume만 18:05 KST 재시작했고, 공개 local/public health는 다시
|
||||
`status=ok, db=true, engine=true`다. clean commit 뒤 두 task를 동일한 detached stable
|
||||
release root로 재등록하고 action pin·watchdog result 0·로그온/reboot smoke를 확인하기 전에는 public runtime
|
||||
자동복구를 DONE으로 부르지 않는다.
|
||||
- 다음 에이전트의 첫 mutation은 **사용자 승인 아래 새 clean commit 생성**이어야 한다. 그 clean source만 G8 NAS
|
||||
preview 또는 G7 public runtime의 배포 입력으로 쓰고, 두 환경을 동시에 변경하지 말며 mutation owner는 한 명만 둔다.
|
||||
|
||||
### 완료 조건 판정 (2026-08-09 재검증)
|
||||
|
||||
원래 active goal의 완료 선언 조건 네 가지를 실행으로 대조한 결과다.
|
||||
|
||||
| # | 조건 | 판정 | 근거 |
|
||||
|---|---|---|---|
|
||||
| 1 | G0~G8 각각의 코드 계약과 실제 runtime 증거 | **부분 충족** | G0~G6·G8은 충족. **G7만 외부 GATE로 열려 있다** |
|
||||
| 1 | G0~G8 각각의 코드 계약과 실제 runtime 증거 | **부분 충족** | G0~G6 충족. G8 receipt는 충족했지만 NAS-origin 회기 재검증이 남았고 G7 외부 GATE가 열려 있다 |
|
||||
| 2 | 학생 홈→회기→리뷰→처방→재연습→전이 폐루프가 실제 API/DB/browser 통과 | **충족** | 일회용 클론에서 `returned-practice-db-closed-loop` desktop 2/2 + mobile 2/2, route mock 0 |
|
||||
| 3 | current source 증거와 대시보드·TODO·backlog 일치 | **충족** | SSOT checker FAIL 0, unit 5/5, dashboard E2E 10/10, count 33/2/0이 카드와 일치 |
|
||||
| 3 | current source 증거와 대시보드·TODO·backlog 일치 | **충족** | SSOT checker FAIL 0, unit 5/5, dashboard E2E 10/10, count 32/3/0이 카드와 일치 |
|
||||
| 4 | G7을 가짜·합성 증거로 DONE 처리하지 않음 | **충족** | `check-g7-external-proof.py` exit 1 유지, G7 카드는 `GATE` |
|
||||
|
||||
재검증 수치: API **914 passed**, gateway **58**, executor **28**, probe **11**, helper launcher **37**,
|
||||
release agent **21**, MeloTTS 사이드카 **16**, local whisper 사이드카 **37**, SSOT FAIL **0**,
|
||||
재검증 수치: API **921 passed**, gateway **58**, executor **28**, probe **11**, helper launcher **37**,
|
||||
release agent **30**, G7 runner/checker/topology 묶음 **86**, G7 public launcher/sidecar **80**, G7 통합 **166**,
|
||||
API voice 통합 **71**,
|
||||
MeloTTS 사이드카 **16**, local whisper 사이드카 **38**,
|
||||
SSOT FAIL **0**,
|
||||
dashboard E2E **10/10**, web api-types·typecheck·build 통과, ruff clean.
|
||||
|
||||
**따라서 `update_goal complete`를 호출하지 않는다.** 조건 1이 G7 때문에 미충족이고, 조건 4가 그것을
|
||||
합성 증거로 메우는 것을 금지한다. G8 자체는 종료됐다(§3).
|
||||
current-source fixture 묶음은 16:49 KST 단일 실행에서 **120/120 PASS(92.0s)**했다. dev dashboard file URL은 실행 cwd가
|
||||
아니라 spec의 `import.meta.url`로 repo root를 찾으며, session-review fixture는 alliance pulse까지 소유한다. 관리자
|
||||
live health 테스트는 React StrictMode 중복 조회의 첫 응답을 고정하지 않고 화면이 실제 반영한 성공
|
||||
`/admin/users` 스냅샷과 KPI를 결속한다. 그 뒤 dev-dashboard spec/HTML이 변경됐고 최신 dashboard 10/10만 따로
|
||||
재검증됐다. 따라서 current tree의 정직한 판정은 **110 불변 + 10 최신 분할 GREEN**이며, 새 clean commit에서
|
||||
같은 120건을 한 번 재실행해 archive/source 결속 뒤의 결과를 고정한다.
|
||||
|
||||
**따라서 `update_goal complete`를 호출하지 않는다.** 조건 1이 G7과 G8 NAS-origin 재검증 때문에 미충족이고,
|
||||
조건 4가 G7을 합성 증거로 메우는 것을 금지한다. G8 receipt 자체는 종료됐다(§3).
|
||||
|
||||
### G0~G8 목표별 인수 기준
|
||||
|
||||
| Goal | 원 계획의 종료 조건 | 현재 인정 가능한 증거 | 다음 에이전트가 최종 종료 전에 할 일 |
|
||||
|---|---|---|---|
|
||||
| G0 | 100% provenance, evaluator/client 누수 0, Python/TS/DB schema conformance, 기존 회기 무회귀 | current producer census 29/29, 위반 0, migrations 14~16 2회 멱등 | current HEAD 전체 backend/schema 회귀와 최종 census를 다시 실행 |
|
||||
| G1 | goal/task/bond 독립 저장, 축별 UI, gold 근거 span, 평가 실패 fail-closed | prediction 24/24 ready, 방향 9/9, error 0, recall 94.4%, precision 73.9%, pre/mid/post UI | current HEAD의 alliance desktop/mobile·DB-backed session 경로 재실행 |
|
||||
| G2 | 5회기 연속성, 조기 경보 recall, false alert/uncertainty, synthetic 비임상 경계 | accuracy/recall 1.0, false alert 0, 실제 DB 10 sessions/30 measurements | current HEAD outcome trajectory API/UI 회귀 재실행 |
|
||||
| G3 | rupture 유형 benchmark, missed/partial/resolved E2E, gaming/암기 방지 | macro-F1/status 1.0, gaming 0, 실제 DB/API missed→partial→resolved | text/SSE 회기 및 rupture UI desktop/mobile 재실행 |
|
||||
| G4 | 모든 코칭 카드의 실행 재연습, 전후 근거 저장, unseen 전이 전 mastery 금지 | 실제 API/DB attempt·observer·teacher correction, browser route mock 0 | disposable DB에서 returned-practice desktop/mobile 4/4와 멱등 재확인 |
|
||||
| G5 | 자기평가 잠금, calibration 감소, unseen transfer 유지, subgroup drift | actual transfer execution과 independent/model-run/instrument/evidence provenance | G4와 같은 disposable DB에서 actual POST·read reload·1→1 멱등 재확인 |
|
||||
| G6 | 3-click queue, 원장 drilldown, 버전 재현, 역할/RLS/audit | safety metadata-only, safety priority 1 > deterioration 2, raw detail 0 | current HEAD producer/store/API와 supervisor UI 회귀 재실행 |
|
||||
| G7 | 시간 정렬, **물리 마이크+공개 WSS 장시간 E2E**, text-only 대비 음성 이득, 동의/삭제 정책 | 내부 계약·synthetic soak만 완료. canonical checker는 의도적으로 exit 1 | current 공개 배포→인증 WSS→명시 동의 mic 50분→동시 runtime/topology→독립 human pack→checker exit 0 |
|
||||
| G8 | source→draft→red-team→benchmark→catalog 재현, 누수/PII/무근거 0, 자동 calibration+rollback, incident→test/backlog 환류 | agentic worker·human gate·실제 rollback/restore receipt 2건, timestamp focused 실DB/browser GREEN | 사용자 승인 아래 새 clean commit→NAS 배포→**실제 NAS origin** 전체 회기 E2E GREEN→SSOT 동기화 |
|
||||
|
||||
## 1. 작업 시작 전 반드시 읽을 파일
|
||||
|
||||
|
|
@ -61,14 +126,29 @@ dashboard E2E **10/10**, web api-types·typecheck·build 통과, ruff clean.
|
|||
- Windows 11 + PowerShell 환경이다. 실행 전 OS·셸·도구 경로를 다시 확인한다.
|
||||
- 명령은 PowerShell 5.1 호환 문법을 기본으로 작성한다. NAS 원격 스크립트는 **UTF-8 base64 전달 방식**을 쓴다.
|
||||
(`$()`가 PowerShell에서 먼저 확장되는 것을 막는다.)
|
||||
- worktree는 큰 규모로 dirty하다. `git reset --hard`, `git checkout --`, 대규모 자동 포맷, 임의 삭제 금지.
|
||||
- 이 핸드오프 작성 시 branch/HEAD는 `master` / `94c681d450d2f3b4df55c036e333cd817db3f7fb`이고 include 후보는
|
||||
52개다: UTC/KST timestamp, G7 voice/topology/public launcher, pinned `psutil`, G8 clean-head controller, public task source pin,
|
||||
학생 boot recovery, current fixture 안정화, dashboard/TODO/backlog/HANDOFF·런북 동기화다. 이 묶음들은 아직
|
||||
commit에 들어 있지 않다.
|
||||
- clean-head 구현은 unit 30/30, G7 runner/checker/topology는 86/86, G7 public launcher/sidecar는 80/80,
|
||||
public task source pin은 provenance 11개를 포함한 combined 22/22, timestamp·voice 계약을 포함한 API는
|
||||
921/921와 KST 실DB browser 1/1, current fixture는 110 불변 + 최신 dashboard 10 분할 GREEN,
|
||||
SSOT는 FAIL 0와 dashboard E2E 10/10을 통과했다. 다음 에이전트는 최초 diff review 뒤 동일한 focused gate를
|
||||
다시 실행하고, 서로 다른 lane의 파일을
|
||||
누락한 채 clean commit을 만들지 않는다.
|
||||
- 기존·watchdog 생성 엔진 로그 4개와 `apps/web/test-results/`의 PNG 2개는 commit에서 제외하고 보존한다. 신규
|
||||
`apps/api/app/test_session_read_model.py`와
|
||||
`docs/ops/evidence/g8-clean-head-material-milestone-2026-08-09.json`은 이번 목표 산출물이므로 clean source에
|
||||
포함해야 한다.
|
||||
시작 즉시 `git status --short`로 다시 확인하고 `git reset --hard`, `git checkout --`, 대규모 자동 포맷, 임의 삭제는 금지한다.
|
||||
- Git commit/stage/push는 사용자가 요청하기 전에는 하지 않는다.
|
||||
- 공개 복구 DB, 원본 volume, pre-recovery container, dump, NAS named volume, 이전 API/Web 이미지를 삭제하지 않는다.
|
||||
- **로컬 포트 8001(공개 API) · 5173(공개 Web) · 55432(공개 DB) · 9099/9100(엔진)은 공개 런타임이다.**
|
||||
`scripts/dev-up.ps1`은 8000/5173 리스너를 정리하므로 공개 런타임이 떠 있는 동안 실행하지 않는다.
|
||||
- **로컬 포트 8001(공개 API origin) · 55432(공개 DB) · 9099/9100(엔진)은 공개 런타임이다.**
|
||||
공개 Web은 Cloudflare Pages이고 5174는 `vnet.18ka.net`용 로컬 preview다. `scripts/dev-up.ps1`은 런타임
|
||||
리스너를 정리할 수 있으므로 공개 런타임이 떠 있는 동안 실행하지 않는다.
|
||||
- 비밀값, 이메일, cookie, 사용자 UUID가 포함된 raw response, `.env` 내용은 로그·증거·채팅에 출력하지 않는다.
|
||||
|
||||
## 3. G8 — 종료됨 (2026-08-07 21:04~21:12 KST)
|
||||
## 3. G8 receipt — 종료됨 · NAS-origin runtime 재검증은 열림
|
||||
|
||||
### Gate6 계약 정정 (성립 불가능했던 이전 계약을 코드로 교체)
|
||||
|
||||
|
|
@ -104,31 +184,55 @@ dashboard E2E **10/10**, web api-types·typecheck·build 통과, ruff clean.
|
|||
|
||||
## 4. 공개 서비스 복구 상태 — 이미 완료, 다시 망가뜨리지 말 것
|
||||
|
||||
공개 계정·회기 DB 복구는 2026-08-07 18:26 KST에 완료됐다.
|
||||
공개 계정·회기 DB 복구는 2026-08-07 18:26 KST에 완료됐다. 2026-08-09 14:03 KST에는 Docker Desktop이
|
||||
중단돼 DB listener가 사라지면서 public health가 일시적으로 `db=false`가 됐고, 18:02:55 KST에는 Docker Desktop
|
||||
UI가 active DB container에 직접 stop을 보내 같은 증상이 재발했다. 두 번 모두 보존된 동일 컨테이너·볼륨만
|
||||
재기동해 복구했다. 새 컨테이너·볼륨 생성이나 데이터 복원 덮어쓰기는 없었다.
|
||||
|
||||
- Public Web `https://vignette.chanpaca.net` · Public API `https://api-vignette.chanpaca.net` (origin `127.0.0.1:8001`)
|
||||
- active DB container `vignette-dev-db`, volume `vignette_recovered_prod_20260807`, restart `unless-stopped`
|
||||
- 복구 집계: users 84, sessions 30, turns 705, Google users 16, orphan sessions 0
|
||||
- cutover 기준 복구 집계: users 84, sessions 30, turns 705, Google users 16, orphan sessions 0
|
||||
- 2026-08-09 18:05 KST 재기동 후 owner read-only 집계: users 356, sessions 193, turns 726, Google users 16,
|
||||
Google-owned sessions 31. 조회는 `BEGIN READ ONLY` 뒤 `ROLLBACK`했고 PII를 출력하지 않았다.
|
||||
- health `environment=prod`, `db=true`, `engine=true`, `engine_mode=claude_cli`, dev-login disabled, unauth `/auth/me` 401
|
||||
- 공개 OpenAPI 119 paths · watchdog `VignettePublicRuntimeWatchdog` 5분 주기
|
||||
- **2026-08-09 18:05 KST recovery snapshot:** local/public health는 `status=ok, db=true, engine=true`, Google OAuth
|
||||
start는 302→`accounts.google.com`, 공개 OpenAPI는 119 paths다. `/voice/health`는 아직 OpenAI STT/TTS이고
|
||||
local Whisper 9882·MeloTTS 9883 listener는 0이다.
|
||||
- DB 컨테이너는 `vignette_recovered_prod_20260807` named volume과 `unless-stopped`를 유지하고 local/public
|
||||
health는 `status=ok, db=true, engine=true`다. 다만 watchdog·로그온 task action은 아직 shared worktree이며
|
||||
stable root/commit/tree pin 인자가 없다. 새 source-pin 코드가 5분 watchdog을 mutation 전에 차단해 반복 실행이
|
||||
`LastTaskResult=1`이다. clean commit의 detached release root로 두 task를 재등록하기 전까지 자동복구가
|
||||
의도적으로 비활성인 상태다.
|
||||
|
||||
보안상 과거 `auth_session`은 복원하지 않았다. 사용자는 Google 재로그인이 필요하다.
|
||||
|
||||
보존물: pre-recovery container `vignette-dev-db-pre-recovery-20260807-182642`,
|
||||
pre-cutover dump `…20260807-091057Z.dump` SHA256 `6b84d5c8…af1f`,
|
||||
recovered dump `…20260807-092630Z.dump` SHA256 `d7bcc396…68ed`,
|
||||
post-cutover dump `…20260807-095511Z.dump` SHA256 `660695c8…65b88`.
|
||||
보존물: pre-recovery container `vignette-dev-db-pre-recovery-20260807-182642`과 원본/recovery Docker volumes.
|
||||
과거 증거에는 pre-cutover dump `…20260807-091057Z.dump` SHA256 `6b84d5c8…af1f`, recovered dump
|
||||
`…20260807-092630Z.dump` SHA256 `d7bcc396…68ed`, post-cutover dump `…20260807-095511Z.dump` SHA256
|
||||
`660695c8…65b88`이 기록돼 있으나, **인계 시점 로컬에는 `D:\workspace\vignette-backups` 디렉터리가 없고
|
||||
`D:\workspace\vignette-recovery`에도 이 dump들이 보이지 않는다.** 삭제로 단정하지 말고 off-host/NAS/다른 경로를
|
||||
확인하기 전 restore·cleanup을 금지한다. NAS의 pre-proof/pre-clean-head dump는 §5 기준으로 실제 존재와 hash를
|
||||
재확인했다.
|
||||
|
||||
공개 DB에는 current dirty source를 배포하지 않는다.
|
||||
|
||||
## 5. current source NAS 배포 — 완료
|
||||
## 5. NAS 배포 기준선 — 현재 HEAD가 아니라 2026-08-07 릴리스
|
||||
|
||||
- URL `http://100.116.83.60:8088` · project `vignette-preview-20260807` · remote root `/volume1/docker/vignette-preview-20260807`
|
||||
- active release SHA `6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611`
|
||||
- API `sha256:52e0e816…8b2d` / Web `sha256:6fdbb646…f215` (previous: `79ec731f…0450` / `c530213f…62f28`)
|
||||
- **2026-08-09 17:07 KST read-only snapshot:** URL `http://100.116.83.60:8088` · project
|
||||
`vignette-preview-20260807` · remote root `/volume1/docker/vignette-preview-20260807` · active release SHA
|
||||
`6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611`.
|
||||
- API `sha256:52e0e816…8b2d` / Web `sha256:6fdbb646…f215` exact image가 실행 중이고 previous
|
||||
`79ec731f…0450` / `c530213f…62f28`도 보존돼 있다. Compose `api/web/db/proxy` 4개는 모두 running,
|
||||
restart policy는 `unless-stopped`다.
|
||||
- DB volume `vignette-preview-20260807_pgdata` · pre-proof dump `vignette-preview-g8-preproof-20260807T110610Z.dump`
|
||||
SHA256 `92ed4736…1570f`
|
||||
- candidate browser E2E 108/108(634.578s), postdeploy SSE→DB review PASS
|
||||
SHA256 `92ed4736…1570f`, 960,970 bytes · pre-clean-head dump
|
||||
`vignette-preview-pre-clean-head-20260809T051611Z.dump` SHA256 `726982…5d41c`, 1,015,222 bytes.
|
||||
- health 3/3(`db=true`, `engine=true`), auth 401, OpenAPI 126 + G1~G8 route, root/JS/CSS 200이다.
|
||||
- helper container 0, NAS listener 8018/18149 0, Windows tunnel listener 18018 0이다.
|
||||
- **P1 권한 관측:** remote release root와 Compose 파일이 mode `0777`이다. 다음 execute 전에 owner/mode를
|
||||
최소권한으로 좁히고 release agent preflight가 이를 fail-closed로 검증해야 한다. 현재 상태를 안전한 배포 신뢰
|
||||
경계로 간주하지 않는다.
|
||||
- localhost candidate browser E2E 108/108(634.578s), postdeploy SSE→DB review PASS
|
||||
- **G8 rollback/restore 이후 재검증:** 실제 브라우저 SSE→DB review 재실행 PASS(35.0s)
|
||||
|
||||
배포 중 첫 108 gate가 103/5로 fail-closed된 이력(G5 fixture drift, StrictMode retry fixture, `shell.css` manifest 누락)은
|
||||
|
|
@ -151,29 +255,235 @@ post-cutover dump `…20260807-095511Z.dump` SHA256 `660695c8…65b88`.
|
|||
- 교훈: candidate gate를 localhost에서만 돌리면 secure-context 전용 API 결함을 못 잡는다. 배포 대상과
|
||||
같은 scheme/host 형태에서 최소 한 번은 회기 리뷰 라우트를 열어봐야 한다.
|
||||
|
||||
## 6. **다음 에이전트의 유일한 남은 목표 — G7 외부 종료 Gate**
|
||||
## 6. 다음 실행 순서 — G8 runtime 재검증 후 G7 외부 종료
|
||||
|
||||
G7 내부 구현은 완료됐고 `scripts/check-g7-external-proof.py`는 현재 의도적으로 exit 1이다.
|
||||
### 6.1 G8: clean-head 릴리스 경로부터 완성
|
||||
|
||||
`scripts/run-outcome-os-release-agent.py`의 명시적 clean-head 경로는 구현됐다. 기존 patch mode를 기본값으로
|
||||
보존하며, tracked-clean source worktree의 exact HEAD/tree/archive만 candidate로 사용한다. 수동 복사나 NAS 직접
|
||||
빌드는 계속 금지한다.
|
||||
|
||||
구현된 **`--source-mode clean-head`** 계약은 다음과 같다.
|
||||
|
||||
1. tracked dirty가 있으면 fail-closed. untracked 로그·테스트 결과는 archive에서 제외한다.
|
||||
2. exact Git HEAD, tree, `git archive` SHA를 증거에 기록한다.
|
||||
3. 같은 HEAD의 archive를 두 번 만들어 SHA가 동일한지 검증한다.
|
||||
4. candidate는 그 archive만 풀어 만들고 patch/manifest를 암묵 적용하지 않는다.
|
||||
5. 기존 preflight·API full·Web type/build·release E2E·DB snapshot·rollback 절차는 그대로 유지한다.
|
||||
6. Vite source 전용 `e2e/insecure-context-uuid.spec.ts`는 별도 localhost gate(5198)로 6/6 실행하고,
|
||||
production candidate/NAS-origin은 실제 배포 번들에서 동작하는 11개 회기 스펙만 실행한다.
|
||||
7. unit/fake runner가 tracked dirty, archive SHA drift, evidence binding, patch-mode 무회귀를 검증해야 한다.
|
||||
|
||||
검증: release-agent unit 30/30, source-only UUID gate 6/6, Ruff, py_compile, `git diff --check` PASS. 공유 worktree에는
|
||||
인계 문서와 controller 변경이 있으므로 이 경로 자체가 의도대로 tracked-dirty를 차단한다. 실제 배포는 새 commit을
|
||||
가리키는 별도 detached clean worktree를 만들고 `--source-repo-root <clean-worktree>`로 지정한 뒤
|
||||
dry-run→execute 순서로 수행한다.
|
||||
|
||||
#### clean commit preflight — include 52 / exclude 6
|
||||
|
||||
2026-08-09 최종 경로 목록 SHA-256은 ordinal 정렬된 UTF-8 LF 경로를 줄바꿈으로 연결하고 마지막 개행 없이 계산해
|
||||
include `4d94694ccf9def6945272c61f673cac0eaa9fb59f4f9b6c165d661d819f891a1`, exclude
|
||||
`1ff8c26732772df6b5857bf84de01a53d7da9c3b7d52df08938e77f3a527f022`다. 아래 include 52개만
|
||||
사용자 승인 후 **명시적으로** stage한다. 그 직전 `git status --porcelain=v1 -uall`로 목록·해시를 다시 계산하고,
|
||||
새 경로가 하나라도 있으면 멈춘다. `git add -A`는 금지한다.
|
||||
|
||||
```text
|
||||
apps/api/app/config.py
|
||||
apps/api/app/routes/voice.py
|
||||
apps/api/app/services/session_metrics.py
|
||||
apps/api/app/services/voice.py
|
||||
apps/api/app/session_read_model.py
|
||||
apps/api/app/test_session_read_model.py
|
||||
apps/api/app/test_voice_service.py
|
||||
apps/api/app/test_voice_ws.py
|
||||
apps/api/requirements.txt
|
||||
apps/web/e2e/admin.spec.ts
|
||||
apps/web/e2e/dev-dashboard.spec.ts
|
||||
apps/web/e2e/self-directed-learning-loop.spec.ts
|
||||
apps/web/e2e/session-mvp.spec.ts
|
||||
apps/web/e2e/session-persistence.spec.ts
|
||||
apps/web/e2e/session-review-fixture.ts
|
||||
apps/web/e2e/session-review.spec.ts
|
||||
apps/web/index.html
|
||||
docs/HANDOFF.md
|
||||
docs/TODO.md
|
||||
docs/decisions/local-voice-stack.md
|
||||
docs/dev_dashboard.html
|
||||
docs/guides/local-development.md
|
||||
docs/guides/testing.md
|
||||
docs/ops/backlog-2026-06-26.md
|
||||
docs/ops/evidence/g8-clean-head-material-milestone-2026-08-09.json
|
||||
docs/ops/nas-preview-g8-rollback-proof-runbook.md
|
||||
docs/ops/outcome-os-g7-external-proof-readiness-2026-08-07.md
|
||||
docs/ops/public-runtime-watchdog.md
|
||||
scripts/boot-public-runtime.ps1
|
||||
scripts/capture-g7-topology-evidence.py
|
||||
scripts/check-dev-dashboard-ssot.py
|
||||
scripts/check-g7-external-proof.py
|
||||
scripts/install-public-runtime-task.ps1
|
||||
scripts/local-whisper-stt-server.py
|
||||
scripts/melotts-server.py
|
||||
scripts/probe-public-voice-sidecars.py
|
||||
scripts/register-boot-task.ps1
|
||||
scripts/run-g7-external-proof-window.py
|
||||
scripts/run-outcome-os-release-agent.py
|
||||
scripts/start-local-whisper-stt.ps1
|
||||
scripts/start-public-runtime.ps1
|
||||
scripts/test_g7_external_proof.py
|
||||
scripts/test_g7_topology_evidence.py
|
||||
scripts/test_local_whisper_stt_server.py
|
||||
scripts/test_melotts_server.py
|
||||
scripts/test_outcome_os_release_agent.py
|
||||
scripts/test_public_runtime_watchdog_provenance.py
|
||||
scripts/test_public_voice_sidecar_probe.py
|
||||
scripts/test_run_g7_external_proof_window.py
|
||||
scripts/test_start_public_runtime_contract.py
|
||||
scripts/watch-public-runtime-hidden.vbs
|
||||
scripts/watch-public-runtime.ps1
|
||||
```
|
||||
|
||||
아래 6개와 ignored `infra/.env.nas-preview`는 절대 stage하지 않는다. NAS 실행 시 env는
|
||||
`--nas-env-file D:\workspace\vignette\infra\.env.nas-preview`로 외부 주입한다.
|
||||
|
||||
```text
|
||||
apps/api/engine.public.err.log.2026-08-07T2200.bak
|
||||
apps/api/engine.public.err.log.20260809-160042.bak
|
||||
apps/api/engine.public.out.log.2026-08-07T2200.bak
|
||||
apps/api/engine.public.out.log.20260809-160042.bak
|
||||
apps/web/test-results/g7-voice-consent-desktop.png
|
||||
apps/web/test-results/g7-voice-consent-mobile.png
|
||||
```
|
||||
|
||||
#### 2026-08-09 clean-head 실행 결과 — NAS mutation 0
|
||||
|
||||
- dry-run `D:\workspace\vignette-recovery\g8-clean-head-dry-run-20260809.json`: `ok=true`,
|
||||
active `6030a677…c611`, desired archive `6b1b15bd…a3f7`, `deployment_mutated=false`.
|
||||
- execute 1 `...\g8-clean-head-execute-20260809.json`: Windows Git archive의 CRLF 때문에 생성 API 타입 byte check가
|
||||
실패했다. archive SHA 검증 후 candidate의 생성 타입/셸 스크립트만 LF로 정규화하도록 controller를 수정했다.
|
||||
NAS mutation 0, rollback 불필요.
|
||||
- execute 2 `...\g8-clean-head-execute-20260809-r2.json`: API/types/typecheck/build/candidate stack은 통과했고
|
||||
Playwright는 **109 passed / 5 failed**였다. source-only UUID 스펙 4건은 production build에서
|
||||
`/src/lib/uuid.ts`를 import할 수 없는 gate 배치 오류라 별도 Vite gate로 분리했다. 남은 1건은 아래 제품 버그다.
|
||||
NAS mutation 0, rollback 불필요. 동일 전체 execute를 아직 다시 시도하지 않았다.
|
||||
|
||||
#### P0 수정 완료 — 회기 timestamp UTC 전송과 KST 표시 날짜
|
||||
|
||||
- 최초 실패 스펙: `session-persistence.spec.ts`의 `persists AI tutor coaching history through reload @single-run`.
|
||||
- 코칭 생성·DB 저장·reload 후 `C` 마커까지는 성공했지만, 수정 전 화면이 `9:03:59 / 543:59` 경과로 계산해
|
||||
`회기 시간이 종료됐어요` 모달을 띄웠고 그 모달이 코칭 마커 클릭을 가로막았다.
|
||||
- 원인: `apps/api/app/session_read_model.py::iso()`가
|
||||
`datetime.fromtimestamp(ts).isoformat(timespec="seconds")`로 timezone 없는 문자열을 반환한다.
|
||||
`apps/web/src/pages/Session.tsx::elapsedFromSession()`은 `Date.parse(detail.started_at)`로 이를 브라우저 로컬 KST로
|
||||
해석한다. Linux API UTC와 KST 브라우저 사이에서 약 9시간 오차가 생긴다.
|
||||
- 수정: `session_read_model.iso()`와 dashboard용 `session_metrics.iso_datetime()`은 UTC `+00:00`을 반환한다.
|
||||
회기 리뷰 달력 날짜는 서버 timezone이 아니라 고정 KST(+09:00)를 사용한다.
|
||||
- backend 관련 65 passed, 후속 voice 계약을 포함한 API 전체 **921 passed**, Ruff·compile·web typecheck가 통과했다.
|
||||
- 고유 Compose project/volume의 실제 DB/API/engine/browser에서 KST context로 focused 1/1 PASS했다. UTC suffix,
|
||||
`status=active`, `ended_at=null`, 회기 나이 10분 미만, 화면 `00:00-09:59`, timebar/dialog 0,
|
||||
coach mark 실제 클릭과 DB history/source-pack dialog를 모두 검증했다. 종료 뒤 container/volume/listener는 0이다.
|
||||
- **테스트 강제 클릭이나 모달 닫기 우회는 사용하지 않았다.** 현재 clean source HEAD `94c681d…7fb`에는 이 수정과
|
||||
controller 변경이 없으므로 사용자에게 commit 승인을 받은 뒤 새 clean commit의 HEAD/tree/archive를 다시 산출한다.
|
||||
|
||||
### 6.2 G8: NAS preview 배포와 실제 origin E2E
|
||||
|
||||
clean-head release mode가 GREEN이면 전용 NAS preview만 대상으로 백업→dry-run→배포→검증한다. 공개 DB·공개 API는
|
||||
이 단계에서 건드리지 않는다. 성공 조건은 다음 전부다.
|
||||
|
||||
직전 새 backup은
|
||||
`/volume1/docker/vignette-preview-20260807/backups/vignette-preview-pre-clean-head-20260809T051611Z.dump`,
|
||||
SHA-256 `72698232d132a8f848d81ff9d8602430e7e623c7646cc236cb39f978aab5d41c`,
|
||||
1,015,222 bytes, TOC 1,752다. 하지만 release agent 자체에는 `pg_dump`/`pg_restore` 실행 계약이 없다.
|
||||
따라서 **매 execute 직전** 별도 승인된 백업 단계에서 fresh custom dump를 만들고 SHA-256·byte size·TOC parse/count와
|
||||
current DB identity를 증거에 결속한 뒤에만 승격한다. 기존 dump 존재만으로 이 단계를 생략하지 않는다.
|
||||
|
||||
release agent의 자동 rollback 범위는 exact API/Web image, Compose 적용 상태, active-state 파일뿐이다. 이미 적용된
|
||||
DB migration이나 데이터 변경은 자동 복구하지 않는다. migration/data restore는 별도 owner 승인·대상 DB identity·dump
|
||||
hash를 갖춘 복원 절차로만 수행한다. 이미지 rollback 성공을 DB rollback 성공으로 기록하면 안 된다.
|
||||
|
||||
- 새로운 active SHA와 exact API/Web image ID, predeploy dump SHA, previous image 보존.
|
||||
- health 3/3, db/engine true, auth 401, OpenAPI와 G0~G8 route 존재.
|
||||
- `http://100.116.83.60:8088` **실제 평문 NAS origin**에서 §8의 회기 E2E 묶음 0 failure.
|
||||
- `crypto.randomUUID` 예외 0, error boundary 0, SSE→DB review PASS.
|
||||
- 학생 홈→회기→리뷰→G4/G5 반환 연습의 desktop/mobile·DB read reload·멱등·UUID 비노출 유지.
|
||||
- 실패 시 자동 rollback과 previous image 복구를 검증하고, 성공 전에는 G8 카드 전체를 DONE으로 바꾸지 않는다.
|
||||
|
||||
### 6.3 G7: 외부 종료 Gate
|
||||
|
||||
G7 내부 소스 계약은 완료됐고 `scripts/check-g7-external-proof.py`는 현재 의도적으로 exit 1이다.
|
||||
네 artifact를 **같은 public host, 겹치는 50분 시간창**으로 수집해야 한다.
|
||||
|
||||
먼저 닫아야 할 운영·코드 선행조건이 있다.
|
||||
|
||||
- 공개 API는 여전히 119 paths 구버전이며 `/admin/voice-runtime`이 없고(404), `/voice/health`는
|
||||
`openai/gpt-4o-transcribe` + `openai/gpt-4o-mini-tts`다.
|
||||
- local Whisper 9882와 MeloTTS 9883 listener는 아직 공개 호스트에 떠 있지 않다. 다만 current source의
|
||||
`start-public-runtime.ps1`는 두 sidecar를 API보다 먼저 시작하고 `local_whisper/small/cpu-int8`,
|
||||
`melotts/melotts-korean` exact provider/model·WS/HTTP readiness를 fail-closed로 확인하며, 기존 비정상 리스너를
|
||||
임의 종료·재사용하지 않는다. Uvicorn도 `--ws websockets --ws-max-queue 4`로 고정했다. source 검증은
|
||||
launcher/sidecar 80/80, API voice 71/71, PowerShell parse·Ruff·compile·diff-check PASS지만 공개 실행은 0회다.
|
||||
- **외부 실행 전 코드 P0 3건은 완료:** (1) production 프로세스 exit 0은 canonical checker
|
||||
`returncode == 0 && gate_closed == true`에 결속한다. (2) 실제 브라우저 Origin
|
||||
`https://vignette.chanpaca.net`은 exact HTTPS allowlist로, API/WSS/admin/topology
|
||||
`api-vignette.chanpaca.net`은 별도 transport host와 `wss`/`https` scheme으로 검증한다. (3) 실제 capture 기본·최소를
|
||||
3,120초로 올리고 sampler를 ceil+terminal sample로 계산하며, checker가 voice/runtime/topology 공통 교집합
|
||||
**3,000초 이상**을 강제한다. focused runner 37/37과 통합 G7 166/166을 통과했다.
|
||||
- 현재 공개 topology는 Windows host Uvicorn + cloudflared다. 명시적 `windows_host` topology mode는 구현 완료됐다.
|
||||
API/cloudflared의 PID·start time·executable name/SHA256·command-line SHA256·Git SHA·cwd를 pin하고, 각 sample 전후
|
||||
identity와 Git HEAD drift를 검사한다. RSS/peak RSS/CPU/handles/threads, process TCP/established/listener,
|
||||
API listen-port owner/conflict와 host TCP high-water도 수집한다. 기존 `linux_compose`의 api+caddy·fresh-container
|
||||
규칙은 그대로 유지한다.
|
||||
- Windows topology focused 검증은 G7 runner/checker/topology 86/86, Ruff, `py_compile`, `git diff --check`를 통과했고 실제
|
||||
read-only adapter가 API 8001 owner=1/conflict=0, cloudflared established TCP>0를 읽었다. raw command line이나
|
||||
endpoint는 저장하지 않았다.
|
||||
- **남은 provenance blocker는 배포/실증:** 현재 API cwd는
|
||||
`D:\workspace\vignette-outcome-g7-release-20260807\apps\api`이고 그 release 디렉터리에는 `.git`이 없다. 소스의
|
||||
`-RequireFreshPublicProvenance` 모드는 detached-clean commit/tree와 Python/cloudflared/config SHA를 mutation 전에
|
||||
검증하고, legacy API와 exact-config cloudflared를 bounded stop한 뒤 pinned release cwd에서 새 PID로 교체한다.
|
||||
PID/start/exe SHA/command SHA/실제 cwd를 재검증한 safe receipt에는 raw command line·config 내용을 남기지 않는다.
|
||||
topology artifact도 같은 commit/tree, runner/collector/checker SHA와 `psutil==6.1.1`을 매 sample 전후 결속한다.
|
||||
이 코드는 atomic receipt contract 18/18을 포함한 G7 통합 166/166으로 GREEN이지만 **현재 public에는 아직 실행하지 않았다.**
|
||||
clean commit 전 legacy cwd/Git pin을 약화하거나 기존 PID를 증거로 재사용하지 않는다.
|
||||
- watchdog·로그온 boot의 detached-clean source pin 코드는 완료됐지만 실제 task action은 아직 legacy shared
|
||||
source다. clean commit 뒤 `docs/ops/public-runtime-watchdog.md` 순서대로 같은 stable release root에 두 task를
|
||||
재등록하고 commit/tree/script hash marker와 `LastTaskResult=0`을 확인한다. 그 전에는 current
|
||||
`LastTaskResult=1`을 실패가 아니라 안전 차단으로 유지하고, 단일 mutation owner가 public 배포 시간창을 소유한다.
|
||||
- 운영 Python에 `psutil`이 없으면 collector는 `command_unavailable:psutil`로 fail-closed한다. current
|
||||
`apps/api/requirements.txt`와 명시적 Python 3.11 모두 `psutil==6.1.1`로 고정됐다.
|
||||
- 현재 운영 STT 모델은 이 호스트에서 실측된 CPU int8 `small`로 고정한다. cuDNN 9가 설치되고 별도 성능·정확도
|
||||
gate를 통과하기 전까지 launcher·API runtime metadata·50분 runner/checker expected model을 모두 `small`로 유지한다.
|
||||
|
||||
```powershell
|
||||
& $py -X utf8 -B scripts/check-g7-external-proof.py `
|
||||
--voice-soak <soak.json> --runtime <runtime.json> `
|
||||
--topology <topology.json> --human-voice-gain <pack.json>
|
||||
```
|
||||
|
||||
1. `scripts/soak-public-voice-websocket.py` v4 — 운영 Deepgram/OpenAI TTS, authenticated public WSS,
|
||||
1. `scripts/soak-public-voice-websocket.py` v4 — 운영 기본값 `local_whisper`/`melotts`의 ready metadata가
|
||||
실제 provider/model과 정확히 일치하는 authenticated public WSS,
|
||||
**명시 동의 물리 마이크** 50분 양방향 `passed`. `--confirm-physical-capture` 없이는 장치 열거·캡처를 하지 않는다.
|
||||
2. `scripts/capture-g7-runtime-evidence.py` — 같은 시간창의 관리자 endpoint worker/Uvicorn queue high-water.
|
||||
3. `scripts/capture-g7-topology-evidence.py` — 같은 host·exact image의 50분 cgroup/proc/Docker/TCP high-water.
|
||||
4. 독립 blind human voice-gain pack — 최소 30명 / 50회기 / 150 paired axis / blind evaluator 2인 /
|
||||
4. 독립 blind human voice-gain pack — held-out 30명 외 calibration split 참가자 포함 총 최소 31명 /
|
||||
held-out 50회기 / 150 paired axis / blind evaluator 2인 /
|
||||
ICC(A,1) ≥ 0.75 · κ ≥ 0.70 · gain ≥ 0.01 · participant-cluster bootstrap 10,000회 95% CI lower > 0.
|
||||
|
||||
준비 상세는 `docs/ops/outcome-os-g7-external-proof-readiness-2026-08-07.md`.
|
||||
|
||||
G7 Windows topology 회귀 명령:
|
||||
|
||||
```powershell
|
||||
Set-Location D:\workspace\vignette
|
||||
$env:PYTHONPATH='D:\workspace\vignette\apps\api'
|
||||
& $py -X utf8 -m unittest scripts/test_g7_external_proof.py scripts/test_g7_topology_evidence.py `
|
||||
scripts/test_g7_runtime_evidence.py scripts/test_g7_external_voice_soak.py `
|
||||
scripts/test_run_g7_external_proof_window.py apps/api/app/test_g7_voice_gain_evidence.py
|
||||
& $ruff check scripts/capture-g7-topology-evidence.py scripts/check-g7-external-proof.py `
|
||||
scripts/run-g7-external-proof-window.py scripts/test_g7_topology_evidence.py `
|
||||
scripts/test_g7_external_proof.py scripts/test_run_g7_external_proof_window.py
|
||||
```
|
||||
|
||||
**사용자에게 받아야 하는 것 (코드로 대체 불가):**
|
||||
|
||||
- 운영 Deepgram key·quota 사용 권한
|
||||
- authenticated learner/admin session 또는 안전한 `storageState` 생성 협조
|
||||
- 물리 마이크 50분 실행에 대한 **명시적 동의**
|
||||
- 실제 참가자·독립 평가자 운영 승인
|
||||
|
|
@ -220,7 +530,7 @@ $py = 'C:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe'
|
|||
$ruff = 'C:\Users\encep\AppData\Local\hermes\hermes-agent\venv\Scripts\ruff.exe'
|
||||
```
|
||||
|
||||
### Backend full — 기준 API **898 passed**, gateway **58 passed**
|
||||
### Backend full — 기준 API **921 passed**, gateway **58 passed**
|
||||
|
||||
```powershell
|
||||
Set-Location D:\workspace\vignette\apps\api
|
||||
|
|
@ -228,7 +538,7 @@ Set-Location D:\workspace\vignette\apps\api
|
|||
& $py -X utf8 -B -m pytest -p no:cacheprovider engine_gateway -q
|
||||
```
|
||||
|
||||
### G8 executor/probe/helper/release governance — 기준 28 / 11 / 37 / 21
|
||||
### G8 executor/probe/helper/release governance — 기준 28 / 11 / 37 / 30
|
||||
|
||||
```powershell
|
||||
Set-Location D:\workspace\vignette
|
||||
|
|
@ -248,6 +558,89 @@ npm run typecheck
|
|||
npm run build
|
||||
```
|
||||
|
||||
### 현재 HEAD 학생 UX·접근성 focused — 기준 **20/20**
|
||||
|
||||
2026-08-09 읽기 전용 재감사에서 `check:api-types`·typecheck·build와 아래 fixture-only 묶음이 desktop/mobile
|
||||
20/20을 통과했다: Alliance Pulse 8, 자기주도 폐루프 2, insecure-origin UUID 6, pre/mid 자기점검 2,
|
||||
시간만료 경고·종료 모달 2. 390×844·320×568 overflow 0, tab 키보드, Enter CTA, 44px 조작부,
|
||||
4.5:1 대비, raw UUID/theory key 비노출, 동의 전 `getUserMedia`/`enumerateDevices` 0도 확인했다.
|
||||
|
||||
추가 회귀에서 새 음성 동의 modal을 실제로 수락한 뒤 위기 음성 종료까지 가는 desktop/mobile, 모바일 review
|
||||
filter 44px 계약을 desktop/mobile로 검증해 4/4 통과했다. dashboard의 file-origin은 spec-relative repo root로
|
||||
고정했고, session-review fixture 소유권과 viewport assertion, admin live snapshot race를 교정했다. 16:49 단일 실행은
|
||||
**120/120 PASS(92.0s)**였지만, 그 뒤 dashboard 변경분은 dashboard 10/10으로만 따로 통과했다. 따라서 현 트리는
|
||||
110 불변 + 10 최신 분할 GREEN이고, 최종 clean source에서 아래 exact 묶음을 다시 단일 실행해 HEAD/tree/archive
|
||||
결속 뒤 120/120을 요구한다.
|
||||
|
||||
#### current-source fixture exact 120 — clean HEAD 재현 명령
|
||||
|
||||
정확한 수집 구성은 `dev-dashboard` 10 + `session-mvp` 24 + `session-review` 24 + `learner` 14 +
|
||||
`session-layout` 8 + `admin` 40 = desktop/mobile 120이다. `127.0.0.1:8000`은 반드시 복구 dump에서 만든
|
||||
**disposable API/DB/engine 전용 스택**이어야 한다. 이 묶음은 dev-login·세션 쓰기를 포함할 수 있으므로 public
|
||||
8001, 복구 원본 55432, candidate DB, NAS DB에 연결하지 않는다.
|
||||
|
||||
```powershell
|
||||
$ErrorActionPreference = 'Stop'
|
||||
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
$OutputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
$cleanRoot = 'D:\workspace\vignette-clean-<NEW_SHA>'
|
||||
$cleanWeb = Join-Path $cleanRoot 'apps\web'
|
||||
$tempRoot = Join-Path $env:TEMP 'vignette-clean-fixture-120'
|
||||
$tempWeb = Join-Path $tempRoot 'apps\web'
|
||||
$tempEvidence = Join-Path $tempRoot 'docs\ops\evidence'
|
||||
if (Test-Path -LiteralPath $tempRoot) { throw "기존 TEMP와 충돌: $tempRoot" }
|
||||
$pw = Join-Path $cleanWeb 'node_modules\.bin\playwright.cmd'
|
||||
if (-not (Test-Path -LiteralPath $pw -PathType Leaf)) {
|
||||
throw 'clean worktree에서 npm ci --ignore-scripts를 먼저 실행해 node_modules를 준비할 것'
|
||||
}
|
||||
$health = Invoke-RestMethod -Uri 'http://127.0.0.1:8000/health' -TimeoutSec 5
|
||||
if ($health.status -ne 'ok' -or -not $health.db -or -not $health.engine) {
|
||||
throw 'disposable API8000의 db/engine ready가 아님'
|
||||
}
|
||||
New-Item -ItemType Directory -Path $tempWeb -Force | Out-Null
|
||||
New-Item -ItemType Directory -Path $tempEvidence -Force | Out-Null
|
||||
$config = Join-Path $cleanWeb 'playwright.config.ts'
|
||||
$files = @(
|
||||
'e2e/dev-dashboard.spec.ts',
|
||||
'e2e/session-mvp.spec.ts',
|
||||
'e2e/session-review.spec.ts',
|
||||
'e2e/learner.spec.ts',
|
||||
'e2e/session-layout.spec.ts',
|
||||
'e2e/admin.spec.ts'
|
||||
)
|
||||
$env:PLAYWRIGHT_PORT = '15374'
|
||||
$env:VITE_API_PROXY_TARGET = 'http://127.0.0.1:8000'
|
||||
Remove-Item Env:PLAYWRIGHT_BASE_URL -ErrorAction SilentlyContinue
|
||||
Remove-Item Env:PLAYWRIGHT_SKIP_WEB_SERVER -ErrorAction SilentlyContinue
|
||||
$code = 99
|
||||
try {
|
||||
Push-Location $tempWeb
|
||||
try {
|
||||
& $pw test @files ("--config={0}" -f $config) `
|
||||
--project=chromium-desktop --project=chromium-mobile `
|
||||
--workers=4 --reporter=line ("--output={0}" -f (Join-Path $tempRoot 'pw-results'))
|
||||
$code = $LASTEXITCODE
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
} finally {
|
||||
$listeners = @(Get-NetTCPConnection -State Listen -LocalPort 15374 -ErrorAction SilentlyContinue)
|
||||
$listenerError = $null
|
||||
if ($listeners.Count -ne 0) { $listenerError = "Playwright Vite listener 잔존: $($listeners.OwningProcess -join ',')" }
|
||||
if (Test-Path -LiteralPath $tempRoot) {
|
||||
$resolved = (Resolve-Path -LiteralPath $tempRoot).Path
|
||||
if ($resolved -ne $tempRoot) { throw "unexpected TEMP target: $resolved" }
|
||||
[IO.Directory]::Delete($tempRoot, $true)
|
||||
}
|
||||
if ($listenerError) { throw $listenerError }
|
||||
}
|
||||
if ($code -ne 0) { throw "clean fixture 120 failed: $code" }
|
||||
```
|
||||
|
||||
과거 API-ready 없이 돌린 별도 Vite-only 혼합 실행의 `session-layout` 8건은 DB persona가 없어 제품 assertion 전에
|
||||
`seed_fallback/degraded`로 중단됐다. 위 exact 120 명령은 이 사각지대를 닫기 위해 disposable API의 db/engine health를
|
||||
선행 강제한다. 최종 완료 때는 `session-layout` 8/8과 returned-practice DB closed-loop 4/4를 모두 다시 통과시킨다.
|
||||
|
||||
### Dashboard SSOT + E2E — 기준 SSOT PASS(FAIL 0), unit 5/5, dashboard 10/10
|
||||
|
||||
```powershell
|
||||
|
|
@ -275,7 +668,7 @@ node.exe .\node_modules\@playwright\test\cli.js test e2e/session-layout.spec.ts
|
|||
--project=chromium-desktop --project=chromium-mobile --project=chromium-single-run --workers=1 --reporter=line
|
||||
```
|
||||
|
||||
전체 Playwright inventory는 610 tests / 44 files다. 실행 환경/API/DB를 정확히 맞추지 않고 fixture failure를
|
||||
전체 Playwright inventory는 614 tests / 44 files다. 실행 환경/API/DB를 정확히 맞추지 않고 fixture failure를
|
||||
제품 failure로 오인하지 않는다. 같은 blocker가 두 번 반복되면 전체 재시도 대신 원인·증거·수정 계획을 먼저 보고한다.
|
||||
|
||||
## 9. 핵심 변경 파일
|
||||
|
|
@ -286,9 +679,18 @@ node.exe .\node_modules\@playwright\test\cli.js test e2e/session-layout.spec.ts
|
|||
- `scripts/run-outcome-os-release-agent.py` · `scripts/test_outcome_os_release_agent.py`
|
||||
- `scripts/check-dev-dashboard-ssot.py` · `scripts/test_dev_dashboard_ssot.py`
|
||||
- `scripts/check-g7-external-proof.py` · `scripts/soak-public-voice-websocket.py` ·
|
||||
`scripts/capture-g7-runtime-evidence.py` · `scripts/capture-g7-topology-evidence.py`
|
||||
`scripts/capture-g7-runtime-evidence.py` · `scripts/capture-g7-topology-evidence.py` ·
|
||||
`scripts/run-g7-external-proof-window.py`와 대응 G7 테스트 3개
|
||||
- `apps/web/src/lib/uuid.ts` · `apps/web/e2e/insecure-context-uuid.spec.ts` (신규, 비-secure origin 결함 수정)
|
||||
- `apps/web/e2e/dev-dashboard.spec.ts` · `apps/web/e2e/returned-practice-db-closed-loop.spec.ts` ·
|
||||
- `apps/api/app/session_read_model.py` · `apps/api/app/services/session_metrics.py` ·
|
||||
`apps/api/app/test_session_read_model.py` · `apps/web/e2e/session-persistence.spec.ts` (UTC/KST P0와 실DB browser 회귀)
|
||||
- `apps/web/index.html` · `apps/web/e2e/self-directed-learning-loop.spec.ts` (SPA route 전환 중 boot diagnostic 회복)
|
||||
- `apps/web/e2e/admin.spec.ts` · `apps/web/e2e/dev-dashboard.spec.ts` ·
|
||||
`apps/web/e2e/session-review-fixture.ts` · `apps/web/e2e/session-review.spec.ts`
|
||||
- `scripts/watch-public-runtime.ps1` · `scripts/boot-public-runtime.ps1` ·
|
||||
`scripts/install-public-runtime-task.ps1` · `scripts/register-boot-task.ps1` ·
|
||||
`scripts/watch-public-runtime-hidden.vbs` · `scripts/test_public_runtime_watchdog_provenance.py`
|
||||
- `apps/web/e2e/returned-practice-db-closed-loop.spec.ts` ·
|
||||
`apps/web/e2e/harness/prepare-returned-practice-db.py`
|
||||
- `docs/dev_dashboard.html` · `docs/TODO.md` · `docs/README.md` · `docs/ops/backlog-2026-06-26.md`
|
||||
- `docs/ops/nas-preview-g8-rollback-proof-runbook.md` ·
|
||||
|
|
@ -303,3 +705,69 @@ node.exe .\node_modules\@playwright\test\cli.js test e2e/session-layout.spec.ts
|
|||
- G7 물리 마이크·사람 평가를 무동의/합성 데이터로 대체하지 않는다.
|
||||
- current/previous images와 DB backup을 확인하기 전 destructive operation을 실행하지 않는다.
|
||||
- 성공보다 정직한 fail-closed 증거가 우선이다.
|
||||
|
||||
## 11. 권장 멀티에이전트 실행 구조
|
||||
|
||||
사람 개발자 작업계획은 만들지 않는다. 구현·테스트·배포·증거 동기화는 에이전트가 수행하고, 사람에게는 코드로
|
||||
대체 불가능한 동의·인증 세션·실제 참가자/평가자 승인만 요청한다. 동시 작업은 최대 세 하위 lane으로 나누되,
|
||||
NAS/public/DB mutation owner는 항상 한 에이전트만 둔다.
|
||||
|
||||
1. **G8 release lane:** clean-head release mode 구현·unit·dry-run·NAS preview 배포·NAS-origin E2E. 이 lane만 NAS mutation.
|
||||
2. **G7 proof lane:** current public route/config 감사, local provider readiness, `--rehearse`, 네 artifact 수집 준비.
|
||||
실제 마이크는 사용자 명시 동의 전 0회, public mutation은 G8 lane과 시간 겹치지 않게 단일 owner에게 인계.
|
||||
3. **E2E/SSOT lane:** local fixture, disposable DB browser, visual/accessibility, dashboard/TODO/backlog 정합을 읽기 전용으로 감사.
|
||||
|
||||
루트 에이전트는 각 lane의 증거 SHA와 실패 원인을 합쳐 게이트를 판정한다. 같은 blocker가 두 번 반복되면 세 번째
|
||||
재시도 전에 원인·증거·수정 계획을 사용자에게 보고한다.
|
||||
|
||||
## 12. 새 에이전트에 그대로 줄 시작 프롬프트
|
||||
|
||||
```text
|
||||
D:\workspace\vignette의 docs/HANDOFF.md를 인수인계 SSOT로 읽고, AGENTS.md → docs/README.md →
|
||||
docs/dev_dashboard.html → docs/TODO.md → docs/ops/backlog-2026-06-26.md 순서로 현재 상태를 재확인해.
|
||||
|
||||
현재 master는 HEAD 94c681d…7fb이고 origin/master보다 2 commit 앞서며, timestamp·voice provider/model,
|
||||
G7 topology·public launcher·G8 clean-head·SSOT가
|
||||
아직 tracked dirty다. 기존 D:\workspace\vignette-clean-head-94c681d와 g8-focus-head-94c681d.tar는 이 최신 변경을
|
||||
포함하지 않으므로 배포 입력으로 사용하지 마. 먼저 git status/diff와 각 lane의 focused test를 재확인해.
|
||||
|
||||
최종 목표는 G0~G8 아홉 목표를 코드-only가 아니라 실제 runtime 증거로 모두 닫는 것이다. G0~G6은 현재 내부 DONE
|
||||
증거를 보존하되 새 clean HEAD 전체 회귀로 재확인해. G8 rollback receipt와 clean-head controller는 구현됐지만
|
||||
2026-08-09 execute 두 번은 모두 NAS mutation 전에 fail-closed됐다. docs/HANDOFF.md §6.1의 naive
|
||||
session timestamp 버그는 UTC/KST 계약과 focused disposable DB/API/browser E2E로 GREEN이다. 테스트 강제 클릭 우회는
|
||||
없다. 사용자 승인을 받아 이 변경을 새 clean commit으로 만들고 HEAD/tree/archive를 다시 결속해 격리 NAS preview에
|
||||
배포한 다음 실제 NAS HTTP origin의 전체 회기 E2E를 0 failure로 통과시켜. G7은 내부 구현만 DONE이다. current source 공개 배포,
|
||||
local_whisper/melotts ready, authenticated public WSS, 사용자의 명시 동의를 받은 물리 마이크 50분 soak,
|
||||
같은 시간창의 runtime/topology high-water, 독립 human voice-gain pack까지 모아 canonical checker exit 0을 만들어야 한다.
|
||||
G7 runner exit의 checker exit 0/gate_closed 결속, browser Origin과 API/WSS host 분리, 3,120초 capture와
|
||||
3,000초 공통 overlap, detached-clean HEAD/tree, runner/collector/checker SHA와 exact psutil pin은 소스에서 완료됐다.
|
||||
API/cloudflared는 legacy PID를 재사용하지 말고 `-RequireFreshPublicProvenance`로 pinned release cwd에서 강제
|
||||
재시작해 새 PID/start/exe/command SHA/cwd safe receipt를 결속해.
|
||||
G7 runner/checker/topology는 86/86, public launcher/sidecar 계약은 80/80, 통합은 166/166, API voice 통합은 71/71로 구현됐지만
|
||||
현재 public API의 Git 없는 냉동 release cwd는 provenance gate를
|
||||
통과하지 못한다. current committed repo 기반 launcher/runtime으로 정렬하거나 동등하게 강한 provenance를 구현하고,
|
||||
cwd/Git pin이나 기존 Linux Compose gate를 약화하지 마. 합성 증거로 G7을 DONE 처리하지 마.
|
||||
public task source-pin 코드는 구현됐다. watchdog·로그온 boot는 detached clean release와 exact Git commit/tree,
|
||||
watchdog|boot/start script SHA가 없으면 mutation 전에 실패한다. 하지만 실제 두 Scheduled Task action은 아직 shared
|
||||
worktree와 legacy 인자를 가리키며, 5분 watchdog은 최신 실행에서 pin 인자 부재로 `LastTaskResult=1`이다. public은
|
||||
현재 health ok지만 자동복구는 의도적으로 fail-closed다. clean commit 뒤 동일 stable release root로 두 task를
|
||||
재등록하고 action pin·watchdog result 0·로그온/reboot smoke를 증명한 뒤에만 runtime DONE으로 바꿔.
|
||||
|
||||
NAS 17:07 기준선은 active 6030a677…c611, API/Web 52e0…8b2d/6fdb…f215, previous images 보존,
|
||||
Compose 4개 running+unless-stopped, volume vignette-preview-20260807_pgdata, health/routes/assets GREEN, helper/listener 0이다.
|
||||
release agent는 DB dump/restore를 지원하지 않으므로 execute 직전 fresh custom dump SHA/size/TOC와 DB identity를
|
||||
별도로 결속해. 자동 rollback은 image/Compose/active-state뿐이고 migration/data restore는 별도 owner 승인이야.
|
||||
remote release root/Compose mode 0777 P1도 최소권한 preflight로 닫기 전에는 execute하지 마.
|
||||
|
||||
학생 자기주도 폐루프는 홈 추천→회기→리뷰→G4 처방/G5 전이→별도 재연습→completed-session observer→
|
||||
actual attempt/execution→read-model reload→before/after·멱등까지 desktop/mobile 실제 DB/browser로 검증해.
|
||||
최종 검증은 API full, gateway full, schema/provenance, Web API types/typecheck/build, fixture E2E, disposable DB E2E,
|
||||
NAS-origin E2E, visual/accessibility, auth/OpenAPI/assets/SSE→DB review를 포함해야 한다.
|
||||
|
||||
독립 작업은 서브에이전트로 병렬화하되 NAS/public/DB mutation은 단일 owner만 수행해. 공개 DB 복구 volume과
|
||||
pre-recovery container/dumps, NAS named volume과 previous images는 삭제하지 마. 비밀/PII/raw UUID를 증거에 남기지 마.
|
||||
과거 로컬 recovery dump 경로는 인계 시점에 보이지 않으므로 삭제로 단정하거나 그 파일을 전제로 복원하지 말고,
|
||||
off-host/NAS 보관 위치와 새 backup 목적지를 먼저 확인해.
|
||||
source-only PASS나 health-only를 DONE으로 부르지 말고, 대시보드·TODO·backlog·HANDOFF를 마지막 증거와 함께 동기화해.
|
||||
모든 게이트가 실제로 닫히기 전에는 goal complete를 선언하지 마.
|
||||
```
|
||||
|
|
|
|||
92
docs/TODO.md
92
docs/TODO.md
|
|
@ -14,10 +14,18 @@
|
|||
> 출처: `ops/backlog-2026-06-26.md` B2 · 대시보드 운영 탭 · `ops/tailscale-vnet-runtime-2026-06-27.md` · `ops/public-runtime-watchdog.md` · `HANDOFF.md`
|
||||
|
||||
- [ ] **공개 DB 계정·회기 복구 안정화** — 2026-08-07 18:26 KST owner 승인으로 recovered named volume을
|
||||
공개 `vignette-dev-db:55432`에 전환했다. public 기준 users 84/sessions 30/turns 705,
|
||||
공개 `vignette-dev-db:55432`에 전환했다. cutover 기준 users 84/sessions 30/turns 705,
|
||||
Google 계정 16/소유 회기 30, orphan 0, health·engine·OAuth·watchdog가 정상이다. 전환 직전 container와
|
||||
dump, 원본 volume을 보존한다. 남은 것은 사용자가 Google 재로그인한 뒤 본인 과거 회기가 보이는지
|
||||
확인하는 일과 자동 백업 운영화다. 기존 인증 세션은 복사하지 않아 재로그인이 필요하다.
|
||||
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 release의 Git commit/tree와 watchdog|boot/start SHA가 없으면 mutation 전에 실패하도록 코드로 고정했다.
|
||||
실제 두 Scheduled Task action은 아직 shared worktree라 watchdog 반복 실행은 의도적으로
|
||||
`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 뒤 동일 stable release root로 두 task를 재등록하기 전까지 자동복구는 DONE이 아니다.
|
||||
상세: `ops/public-db-recovery-rehearsal-2026-08-07.md`.
|
||||
- [ ] **자동 DB 백업·정기 복원 리허설** — `scripts/backup-vignette-db.ps1`의 custom dump→TOC 검증→
|
||||
SHA manifest 원자 게시와 현재 public-target 일회용 복원(129 tables/9,646 rows/orphan 0)을 통과했다.
|
||||
|
|
@ -60,7 +68,9 @@
|
|||
`CLAUDE_BIN` 부재로 기동)에서 `/health`는 `ok:true`로 통과하고 `/ready`는 503으로 검출되어
|
||||
워치독이 `unhealthy (1/3)` + 503 본문을 남겼다.
|
||||
남은 것: NAS용 9100은 실사용 세션 7개가 있어 미접촉이라 (2)는 다음 재기동 때 반영된다.
|
||||
- [ ] **재부팅 후 watchdog smoke** — 실제 Windows 재부팅 후 엔진/API/터널 자동 복구 + public `/turn` 실측.
|
||||
- [ ] **stable-source task 재등록 + 재부팅 후 watchdog smoke** — 사용자 승인 clean commit의 detached release root에
|
||||
watchdog·로그온 boot task를 함께 재등록하고 action의 commit/tree/script SHA pin, 5분 watchdog
|
||||
`LastTaskResult=0`, 로그온 boot, 실제 Windows 재부팅 후 엔진/API/터널 자동 복구 + public `/turn`을 실측한다.
|
||||
DNS 개통 후 `api-vnet.18ka.net`을 `-AdditionalPublicHealthUrls`로 명시 추가.
|
||||
- [x] **노트북 로컬 음성 스택 STT 구현** — 2026-08-08 소유자 결정으로 음성 양쪽을 노트북 상주
|
||||
로컬 모델로 쓴다(STT faster-whisper, TTS는 아래 항목대로 MeloTTS). 이전에는 코드에 로컬 STT 경로가 아예 없었고
|
||||
|
|
@ -69,7 +79,7 @@
|
|||
`voice_stt_provider=local_whisper` 경로를 추가했다. 오디오는 호스트를 벗어나지 않고 외부 STT 키가
|
||||
필요 없다. 실측: 저장소 합성 시드 음성 8.72초로 interim 9 · final 5 · word timestamp 12개 전부 present,
|
||||
전사는 원문과 한 글자(`안녕하세요`→`안녕하세여`) 차이였다. 회귀 `scripts/test_local_whisper_stt_server.py`
|
||||
37/37, `apps/api/app/test_voice_service.py` 로컬 provider 케이스 포함 API 전체 908 passed.
|
||||
37/37, `apps/api/app/test_voice_service.py` 로컬 provider 케이스와 후속 계약을 포함한 current API 전체 921 passed.
|
||||
**발견 1:** 이 노트북은 CUDA 장치는 보이지만 cuDNN(`cudnn_ops64_9.dll`)이 없어 GPU 추론이 **네이티브
|
||||
크래시**로 죽는다. 같은 프로세스 try/except로는 못 잡아서 디바이스 확인을 버릴 수 있는 자식 프로세스
|
||||
(`--self-check`)로 분리하고 `auto`에서 CPU로 폴백하게 했다. cuDNN 9를 설치하면 GPU(float16)로 훨씬
|
||||
|
|
@ -84,19 +94,20 @@
|
|||
사전학습 다화자 모델이라 Higgs처럼 P1 프리셋 한정이 아니라 모든 페르소나에 적용된다.
|
||||
**실측:** CPU 정상 상태 RTF 0.27~0.28(실시간의 3.6배), `POST /tts` 200·WAV 350,566 bytes·3.61s,
|
||||
빈 텍스트 422·미지 경로 404. **왕복 검증**으로 MeloTTS 합성음을 faster-whisper가 완전 일치 전사했다.
|
||||
회귀 사이드카 16/16, API 전체 914 passed, ruff clean.
|
||||
회귀 사이드카 16/16, 후속 계약을 포함한 current API 전체 921 passed, ruff clean.
|
||||
**검토 후 탈락:** Kokoro-82M은 Apache 2.0이지만 공식 `VOICES.md`에 **한국어가 없다**(검색 요약이
|
||||
지원한다고 했으나 오답). Piper는 GPL, XTTS-v2·Fish Speech는 비상업이라 Higgs와 같은 문제다.
|
||||
설치 함정 3가지는 `decisions/local-voice-stack.md`에 남겼다(setuptools<81, unidic download, eunjeon).
|
||||
근거: [`decisions/local-voice-stack.md`](./decisions/local-voice-stack.md).
|
||||
- [ ] **G7 외부 종료 게이트 — 남은 것은 사람 입력 둘뿐** — provider 의존은 해소됐다. 2026-08-08 로컬 스택
|
||||
- [ ] **G7 외부 종료 게이트 — 공개 runtime 선행 구현 + 사람 입력** — provider 의존 코드는 해소됐다. 2026-08-08 로컬 스택
|
||||
(faster-whisper STT + MeloTTS TTS, 둘 다 MIT)으로 바꿔 외부 STT 키가 더 이상 필요 없고, 체커의 벤더
|
||||
하드코딩도 운영 provider 허용목록으로 교정했다. 기계적인 부분도
|
||||
`scripts/run-g7-external-proof-window.py`가 soak·runtime·topology 세 캡처를 **하나의 겹치는 시간창**에서
|
||||
동시에 돌리고 checker까지 이어서 실행하도록 묶었다(회귀 20/20).
|
||||
**남은 것은 코드로 만들 수 없는 둘이다.**
|
||||
사람에게서만 받을 수 있는 마지막 두 입력은 다음이다.
|
||||
(1) **명시 동의 하 물리 마이크 50분 양방향 soak** — 소유자가 실제로 50분 발화해야 한다.
|
||||
(2) **독립 blind human voice-gain pack** — 참가자 30명 / 50회기 / 150 paired axis / 독립 평가자 2인 /
|
||||
(2) **독립 blind human voice-gain pack** — held-out 30명 외 calibration split 참가자 포함 총 최소 31명 /
|
||||
held-out 50회기 / 150 paired axis / 독립 평가자 2인 /
|
||||
ICC(A,1) ≥ 0.75 · κ ≥ 0.70 · gain ≥ 0.01 · participant-cluster bootstrap 10,000회 95% CI lower > 0.
|
||||
**선행 조건(2026-08-08 발견):** 공개 런타임은 119 paths 구버전이라 `/admin/voice-runtime`이
|
||||
배포돼 있지 않다. 마이크와 사람을 다 준비해도 오늘 실행하면 50분을 버리고 artifact 2에서 실패한다.
|
||||
|
|
@ -104,10 +115,36 @@
|
|||
오케스트레이터가 시작 전에 `admin_voice_runtime_not_deployed`로 막는다 — 실측 exit 2.
|
||||
이 판정은 미인증 HTTP 상태가 아니라 OpenAPI `paths`로 한다. Cloudflare가 자동화 클라이언트에
|
||||
모든 경로를 403(error 1010)으로 주기 때문에 상태 코드로는 배포 누락을 알 수 없다.
|
||||
실행 전 `--rehearse`로 배관을 먼저 검증한다. fail-closed 경계 4종(동의 없음·pack 없음·50분 미만·host
|
||||
불일치)은 CLI로 실증했다. 상세: `ops/outcome-os-g7-external-proof-readiness-2026-08-07.md`.
|
||||
2026-08-09 재감사에서 공개 `/voice/health`는 여전히 OpenAI STT/TTS이고 local Whisper 9882·MeloTTS
|
||||
9883 listener가 없음을 확인했다. current source에는 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).
|
||||
Windows host topology mode도 PID/start/exe·command SHA/cwd와 process/TCP high-water를 검증한다.
|
||||
하지만 이 source를 공개에 배포한 적은 없고, 현재 API의 Git 없는 냉동 release cwd는 provenance gate를 통과하지
|
||||
못한다. current committed repo 기반으로 공개 runtime을 정렬한 뒤에만 50분 창을 연다.
|
||||
**외부 실행 전 코드 P0 완료:** runner 종료 코드는 canonical checker `exit 0`/`gate_closed=true`에 결속됐고,
|
||||
브라우저 Origin(`vignette.chanpaca.net`)은 API/WSS/admin/topology host(`api-vignette.chanpaca.net`)와 분리된 HTTPS
|
||||
allowlist로 검증한다. 실제 capture는 최소 3,120초·ceil+terminal sample이고 voice/runtime/topology 공통 overlap
|
||||
`≥3000s`를 checker가 강제한다. Windows artifact는 detached-clean HEAD/tree, runner/collector/checker SHA,
|
||||
exact `psutil==6.1.1`을 매 sample 전후 pin한다. fresh launcher는 legacy API와 exact-config cloudflared를 bounded
|
||||
교체하고 새 PID/start/exe/command SHA/실제 cwd의 raw command-line 없는 receipt를 만든다. G7 runner/checker/topology
|
||||
86/86, launcher/sidecar 80/80, 통합 166/166을 통과했다. 실제 공개 실행 전 `--rehearse`로 배관을 먼저 검증한다.
|
||||
fail-closed 경계(동의 없음·pack 없음·3,120초 미만·host/Origin/scheme 불일치)는 CLI로 실증했다. 상세:
|
||||
`ops/outcome-os-g7-external-proof-readiness-2026-08-07.md`.
|
||||
|
||||
> **완료된 격리 검증 경로:** NAS 프리뷰 `http://100.116.83.60:8088`을 전용 Compose 프로젝트·포트·네트워크·볼륨에 배포하고 실제 브라우저 회기와 review API 저장 축어록을 확인했다. current source exact SHA `6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611`은 전체 browser E2E 108/108, postdeploy SSE→DB review, health 3/3, OpenAPI 126·auth 401·G0~G8 route를 통과했다. 기존 프로젝트를 대상으로 한 중단·재생성 명령은 실행하지 않았다. 매일 04:30 KST `Vignette 회기 E2E 정기 검증`(automation id `vignette-e2e`)은 ACTIVE다. 새 기능이 material milestone이고 release-only patch 결정성·manifest checker·canonical clean-index·NAS preflight를 통과하며 배포 SHA가 달라질 때만 같은 격리 프리뷰를 갱신한다. 첫 스케줄 실행 이력은 아직 증거에 포함하지 않았다. 증거: [배포 증거](./ops/nas-preview-deployment-evidence-2026-08-07.md), [current 기계 판독 증거](./ops/evidence/nas-preview-current-deploy-2026-08-07.json), [브라우저 증거](./ops/evidence/nas-preview-live-turn-2026-08-07.png). 이는 G8 실제 receipt-bound rollback이나 미승인 물리 마이크·부재한 Deepgram 운영 키를 대체하지 않는다.
|
||||
> **격리 NAS current baseline / G8 `runtime REVALIDATION`:** 2026-08-09 17:07 KST 프리뷰
|
||||
> `http://100.116.83.60:8088`, project `vignette-preview-20260807`은 active SHA `6030a677…c611`, exact
|
||||
> API/Web `52e0…8b2d`/`6fdb…f215`로 실행 중이며 previous `79ec…4450`/`c530…2f28`도 보존한다.
|
||||
> `api/web/db/proxy` 4개는 running + `unless-stopped`, 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다. release agent는 DB
|
||||
> dump/restore를 수행하지 않으므로 매 execute 직전 fresh custom dump SHA/size/TOC와 DB identity를 별도 결속한다.
|
||||
> 자동 rollback은 image/Compose/active-state만 복구하며 적용 migration/data restore는 별도 owner 승인이 필요하다.
|
||||
> remote release root/Compose mode `0777`은 P1 권한 위험이므로 execute 전 최소권한·preflight를 닫는다. localhost
|
||||
> candidate 108/108과 postdeploy SSE→DB review는 과거 증거이고, 같은 SHA의 평문 origin은 UUID 결함 24건 실패라
|
||||
> current source의 완료 증거가 아니다. `randomUuid()` 수정 배포와 NAS-origin 전체 회기·SSE→DB·G4/G5
|
||||
> returned-practice 0 failure 전에는 G8을 DONE으로 바꾸지 않는다. 매일 04:30 KST `vignette-e2e` 자동화는 ACTIVE다.
|
||||
> 증거: [배포 증거](./ops/nas-preview-deployment-evidence-2026-08-07.md), [기계 판독 증거](./ops/evidence/nas-preview-current-deploy-2026-08-07.json), [브라우저 증거](./ops/evidence/nas-preview-live-turn-2026-08-07.png). 이는 G7 물리 마이크·독립 사람 평가 증거를 대체하지 않는다.
|
||||
- [ ] **claude_cli ↔ Anthropic API live 동일성** — provider 라우팅·모델 탐색·설정 저장 경로는 구현 완료. 남은 게이트는 연구팀/기관 `ANTHROPIC_API_KEY`를 게이트웨이 호스트에 주입한 뒤 같은 프롬프트의 live 응답·계량·오류 표면화를 비교하는 것이다.
|
||||
|
||||
## B. 외부 거버넌스 [외부]
|
||||
|
|
@ -364,12 +401,13 @@
|
|||
- [x] 텍스트·음성 평가 독립 산출 후 calibrated fusion 구현.
|
||||
- [x] 장면별 오디오 재생과 음성 재연습 구현.
|
||||
- [x] 비언어 특징의 과도한 임상 추론 차단 계약·테스트 구현.
|
||||
- [x] Deepgram streaming STT adapter, interim/final·word timestamp·provider event 계약과 OpenAI batch fallback 구현.
|
||||
- [x] Deepgram streaming STT adapter와 OpenAI batch fallback을 보존하고, 운영 기본 경로를 노트북 상주
|
||||
`local_whisper`/`melotts`로 전환해 interim/final·word timestamp·provider event 계약을 동일하게 구현.
|
||||
- [x] 저장 word token을 deployment `SESSION_SECRET` 기반 HMAC-SHA256 pseudonym으로 전환하고 원문 미보존을 고정.
|
||||
- [x] 열린 streaming 중 동의를 1초마다 재검사해 철회 시 provider 전송·후속 저장을 중단하는 fail-closed 계약 구현.
|
||||
- [x] 음성 단절/실패 뒤 작성 중 텍스트를 보존하고 텍스트 계속하기·키보드 가능한 음성 재연결 recovery UX 구현.
|
||||
- [x] 운영 TTS를 OpenAI API `gpt-4o-mini-tts`로 고정하고 공식 Services Agreement의 앱 통합·출력 소유 근거와
|
||||
최종 사용자 AI 생성 음성 고지를 제품·증거 계약에 반영.
|
||||
- [x] 외부 OpenAI TTS adapter와 AI 생성 음성 고지를 보존하면서, 운영 기본 TTS를 상업 사용 가능한
|
||||
MeloTTS Korean(MIT) loopback 사이드카로 전환.
|
||||
- [x] authenticated WSS `ready`에 STT/TTS provider·model을 노출하고, 마이크 열거·캡처 없는 public preflight가
|
||||
네 기대값과의 정확 일치를 검사하도록 구현. full soak만 명시적 `--confirm-physical-capture` 뒤 장치를 연다.
|
||||
- [x] synthetic gold target에 대해 text-only와 calibrated multimodal 출력을 직접 비교하는 1-MAE benchmark를
|
||||
|
|
@ -397,7 +435,7 @@
|
|||
격리 synthetic PCM soak는 59/59 연결 종료·성공 발화 55·철회 차단 2·미동의 차단 1·overflow 거부/복구 1/1,
|
||||
focused 119 passed를 통과했다. 공개 G7 route는 TLS 1.3 handshake·비인증 1008·Cloudflare edge와 마이크
|
||||
열거/캡처 0을 확인했다. 증거: `ops/evidence/g7-public-wss-preflight-current-20260807.json`.
|
||||
**남음:** 실제 Deepgram 운영 key·quota와 live interim/final, 기대값과 일치하는 authenticated public WSS ready,
|
||||
**남음:** current source의 공개 배포와 `local_whisper`/`melotts` ready metadata 일치, authenticated public WSS ready,
|
||||
사용자의 명시 동의를 받은 물리 마이크, 실행된 50분 양방향 soak, 독립 라벨 held-out/pilot의 실제 음성 이득,
|
||||
실제 worker·Uvicorn·OS·Cloudflare edge를 같은 50분 시간창으로 묶은 RSS/CPU/queue high-water.
|
||||
preflight·synthetic·옵션 제공만으로 종료하지 않는다.
|
||||
|
|
@ -416,7 +454,7 @@
|
|||
semantic runtime/browser proof, 이미지 rollback, exact-SHA SSOT handoff를 fail-closed로 구현·독립 감사.
|
||||
- [x] 위 release agent로 변경 SHA `e9b482ccba616a610c1f4862366d44aabae20ee8c82d8ef7f2cf16069f2d6e99`를
|
||||
실제 격리 NAS에 승격하고 postdeploy browser·DB review·rollback snapshot·exact-SHA SSOT handoff를 남겼다.
|
||||
- [x] **G8 종료 GATE 재검증** — source→draft→red-team→benchmark→catalog 재현, 정답 누수·PII·무근거
|
||||
- [x] **G8 내부 구현·실제 rollback receipt GATE** — source→draft→red-team→benchmark→catalog 재현, 정답 누수·PII·무근거
|
||||
임상 주장 0건, 모델 교체 자동 calibration/rollback, 운영 오류의 재현 테스트·backlog DAG 자동 환류.
|
||||
**현재 코드 증거:** caller가 완성 JSON을 넣는 기존 경계와 별도로 source hash·PII fail-closed→structured
|
||||
generator→서로 결과를 공유하지 않는 red-team 2개→variant generator→독립 judge 3개 이상→durable
|
||||
|
|
@ -450,9 +488,22 @@
|
|||
공개 런타임 미접촉, 비밀 env 파기, helper 0·listener 0 복귀. 상세:
|
||||
[런북](./ops/nas-preview-g8-rollback-proof-runbook.md) ·
|
||||
[기계 판독 증거](./ops/evidence/nas-preview-g8-actual-rollback-2026-08-07.json).
|
||||
- [x] current source exact SHA `6030a677…c611`을 격리 NAS에 승격했다. release-only patch 3회 결정성,
|
||||
전체 browser E2E 108/108, postdeploy SSE→DB review 32.437s, health 3/3, OpenAPI 126·auth 401·G0~G8
|
||||
route, named DB volume 보존을 확인했다. 이는 runtime promotion 증거이며 실제 rollback receipt와 구분한다.
|
||||
- [x] 2026-08-07 release-only SHA `6030a677…c611`을 격리 NAS에 승격했다. patch 3회 결정성,
|
||||
localhost candidate browser E2E 108/108, postdeploy SSE→DB review 32.437s, health 3/3,
|
||||
OpenAPI 126·auth 401·G0~G8 route, named DB volume 보존을 확인했다. 이는 당시 runtime promotion
|
||||
증거이며 현재 HEAD 배포 증거가 아니다.
|
||||
- [x] **clean-head release mode** — 명시적 `--source-mode clean-head`와 별도 detached source root를 추가했다.
|
||||
tracked-dirty 차단, exact HEAD/tree/archive SHA, 2회 결정성, archive-only candidate, patch-mode 무회귀,
|
||||
증거 binding, source-only insecure-context 6/6 gate, commit 전 NAS-origin 11-spec rollback gate를 unit
|
||||
30/30·Ruff·py_compile로 고정했다. 첫 execute는 archive CRLF를 candidate에서 정규화하도록 fail-closed했고,
|
||||
두 번째 execute는 API/types/build 뒤 109/5에서 fail-closed해 둘 다 NAS mutation 0이다.
|
||||
- [ ] **G8 NAS-origin 재검증** — `randomUuid()` 수정과 insecure-origin 6/6 회귀를 포함한 current HEAD를
|
||||
위 clean-head mode로 격리 NAS preview에 배포한다. `started_at`의 9시간 오차 P0는 UTC `+00:00`, KST 달력 날짜,
|
||||
KST browser focused 실DB E2E로 수정했고 API 전체 921 passed와 코칭 이력 1/1을 통과했다. 시간종료 모달 강제
|
||||
닫기 우회는 없다. 사용자 승인 아래 새 clean commit·HEAD/tree/archive를 결속해 실제
|
||||
`http://100.116.83.60:8088` origin에서 전체 회기
|
||||
E2E 0 failure·SSE→DB review·student returned-practice desktop/mobile을 다시 검증하기 전에는 G8 전체를 DONE으로
|
||||
올리지 않는다.
|
||||
|
||||
### I-DAG. 첫 에이전트 실행팩 AOS-001~012
|
||||
|
||||
|
|
@ -475,7 +526,6 @@
|
|||
납품(공개 데모)의 핵심 블로커는 **A(공개 `/turn` proof + 배포 secrets)**. 그 다음이 **B(한신대 거버넌스)**와
|
||||
**C(Phase 3 파일럿)** — C는 2026-07-13 회의로 **7월 말~8월 초 파일럿 일정 확정**, 그 전제로 **H의 P1(회기 종료 시간 기반)**이
|
||||
새 구현 게이트다. **I(Outcome & Alliance OS)는 현재 감사 기준 G0~G6이 internal DONE이다.
|
||||
G8은 receipt-bound 실제 NAS rollback proof가 남았다. G7은 내부 구현 DONE과 외부 proof GATE를 분리하며,
|
||||
Deepgram live·auth public WSS·명시 동의 mic·
|
||||
50분 soak·독립 라벨 benchmark·운영 resource high-water가 남았다.** D~G는
|
||||
G8 receipt-bound 실제 NAS rollback은 완료됐고 UUID 수정 배포·NAS-origin 회기 재검증이 남았다. G7은 내부 구현 DONE과 외부 proof GATE를 분리하며,
|
||||
current source 공개 배포·`local_whisper`/`melotts` ready·auth public WSS·명시 동의 mic·50분 soak·독립 라벨 benchmark·운영 resource high-water가 남았다.** D~G는
|
||||
품질·확장 작업으로 게이트가 아니다. 상세 상태·검증 증거는 SSOT 대시보드가 소유한다.
|
||||
|
|
|
|||
|
|
@ -71,6 +71,18 @@ P1 프리셋 한정이었던 이유(권리 안전한 synthetic reference 를 P1
|
|||
- `scripts/melotts-server.py` — loopback HTTP 사이드카(`/health`, `POST /tts` → WAV).
|
||||
`VIGNETTE_VOICE_TTS_PROVIDER=melotts` + `VIGNETTE_MELOTTS_TTS_URL` 설정.
|
||||
실행: `scripts/start-melotts.ps1`.
|
||||
- 공개 런처 `scripts/start-public-runtime.ps1`은 현재 CPU 운영값인
|
||||
`local_whisper/small/cpu-int8`(9882)과 `melotts/melotts-korean`(9883)을 직접 소유한다.
|
||||
`scripts/probe-public-voice-sidecars.py`가 Whisper의 첫 WebSocket `ready` 프레임과 MeloTTS
|
||||
`/health`의 provider/model/license/reference metadata를 정확히 검증한다. 기존 포트 리스너가 있어도
|
||||
이 계약을 통과하지 않으면 임의 종료·재사용하지 않고 API 재시작 전에 fail-closed한다.
|
||||
- 공개 API는 같은 provider/model을 `/voice/health`로 다시 증명해야 기존 프로세스를 유지한다. 새 Uvicorn은
|
||||
`--ws websockets --ws-max-queue 4`로 고정한다. 이 항목은 런처 소스 계약이며, 실제 공개 적용 완료 증거는
|
||||
authenticated WSS·동의 기반 50분 soak·동시간 topology/runtime 증거가 모두 통과한 뒤 G7 pack에 남긴다.
|
||||
- G7 실제 증거 승격은 `-RequireFreshPublicProvenance`로 detached-clean commit/tree와 Python/cloudflared/config SHA를
|
||||
mutation 전에 검증한다. legacy API와 exact-config cloudflared를 bounded 교체한 뒤 새 PID/start/executable·command
|
||||
SHA/실제 cwd의 safe receipt를 남기며 raw command line·config contents는 저장하지 않는다. runner는 최소 3,120초를
|
||||
캡처하고 voice/runtime/topology 공통 3,000초를 canonical checker와 결속한다.
|
||||
|
||||
## 실측 증거 (2026-08-08)
|
||||
|
||||
|
|
|
|||
|
|
@ -46,10 +46,16 @@
|
|||
section.block{margin-top:34px;scroll-margin-top:76px;}
|
||||
/* PULSE / hero */
|
||||
.pulse{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(330px,.95fr);gap:22px;align-items:stretch;margin-top:22px;}
|
||||
.pulse-main{background:var(--surface);border:1px solid var(--hair);border-radius:16px;padding:22px 24px;box-shadow:0 1px 2px rgba(28,42,42,.05);}
|
||||
.pulse-main{min-width:0;background:var(--surface);border:1px solid var(--hair);border-radius:16px;padding:22px 24px;box-shadow:0 1px 2px rgba(28,42,42,.05);}
|
||||
.pulse-main h1{margin-top:9px;}
|
||||
.pulse-state{margin:13px 0 0;color:var(--ink-2);font-size:13.5px;max-width:640px;}
|
||||
.pulse-state b{color:var(--ink);}
|
||||
.pulse-details{max-width:640px;margin-top:12px;border-top:1px solid var(--hair);}
|
||||
.pulse-details summary{display:flex;align-items:center;min-height:40px;color:var(--accent-deep);font-size:12.5px;font-weight:750;cursor:pointer;list-style:none;}
|
||||
.pulse-details summary::-webkit-details-marker{display:none;}
|
||||
.pulse-details summary:after{content:"+";margin-left:auto;color:var(--ink-3);font:700 14px/1 var(--mono);}
|
||||
.pulse-details[open] summary:after{content:"−";}
|
||||
.pulse-details .pulse-state{margin:0;padding:2px 0 4px;font-size:12.5px;line-height:1.6;}
|
||||
.phase-rail{display:grid;grid-template-columns:repeat(6,1fr);gap:7px;margin-top:18px;}
|
||||
.pr-seg{position:relative;}
|
||||
.pr-bar{height:7px;border-radius:999px;background:var(--paper-3);overflow:hidden;}
|
||||
|
|
@ -78,7 +84,7 @@
|
|||
.kpi-badge{position:absolute;right:13px;top:12px;display:none;align-items:center;gap:5px;font:800 10px/1 var(--mono);letter-spacing:.02em;color:#fff;background:var(--crit);padding:5px 8px;border-radius:7px;box-shadow:0 1px 3px rgba(168,53,44,.35);}
|
||||
.kpi.k-plan{border-color:color-mix(in srgb,var(--crit) 18%,var(--hair));}
|
||||
/* metric strip */
|
||||
.mstrip{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px;}
|
||||
.mstrip{display:flex;flex-wrap:wrap;min-width:0;max-width:100%;gap:8px;margin-top:16px;}
|
||||
.mchip{display:inline-flex;align-items:center;gap:7px;padding:6px 11px;border:1px solid var(--hair);border-radius:999px;background:var(--paper);font-size:11.5px;color:var(--ink-2);}
|
||||
.mchip b{font:700 12px/1 var(--mono);color:var(--ink);}
|
||||
.mchip.good b{color:var(--pos);}
|
||||
|
|
@ -262,6 +268,9 @@
|
|||
.task-row,.task-row.r3{grid-template-columns:1fr;}
|
||||
.task-row>div{border-bottom:1px solid var(--hair);} .task-row>div:last-child{border-bottom:0;}
|
||||
.brand small{display:none;}
|
||||
.owner-pill,.fbtn,.tab-button,.dg-link,.pulse-details summary{min-height:44px;}
|
||||
.mstrip{flex-wrap:nowrap;overflow-x:auto;padding-bottom:4px;scrollbar-width:thin;}
|
||||
.mchip{flex:0 0 auto;}
|
||||
}
|
||||
@media (max-width:460px){ .kpis{grid-template-columns:1fr;} h1{font-size:22px;} .stamp{display:none;} }
|
||||
@media (prefers-reduced-motion:reduce){ *{transition:none!important;} }
|
||||
|
|
@ -272,7 +281,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-07 KST</span>
|
||||
<span class="stamp">2026-08-09 KST</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
@ -281,12 +290,16 @@
|
|||
<section class="pulse">
|
||||
<div class="pulse-main">
|
||||
<span class="eyebrow">project pulse</span>
|
||||
<h1>지금 위치: <em>Phase 0 제품 데모는 public /turn proof가 핵심 블로커</em>.<br><em>Phase 1 MVP 코어 루프</em>는 로컬 DB-backed 실증 완료.</h1>
|
||||
<p class="pulse-state"><b>긴급 운영 상태:</b> 2026-08-07 18:26 KST 공개 DB를 복구 named volume으로 전환했다. public owner 집계 users 84·sessions 30·turns 705, Google 계정 16·소유 회기 30, orphan 0이며 health·engine·OAuth·로그인 화면·watchdog가 정상이다. 보안상 기존 auth session은 복사하지 않아 모든 사용자는 Google 재로그인이 필요하고, 전환 직전 container·dump와 원본 volume은 삭제하지 않았다. <b>개발 상태:</b> G0 provenance census 29/29·위반 0과 G6 safety metadata-only 우선 runtime은 disposable clone에서 통과했다. G4·G5 실제 API/DB/browser 폐루프와 current exact SHA <code>6030a677…c611</code>의 NAS 승격은 완료됐고 전체 browser E2E 108/108·postdeploy SSE→DB review·health 3/3·OpenAPI 126을 통과했다. G8은 격리 NAS에서 실제 image rollback 2회를 executed receipt(<code>nas-g8-723eeef2…</code>/<code>nas-g8-2738846c…</code>)로 실행해 DONE이며, G7은 Deepgram live·인증 WSS·명시 동의 물리 마이크 50분·독립 라벨 voice-gain과 resource high-water 전까지 외부 GATE다.</p>
|
||||
<h1>지금 위치: <em>G0~G6 internal DONE</em>.<br><em>G8 runtime 재검증</em>과 <em>G7 external proof</em>를 닫는 중.</h1>
|
||||
<p class="pulse-state"><b>현재:</b> 18:02 KST Docker Desktop UI의 직접 DB stop을 같은 container·recovered named volume 재시작으로 복구했고 public DB·engine은 다시 정상이다. rollback receipt 2건도 보존했다. current fixture는 불변 110 + 최신 dashboard 10 분할 GREEN이고 자기주도 desktop/mobile 6/6을 재확인했다. <b>다음:</b> clean commit에서 exact 120 재실행 → NAS-origin 0 failure → G7 공개 음성·물리 마이크·독립 사람 증거 순서로 진행한다.</p>
|
||||
<details class="pulse-details">
|
||||
<summary>운영 복구·watchdog·배포 상세 보기</summary>
|
||||
<p class="pulse-state">2026-08-07 18:26 KST 공개 DB를 <code>vignette_recovered_prod_20260807</code> named volume으로 전환했고 원본 container·dump·volume은 삭제하지 않았다. 기존 auth session은 복사하지 않아 사용자는 Google 재로그인이 필요하다. 2026-08-09 18:02:55 KST Docker Desktop UI가 active DB container에 직접 stop을 보내 public <code>db=false</code>가 재발했지만 같은 container ID와 recovered volume만 18:05 KST 재시작했다. 재기동 뒤 owner read-only 집계는 users 356·sessions 193·turns 726, Google users 16·Google-owned sessions 31이고 public health는 다시 <code>status=ok·db=true·engine=true</code>다. public task source pin 코드는 완료됐다. watchdog·로그온 boot는 detached clean release와 exact Git commit/tree·watchdog|boot/start SHA가 없으면 health·Docker·process mutation 전에 실패하고 hidden VBS도 pinned task만 trigger한다(provenance 11개 포함 combined 22/22, PS5.1·WSH PASS). 다만 실제 두 Scheduled Task action은 아직 shared worktree라 watchdog은 필수 pin 인자 부재로 <code>LastTaskResult=1</code>이며 자동복구가 의도적으로 fail-closed다. clean commit 뒤 두 task를 동일 stable release root로 재등록하고 action pin·result 0·로그온/reboot smoke를 확인해야 한다. G8 NAS SHA <code>6030a677…c611</code>은 localhost candidate와 SSE→DB review는 통과했지만 평문 origin UUID 결함 빌드이므로 current clean source 재배포가 필요하다. G7 source는 runner/checker/topology 86/86, launcher/sidecar 80/80, 통합 166/166, API voice 71/71이지만 public은 아직 OpenAI provider·Git 없는 냉동 release라 external GATE다.</p>
|
||||
</details>
|
||||
<div class="phase-rail" id="phase-rail" aria-label="Phase 진행 추정"></div>
|
||||
<div class="mstrip" aria-label="검증 지표">
|
||||
<span class="mchip warn">Full E2E 현재 수집 <b>604</b> · 전체 GREEN 대기</span>
|
||||
<span class="mchip good">현재 전체 회귀 · gateway <b>58</b> · backend <b>898</b></span>
|
||||
<span class="mchip warn">Fixture E2E <b>110 + dashboard 10 분할 GREEN</b> · clean HEAD 120/120 대기</span>
|
||||
<span class="mchip good">현재 전체 회귀 · gateway <b>58</b> · backend <b>921</b></span>
|
||||
<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>
|
||||
|
|
@ -637,6 +650,11 @@
|
|||
<p class="dg-note">182차 적용(2026-08-08): <b>회기 전 자기점검(pre alliance gate) 화면을 데스크톱·모바일 전 폭에서 다시 짰다.</b> 계측이 먼저였다. gate 상태에서 카드가 <code>align-self:stretch</code> + <code>grid-template-rows: auto minmax(0,1fr) auto auto</code>인데 base flex의 <code>align-items:center</code>가 살아 있어 3축 블록이 1fr 행 한가운데 떠 있었다. 1920×1080 기준 카드의 <b>66.5%가 빈 공간</b>이고 축 위·아래로 각각 <code>277px</code>의 죽은 여백이 생겼다. 척도 라벨은 <code>0.54rem</code>+<code>nowrap</code>+ellipsis라 901~1279px에서 최대 <b>12개가 "전허 아…"</b>로 잘렸고, ≤620px에서는 <code>display:none</code>으로 <b>15개 전부 사라져</b> 숫자 1~5만 남았다. 320×568에서는 <code>@media (max-width:420px) and (max-height:620px)</code>의 collapsed 전용 <code>40px</code> 고정 행이 gate에도 걸려 카드가 <b>40px로 눌리고 헤더와 축이 겹쳐</b> 화면이 사실상 깨져 있었다. 수정은 네 가지다. (1) gate 카드는 행 높이만큼 늘리지 않고 <code>align-self:center</code>+<code>max-height:100%</code>+내부 스크롤로 화면 중앙에 서게 했다. (2) 3열 격자를 버리고 축마다 <b>질문 한 줄 + 전폭 5칸 척도</b> 세로 스택으로 바꿔 어느 폭에서도 라벨을 자르거나 숨기지 않는다(<code>word-break:keep-all</code>+<code>overflow-wrap:normal</code>로 어절 경계만 줄바꿈). (3) 정보가 없던 <code>3 AXES</code> 모노 배지를 제출 버튼이 왜 잠겨 있는지 답하는 <code>0/3 축 선택함</code> 진행 표시(<code>aria-live</code>)로 교체했다. (4) 40px 행 규칙에 <code>:not(.sx-alliance-gated)</code>를 걸어 collapsed mid 전용으로 되돌렸다. 선택 상태 글자색도 반투명 <code>--surface</code>에서 <code>--text-on-accent</code>로 고쳤다(라이트 4.85:1 · 다크 6.36:1, AA). 15개 뷰포트 재계측 결과 빈 공간 비율 <b>0.665→0.127</b>(1920), 라벨 잘림 <b>12→0</b>, 숨김 <b>15→0</b>, 최소 탭 타깃 <b>38×38→46×60</b>, 문서 가로 오버플로 전 구간 0이고, 320×568에서는 카드 내부 스크롤로 제출까지 도달해 실제 잠금·발화 입력 활성까지 확인했다. 인라인 mid 카드는 ≥1080px에서만 3열로 눕혀 축어록을 밀지 않게 두었고 높이는 오히려 <b>약 55px 줄었다</b>. 무회귀: <code>typecheck</code> 통과, <code>session-layout</code> <b>8/8</b>, <code>alliance-pulse</code> <b>8/8</b>, pre/mid 잠금 E2E <b>2/2</b>, <code>layout-visual-gate</code> 22/23(실패 1건 <code>settings</code>는 변경 전 baseline에서도 동일 실패로 확인된 기존 회귀). session-mvp 음성 4건 실패도 baseline 동일이라 이 변경과 무관하다.</p>
|
||||
<p class="dg-note">183차 적용(2026-08-08): 182차 뒤 소유자 스크린샷에서 <b>시간 만료 상태의 게이트 화면</b>이 여전히 깨져 있어 네 개의 별개 결함을 계측으로 특정하고 닫았다. (1) <code>.sx-timebar</code>는 세션이 다크 stage 팔레트를 강제하던 시절의 잔재로, 배경이 <b>어디에도 정의된 적 없는</b> <code>--bg-panel</code>의 하드코딩 다크 폴백으로 칠해지는데 글자만 테마 토큰을 따라가 라이트에서 제목 <b>1.13:1</b>·설명 <b>1.76:1</b>이었다. 의미색 토큰(<code>--warn-*</code>/<code>--crit-*</code>)으로 되돌려 <b>13.88:1</b>·<b>6.94:1</b>(다크 13.86·9.30)로 올렸다. (2) 같은 이유로 <code>.sx-end-dialog__panel</code>이 어두운 gradient 위 어두운 글자라 제목 <b>2.68:1</b>·본문 <b>1.14:1</b>로 사실상 보이지 않았다. 불투명 <code>--bg-app</code> 표면으로 바꿔 <b>15.79:1</b>·<b>5.19:1</b>(다크 15.18·6.52)이 됐다. (3) 보태니컬 장식 위로 본문을 올리는 <code>.sx-page--active > *{position:relative}</code>가 같은 특이도(0,1,0)로 <b>뒤에 있어</b> 종료 모달의 <code>position:fixed</code>를 덮어썼다. 오버레이가 화면을 덮는 대신 그리드 한 칸(<code>1532×32px</code>)으로 눌려 들어갔다. <code>:not(.sx-end-dialog)</code>로 제외해 <code>fixed</code> <b>1600×900 뷰포트 전체</b>를 회복했다. (4) 게이트 행 정의 <code>.sx-page--active.sx-alliance-gated</code>(0,2,0)가 <code>:has()</code> 두 개짜리 규칙(0,3,0)보다 특이도가 낮아 <b>시간 만료 바가 뜨는 순간 밀려났다</b>. 숨긴 축어록·컨트롤바의 빈 트랙이 남아 화면 절반이 비었다(<code>80px 59px 628px 0px 116px</code> 5행). 게이트 규칙에도 같은 <code>:has()</code>를 붙여 <b>3행</b>(<code>80px 59px 693px</code>)으로 정정했다. 무회귀: <code>typecheck</code> 통과, <code>session-layout</code>+<code>alliance-pulse</code>+<code>layout-visual-gate</code> <b>31/31</b>(182차에서 실패했던 <code>settings</code>도 통과), 시간만료 종료 다이얼로그 E2E <b>2/2</b>, pre/mid 잠금 E2E <b>2/2</b>, <code>full-sweep-session</code>+<code>self-directed-learning-loop</code> 30 pass. 남은 음성 4건(<code>voice skip</code>·<code>Space mic</code>)은 변경분 stash 후 baseline에서도 동일 실패라 이 변경과 무관한 기존 회귀다.</p>
|
||||
<p class="dg-note">184차 적용(2026-08-08): 182·183차 변경이 <b>로컬 워크트리에만</b> 있어 소유자가 보던 공개 사이트에는 반영될 수 없던 상태를 배포로 닫았다. 소유자 스크린샷의 <code>3 AXES</code>는 이미 소스에서 사라진 문자열이었고, 접속 주소가 <code>https://vignette.chanpaca.net</code>이라 커밋 전 변경이 보일 수 없는 것이 원인이었다. 커밋 <code>f0c2240f</code>로 고정한 뒤 <code>npm run build</code>(Session 청크 <code>Session-DnhnKvTy.js</code>·<code>Session-Xn1JLv-d.css</code>), <code>scripts\preserve-pages-assets.ps1</code>로 직전 5개 production 배포의 지연 로드 자산 <b>104개</b>를 보존하고(진행 중 세션의 lazy chunk 404 방지), <code>wrangler pages deploy dist --project-name vignette --branch main --commit-hash f0c2240f</code>로 배포했다(deployment <code>a12c195c</code>, 신규 업로드 37 / 기존 666). 커스텀 도메인 검증은 <code>index-BTkJUPLZ.js</code> 200, Session JS/CSS 모두 올바른 MIME 200이며 <b>신규 마커</b>(<code>축 선택함</code>·<code>is-gate</code>·<code>:not(.sx-end-dialog)</code>) 각 1건, <b>구버전 마커</b>(<code>3 AXES</code>·<code>--bg-panel</code>·척도 <code>text-overflow</code>) 전부 0건이다. 첫 조회에서 edge가 이전 index를 준 건 배포 직후 타이밍이었고 재조회에서 신규 index로 확정됐다. 공개 API·런타임은 건드리지 않았다.</p>
|
||||
<p class="dg-note">185차 적용(2026-08-09): 177·181차의 G8 DONE 판정은 <b>rollback receipt gate 완료</b>로 범위를 정정했다. 108/108은 localhost candidate 결과였고 같은 NAS 평문 origin에서는 secure-context 전용 <code>crypto.randomUUID</code> 예외로 회기 E2E 24건이 실패했다. 소스는 <code>randomUuid()</code>로 통일해 insecure-origin 6/6·API type·typecheck·build를 통과했지만 NAS에는 아직 미배포다. 따라서 G8 카드는 <b>BUILD · RECEIPT DONE · RUNTIME REVALIDATION</b>으로 되돌리고, clean-head release mode 구현→수정 배포→실제 NAS-origin 전체 회기 E2E GREEN까지를 남긴다. G7은 운영 기본 <code>local_whisper</code>/<code>melotts</code>의 current 공개 배포·인증 WSS·명시 동의 mic 50분·동시 runtime/topology·독립 human pack 전까지 외부 GATE다.</p>
|
||||
<p class="dg-note">186차 적용(2026-08-09): 공개 DB listener 소실 사고는 Docker Desktop 중단으로 좁혀졌고 recovered named volume을 보존한 기존 컨테이너만 재기동해 복구했다. public/local health는 db·engine true, current owner 집계는 users 171·sessions 93·turns 726이다. 당시 워치독 action의 삭제된 옛 workspace 드리프트를 shared current workspace·5분 주기로 교정하고 즉시 실행 0을 확인했다. 이 임시 조치는 후속 source-pin에서 안전하지 않은 입력으로 판정됐고, 현재 task는 detached-clean pin 인자 부재로 mutation 전에 fail-closed한다. G8 clean-head release mode는 tracked-dirty 차단·HEAD/tree/archive 2회 결정성·archive-only candidate·patch 무회귀·evidence binding과 commit 전 NAS-origin 11-spec rollback gate를 unit 30/30으로 고정했고, 새 NAS preview predeploy dump <code>72698232…5d41c</code>(1,015,222 bytes·TOC 1,752)를 보존했다. G7 재감사에서는 공개 OpenAPI 119·<code>/admin/voice-runtime</code> 부재, OpenAI voice provider, local sidecar listener 0, Windows host topology와 Compose-only collector 불일치를 확인했으므로 사람 증거 전에 launcher·topology 선행 구현이 필요하다.</p>
|
||||
<p class="dg-note">187차 적용(2026-08-09): G8 clean-head dry-run은 active <code>6030a677…c611</code>→desired archive <code>6b1b15bd…a3f7</code>를 mutation 0으로 결속했다. execute 1은 archive CRLF/API type byte-check에서, execute 2는 API·types·build 통과 뒤 session E2E <code>109 passed / 5 failed</code>에서 모두 승격 전에 fail-closed되어 NAS active/image/DB는 바뀌지 않았다. source-only UUID 4건은 production 번들에서 <code>/src/lib/uuid.ts</code> import가 불가능한 gate 배치 오류라 별도 Vite gate 6/6으로 분리했다. 남은 실제 제품 blocker는 timezone 없는 <code>started_at</code>을 KST 브라우저가 약 9시간 경과로 오인해 시간종료 모달이 정상 코칭 이력을 가리는 문제다. backend UTC offset/Z 계약과 focused disposable DB/browser 회귀를 닫고 새 clean commit을 만든 뒤에만 NAS 배포를 재개한다.</p>
|
||||
<p class="dg-note">188차 적용(2026-08-09): 회기 시간 P0를 우회 없이 수정했다. browser-facing <code>started_at</code>/<code>ended_at</code>과 learner/teacher dashboard timestamp는 UTC <code>+00:00</code>을 반환하고, 리뷰의 달력 날짜는 서버 locale 대신 KST(+09:00)를 사용한다. backend 관련 65 passed, 후속 voice 계약을 포함한 API 전체 921 passed, Ruff·compile·web typecheck를 통과했다. 고유 Compose DB/API/Web/engine과 <code>Asia/Seoul</code> browser에서 코칭 이력 focused 1/1은 active/null·UTC suffix·10분 미만 경과·timebar/dialog 0·reload 후 coach mark 실제 클릭과 source-pack dialog를 확인했다. 종료 뒤 container·volume·listener 0이다. 이제 새 clean commit 결속과 NAS-origin 전체 E2E가 남았다.</p>
|
||||
<p class="dg-note">189차 적용(2026-08-09): G7 외부 실행 전 P0를 소스에서 닫았다. production runner exit 0은 canonical checker 0·<code>gate_closed=true</code>에 결속하고, browser Origin allowlist를 API/WSS/admin/topology transport host·scheme과 분리했다. capture는 최소 3,120초·ceil+terminal sample이며 checker는 voice/runtime/topology 공통 교집합 3,000초를 강제한다. Windows 증거는 detached-clean commit/tree, runner/collector/checker SHA, <code>psutil==6.1.1</code>을 매 sample 전후 검증한다. fresh public launcher는 mutation 전에 source·Python·cloudflared·config SHA를 고정하고, legacy API와 exact-config cloudflared를 bounded 교체해 새 PID/start/exe·command SHA/실제 cwd의 raw command-line 없는 receipt를 남긴다. receipt destination은 runtime mutation 전에 create/flush/atomic-replace 권한을 검사하고 기존 receipt를 보존한 채 원자 게시한다. 검증: G7 runner/checker/topology 86/86, launcher/sidecar 80/80, 통합 166/166, API 921, gateway 58, Web api-types·typecheck·build, 자기주도 desktop/mobile 6/6, Ruff·compile·PS5.1 parser·diff-check PASS. 실제 public 실행·마이크·사람 평가는 하지 않았으므로 G7은 external GATE다.</p>
|
||||
<div class="dg-principles" aria-label="디자인 생성 가드레일">
|
||||
<div><b>래스터만 사용</b><span>이미지 생성 도구 산출물은 PNG 기반 시안이다. SVG·벡터·와이어프레임·로고 시트로 해석하지 않는다.</span></div>
|
||||
<div><b>기능 우선</b><span>메인 라우트의 실제 액션과 정보 구조를 먼저 반영한다. 장식은 기능을 가리지 않는 수준에서만 쓴다.</span></div>
|
||||
|
|
@ -819,8 +837,8 @@
|
|||
<div class="tg-head"><h3>코드품질 · 로컬 개발 스택</h3><span class="tg-note">리팩토링 연구 + 1-커맨드 dev 스택 (2026-06-26~27). code-quality-research-2026-06-26.md · local-development.md</span></div>
|
||||
<div class="scards">
|
||||
<article class="scard" data-status="doing" data-cat="인프라·스택·RAG" data-owner="0">
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-doing">DB RECOVERY · PUBLIC LIVE</span><span class="scard-mid"><span class="scard-title">공개 계정·회기 복구와 무손실 전환</span><span class="scard-sum">복구 named volume을 공개 55432에 전환했다. 데이터·auth·watchdog는 정상이며 사용자 재로그인 후 과거 소유 회기 확인만 남았다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
<div class="scard-body"><div class="kv k-good"><b>공개 전환</b><p>2026-08-07 18:26 KST owner 승인으로 <code>vignette_recovered_prod_20260807</code>을 <code>vignette-dev-db:55432</code>에 연결했다. public owner 집계 users 84·sessions 30·turns 705, Google 계정 16·소유 회기 30, orphan 0이다. local/public health는 prod·DB true·engine true·claude_cli, OpenAPI 119, Google OAuth 302, 공개 로그인 화면 렌더와 watchdog check-only를 통과했다.</p></div><div class="kv"><b>보존·rollback</b><p>전환 직전 DB는 <code>vignette-dev-db-pre-recovery-20260807-182642</code>로 중지 보존했고 exact dump SHA는 <code>6b84d5c8…8af1f</code>다. 원본 volume, post-merge dump <code>d24eaa75…2e32</code>, 최종 recovered dump <code>d7bcc396…68ed</code>, 자동 rollback을 실증한 실패 후보 2개도 삭제하지 않았다.</p></div><div class="kv k-warn"><b>사용자 확인</b><p>보안상 기존 <code>auth_session</code>은 복사하지 않아 모두 Google 재로그인이 필요하다. 계정·회기 원장은 복구돼 있다. 실제 사용자가 재로그인한 뒤 과거 소유 회기 목록이 보이는지 확인할 때까지 이 카드는 안정화 중으로 유지한다. 상세: <code>ops/public-db-recovery-rehearsal-2026-08-07.md</code>.</p></div></div>
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-doing">DB RECOVERY · PUBLIC LIVE</span><span class="scard-mid"><span class="scard-title">공개 계정·회기 복구와 무손실 전환</span><span class="scard-sum">복구 named volume을 공개 55432에 전환했다. 데이터·auth·public health는 정상이고 task 자동복구는 stable-source 재등록을 기다린다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
<div class="scard-body"><div class="kv k-good"><b>공개 전환</b><p>2026-08-07 18:26 KST owner 승인으로 <code>vignette_recovered_prod_20260807</code>을 <code>vignette-dev-db:55432</code>에 연결했다. cutover 집계 users 84·sessions 30·turns 705, Google 계정 16·소유 회기 30, orphan 0이다. 2026-08-09 Docker Desktop 중단 후 동일 컨테이너만 재기동했으며 현재 users 171·sessions 93·turns 726, local/public health prod·DB true·engine true·claude_cli, OpenAPI 119, Google OAuth 302다. source-pin 코드는 완료됐지만 실제 watchdog·로그온 task action은 shared worktree라 최신 watchdog은 pin 인자 부재로 mutation 전에 exit 1이다. clean commit의 detached stable release root로 두 task를 재등록하기 전까지 자동복구는 의도적으로 fail-closed다.</p></div><div class="kv"><b>보존·rollback</b><p>전환 직전 DB는 <code>vignette-dev-db-pre-recovery-20260807-182642</code>로 중지 보존했고 exact dump SHA는 <code>6b84d5c8…8af1f</code>다. 원본 volume, post-merge dump <code>d24eaa75…2e32</code>, 최종 recovered dump <code>d7bcc396…68ed</code>, 자동 rollback을 실증한 실패 후보 2개도 삭제하지 않았다.</p></div><div class="kv k-warn"><b>사용자 확인</b><p>보안상 기존 <code>auth_session</code>은 복사하지 않아 모두 Google 재로그인이 필요하다. 계정·회기 원장은 복구돼 있다. 실제 사용자가 재로그인한 뒤 과거 소유 회기 목록이 보이는지 확인할 때까지 이 카드는 안정화 중으로 유지한다. 상세: <code>ops/public-db-recovery-rehearsal-2026-08-07.md</code>.</p></div></div>
|
||||
</article>
|
||||
<article class="scard" data-status="done" data-cat="인프라·스택·RAG" data-owner="0">
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">DEV STACK · DB 보존 패치</span><span class="scard-mid"><span class="scard-title">로컬 개발 스택 1-커맨드 기동 — <code>scripts/dev-up.ps1</code></span><span class="scard-sum">정지 DB는 start 우선, 신규 PGDATA는 고정 named volume, 일반 dev-up의 DB 삭제 경로는 0.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
|
|
@ -866,8 +884,8 @@
|
|||
<div class="scard-body"><div class="kv"><b>구현</b><p><code>app.measurement_event</code>·<code>app.measurement_instrument</code>·<code>audit.model_run</code>과 6종 provenance, Python→JSON Schema→TypeScript 계약, 기존 rapport/alliance/fast/deep/Phase 3 adapter, 혼합층 집계 차단, 8개 deterministic benchmark 장면을 구축했다. <b>AOS-001~004 완료</b>.</p></div><div class="kv k-good"><b>종료 증거</b><p>post-merge dump disposable clone에 migrations 14/15/16을 각각 두 번 원자 재적용했다. current source census는 producer 29/29, unknown/missing table·null instrument/source/perspective·orphan instrument/model-run/evidence·호환성 위반 0이다. 기존 계약/schema·live append-only/RLS 증거도 유지한다. 상세: <code>ops/evidence/g0-provenance-census-scratch-post-g6-g8-20260807.json</code>, <code>ops/evidence/measurement-migration-rehearsal-2026-08-07.json</code>.</p></div></div>
|
||||
</article>
|
||||
<article class="scard" data-status="done" data-cat="Outcome OS·동맹" data-owner="0">
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">G1 DONE · EXTERNAL ICC 분리</span><span class="scard-mid"><span class="scard-title">Alliance Core — goal/task/bond 다관점 측정</span><span class="scard-sum">승격 prompt 1.2는 24/24 ready·방향 9/9·오류 0으로 재검증했고 human-AI ICC는 외부 파일럿 품질 목표로 분리한다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
<div class="scard-body"><div class="kv"><b>구현</b><p>회기 pulse, legacy alliance 격리, 자기평가 선잠금, 독립 모델 실행·재시도, 학습자 관점차 리뷰, 교수자 append-only 재평가를 구축했다. 실제 Session은 첫 발화 전 pre를 fail-closed로 잠그고 첫 왕복 뒤 흐름을 막지 않는 mid 30초 점검을 제공한다. <b>AOS-005~010 완료</b>.</p></div><div class="kv k-good"><b>종료 증거</b><p>focused backend 43 passed, live PostgreSQL reveal/RLS/audit/supersession smoke, desktop/mobile Alliance Pulse 6/6와 기존 회기 리뷰 12/12 통과. 동일 payload stable ID, 변경 payload 409, 타 학습자·교차 cohort 404와 pre/mid desktop·mobile 2/2·고정 캡처 4장을 추가 검증했다. 최신 source API/dev DB는 한 회기의 <code>pre(degraded/insufficient_transcript) → mid(ready) → post(ready)</code>를 실제 turn UUID·ended_at으로 닫고, awaiting 응답의 external-event read skew와 degraded terminal DB 제약을 교정했다. prompt 1.2.0 합성 gold 방향 정확도 9/9, 최종 실패 0, 근거 recall 88.9%. 상세: <code>ops/evidence/alliance-pulse-pre-mid-post-live-2026-08-07.json</code>, <code>ops/outcome-os-g1-g4-g5-g7-self-directed-integration-evidence-2026-08-07.md</code>.</p></div></div>
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">G1 DONE · INTERNAL · EXTERNAL ICC 분리</span><span class="scard-mid"><span class="scard-title">Alliance Core — goal/task/bond 다관점 측정</span><span class="scard-sum">승격 prompt 1.2는 24/24 ready·방향 9/9·오류 0으로 재검증했고 human-AI ICC는 외부 파일럿 품질 목표로 분리한다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
<div class="scard-body"><div class="kv"><b>구현</b><p>회기 pulse, legacy alliance 격리, 자기평가 선잠금, 독립 모델 실행·재시도, 학습자 관점차 리뷰, 교수자 append-only 재평가를 구축했다. 실제 Session은 첫 발화 전 pre를 fail-closed로 잠그고 첫 왕복 뒤 흐름을 막지 않는 mid 30초 점검을 제공한다. <b>AOS-005~010 완료</b>.</p></div><div class="kv k-good"><b>종료 증거</b><p>focused backend 43 passed, live PostgreSQL reveal/RLS/audit/supersession smoke, desktop/mobile Alliance Pulse 6/6와 기존 회기 리뷰 12/12 통과. 동일 payload stable ID, 변경 payload 409, 타 학습자·교차 cohort 404와 pre/mid desktop·mobile 2/2·고정 캡처 4장을 추가 검증했다. 최신 source API/dev DB는 한 회기의 <code>pre(degraded/insufficient_transcript) → mid(ready) → post(ready)</code>를 실제 turn UUID·ended_at으로 닫고, awaiting 응답의 external-event read skew와 degraded terminal DB 제약을 교정했다. prompt 1.2.0 read-skew 재검증은 prediction 24/24 ready·비교 대상 9/9·방향 9/9·오류 0, 근거 recall 94.4%·precision 73.9%다. 상세: <code>ops/evidence/alliance-calibration-comparison-prompt-1.2.0-readskewfix-2026-08-07.json</code>, <code>ops/evidence/alliance-pulse-pre-mid-post-live-2026-08-07.json</code>.</p></div></div>
|
||||
</article>
|
||||
<article class="scard" data-status="done" data-cat="Outcome OS·종단" data-owner="0">
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">G2 DONE · INTERNAL</span><span class="scard-mid"><span class="scard-title">Longitudinal Outcome Trajectory — 종단 성과 궤적</span><span class="scard-sum">5회기 예상 궤적·악화·관계 사건을 세 개 독립 축과 장면 근거로 조기에 추적한다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
|
|
@ -879,7 +897,7 @@
|
|||
</article>
|
||||
<article class="scard" data-status="done" data-cat="Outcome OS·수련" data-owner="0">
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">G4 DONE · INTERNAL</span><span class="scard-mid"><span class="scard-title">Deliberate Practice Engine — 의도적 수련 엔진</span><span class="scard-sum">실제 API/DB/엔진과 route-mock 0 브라우저에서 처방→후속 회기→observer 폐루프를 닫았다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
<div class="scard-body"><div class="kv"><b>구현</b><p>원자적 practice prescription, 5개 연습 모드, competency graph·attempt evidence, 약점/망각 우선 선택, append-only PostgreSQL/API·RLS·내부 토큰, 학습자/교수자 리뷰 UI를 연결했다. ready 회기말 평가 저장 뒤 production <code>session_learning_producer</code>가 durable turn intent deviation을 실제 처방으로 변환한다. 학습자 CTA는 처방·원본 회기·성공 기준을 학습 홈→시작 전 브리핑→실제 새 세션 URL까지 보존한다.</p></div><div class="kv k-good"><b>종료 증거</b><p>실제 API/DB/엔진 smoke는 prescriptions 2·episodes 6·attempts 6·append-only correction 1·ready observer model-run 2를 통과했다. post-merge disposable clone의 실제 브라우저는 route mock 0으로 desktop 2/2·mobile 2/2, returned 카드 우선·G4 POST·read-model reload·before/after·멱등 retry를 확인했고 최종 episode/attempt/ready observer 각 1, 중복·UUID 노출·마이크 호출·overflow 0이다. 전체 API 현재 898 passed. 상세: <code>ops/evidence/scratch-browser-returned-practice-closed-loop-evidence-20260807.json</code>, <code>ops/outcome-os-g4-g5-production-caller-evidence-2026-08-07.md</code>.</p></div></div>
|
||||
<div class="scard-body"><div class="kv"><b>구현</b><p>원자적 practice prescription, 5개 연습 모드, competency graph·attempt evidence, 약점/망각 우선 선택, append-only PostgreSQL/API·RLS·내부 토큰, 학습자/교수자 리뷰 UI를 연결했다. ready 회기말 평가 저장 뒤 production <code>session_learning_producer</code>가 durable turn intent deviation을 실제 처방으로 변환한다. 학습자 CTA는 처방·원본 회기·성공 기준을 학습 홈→시작 전 브리핑→실제 새 세션 URL까지 보존한다.</p></div><div class="kv k-good"><b>종료 증거</b><p>실제 API/DB/엔진 smoke는 prescriptions 2·episodes 6·attempts 6·append-only correction 1·ready observer model-run 2를 통과했다. post-merge disposable clone의 실제 브라우저는 route mock 0으로 desktop 2/2·mobile 2/2, returned 카드 우선·G4 POST·read-model reload·before/after·멱등 retry를 확인했고 최종 episode/attempt/ready observer 각 1, 중복·UUID 노출·마이크 호출·overflow 0이다. 전체 API 현재 921 passed. 상세: <code>ops/evidence/scratch-browser-returned-practice-closed-loop-evidence-20260807.json</code>, <code>ops/outcome-os-g4-g5-production-caller-evidence-2026-08-07.md</code>.</p></div></div>
|
||||
</article>
|
||||
<article class="scard" data-status="done" data-cat="Outcome OS·보정" data-owner="0">
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">G5 DONE · INTERNAL</span><span class="scard-mid"><span class="scard-title">Calibration Mirror & Transfer — 자기보정과 전이</span><span class="scard-sum">학생 actual execution을 실제 API/DB/엔진과 브라우저에서 독립 observer·provenance까지 닫았다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
|
|
@ -890,12 +908,12 @@
|
|||
<div class="scard-body"><div class="kv"><b>구현</b><p>append-only attention queue·원장 pointer, metadata-only 교수자/AI 불일치 dataset·audit, cohort gap, version/subgroup drift, 4-domain Phase 3 manifest와 역할 안전 교수자·연구 UX를 연결했다. scheduled producer가 G0~G5 실제 원장에서 attention/gap을 파생하고 repo-approved synthetic version comparison을 별도 research 트랜잭션으로 실행한다.</p></div><div class="kv k-good"><b>종료 증거</b><p>기존 6-signal queue·3-click·네 도메인 manifest·실제 API/DB/RLS/audit·version/subgroup 증거에 더해 post-merge disposable clone의 current source에서 safety event와 deterioration 경쟁 신호를 실제 derive했다. 결과는 <code>safety_boundary(1) > deterioration(2)</code>, direct pointer 각 1, source detail canary 1·G6 projection canary 0, raw transcript/임상 주장 0이고 replay가 멱등이다. 상세: <code>ops/evidence/g6-safety-priority-runtime-scratch-20260807.json</code>, <code>ops/outcome-os-g6-version-comparison-producer-evidence-2026-08-07.md</code>.</p></div></div>
|
||||
</article>
|
||||
<article class="scard" data-status="doing" data-cat="Outcome OS·음성" data-owner="0">
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-doing">G7 GATE · INTERNAL DONE · EXTERNAL PROOF</span><span class="scard-mid"><span class="scard-title">Multimodal Alliance — 음성·비언어 동맹 신호</span><span class="scard-sum">내부 안전·복구·용량 계약은 닫았고 실제 provider·동의 마이크·사람 평가 증거를 기다린다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
<div class="scard-body"><div class="kv k-good"><b>내부 구현 DONE</b><p>consent→단일 clock→독립 text/voice/fusion→철회·tombstone, streaming interim/final·word timestamp·provider event, HMAC word pseudonym, 열린 stream의 1초 동의 재검사·abort, 텍스트 보존·음성 재연결 UX를 연결했다. 격리 synthetic PCM soak는 59/59 연결 종료·성공 발화 55·철회 차단 2·미동의 차단 1·overflow 거부/복구 1/1·10MiB 선초과 거부를 통과했고, G7 focused 119 passed·Ruff·compile을 통과했다. 공개 WSS preflight는 TLS 1.3/Cloudflare 연결과 비인증 1008 차단, 마이크 열거·캡처 0을 확인했다. 증거: <code>ops/evidence/g7-public-wss-preflight-current-20260807.json</code>.</p></div><div class="kv k-warn"><b>외부 종료 GATE</b><p>실제 Deepgram 운영 key·quota와 live interim/final, 기대값과 일치하는 authenticated public WSS ready, 실행 직전 명시 동의 물리 마이크 50분 양방향 soak, 같은 public host·겹치는 시간창의 worker/Uvicorn queue와 exact image·cgroup/proc/Docker/TCP high-water, 30명/50회기/150축 독립 라벨 held-out voice-gain이 필요하다. 네 artifact가 <code>scripts/check-g7-external-proof.py</code> exit 0을 만들기 전에는 메인 상태를 DONE으로 바꾸지 않는다. 현재 canonical checker exit 1은 이 외부 증거 부재를 정직하게 차단한 결과다.</p></div></div>
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-doing">G7 GATE · INTERNAL DONE · EXTERNAL PROOF</span><span class="scard-mid"><span class="scard-title">Multimodal Alliance — 음성·비언어 동맹 신호</span><span class="scard-sum">내부 안전·복구·용량 계약은 닫았고 current 공개 runtime·동의 마이크·사람 평가 증거를 기다린다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
<div class="scard-body"><div class="kv k-good"><b>내부 구현 DONE</b><p>consent→단일 clock→독립 text/voice/fusion→철회·tombstone, streaming interim/final·word timestamp·provider event, HMAC word pseudonym, 열린 stream의 1초 동의 재검사·abort, 텍스트 보존·음성 재연결 UX를 연결했다. 운영 기본 provider는 노트북 상주 <code>local_whisper</code>/<code>melotts</code>이고 외부 Deepgram/OpenAI adapter는 fallback으로 보존한다. 격리 synthetic PCM soak는 59/59 연결 종료·성공 발화 55·철회 차단 2·미동의 차단 1·overflow 거부/복구 1/1·10MiB 선초과 거부를 통과했다. Windows host topology는 detached-clean commit/tree·도구 SHA·<code>psutil</code> version과 PID/start/exe·command SHA/cwd·process/TCP high-water를 fail-closed로 수집하고, 3,120초 capture의 공통 3,000초를 canonical exit와 결속한다. current source의 public launcher도 sidecar-before-API lifecycle, <code>local_whisper/small/cpu-int8</code>·<code>melotts/melotts-korean</code> exact readiness, provider env, Uvicorn <code>--ws websockets --ws-max-queue 4</code>, fresh API/cloudflared provenance receipt를 연결했다. runner/checker/topology 86/86·launcher/sidecar 80/80·통합 166/166·API voice 71/71을 통과했다.</p></div><div class="kv k-warn"><b>외부 종료 GATE</b><p>새 launcher는 아직 public에 실행하지 않았다. 현재 public API는 Git metadata가 없는 냉동 release cwd와 OpenAI voice provider로 실행돼 새 Windows provenance gate를 통과하지 못한다. 검증된 clean commit 기반 runtime으로 정렬하고 <code>/admin/voice-runtime</code>과 local provider/model ready를 실제 공개 경로에서 확인한 뒤 authenticated public WSS, 실행 직전 명시 동의 물리 마이크 50분 양방향 soak, 같은 public host·공통 3,000초 시간창의 worker/Uvicorn queue와 process/TCP high-water, 30명/50회기/150축 독립 라벨 held-out voice-gain을 수집해야 한다. 네 artifact가 <code>scripts/check-g7-external-proof.py</code> exit 0을 만들기 전에는 메인 상태를 DONE으로 바꾸지 않는다.</p></div></div>
|
||||
</article>
|
||||
<article class="scard" data-status="done" data-cat="Outcome OS·에이전틱" data-owner="0">
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">G8 DONE</span><span class="scard-mid"><span class="scard-title">Autonomous Content & Continuous Improvement</span><span class="scard-sum">receipt-bound rollback executor가 격리 NAS에서 실제 image rollback과 restore를 두 번의 executed receipt로 실행했다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
<div class="scard-body"><div class="kv"><b>agentic worker + 운영 환류 + human gate</b><p>승인 source의 실제 hash·PII를 engine 호출 전에 검사하고 structured generator 1개, 결과를 공유하지 않는 red-team 2개, variant generator와 variant별 독립 judge 3개 이상을 실행한다. 정답 누수·PII·unknown source claim·open finding·안전/보상 해킹·모델/structured 실패는 store 호출 전에 차단한다. repo-approved source는 기본 비활성 scheduler가 durable lease/retry job으로 관리한다. 실제 <code>claude_cli/claude-opus-4-8</code> 7 calls와 dev PostgreSQL reviewer 2·variant 3·pass rate 1.0을 거쳐 synthetic payload를 <code>pending_human_approval</code>로 저장했다. G6 canonical drift trigger는 metadata-only incident→DAG→job을 원자 enqueue하고, 실제 pending 후보의 관리자 검수는 catalog 0→1·approval 1과 역할 403·멱등/409·민감정보 비노출을 통과했다. 별도 누수 후보는 422·저장 0이다. 상세: <code>ops/outcome-os-g8-agentic-worker-evidence-2026-08-07.md</code>.</p></div><div class="kv k-good"><b>current NAS runtime</b><p>release-only SHA <code>6030a677…c611</code>를 전용 NAS Compose 프로젝트에 승격했다. API/Web 이미지 <code>52e0…8b2d</code>/<code>6fdb…f215</code>, health 3/3·db/engine true, OpenAPI 126·auth 401·G0~G8, browser E2E 108/108과 실제 SSE→DB-backed review를 확인했다. named DB volume과 predeploy dump를 보존했다.</p></div><div class="kv k-good"><b>실제 rollback receipt DONE</b><p>runtime+release_gate 전용 executor는 승인·idempotency·artifact·Compose/env/resolved-policy SHA와 exact API/Web image를 일회성 manifest에 결속하고 HMAC journal·crash recovery·previous-image 복구를 고정한다. 2026-08-07 격리 NAS에서 별도 loopback control-plane API(<code>127.0.0.1:8018</code>)와 별도 executor(<code>127.0.0.1:18149</code>)로 <b>실제 image rollback 2회</b>를 실행했다. <code>rollback-old</code>는 receipt <code>nas-g8-723eeef2…</code>로 previous 이미지 <code>79ec…4450</code>/<code>c530…2f28</code>를, <code>restore-current</code>는 receipt <code>nas-g8-2738846c…</code>로 current 이미지 <code>52e0…8b2d</code>/<code>6fdb…f215</code>를 활성화했다. release gate·approval 각각 2회 멱등, lifecycle <code>rollback/executed</code> 2건, 승인 <code>authorize_rollback</code> 2건, silent auto-promotion 0, HMAC journal 6-record 체인 검증, health 3/3·OpenAPI 126·auth 401·Web 200을 확인했다. helper는 image 상속 compose label을 baseline으로 두고 project/service를 격리 override하는 계약으로 검증했다(<code>launch-nas-preview-g8-helpers.py</code>, 37/37). 상세: <a href="./ops/nas-preview-g8-rollback-proof-runbook.md">런북</a> · <a href="./ops/evidence/nas-preview-g8-actual-rollback-2026-08-07.json">기계 판독 증거</a>.</p></div></div>
|
||||
<article class="scard" data-status="doing" data-cat="Outcome OS·에이전틱" data-owner="0">
|
||||
<button class="scard-head" aria-expanded="false"><span class="chip c-doing">G8 BUILD · RECEIPT DONE · RUNTIME REVALIDATION</span><span class="scard-mid"><span class="scard-title">Autonomous Content & Continuous Improvement</span><span class="scard-sum">실제 rollback receipt는 완료됐다. 비-secure origin UUID 수정 배포와 NAS-origin 회기 E2E 재검증이 남았다.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||
<div class="scard-body"><div class="kv"><b>agentic worker + 운영 환류 + human gate</b><p>승인 source의 실제 hash·PII를 engine 호출 전에 검사하고 structured generator 1개, 결과를 공유하지 않는 red-team 2개, variant generator와 variant별 독립 judge 3개 이상을 실행한다. 정답 누수·PII·unknown source claim·open finding·안전/보상 해킹·모델/structured 실패는 store 호출 전에 차단한다. repo-approved source는 기본 비활성 scheduler가 durable lease/retry job으로 관리한다. 실제 <code>claude_cli/claude-opus-4-8</code> 7 calls와 dev PostgreSQL reviewer 2·variant 3·pass rate 1.0을 거쳐 synthetic payload를 <code>pending_human_approval</code>로 저장했다. G6 canonical drift trigger는 metadata-only incident→DAG→job을 원자 enqueue하고, 실제 pending 후보의 관리자 검수는 catalog 0→1·approval 1과 역할 403·멱등/409·민감정보 비노출을 통과했다. 별도 누수 후보는 422·저장 0이다. 상세: <code>ops/outcome-os-g8-agentic-worker-evidence-2026-08-07.md</code>.</p></div><div class="kv k-warn"><b>NAS-origin runtime 재검증</b><p>SHA <code>6030a677…c611</code>의 API/Web 이미지는 health 3/3·db/engine true, OpenAPI 126·auth 401·실제 SSE→DB-backed review를 통과했다. 그러나 108/108은 localhost candidate 결과였고 같은 NAS 평문 origin에서는 <code>crypto.randomUUID</code> 예외로 24건이 실패했다. 소스의 <code>randomUuid()</code> 수정은 source-only Vite 6/6·typecheck·build를 통과했다. clean-head controller도 HEAD/tree/archive 2회 결정성·archive-only candidate·patch 무회귀와 commit 전 actual-origin 11-spec rollback gate를 unit 30/30로 고정했고 새 predeploy dump <code>72698232…5d41c</code>를 보존했다. 두 execute는 모두 승격 전 fail-closed되어 mutation 0이다. 후속으로 UTC timestamp·KST 날짜 계약을 구현해 API 전체 921 passed와 KST 실DB browser 코칭 이력 1/1을 통과했다. 사용자 승인 아래 새 clean commit을 결속하고 동일 NAS-origin 회기 묶음이 GREEN이 되기 전에는 G8 전체를 DONE으로 올리지 않는다.</p></div><div class="kv k-good"><b>실제 rollback receipt DONE</b><p>runtime+release_gate 전용 executor는 승인·idempotency·artifact·Compose/env/resolved-policy SHA와 exact API/Web image를 일회성 manifest에 결속하고 HMAC journal·crash recovery·previous-image 복구를 고정한다. 2026-08-07 격리 NAS에서 별도 loopback control-plane API(<code>127.0.0.1:8018</code>)와 별도 executor(<code>127.0.0.1:18149</code>)로 <b>실제 image rollback 2회</b>를 실행했다. <code>rollback-old</code>는 receipt <code>nas-g8-723eeef2…</code>로 previous 이미지 <code>79ec…4450</code>/<code>c530…2f28</code>를, <code>restore-current</code>는 receipt <code>nas-g8-2738846c…</code>로 current 이미지 <code>52e0…8b2d</code>/<code>6fdb…f215</code>를 활성화했다. release gate·approval 각각 2회 멱등, lifecycle <code>rollback/executed</code> 2건, 승인 <code>authorize_rollback</code> 2건, silent auto-promotion 0, HMAC journal 6-record 체인 검증, health 3/3·OpenAPI 126·auth 401·Web 200을 확인했다. helper는 image 상속 compose label을 baseline으로 두고 project/service를 격리 override하는 계약으로 검증했다(<code>launch-nas-preview-g8-helpers.py</code>, 37/37). 상세: <a href="./ops/nas-preview-g8-rollback-proof-runbook.md">런북</a> · <a href="./ops/evidence/nas-preview-g8-actual-rollback-2026-08-07.json">기계 판독 증거</a>.</p></div></div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -917,9 +935,9 @@
|
|||
<article class="phase-card current"><div class="phase-top"><span class="phase-id">Phase 0</span><span class="tag doing">closing</span></div><h3>기반 정렬 / 운영 안전장치</h3><p>인프라, 인증 경계, fail-closed, 공개 배포, 회귀 테스트 안정화.</p><ul class="phase-list"><li><b>상태:</b> 대부분 완료, 공개 <code>/turn</code> proof만 남음</li><li><b>검증:</b> build + backend 178 기준선 + Playwright 113 기준선, Node/backend focused 120</li><li><b>운영:</b> prod API 8001, engine 9099, tunnel 1개</li></ul></article>
|
||||
<article class="phase-card next"><div class="phase-top"><span class="phase-id">Phase 1</span><span class="tag doing">next</span></div><h3>MVP 코어 루프</h3><p>1 페르소나 상담, 실제 턴 저장, 회기말 피드백, 저항 엔진 검증.</p><ul class="phase-list"><li><b>DoD:</b> 로그인 → P1 상담 완주 → 피드백 수신</li><li><b>추가:</b> 음성 왕복 1턴 PoC 포함</li><li><b>증거:</b> cost/turn DB 적재 + openness 곡선 대조</li></ul></article>
|
||||
<article class="phase-card"><div class="phase-top"><span class="phase-id">Phase 2a</span><span class="tag info">planned</span></div><h3>3역할 / OAuth / 페르소나</h3><p>학습자, 교수자, 관리자 권한 분리와 페르소나 확장.</p><ul class="phase-list"><li><b>DoD:</b> 타인 session 직접 접근 403</li><li><b>범위:</b> OAuth BFF, RBAC, 교수 검수 루프</li><li><b>주의:</b> visible_to와 인간 권한은 별도 레이어</li></ul></article>
|
||||
<article class="phase-card"><div class="phase-top"><span class="phase-id">Phase 2b</span><span class="tag info">planned</span></div><h3>음성 캐스케이드</h3><p>STT → 엔진 → TTS 왕복, WSS/터널 안정성, barge-in 로깅.</p><ul class="phase-list"><li><b>DoD:</b> 50분 WSS 끊김 실측</li><li><b>범위:</b> Deepgram STT, OpenAI TTS, voice preset</li><li><b>리스크:</b> 실시간 레이턴시는 실측 전 완료 표기 금지</li></ul></article>
|
||||
<article class="phase-card"><div class="phase-top"><span class="phase-id">Phase 2b</span><span class="tag info">planned</span></div><h3>음성 캐스케이드</h3><p>STT → 엔진 → TTS 왕복, WSS/터널 안정성, barge-in 로깅.</p><ul class="phase-list"><li><b>DoD:</b> 50분 WSS 끊김 실측</li><li><b>범위:</b> local_whisper STT + MeloTTS 기본, Deepgram/OpenAI fallback</li><li><b>리스크:</b> 실시간 레이턴시는 실측 전 완료 표기 금지</li></ul></article>
|
||||
<article class="phase-card"><div class="phase-top"><span class="phase-id">Phase 3</span><span class="tag info">planned</span></div><h3>교육용 파일럿 / 효과성</h3><p>수련생 파일럿, 사전사후 효과 측정, 재귀학습 데이터셋 산출.</p><ul class="phase-list"><li><b>DoD:</b> 20명 완주 + KPI 충족</li><li><b>측정:</b> SUS, 자기효능감, κ/ICC, 환각률</li><li><b>정정:</b> IRB 게이트가 아니라 데이터 활용동의/개인정보 게이트</li></ul></article>
|
||||
<article class="phase-card next"><div class="phase-top"><span class="phase-id">Outcome OS</span><span class="tag doing">G7 external proof</span></div><h3>성과·동맹·수련 폐루프</h3><p>측정 → 이상 탐지 → 원인 가설 → 재연습 → 전이 검증 → 숙련도 갱신.</p><ul class="phase-list"><li><b>상태:</b> G0~G6 internal DONE · G8 실제 NAS rollback receipt DONE · G7 외부 voice/resource/human 대기</li><li><b>완료 DAG:</b> AOS-001~012</li><li><b>운영:</b> current source NAS SHA <code>6030a677…c611</code> 승격 완료 · G8 loopback control-plane rollback/restore receipt 2건 · G7 canonical four-artifact checker 준비</li></ul></article>
|
||||
<article class="phase-card next"><div class="phase-top"><span class="phase-id">Outcome OS</span><span class="tag doing">G7 external · G8 runtime</span></div><h3>성과·동맹·수련 폐루프</h3><p>측정 → 이상 탐지 → 원인 가설 → 재연습 → 전이 검증 → 숙련도 갱신.</p><ul class="phase-list"><li><b>상태:</b> G0~G6 internal DONE · G8 실제 NAS rollback receipt DONE/runtime 재검증 중 · G7 외부 voice/resource/human 대기</li><li><b>완료 DAG:</b> AOS-001~012</li><li><b>운영:</b> NAS SHA <code>6030a677…c611</code>은 UUID 수정 전 빌드 · G8 receipt 2건 완료 · 수정 배포/NAS-origin E2E와 G7 canonical four-artifact checker exit 0 필요</li></ul></article>
|
||||
</div>
|
||||
<div class="source-note">정정 기준: <code>MASTERPLAN</code> 원본의 IRB 신청/승인 게이트는 <code>MASTERPLAN_REVISIONS</code> 기준으로 제거한다. Phase 2는 2a(3역할/OAuth/페르소나)와 2b(음성 캐스케이드)로 분리하고, Phase 1 DoD에는 얇은 음성 왕복 1턴 PoC를 포함한다.</div>
|
||||
</div>
|
||||
|
|
@ -1006,7 +1024,7 @@
|
|||
<div class="task-row"><div><span class="task-status s-done">DONE</span></div><div><b>OpenAI TTS voice preset 추상화</b><p>P1/P2/P3 persona voice preset, invalid preset fallback, speed clamp, instructions payload 계약을 회귀화했고, <code>app.persona_voice_map</code>을 live <code>/voice/ws</code> TTS 선택에 연결했다. 명시 preset은 DB map보다 우선하고, OpenAI가 아닌 provider row는 기존 fallback으로 안전하게 흡수한다.</p></div><div><b>산출물</b><p><code>resolve_voice</code>, <code>resolve_voice_from_map</code>, <code>build_tts_payload</code>, <code>get_session_voice_map</code>, seed voice map row, dev runtime schema guard</p></div><div><b>검증</b><p><code>pytest app/test_runtime_policy.py app/test_persona_review.py app/test_voice_service.py app/test_voice_ws.py -q</code> 75 passed; full API 178 passed + gateway 11 passed.</p></div></div>
|
||||
<div class="task-row"><div><span class="task-status s-done">POC DONE</span></div><div><b>P1 서연 무참조 synthetic voice 샘플</b><p>Higgs v3 로컬 런타임에서 실존 reference voice 없이 synthetic seed를 먼저 만들고, 그 seed만 재참조해 우울·피로·불안·라포·회복감 5개 변주를 생성했다. 개발용 <code>VIGNETTE_VOICE_POC_SAMPLE_TTS</code> 플래그로 <code>/voice/ws</code> P1 sample TTS provider에도 연결했다.</p></div><div><b>산출물</b><p><code>docs/archive/voice-art/bimec-persona-voice-poc-2026-06-27.md</code>, <code>docs/voice-art/p1-seoyeon-higgs-v3-20260627/manifest.json</code>, <code>apps/api/app/services/voice.py</code></p></div><div><b>검증</b><p>mp3/wav 6개(seed+5) 생성, 24kHz, 7.72~11.68초. live <code>/voice/ws</code> text_turn smoke: provider <code>p1-sample-poc</code>, binary 20 chunks/78,573 bytes. 샘플 provider는 TTS override만 담당하고 마이크/STT는 OpenAI 키가 필요하다. 프로덕션 탑재는 모델 라이선스·권리정책 결정 전 금지.</p></div></div>
|
||||
<div class="task-row"><div><span class="task-status s-done">LOCAL DONE</span></div><div><b>barge-in / paralinguistic logging</b><p>음성 learner turn에 audio_ref, silence_ms, speech_rate, barge_in을 nullable 메타로 저장하고, 회기 리뷰에서 침묵·발화 속도·끼어듦·음성 입력 비언어 칩으로 표시한다. 2차로 <code>provider_events</code> JSONB 보존 슬롯을 추가해 control/STT provider 이벤트를 allowlist·size limit 후 저장하고, 3차로 내부 taxonomy <code>event_type</code>/<code>category</code>를 붙인다. 4차로 인증된 회기 리뷰에는 한숨·울음·웃음·호흡·운율·배경소음 같은 정규화 이벤트만 label/detail 칩으로 제한 노출한다. 실제 Session 마이크 UI는 browser voice activity/trailing silence 메타를 <code>audio_end</code>에 싣고, readiness는 음성 메타 5개 컬럼 전체를 확인한다. raw transcript/text/provider/source/raw type은 응답에서 제외하고, 공개 공유 카드는 축어록과 provider raw를 계속 싣지 않는다.</p></div><div><b>산출물</b><p><code>app.turns.provider_events</code>, <code>TurnRecord.provider_events</code>, voice route 이벤트 sanitizing/taxonomy, <code>ReviewNonverbalEvent</code>, <code>SessionReview</code> 칩 UI, browser voice metadata capture, voice metadata readiness guard</p></div><div><b>검증</b><p><code>python -X utf8 -m pytest -p no:cacheprovider app/test_runtime_policy.py app/test_session_turn_persistence.py app/test_voice_ws.py app/test_voice_service.py -q</code> 77 passed; provider_events DB insert placeholder/args, WS raw key drop, STT+control event merge, internal taxonomy, client turn 미혼입, review 파생 칩 raw provider 미노출, 침묵 칩 중복 방지, 5-column readiness 검증. <code>PLAYWRIGHT_PORT=5197 npx playwright test e2e/voice-success.spec.ts --project=chromium-single-run --workers=1</code> 2 passed, <code>PLAYWRIGHT_PORT=5199 npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1</code> 5 passed, <code>npm run typecheck</code>.</p></div></div>
|
||||
<div class="task-row"><div><span class="task-status s-plan">GATE</span></div><div><b>공개·실기기 음성 장시간 실측</b><p>Deepgram adapter, HMAC word pseudonym, 1초 동의 재검사, 공통 consent transaction lock, bounded queue/10MiB cap, recovery UX와 provider/model ready 계약은 내부 완료다. 남은 범위는 실제 Deepgram key·quota live, 기대 provider/model authenticated public WSS, 명시 동의 물리 마이크, 50분 양방향 soak, 독립 라벨 voice-gain benchmark, 실제 운영 topology resource high-water와 provider 기반 한숨·울음 표본이다. mic-free preflight와 synthetic E2E/label은 이를 대체하지 않는다.</p></div><div><b>산출물</b><p>expected provider/model public WSS ready log, Deepgram interim/final 표본, explicit-consent physical mic capture log, 50분 soak evidence, independently labeled voice-gain report, worker/proxy resource high-water, provider paralinguistic event sample</p></div><div><b>검증</b><p>실제 공개/실기기/외부 provider 환경에서만 완료 가능. 현재는 B2 음성 캐스케이드 live gate로 추적한다.</p></div></div>
|
||||
<div class="task-row"><div><span class="task-status s-plan">GATE</span></div><div><b>공개·실기기 음성 장시간 실측</b><p>provider-neutral streaming adapter, HMAC word pseudonym, 1초 동의 재검사, 공통 consent transaction lock, bounded queue/10MiB cap, recovery UX와 provider/model ready 계약은 내부 완료다. 남은 범위는 current committed source 기반 public launcher와 <code>local_whisper</code>/<code>melotts</code> exact ready, authenticated public WSS, 명시 동의 물리 마이크, 50분 양방향 soak, 독립 라벨 voice-gain benchmark, 실제 운영 topology resource high-water와 provider 기반 한숨·울음 표본이다. Deepgram/OpenAI는 fallback이며 mic-free preflight와 synthetic E2E/label은 외부 증거를 대체하지 않는다.</p></div><div><b>산출물</b><p>expected provider/model public WSS ready log, local Whisper interim/final 표본, explicit-consent physical mic capture log, 50분 soak evidence, independently labeled voice-gain report, worker/proxy resource high-water, provider paralinguistic event sample</p></div><div><b>검증</b><p>실제 공개/실기기/운영 provider 환경에서만 완료 가능. 현재는 B2 음성 캐스케이드 live gate로 추적한다.</p></div></div>
|
||||
<div class="task-row"><div><span class="task-status s-done">DECIDED</span></div><div><b>s2s 2차 PoC 판단</b><p>소유자 결정(2026-06-30): s2s 2차 PoC 유지. 캐스케이드 1차를 운영 기준으로 두고 <code>voice-s2s-poc.md</code> 비교 PoC를 계속 연다. 최종 채택/폐기는 비교 리포트 이후 재판단한다. 채택 여부 자체는 확정됐고, 비교 PoC 구현·실측이 후속 build로 남는다.</p></div><div><b>산출물</b><p><code>docs/decisions/voice-s2s-poc.md</code></p></div><div><b>검증</b><p>transcript, safety, audit, latency, failure, privacy 기준 명시. 실제 비교 실측은 후속.</p></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1047,7 +1065,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-07 현재 수집은 <b>610 tests / 44 files</b>다. 현 작업트리의 610개 전체 GREEN은 아직 검증 전이며, 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-09 현재 수집은 <b>614 tests / 44 files</b>다. 현 작업트리의 614개 전체 GREEN은 아직 검증 전이며, current 핵심 fixture는 불변 110 + 최신 dashboard 10의 분할 GREEN이다. 이전 단일 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 & 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>
|
||||
|
|
@ -1108,7 +1126,7 @@
|
|||
<tr><td>Crisis safety gate UI</td><td><code>PLAYWRIGHT_PORT=5193 npx playwright test e2e/session-mvp.spec.ts --project=chromium-single-run --workers=1</code> / <code>PLAYWRIGHT_PORT=5259 npx playwright test e2e/session-mvp.spec.ts --project=chromium-desktop --workers=1 --grep "voice conversation stop"</code></td><td>mock SSE <code>safety</code> + <code>done.conversation_stopped</code>와 mock voice <code>reply.conversation_stopped</code>에서 안전 자원 109와 입력 disabled가 유지되고, 빈 client reply를 <code>내담자 응답 없음</code>으로 덮지 않으며 live-coach를 호출하지 않는지 검증한다. 음성 위기 경로는 TTS 종료 이벤트 없이도 socket close를 확인한다.</td></tr>
|
||||
<tr><td>Persona/session review UI E2E</td><td><code>PLAYWRIGHT_PORT=5215 npx playwright test e2e/teacher.spec.ts --project=chromium-single-run --workers=1</code></td><td>9 passed mixed suite; real server-row rendering과 route-fixture UI 흐름을 함께 포함한다. 교수자가 pending persona를 승인하고, pending/recent review detail로 진입하며, 별도 student analysis 메뉴·학습자 검색·행 펼침·full learner timeline drilldown을 검증한다.</td></tr>
|
||||
<tr><td>Voice UI synthetic E2E</td><td><code>npx playwright test e2e/voice-success.spec.ts --project=chromium-single-run</code> / <code>PLAYWRIGHT_PORT=5248 npx playwright test e2e/session-mvp.spec.ts --project=chromium-single-run --workers=1 --grep "pending voice transcript"</code></td><td>voice-success 2 passed + pending transcript failure UI 1 passed; direct WS cascade, Session 텍스트 턴의 persisted reply <code>/voice/speech</code>→OpenAI speech→Web Audio 재생, Session mic button path with synthetic browser audio/Web Audio playback, and final transcript followed by persistence failure becoming a visible <code>저장 실패</code> turn instead of a silent successful transcript</td></tr>
|
||||
<tr><td>G7 explicit consent · external proof</td><td><code>pytest app/test_g7_voice_gain_evidence.py app/test_voice_runtime.py ...</code> / <code>pytest scripts/test_g7_external_proof.py scripts/test_g7_runtime_evidence.py scripts/test_g7_topology_evidence.py scripts/test_g7_external_voice_soak.py</code> / <code>voice-success.spec.ts</code></td><td>API/G7 92 passed, evidence runner/checker·release gate 31 passed, voice UI 2/2. 서버 30일·원음 미보존 consent ledger 성공 전 getUserMedia/voice WS 0회, public v4 interim/final latency, worker/Uvicorn·Linux topology high-water와 independent human-held-out gate를 fail-closed로 고정했다. exact SHA <code>a8c27b0a…1a0d</code> NAS 승격과 clean candidate session E2E 24/24도 통과했다. 실제 마이크·Deepgram·human evidence는 미실행이라 G7 BUILD.</td></tr>
|
||||
<tr><td>G7 explicit consent · external proof</td><td><code>pytest app/test_g7_voice_gain_evidence.py app/test_voice_runtime.py ...</code> / <code>python -m unittest scripts/test_g7_external_proof.py scripts/test_g7_topology_evidence.py ...</code> / <code>voice-success.spec.ts</code></td><td>내부 voice/G7 계약과 consent ledger, 동의 전 getUserMedia/voice WS 0회, public v4 interim/final latency, worker/Uvicorn 및 Linux/Windows topology, independent human-held-out gate를 fail-closed로 고정했다. runner/checker/topology는 86/86, public launcher/sidecar는 80/80, G7 통합은 166/166, API voice 통합은 71/71·Ruff·compile을 통과했다. 현재 public API는 Git 없는 냉동 release cwd라 provenance gate를 통과하지 못하고 <code>local_whisper</code>/<code>melotts</code> sidecar도 미기동이다. 실제 명시 동의 마이크·50분·동시 runtime/topology·human evidence가 없어 G7 external GATE다.</td></tr>
|
||||
<tr><td>Voice s2s decision memo</td><td><code>docs/decisions/voice-s2s-poc.md</code></td><td>criteria recorded; keep/drop decision remains owner DECIDE</td></tr>
|
||||
<tr><td>Hanshin data/SSO gate</td><td><code>docs/ops/hanshin-data-governance-gate.md</code></td><td>artifact created; written external evidence still required</td></tr>
|
||||
<tr><td>Hanshin feedback 1차 improvement pack</td><td><code>docs/ops/hanshin-feedback-improvement-plan-2026-07-03.md</code> / <code>docs/redteam/hanshin-feedback-plan-redteam-2026-07-03.md</code> / <code>docs/redteam/hanshin-feedback-plan-counter-redteam-2026-07-03.md</code> / backend+web implementation</td><td>1차 implemented: P1 role mapping + client-only history injection, P2 quality retry/no-save gate + SSE full-response buffer, P3 review display humanization/raw error mapper/fast-deep labels/client feedback label, P4 supervisor scope panel. Verification: backend focused 84 passed, web typecheck and API type check passed, session-review focused 8 passed, layout visual gate 12 passed, session-layout 8 passed. Remaining follow-ups are stored fallback metadata, lower-latency partial stream policy, fast/deep reconciliation, resident namespace/prompt caching, and clinical rubric/CBT wording gates.</td></tr>
|
||||
|
|
@ -1124,9 +1142,11 @@
|
|||
<tr><td>Docker image smoke</td><td><code>docker build -f apps/api/Dockerfile .</code> / <code>docker run ... python -c "import app.main"</code> / <code>docker build -f apps/web/Dockerfile apps/web</code></td><td>API/Web image build and API import smoke pass after packaging cleanup. Web build uses npm lockfile and ignores host <code>node_modules</code>; API image excludes local <code>.env</code> files.</td></tr>
|
||||
<tr><td>Deploy preflight</td><td><code>python scripts\check-deploy-preflight.py --skip-db --env-file infra\.env.example --allow-placeholder-secrets</code> / DB mode with local <code>DATABASE_URL</code></td><td>Passed: exact-pinned API requirements, live coaching <code>data/kb</code> source pack, env template keys, and DB readiness (<code>current_user=vignette</code>). DB mode can additionally check app-role DSN with <code>--require-app-role</code> and now verifies session read-model columns including <code>app.turns</code> voice metadata columns(<code>audio_ref</code>/<code>silence_ms</code>/<code>speech_rate</code>/<code>barge_in</code>/<code>provider_events</code>) plus worksheet review columns.</td></tr>
|
||||
<tr><td>Fresh compose smoke</td><td><code>docker compose -p vignette-packaging-smoke -f infra/docker-compose.yml up -d --build</code> + proxy <code>/api/health</code></td><td>Passed with dummy production-safe env: API healthy, DB healthy, web/proxy up, <code>http://localhost:18080/api/health</code> 200 with <code>db:true</code>, <code>engine:true</code>, <code>engine_mode:"claude_cli"</code>. Smoke volumes/network removed after run.</td></tr>
|
||||
<tr><td>격리 NAS 프리뷰 · 회기 E2E 자동화</td><td><code>http://100.116.83.60:8088</code> / <code>vignette-e2e</code> 매일 04:30 KST / <a href="./ops/nas-preview-deployment-evidence-2026-08-07.md">배포 증거</a> / <a href="./ops/evidence/nas-preview-current-deploy-2026-08-07.json">기계 판독 증거</a></td><td>current SHA <code>6030a677…c611</code>을 전용 Compose 프로젝트·포트·네트워크·named volume에 승격했다. browser E2E 108/108, health 3/3, OpenAPI 126·auth 401·G0~G8, postdeploy SSE→DB review를 통과했고 predeploy dump와 이전 exact images를 보존했다. 자동화는 ACTIVE이며 material milestone+release gate+배포 SHA 변경 때만 프리뷰를 갱신한다. G7 외부 mic/provider/human 증거는 별도다.</td></tr>
|
||||
<tr><td>G7 외부 증거 3-artifact 동시 시간창 오케스트레이터</td><td><code>scripts/run-g7-external-proof-window.py</code> · <a href="./ops/outcome-os-g7-external-proof-readiness-2026-08-07.md">준비 문서</a></td><td>soak·runtime·topology 세 캡처는 같은 host의 겹치는 시간창이어야 하는데 지금까지는 운영자가 세 명령을 손으로 맞춰야 했다. 한 명령으로 동시에 띄우고 checker까지 잇도록 묶었다. 게이트는 약화시키지 않았다 — CLI 실증 fail-closed 4종: 동의·장치 없음 <code>physical_microphone_consent_required</code>, human pack 없음 <code>human_voice_gain_pack_required</code>, 50분 미만 <code>production_window_too_short</code>, host 불일치 <code>hosts_must_match</code>, 전부 exit 2. <code>--rehearse</code>는 마이크를 열지 않고 배관만 확인하며 <code>gate_closed</code>는 항상 false다. 회귀 <code>20/20</code>. <b>선행 조건 발견:</b> 공개 런타임은 119 paths 구버전이라 <code>/admin/voice-runtime</code>이 배포돼 있지 않아 마이크·사람을 준비해도 artifact 2 에서 실패한다. current source 를 공개에 먼저 승격해야 하며, 오케스트레이터가 시작 전 <code>admin_voice_runtime_not_deployed</code>로 막는다(실측 exit 2). 이 판정은 Cloudflare 가 모든 경로를 403(error 1010)으로 주기 때문에 미인증 상태 코드가 아니라 OpenAPI <code>paths</code>로 한다. 그 뒤 남는 것은 <b>물리 마이크 50분</b>과 <b>독립 human voice-gain pack</b> 둘뿐이다.</td></tr>
|
||||
<tr><td>노트북 로컬 음성 스택 — faster-whisper STT + MeloTTS TTS</td><td><code>scripts/local-whisper-stt-server.py</code> · <code>scripts/start-local-whisper-stt.ps1</code> · <code>voice_stt_provider=local_whisper</code></td><td>2026-08-08 소유자 결정으로 음성 양쪽을 노트북 상주 로컬 모델로 정했다. TTS는 처음 Higgs로 잡았으나 연구/비상업 라이선스라 <code>config.py</code>가 운영에서 차단하고 있었고 그 가드를 푸는 건 법적 판단이라, 상업 사용이 허용된 <b>MeloTTS Korean(MIT)</b>으로 바꿔 가드 자체를 불필요하게 만들었다. Kokoro-82M은 Apache 2.0이지만 공식 <code>VOICES.md</code>에 <b>한국어가 없어</b> 탈락했다. 이전에는 로컬 STT 경로가 코드에 없었고 interim/final 스트리밍이 Deepgram 전용이었다. Higgs와 같은 loopback 사이드카를 추가해 오디오가 호스트를 벗어나지 않고 외부 STT 키도 필요 없다. <b>실측</b>: 저장소 합성 시드 8.72초에서 interim <code>9</code>·final <code>5</code>·word timestamp 12개 present, 전사는 원문과 한 글자 차이. 회귀 사이드카 <code>37/37</code>, API 전체 <code>908 passed</code>, gateway <code>58</code>, G7 checker <code>23</code>, ruff clean. 이 노트북은 CUDA는 보이지만 cuDNN 부재로 GPU 추론이 <b>네이티브 크래시</b>라 디바이스 확인을 자식 프로세스(<code>--self-check</code>)로 분리하고 CPU 폴백을 넣었다. cuDNN 9 설치는 소유자 판단으로 남겼다. MeloTTS는 CPU 정상 상태 RTF <code>0.27~0.28</code>(실시간 3.6배), <code>POST /tts</code> 200·WAV 350,566 bytes·3.61s, 빈 텍스트 422·미지 경로 404이며, <b>합성음을 로컬 STT가 완전 일치 전사</b>하는 왕복 검증을 통과했다(사이드카 16/16, API 914 passed). G7 체커의 <code>deepgram</code> 하드코딩도 운영 provider 허용목록으로 교정했고 선언/실제 불일치 차단은 유지한다. Higgs 운영 사용은 라이선스 가드가 남아 dev 전용이다.</td></tr>
|
||||
<tr><td>격리 NAS 프리뷰 · 회기 E2E 자동화</td><td><code>http://100.116.83.60:8088</code> / <code>vignette-e2e</code> 매일 04:30 KST / <a href="./ops/nas-preview-deployment-evidence-2026-08-07.md">배포 증거</a> / <a href="./ops/evidence/nas-preview-current-deploy-2026-08-07.json">기계 판독 증거</a></td><td>SHA <code>6030a677…c611</code>을 전용 Compose 프로젝트·포트·네트워크·named volume에 승격했다. localhost candidate browser E2E 108/108, health 3/3, OpenAPI 126·auth 401·G0~G8, postdeploy SSE→DB review를 통과했고 predeploy dump와 이전 exact images를 보존했다. 같은 NAS 평문 origin은 UUID 결함으로 24건 실패했으므로 현재 소스 수정 배포와 NAS-origin 재검증이 남았다. 자동화는 ACTIVE이며 material milestone+release gate+배포 SHA 변경 때만 프리뷰를 갱신한다. G7 외부 mic/provider/human 증거는 별도다.</td></tr>
|
||||
<tr><td>G7 외부 증거 3-artifact 동시 시간창 오케스트레이터</td><td><code>scripts/run-g7-external-proof-window.py</code> · <a href="./ops/outcome-os-g7-external-proof-readiness-2026-08-07.md">준비 문서</a></td><td>soak·runtime·topology 세 캡처를 같은 host의 공통 3,000초 시간창으로 실행하고 checker까지 잇는다. CLI는 동의·장치 없음, human pack 없음, 3,120초 미만, host 불일치를 전부 fail-closed하며 <code>--rehearse</code>는 마이크를 열지 않는다. 기존 Linux Compose gate를 보존한 채 Windows host Uvicorn+cloudflared topology mode를 추가해 detached-clean source/tool SHA와 PID/start/exe·command SHA/cwd·process/TCP high-water를 검증했고 runner/checker/topology 86/86을 통과했다. current source launcher도 local sidecar/provider/queue와 fresh PID provenance를 fail-closed로 연결했지만 아직 공개에 실행하지 않았다. <b>남은 선행 조건:</b> 공개 런타임은 119 paths 구버전이고 <code>/admin/voice-runtime</code>이 없으며 <code>/voice/health</code>는 OpenAI STT/TTS, local sidecar 9882/9883은 내려가 있다. API는 Git 없는 냉동 release cwd라 provenance gate도 닫혀 있다. clean committed source runtime으로 정렬한 뒤에만 <b>물리 마이크 50분</b>과 <b>독립 human voice-gain pack</b>을 수집한다.</td></tr>
|
||||
<tr><td>G7 external GATE · 최종 감사 정정</td><td><code>run-g7-external-proof-window.py</code> → <code>check-g7-external-proof.py</code></td><td><b>상태는 external GATE 유지.</b> 물리 마이크 전 코드 P0 3건은 완료됐다. runner 프로세스 exit는 canonical checker <code>exit 0</code>·<code>gate_closed=true</code>에 결속되고, browser Origin <code>vignette.chanpaca.net</code>은 API/WSS/admin/topology host <code>api-vignette.chanpaca.net</code>과 분리된 HTTPS allowlist로 검증되며, 세 artifact 교집합은 <code>≥3000s</code> + 120초 sampler margin으로 강제된다. Windows proof는 detached-clean HEAD/tree·runner/collector/checker script SHA·exact <code>psutil==6.1.1</code>을 pin한다. 공개 창에서는 legacy PID를 재사용하지 않고 API/cloudflared를 pinned executable/config와 stable release cwd에서 강제 재시작해 safe provenance receipt를 결속한다. 남은 것은 clean-source 실제 배포와 4-artifact 실증이다.</td></tr>
|
||||
<tr><td>G8 runtime REVALIDATION · 17:07 NAS 기준선</td><td><code>vignette-preview-20260807</code> · <code>run-outcome-os-release-agent.py</code></td><td><b>상태는 runtime REVALIDATION 유지.</b> active SHA <code>6030a677…c611</code>, API/Web <code>52e0…8b2d</code>/<code>6fdb…f215</code> exact running, previous <code>79ec…4450</code>/<code>c530…2f28</code> 보존. <code>api/web/db/proxy</code> 4개 running + <code>unless-stopped</code>, volume <code>vignette-preview-20260807_pgdata</code>, health 3/3·db/engine true·auth 401·OpenAPI 126 + G1~G8 routes·root/JS/CSS 200, helper/listener 0이다. dump는 <code>92ed…1570f</code> 960,970 bytes와 <code>726982…5d41c</code> 1,015,222 bytes. release agent는 DB dump/restore를 지원하지 않으므로 매 execute 직전 fresh dump SHA/size/TOC·DB identity를 별도 결속한다. 자동 rollback은 image/Compose/active-state뿐이고 migration/data restore는 별도 owner 승인이다. remote release root/Compose mode <code>0777</code> P1은 최소권한 preflight로 닫는다.</td></tr>
|
||||
<tr><td>노트북 로컬 음성 스택 — faster-whisper STT + MeloTTS TTS</td><td><code>scripts/local-whisper-stt-server.py</code> · <code>scripts/start-local-whisper-stt.ps1</code> · <code>voice_stt_provider=local_whisper</code></td><td>2026-08-08 소유자 결정으로 음성 양쪽을 노트북 상주 로컬 모델로 정했다. TTS는 처음 Higgs로 잡았으나 연구/비상업 라이선스라 <code>config.py</code>가 운영에서 차단하고 있었고 그 가드를 푸는 건 법적 판단이라, 상업 사용이 허용된 <b>MeloTTS Korean(MIT)</b>으로 바꿔 가드 자체를 불필요하게 만들었다. Kokoro-82M은 Apache 2.0이지만 공식 <code>VOICES.md</code>에 <b>한국어가 없어</b> 탈락했다. local Whisper는 현재 호스트의 cuDNN 부재를 반영해 <code>small/cpu-int8</code>를 운영 기본으로 고정했고, API health도 MeloTTS model을 실제 provider와 맞춰 보고한다. 합성 시드 8.72초에서 interim 9·final 5·word timestamp 12개, 한 글자 차이 전사를 확인했고 MeloTTS CPU RTF는 0.27~0.28, 합성음→local STT 왕복은 완전 일치했다. current 통합은 launcher/sidecar 80/80·G7 전체 166/166·API voice 71/71·API 전체 921·gateway 58을 통과했다. Higgs 운영 사용은 라이선스 가드가 남아 dev 전용이다.</td></tr>
|
||||
<tr><td>비-secure origin 회기 리뷰 크래시 수정</td><td><code>apps/web/src/lib/uuid.ts</code> · <code>apps/web/e2e/insecure-context-uuid.spec.ts</code></td><td>격리 NAS 프리뷰(평문 HTTP·비-localhost)에서 회기 리뷰 라우트가 <code>crypto.randomUUID is not a function</code>으로 error boundary에 잡혔다. 이 API는 secure context 전용인데 제품 코드 18곳이 fallback 없이 호출했고 <code>RuptureRepairCard</code>는 렌더 시점 호출이라 화면 전체가 죽었다. 릴리스 게이트는 localhost 후보 스택에서만 돌아 미검출. <code>randomUuid()</code>로 통일하고 fallback도 <code>getRandomValues</code> 우선으로 예측 불가능성을 유지했다. 회귀 <code>6/6</code>(직접 호출 0건 검사 포함), typecheck·build 통과. <b>NAS 프리뷰에는 아직 미배포</b>이며 배포된 SHA <code>6030a677…c611</code>은 여전히 결함 빌드다.</td></tr>
|
||||
<tr><td>공개 워치독 engine readiness 사각지대(해결)</td><td><code>scripts/watch-public-runtime.ps1</code> · <code>scripts/start-public-runtime.ps1</code> · <code>apps/api/engine_gateway/gateway.py</code></td><td>2026-08-07 22:0x KST 소유자 승인으로 9099를 재기동해 복구했다. 재기동 직후 <code>/ready?force=true</code> 200 <code>ok:true·detail:OK</code>, API 8001과 공개 <code>api-vignette.chanpaca.net</code> 모두 <code>status:ok·engine:true·engine_detail:OK</code>다. 원인은 16:06부터 상주한 9099 프로세스 하나였다(CLI·코드·cwd는 정상 — 동일 인자 직접 실행과 <code>EngineSession</code> 재현이 <code>"OK"</code>를 반환). 재발 방지 3건: (1) 워치독·기동 스크립트의 engine 판정을 <code>/health</code>(프로세스 liveness)에서 <code>/ready</code>(실제 생성)로 바꾸고 API 판정에 <code>engine</code>을 추가, shared secret 인스턴스용 토큰 헤더와 503 본문 로깅 포함. (2) 게이트웨이가 자식 <code>claude -p</code>의 stderr를 상시 드레인해(PIPE 미독 시 자식 블록도 차단) 실패 detail에 <code>exit</code>·stderr를 붙인다 — 실증: <code>empty engine response (error: unknown option '--vignette-nonexistent-flag-xyz')</code>. (3) 기동 시 게이트웨이 로그 회전, <code>Stop-UvicornByPort</code>에 <code>Name -like python*</code> 추가(호출자 자기 자신 종료 방지). 검증: <code>pytest engine_gateway</code> 58/58, 워치독 <code>-CheckOnly</code> healthy 5/5, 장애 재현(9299·<code>CLAUDE_BIN</code> 부재)에서 <code>/health</code>는 <code>ok:true</code>로 통과하고 <code>/ready</code>는 503으로 검출되어 워치독이 <code>unhealthy (1/3)</code> + 503 본문을 남겼다. <b>NAS용 9100(06:27 상주)은 실사용 세션 7개가 있어 미접촉</b>이라 게이트웨이 stderr 개선은 다음 재기동 때 반영된다.</td></tr>
|
||||
<tr><td>G8 실제 receipt-bound image rollback</td><td><code>scripts/launch-nas-preview-g8-helpers.py</code> / <code>scripts/serve-nas-preview-rollback-executor.py</code> / <code>scripts/probe-nas-preview-g8-rollback.py</code> / <a href="./ops/nas-preview-g8-rollback-proof-runbook.md">런북</a> / <a href="./ops/evidence/nas-preview-g8-actual-rollback-2026-08-07.json">기계 판독 증거</a></td><td>격리 NAS에서 별도 executor(18149)·control-plane(8018) helper로 실제 image rollback 2회를 실행했다. receipt <code>nas-g8-723eeef2…</code>로 previous <code>79ec…4450</code>/<code>c530…2f28</code>, receipt <code>nas-g8-2738846c…</code>로 current <code>52e0…8b2d</code>/<code>6fdb…f215</code>를 활성화했다. release gate·approval 각각 2회 멱등, <code>audit.ci_lifecycle_event</code> rollback/executed 2건과 receipt id 결속, silent auto-promotion 0, HMAC journal 6-record 체인 검증, health 3/3·OpenAPI 126·auth 401·Web 200, helper 0·listener 0 복귀. helper 격리 계약은 image 상속 label baseline 대비 검증이며 fake-runner 37/37로 고정했다. <code>down</code>/<code>volume rm</code>/<code>prune</code> 미실행, 공개 런타임 미접촉.</td></tr>
|
||||
|
|
@ -1140,8 +1160,9 @@
|
|||
<tr><td>Public OAuth start</td><td><code>/auth/config</code> + <code>/auth/login?provider=google</code></td><td>2026-06-30 복구 후 auth config 200, Google configured true, redirect URI <code>https://api-vignette.chanpaca.net/auth/callback</code>, dev-login disabled. Google login start는 GET 302와 <code>__Host-vignette_oauth_state</code> HttpOnly/Secure cookie를 반환한다. Authenticated public <code>/turn</code> proof는 owner storageState가 필요하다.</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</td><td><code>start/watch/boot-public-runtime*.ps1</code></td><td>PowerShell 5.1 parser 오류 0. 관리자·인증 제어면(<code>prod + db</code>)과 엔진 readiness를 분리하고, API 코드 배포는 <code>-ForceApiRestart -SkipEngineRestart -SkipWebRestart -SkipCloudflaredRestart</code>로 API만 교체한다. 2026-07-30 OAuth upsert hotfix 뒤 API PID <code>40992</code>, local/public <code>status=ok</code>, <code>environment=prod</code>, <code>db=true</code>, <code>engine=true</code>다.</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>2026-07-30 최종 local/public 확인에서 <code>environment=prod</code>, <code>status=ok</code>, <code>db=true</code>, <code>engine=true</code>, <code>engine_detail=OK</code>다. 관리자·인증 제어면과 엔진 readiness를 분리했으며, 엔진 단독 장애는 API 재시작으로 전파되지 않는다. OAuth config는 200/configured true다.</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>PowerShell 5.1 parser 오류 0. detached-clean stable source, Git commit/tree, watchdog|boot/start script SHA를 runtime 검사·mutation 전에 검증하며 hidden VBS는 구조적으로 pin된 task만 trigger한다. 회귀는 provenance 11개를 포함한 combined 22/22·WSH syntax PASS. <b>실제 task migration은 아직 미완료</b>: watchdog과 로그온 task action이 shared worktree/legacy 인자를 가리켜 최신 5분 watchdog은 mutation 전에 exit하고 <code>LastTaskResult=1</code>이다. 사용자 승인 clean commit 뒤 동일 stable release root로 두 task를 재등록하고 action pin·result 0·로그온/reboot smoke를 확인해야 한다.</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>2026-08-09 핸드오프 read-only 확인에서 local/public 모두 <code>status=ok</code>, <code>db=true</code>, <code>engine=true</code>다. DB container는 recovered named volume과 <code>unless-stopped</code>를 유지한다. 이는 source-pin task migration이나 G7 current-source 배포 완료 증거가 아니다.</td></tr>
|
||||
<tr><td>Public runtime 17:54 read-only snapshot</td><td><code>health + OpenAPI + voice + listeners + Scheduled Tasks</code></td><td>local/public health <code>status=ok·db=true·engine=true</code>, 공개 OpenAPI <code>119 paths</code>, <code>/admin/voice-runtime</code> 없음, <code>/voice/health</code> <code>openai/gpt-4o-transcribe</code> + <code>openai/gpt-4o-mini-tts</code>, local Whisper 9882·MeloTTS 9883 listener <code>0</code>. watchdog·로그온 task action은 여전히 legacy shared worktree이고 17:54 KST watchdog <code>LastTaskResult=1</code>이다. 이는 필수 pin 인자 부재로 mutation 전 fail-closed한 값이며 G7 current-source 배포 완료가 아니다.</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-06-30 public web login HEAD 200, public API health 200, auth config 200, Google login start 302. Earlier Tailnet checks remain recorded separately; vnet DNS A records are still 0.</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>
|
||||
|
|
@ -1181,7 +1202,7 @@ $env:E2E_PUBLIC_STORAGE_STATE=".\node_modules\.tmp\public-auth.json"
|
|||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\start-public-runtime.ps1</pre>
|
||||
</div>
|
||||
<div class="source-note"><b>닫은 항목(live 증거):</b> ① engine config 운영값(<code>claude_cli / 9099 / gateway-default</code>, durable, db). ② 상주 엔진풀 probe — 단일 session_id 2회 reused stream, TTFT 1667–4199ms, cost 누적 0.068→0.123. ③ 상주 엔진풀 RSS — gateway session 1개에서 <code>claude -p</code> child RSS 363.3MB, gateway RSS 16.2MB. ④ Postgres RLS/audit smoke 5 checks PASS. ⑤ turn cost telemetry — <code>app.turns</code> client_ai 13행에 provider/model/cost_usd 실적재(합 $1.22). ⑥ 레이아웃 시각 게이트 최신 9/9 + 2026-06-28 적대적 재검수 7/7 accept.</div>
|
||||
<div class="source-note"><b>환경 제약으로 이 세션에서 못 닫는 항목(정직 표기):</b> 공개 Google OAuth 실제 <code>/turn</code>(OAuth start는 2026-06-30 재확인 완료, 실제 계정 storageState 필요) · 실배포 <code>infra/.env</code> owner-secret fill-in(<code>APP_DB_PASSWORD</code>, <code>OAUTH_GOOGLE_CLIENT_ID</code>, <code>OAUTH_GOOGLE_CLIENT_SECRET</code>, <code>OPENAI_API_KEY</code>, <code>SESSION_SECRET</code>) · <code>vnet.18ka.net</code>/<code>api-vnet.18ka.net</code> 공개 런타임(DNS A record 0, Cloudflare 18ka.net zone 권한 불일치, Google OAuth redirect URI 등록 필요) · 한신대 데이터/SSO 거버넌스(owner/기관 written evidence 필요) · G7 실제 Deepgram 운영 key·quota live interim/final, 기대 provider/model authenticated public WSS ready, 명시 동의 물리 마이크와 50분 양방향 soak · claude_cli↔Messages API 폴백 동일성 · 실제 Windows 재부팅 후 watchdog smoke · Phase 3 파일럿 게이트. owner 결정 7건은 2026-06-30 전건 확정됨. 운영 원칙상 가짜 증거로 DONE 표기하지 않는다. 전체 추적: <code>docs/ops/backlog-2026-06-26.md</code>(B1 구현 후속 · B2 환경제약 · B3 소유자결정 · B4 거버넌스).</div>
|
||||
<div class="source-note"><b>환경 제약으로 이 세션에서 못 닫는 항목(정직 표기):</b> 공개 Google OAuth 실제 <code>/turn</code>(OAuth start는 2026-06-30 재확인 완료, 실제 계정 storageState 필요) · 실배포 <code>infra/.env</code> owner-secret fill-in(<code>APP_DB_PASSWORD</code>, <code>OAUTH_GOOGLE_CLIENT_ID</code>, <code>OAUTH_GOOGLE_CLIENT_SECRET</code>, <code>OPENAI_API_KEY</code>, <code>SESSION_SECRET</code>) · <code>vnet.18ka.net</code>/<code>api-vnet.18ka.net</code> 공개 런타임(DNS A record 0, Cloudflare 18ka.net zone 권한 불일치, Google OAuth redirect URI 등록 필요) · 한신대 데이터/SSO 거버넌스(owner/기관 written evidence 필요) · G7 current source 공개 배포, <code>local_whisper</code>/<code>melotts</code> provider/model ready 일치, authenticated public WSS, 명시 동의 물리 마이크와 50분 양방향 soak, 독립 human voice-gain pack · claude_cli↔Messages API 폴백 동일성 · 실제 Windows 재부팅 후 watchdog smoke · Phase 3 파일럿 게이트. owner 결정 7건은 2026-06-30 전건 확정됨. 운영 원칙상 가짜 증거로 DONE 표기하지 않는다. 전체 추적: <code>docs/ops/backlog-2026-06-26.md</code>(B1 구현 후속 · B2 환경제약 · B3 소유자결정 · B4 거버넌스).</div>
|
||||
</div>
|
||||
<div class="tab-panel" id="panel-decisions" role="tabpanel" aria-labelledby="tab-decisions" hidden>
|
||||
<div class="detail-head"><div><h3>윤찬 결정 상세 (전건 확정)</h3><p>람다가 단독으로 정하면 월권인 항목이었다. 2026-06-30 owner 결정으로 전건 확정됐고, 각 결정의 구현·실측은 후속 build/GATE로 남는다.</p></div><div class="mini-metrics"><span>결정 0개(전건 확정)</span><span>조건부 GO 사유</span></div></div>
|
||||
|
|
|
|||
|
|
@ -87,15 +87,28 @@ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\start-tailscale-runt
|
|||
공개 API 복구 스크립트는 `docs/ops/public-runtime-watchdog.md`가 runbook이다. 로컬 개발 서버와 별개로
|
||||
prod API 8001, web preview 5174, engine gateway 9099, cloudflared tunnel을 검사한다.
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\watch-public-runtime.ps1 -CheckOnly
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install-public-runtime-task.ps1 -RunNow
|
||||
```
|
||||
- shared branch worktree의 watchdog/start script를 직접 실행하지 않는다. 승인된 clean commit의 detached release
|
||||
worktree를 만들고, 그 root 내부 registrar에 `-StableSourceRoot`를 명시해 두 task를 승격한다.
|
||||
|
||||
- Scheduled Task는 현재 Windows 사용자 기준 `AtLogOn` + 반복 watchdog이다. 사용자 로그인 전 headless boot service가 아니다.
|
||||
- secret은 task 인자에 넣지 않는다. API secret은 `apps/api/.env`, cloudflared/Claude CLI credential은 사용자 profile에 둔다.
|
||||
$releaseRoot = 'D:\workspace\vignette-public-runtime-<commit>'
|
||||
$bootRegistrar = Join-Path $releaseRoot 'scripts\register-boot-task.ps1'
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $bootRegistrar -StableSourceRoot $releaseRoot
|
||||
$watchdogInstaller = Join-Path $releaseRoot 'scripts\install-public-runtime-task.ps1'
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $watchdogInstaller -StableSourceRoot $releaseRoot -IntervalMinutes 5
|
||||
|
||||
- `VignettePublicRuntime`은 로그온 Docker/DB 복구, `VignettePublicRuntimeWatchdog`은 로그온+5분 반복 health/recovery다.
|
||||
- 두 task action은 release root와 Git commit/tree, boot 또는 watchdog SHA-256, start script SHA-256을 고정한다.
|
||||
등록 뒤 action marker와 working directory를 읽어 검증하기 전에는 `RunNow`를 호출하지 않는다.
|
||||
- secret은 task 인자에 넣지 않는다. API secret은 stable release의 `apps/api/.env`, cloudflared/Claude CLI credential은
|
||||
사용자 profile에 둔다. `.env` 내용을 console이나 evidence에 출력하지 않는다.
|
||||
- 아직 DNS가 없는 future host는 기본 검사에 넣지 않는다. `api-vnet.18ka.net`처럼 실제로 열린 뒤에만 `-AdditionalPublicHealthUrls`로 명시 추가한다.
|
||||
- 완료 판정은 parser/check-only가 아니라 실제 재부팅 또는 로그오프/로그온 뒤 `Get-ScheduledTaskInfo`, watchdog 로그의 `restart verified`, public health, 인증된 public `/turn` smoke까지 한 세트로 남겨야 한다.
|
||||
- `watch-public-runtime-hidden.vbs`는 source를 직접 실행하지 않고 pin marker를 검증한 등록 task만 trigger한다.
|
||||
- 완료 판정은 parser/check-only가 아니라 실제 재부팅 또는 로그오프/로그온 뒤 `Get-ScheduledTaskInfo`, stable release의
|
||||
watchdog 로그, public health, process cwd/Git provenance, 인증된 public `/turn` smoke까지 한 세트로 남겨야 한다.
|
||||
- detached worktree 생성, `.env`/`node_modules` 준비, source pin 검증, read-only `-CheckOnly` 전체 명령은 runbook을 따른다.
|
||||
- G7 공개 증거 승격은 routine API-only 복구와 다르다. `-RequireFreshPublicProvenance` + `-ForceApiRestart`로
|
||||
API와 exact-config cloudflared를 둘 다 새 PID로 교체하고 commit/tree·Python/cloudflared/config SHA와 safe receipt를
|
||||
결속한다. 이 모드에서 `-SkipCloudflaredRestart`는 금지되며, 정확한 명령은 runbook의 G7 fresh 절을 따른다.
|
||||
|
||||
### 수동 기동(대안)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Vignette 저장소의 모든 검증 수단(백엔드 단위 테스트, 웹 타
|
|||
| API 타입 생성 체크 | `apps/web` | `npm run check:api-types` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
|
||||
| 웹 타입체크 | `apps/web` | `npm run typecheck` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
|
||||
| 웹 빌드 | `apps/web` | `npm run build` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
|
||||
| Playwright E2E(전체) | `apps/web` | `npm run e2e` | **필요(+시드)** | **필요** | 자동기동 | 일부만 | **필요** | 현재 수집 610 tests / 44 files · 현 작업트리 전체 GREEN 미검증 |
|
||||
| Playwright E2E(전체) | `apps/web` | `npm run e2e` | **필요(+시드)** | **필요** | 자동기동 | 일부만 | **필요** | 현재 수집 614 tests / 44 files · 현 작업트리 전체 GREEN 미검증 |
|
||||
|
||||
핵심 원칙: **단위 테스트(pytest)와 타입체크/빌드는 외부 서비스 없이 단독 실행된다.**
|
||||
**E2E만 풀스택(DB+API+웹+브라우저)을 요구한다.** 아래 각 절에서 근거와 절차를 설명한다.
|
||||
|
|
@ -271,7 +271,7 @@ VITE_API_BASE=http://127.0.0.1:8000 npm run e2e # 프록시 대신 API
|
|||
|
||||
### 3.6 실측 테스트 개수 (현재)
|
||||
|
||||
2026-08-07 `npx playwright test --list` 기준 **현재 수집 610 tests / 44 files**다.
|
||||
2026-08-09 `npx playwright test --list` 기준 **현재 수집 614 tests / 44 files**다.
|
||||
이 숫자는 수집량이지 통과량이 아니다. 현 작업트리 전체 604개 완주는 아직 증거가 없으며,
|
||||
과거 전체 GREEN 기록과 이번 focused/release gate 결과를 구분해 적는다.
|
||||
|
||||
|
|
@ -401,11 +401,11 @@ VITE_API_BASE=http://127.0.0.1:8000 npm run e2e # 프록시 대신 API
|
|||
```powershell
|
||||
C:\Users\encep\AppData\Local\Programs\Python\Python311\python.exe -X utf8 `
|
||||
scripts\soak-public-voice-websocket.py --preflight-only `
|
||||
--expected-stt-provider deepgram --expected-stt-model nova-3 `
|
||||
--expected-tts-provider <승인된-provider> --expected-tts-model <승인된-model>
|
||||
--expected-stt-provider local_whisper --expected-stt-model small `
|
||||
--expected-tts-provider melotts --expected-tts-model melotts-korean
|
||||
```
|
||||
|
||||
50분 full soak는 같은 기대값에 `--duration-seconds 3000`, `--microphone-device <명시-장치>`,
|
||||
50분 유효 공통창 full soak는 같은 기대값에 `--duration-seconds 3120`, `--microphone-device <명시-장치>`,
|
||||
`--confirm-physical-capture`를 추가하고 사용자가 실행 직전에 물리 캡처에 명시 동의한 경우에만 실행한다.
|
||||
- 2026-08-07 G7 external-proof readiness: Session 첫 음성 사용은 서버 30일·원음 미보존 consent ledger가
|
||||
성공하기 전 `getUserMedia`와 voice WebSocket을 시작하지 않는다. controlled provider와 synthetic stream을
|
||||
|
|
@ -414,9 +414,15 @@ VITE_API_BASE=http://127.0.0.1:8000 npm run e2e # 프록시 대신 API
|
|||
runner/checker와 release gate contract **31 passed**, typecheck·API type
|
||||
contract·Ruff·py_compile을 통과했다. `soak-public-voice-websocket.py` v4, `/admin/voice-runtime`,
|
||||
`capture-g7-runtime-evidence.py`, `capture-g7-topology-evidence.py`, independent human-held-out evaluator를
|
||||
`check-g7-external-proof.py`가 같은 public host·겹치는 50분 시간창으로 묶는다. 실제 마이크·운영 Deepgram·
|
||||
실제 참가자 evidence는 없으므로 G7은 BUILD다. 상세는
|
||||
`check-g7-external-proof.py`가 같은 public transport host·겹치는 50분 시간창으로 묶는다. 실제 마이크·운영
|
||||
local provider·실제 참가자 evidence는 없으므로 G7은 external GATE다. 상세는
|
||||
`ops/outcome-os-g7-external-proof-readiness-2026-08-07.md`가 소유한다.
|
||||
- 2026-08-09 G7 external-proof P0: production runner exit를 canonical checker 0·`gate_closed=true`에 묶고,
|
||||
browser Origin allowlist와 API/WSS/admin/topology host·scheme을 분리했다. capture 최소 3,120초와 세 runtime
|
||||
artifact 공통 3,000초, detached-clean commit/tree·runner/collector/checker SHA·`psutil==6.1.1`, fresh
|
||||
API/cloudflared PID provenance receipt를 fail-closed로 고정했다. runner/checker/topology 86/86,
|
||||
launcher/sidecar 80/80, G7 통합 166/166, API 921, gateway 58을 통과했다. 이 수치는 실제 물리 마이크·human pack을
|
||||
대체하지 않으므로 G7은 external GATE다.
|
||||
- 2026-08-07 G7/G8 시각 QA: 데스크톱·Pixel 5에서 focused **16/16**, typecheck·build·cosmetic-filter를
|
||||
통과했다. G8 승인 차단 이유를 `title` 의존에서 상시 문구·`aria-describedby`·semantic form/Enter 제출로
|
||||
바꾸고, G7 이벤트 최소 24×24px와 시간축 키보드 포커스를 수치 회귀로 고정했다. 상세는
|
||||
|
|
|
|||
|
|
@ -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