주기 실회기 검증과 G7 종료계약 보강

This commit is contained in:
Yun Chan 2026-08-09 23:37:19 +09:00
parent 83590e9ef7
commit 7b4955c3fc
23 changed files with 2916 additions and 117 deletions

View file

@ -28,9 +28,11 @@ G7은 **내부 구현 DONE · external GATE**다. 이번 변경은 물리 마이
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,
participant-cluster bootstrap 10,000회의 95% CI lower `> 0`을 재계산한다.
held-out 공개 전에 끝난 사전등록·동의·participant split·model artifact·blind independent labeler provenance를 요구한다.
production gate는 최소 31명(held-out 30명 외 calibration split 포함), 양 조건이 모두 관측된 완전 paired
50회기/150축, ICC(A,1) 0.75, **필수 categorical κ 0.70**, gain 0.01,
participant-cluster bootstrap 10,000회의 95% CI lower `> 0`을 재계산한다. 한 조건이라도 결측이면 양 조건을
모두 최대오류로 처리해 baseline-only 결측이 candidate gain을 부풀리지 못하게 한다.
- `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 증거가 있을 때만
@ -39,8 +41,8 @@ G7은 **내부 구현 DONE · external GATE**다. 이번 변경은 물리 마이
## 검증
- 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` 통과.
- current G7 scripts `86/86`, API human evaluator `11/11`;
Ruff·`py_compile`·`git diff --check` 통과.
- Web typecheck와 API type contract 통과.
- `voice-success.spec.ts` chromium single-run `2/2` 통과. 동의 원장 응답을 의도적으로 지연한
동안 `getUserMedia=0`, voice WebSocket `0`을 확인했고, 성공 뒤 각각 정확히 1회였다.
@ -70,8 +72,17 @@ DONE으로 표시하지 않는다.
앞의 1~3번은 **같은 public transport host의 공통 3,000초 시간창**이어야 하는데, 지금까지는 운영자가 세 명령을 따로
띄우고 시계를 손으로 맞춰야 했다. 50분짜리 실행에서 한 번 어긋나면 처음부터 다시 해야 한다.
`scripts/run-g7-external-proof-window.py`가 세 캡처를 **동시에** 시작하고, 전부 끝나면 human pack을
더해 checker까지 그대로 돌린다.
`scripts/run-g7-external-proof-window.py`는 production human pack을 **마이크를 열기 전에 먼저 완전 검증**하고,
통과한 경우에만 세 캡처를 동시에 시작한다. 종료 뒤 canonical checker가 같은 pack과 세 runtime artifact를 다시 검증한다.
runtime/topology interval 기본값은 child collector 상한과 같은 60초이며, 60초 초과는 시작 전에 차단한다.
사람 데이터 운영자는 52분 실행과 독립적으로 pack을 먼저 검증할 수 있다. `--print-schema`는 authoritative JSON Schema를
출력하고, 오류는 JSON pointer와 유형만 반환해 participant/labeler key·라벨 값·PII를 반사하지 않는다.
```powershell
& $py -X utf8 -B scripts/check-g7-human-voice-gain.py --print-schema
& $py -X utf8 -B scripts/check-g7-human-voice-gain.py --input <pack.json>
```
```powershell
# 실제 실행 (물리 마이크 50분 + 사람 pack 필요)
@ -98,7 +109,9 @@ DONE으로 표시하지 않는다.
|---|---|
| 동의·장치 없이 운영 실행 | `physical_microphone_consent_required`, exit 2 |
| human pack 없이 운영 실행 | `human_voice_gain_pack_required`, exit 2 |
| malformed·underpowered·κ 미달·불완전 paired human pack | 물리 캡처 전에 human-pack 오류, exit 2 |
| 3,120초 미만 시간창 | `production_window_too_short`, exit 2 |
| runtime/topology interval 60초 초과 | child collector 실행 전 interval 오류, exit 2 |
| transport host/scheme 불일치 | `hosts_must_match` 또는 scheme 오류, exit 2 |
| 허용되지 않은 browser Origin | query/원문을 반사하지 않는 Origin 오류, exit 2 |
@ -106,33 +119,28 @@ DONE으로 표시하지 않는다.
열지 않고, 인자·경로·산출 파일까지 실제로 검증한다. 보고서의 `gate_closed`는 rehearse에서 **항상
false**다. 실제 50분 실행 전에 이걸로 먼저 실패를 뽑아내라.
### ⚠ 선행 조건 — 공개 런타임을 먼저 올려야 한다 (2026-08-08 발견)
### 공개 선행 조건 — 2026-08-09 완료
**마이크와 사람을 다 준비해도 오늘 실행하면 50분을 버리고 artifact 2에서 실패한다.**
공개 API/cloudflared는 detached-clean `b34623f3…`·tree `32cc85c7…`에서 fresh 승격됐다. 공개 OpenAPI 126,
`/admin/voice-runtime`, `local_whisper/small`, `melotts/melotts-korean`, Uvicorn WS queue 4와 receipt
`9f8d1941…a21bf` passed를 확인했다. watchdog·로그온 task도 같은 stable root에 source pin돼 명시 실행 결과 0이다.
공개 런타임은 **119 paths 구버전**이고 `/admin/voice-runtime`이 **배포돼 있지 않다**.
`capture-g7-runtime-evidence.py`는 정확히 그 경로만 부르므로 artifact 2를 만들 수 없다.
현재 소스에는 있다(`apps/api/app/routes/admin.py:1699`, NAS 프리뷰 기준 126 paths).
```
공개 OpenAPI 119 paths · voice 경로 = /users/me/voice-presets, /voice/health, /voice/speech
has /admin/voice-runtime → False
```
그래서 오케스트레이터는 시작 전에 이걸 검사하고 `admin_voice_runtime_not_deployed`로 즉시 멈춘다.
실측: 공개 런타임 대상 `--rehearse` 실행이 exit 2로 몇 초 만에 차단됐다.
30초 `--rehearse`는 authenticated WSS ready/ping/close1000, runtime 7 samples, Windows topology 7 samples를
모두 통과했다. evidence는 `physical_capture=false`이며 UUID/email literal 0이다. 이 리허설은 실제 물리 마이크와
human pack을 대체하지 않는다.
**판정을 미인증 HTTP 상태로 하지 않는다.** Cloudflare 앞단이 자동화 클라이언트에게 존재하지 않는
경로까지 포함해 **모든 경로를 403(error code 1010)** 으로 돌려주기 때문에, 상태 코드로는 "배포 누락"과
"edge 차단"을 구분할 수 없다. 처음엔 404/401 판정으로 짰다가 이 사실을 확인하고 **OpenAPI spec의
`paths`만 authoritative하게 쓰도록** 고쳤다. 회귀에 그 이유를 테스트로 남겼다.
따라서 G7 실행 순서는 이렇다.
따라서 남은 G7 실행 순서는 이렇다.
1. current source를 공개 런타임에 승격한다(비-secure origin `crypto.randomUUID` 수정도 여기 포함된다).
2. `--rehearse`로 배관을 확인한다.
3. 동의 하 물리 마이크 50분 + human pack으로 실제 실행한다.
1. 독립 연구팀이 실제 human pack을 작성하고 standalone validator를 통과시킨다.
2. 사용자가 정확한 마이크 장치를 고르고 3,120초 물리 캡처에 명시 동의한다.
3. production runner로 human pack을 재검증한 뒤 mic/runtime/topology 동시 창을 실행한다.
4. canonical checker exit 0과 `gate_closed=true`를 확인한다.
기대 provider 기본값은 2026-08-08 결정에 맞춰 `local_whisper/small` / `melotts/melotts-korean`이다
(근거: `../decisions/local-voice-stack.md`). runner 회귀는 37/37, G7 통합은 166/166이다. production exit 0은
(근거: `../decisions/local-voice-stack.md`). current G7 scripts는 86/86, API human evaluator는 11/11이다. production exit 0은
`checker_returncode == 0 && gate_closed is true`에 결속되며 rehearse는 성공해도 gate를 닫지 않는다.