vignette/docs/ops/outcome-os-g7-external-proof-readiness-2026-08-07.md
Yun Chan f3491be63f G7 외부 증거 3-artifact 동시 시간창 오케스트레이터
soak / runtime / topology 세 캡처는 같은 public host 의 겹치는 시간창이어야 하는데,
지금까지는 운영자가 세 명령을 따로 띄우고 시계를 손으로 맞춰야 했다. 50분짜리
실행에서 한 번 어긋나면 처음부터 다시 해야 하는 취약점이라 한 명령으로 묶었다.
세 캡처를 동시에 시작하고 전부 끝나면 human pack 을 더해 checker 까지 잇는다.

게이트를 약화시키지 않았다. CLI 로 실증한 fail-closed 경계 4종:
- 동의/장치 없이 운영 실행     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

--rehearse 는 마이크를 열지 않고(soak 을 --preflight-only 로) 배관만 확인하며
보고서의 gate_closed 는 항상 false 다. 실제 실행 전 실패를 먼저 뽑기 위한 모드다.

기대 provider 기본값은 2026-08-08 결정에 맞춰 local_whisper / melotts 다.

이것으로 G7 에서 기계로 할 수 있는 부분은 끝났다. 남은 것은 코드로 만들 수 없는
둘뿐이다. 명시 동의 하 물리 마이크 50분 발화, 그리고 참가자 30명·독립 평가자 2인의
blind human voice-gain pack.

검증: 오케스트레이터 20/20, SSOT FAIL 0, dashboard E2E 10/10, ruff clean.
2026-08-08 09:40:08 +09:00

99 lines
6.7 KiB
Markdown

# G7 external proof readiness — 2026-08-07
## 판정
G7은 계속 **BUILD**다. 이번 변경은 물리 마이크나 운영 Deepgram을 사용한 실증이 아니라,
외부 증거가 들어왔을 때 synthetic·preflight·짧은 probe로 잘못 닫히지 않도록 종료 게이트를
실행 가능한 네 artifact 계약으로 만든 것이다.
## 이번에 닫은 내부 범위
- Session의 첫 음성 사용은 인앱 설명과 명시 동의를 요구한다. 서버의 30일·원음 미보존
동의 원장 기록이 성공하기 전에는 `getUserMedia``/voice/ws`를 시작하지 않는다.
거절·원장 실패·지연 권한 응답·일시정지·회기 전환은 텍스트 대안을 유지하고, 뒤늦게
도착한 MediaStream의 track을 즉시 종료한다. 숨은 Space 단축키는 제거하고 안내 가능한
`Alt+M`만 남겼다.
- public WSS runner v4는 매 turn마다 interim 1개 이상과 speech-final 정확히 1개를 요구하고,
첫 interim·speech-final latency와 전체 합계를 metadata-only evidence에 기록한다. 장치 열거와
캡처는 `--confirm-physical-capture` 없이는 시작하지 않는다.
- `/admin/voice-runtime`은 단일 API worker의 process RSS/peak RSS/CPU/thread/FD와 WebSocket,
provider session, route audio buffer, streaming event queue, overflow/fallback/error high-water를
인증된 관리자에게만 반환한다. 원음·축어록·session ID·provider payload는 저장하지 않는다.
production Docker Uvicorn WebSocket ingress queue는 `--ws-max-queue 4`로 고정했다.
- 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로 검증한다.
- 독립 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,
participant-cluster bootstrap 10,000회의 95% CI lower `> 0`을 재계산한다.
- `scripts/check-g7-external-proof.py`는 같은 public host와 겹치는 시간창의 public soak,
process-local runtime sampling, pinned topology sampling, independent human-gain pack 네 가지를
모두 통과시켜야 성공한다. G7 release gate는 이 checker 증거가 있을 때만
`satisfied-by-validated-g7-proof`로 바뀔 수 있다.
## 검증
- API voice/G7 focused: `92 passed`.
- external evidence runners/checkers와 release gate contract: `31 passed`; Ruff와 `py_compile` 통과.
- Web typecheck와 API type contract 통과.
- `voice-success.spec.ts` chromium single-run `2/2` 통과. 동의 원장 응답을 의도적으로 지연한
동안 `getUserMedia=0`, voice WebSocket `0`을 확인했고, 성공 뒤 각각 정확히 1회였다.
- 모든 브라우저 음성 테스트는 synthetic MediaStream과 controlled provider만 사용했다.
실제 장치 열거·캡처는 수행하지 않았다.
- 내부 hardening release SHA `a8c27b0aadcb9dacea86c4be5e62e19277b9cc2678ebca51aa02998d2c2d1a0d`
격리 NAS에 승격했다. 첫 clean-candidate 실행은 오래된 동의 fixture를 23/24에서 차단하고 원격을
변경하지 않았으며, 실제 동의 순서로 고친 뒤 24/24·OpenAPI 124·auth 401·G0~G8·NAS browser
SSE→DB review를 통과했다. 이 증거에서도 물리 마이크는 사용하지 않았다.
## 실제 종료 때 필요한 네 artifact
1. `soak-public-voice-websocket.py` v4의 운영 Deepgram/OpenAI TTS·authenticated public WSS·
명시 동의 물리 마이크 50분 양방향 `passed` evidence.
2. 같은 시간창에 `capture-g7-runtime-evidence.py`로 얻은 관리자 endpoint worker/Uvicorn queue
high-water evidence.
3. 같은 public host·exact images에 `capture-g7-topology-evidence.py`로 얻은 50분
cgroup/proc/Docker/TCP high-water evidence. Cloudflare 내부 큐는 직접 관측 가능하다고 주장하지
않고, public runner의 edge RTT/gap/disconnect/TLS/CF-Ray 증거를 별도 경계로 사용한다.
4. 실제 동의 참가자와 독립 blind labeler로 만든 human held-out voice-gain evidence pack.
네 파일을 `check-g7-external-proof.py`에 넣어 exit 0과 artifact SHA-256을 얻기 전에는 G7을
DONE으로 표시하지 않는다.
## 실행 오케스트레이터 (2026-08-08 추가)
앞의 1~3번은 **같은 public host의 겹치는 시간창**이어야 하는데, 지금까지는 운영자가 세 명령을 따로
띄우고 시계를 손으로 맞춰야 했다. 50분짜리 실행에서 한 번 어긋나면 처음부터 다시 해야 한다.
`scripts/run-g7-external-proof-window.py`가 세 캡처를 **동시에** 시작하고, 전부 끝나면 human pack을
더해 checker까지 그대로 돌린다.
```powershell
# 실제 실행 (물리 마이크 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 `
--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:... `
--microphone-device "<장치명>" --confirm-physical-capture `
--human-voice-gain <pack.json> --out-dir <증거디렉터리>
```
게이트를 **약화시키지 않는다.** 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 |
`--rehearse`는 마이크·사람 없이 **배관만** 확인하는 모드다. soak을 `--preflight-only`로 돌려 장치를
열지 않고, 인자·경로·산출 파일까지 실제로 검증한다. 보고서의 `gate_closed`는 rehearse에서 **항상
false**다. 실제 50분 실행 전에 이걸로 먼저 실패를 뽑아내라.
기대 provider 기본값은 2026-08-08 결정에 맞춰 `local_whisper` / `melotts`
(근거: `../decisions/local-voice-stack.md`). 회귀는 `scripts/test_run_g7_external_proof_window.py` 20/20.