주기 실회기 검증과 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

@ -453,6 +453,10 @@ G7 내부 소스 계약은 완료됐고 `scripts/check-g7-external-proof.py`는
`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`로 유지한다.
- human voice-gain pack은 category와 categorical κ를 필수로 포함하고, preregistration이 held-out 공개보다 앞서야 하며,
양 조건이 모두 관측된 50회기/150축만 paired 표본으로 집계한다. 한쪽 결측이면 양쪽 모두 최대오류 ITT로 처리한다.
`scripts/check-g7-human-voice-gain.py --input <pack.json>`을 먼저 통과하지 못하면 production runner는 마이크를
열기 전에 exit 2로 끝난다. 스키마는 `--print-schema`로 출력한다. 관련 API+script 계약은 97/97 통과했다.
- 무마이크 rehearsal 산출물은
`D:\workspace\vignette-runtime-evidence\g7-rehearsal-b34623f3db05-20260809T134105Z`에 있다.
voice `61af2e98…009c`, runtime `c6e8670e…4454`, topology `89f1cb86…a251`이며 세 leg 모두 passed,
@ -673,9 +677,14 @@ 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는 626 tests / 44 files다. 실행 환경/API/DB를 정확히 맞추지 않고 fixture failure를
전체 Playwright inventory는 627 tests / 45 files다. 실행 환경/API/DB를 정확히 맞추지 않고 fixture failure를
제품 failure로 오인하지 않는다. 같은 blocker가 두 번 반복되면 전체 재시도 대신 원인·증거·수정 계획을 먼저 보고한다.
주기 실회기 검증은 `scripts/run-periodic-learner-e2e.py`가 소유한다. NAS 112건 중 실 API/DB는 22건이고
route fixture는 90건이므로 전체 숫자를 실제 회기 폐루프로 부르면 안 된다. 새 runner는 clean HEAD/tree에서만
전용 engine/DB/API/Web을 만들고 같은 학습자의 SSE→review→G4/G5 0→1→1과 returned-practice desktop/mobile 4건을
검증한 뒤 sentinel resource·PID·listener·temp 잔여 0을 영수증으로 남긴다. 공개 8001/55432/9099와 NAS는 금지다.
## 9. 핵심 변경 파일
- `scripts/launch-nas-preview-g8-helpers.py` · `scripts/test_launch_nas_preview_g8_helpers.py` (신규, Gate6 계약)

View file

@ -110,6 +110,9 @@
(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.
runner는 이 pack의 category·κ·preregistration·paired completeness·결측 ITT를 3,120초 캡처 전에 검증하며,
부적합하면 마이크를 열지 않고 exit 2로 끝낸다. 독립 검수는
`python -X utf8 -B scripts/check-g7-human-voice-gain.py --input <pack.json>`이고 `--print-schema`로 계약을 확인한다.
**공개 선행 조건 완료(2026-08-09):** detached-clean `b34623f3…`·tree `32cc85c7…`에서 OpenAPI 126,
`/admin/voice-runtime`, exact local voice provider/model과 queue 4를 제공한다. fresh launcher receipt
`9f8d1941…a21bf`가 passed이고 두 Scheduled Task도 동일 source에 pin돼 result 0이다. Cloudflare의 Python
@ -120,7 +123,7 @@
`≥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
87/87과 runtime sampler 4/4를 통과했다. `--rehearse`는 인증 WSS ready/ping/close 1000,
human pack 계약 97/97과 기존 runtime sampler 4/4를 통과했다. `--rehearse`는 인증 WSS ready/ping/close 1000,
runtime 7 samples, Windows topology 7 samples를 모두 통과했고 마이크 capture false·UUID/email literal 0이다.
fail-closed 경계(동의 없음·pack 없음·3,120초 미만·host/Origin/scheme 불일치)는 CLI로 실증했다. 상세:
`ops/outcome-os-g7-external-proof-readiness-2026-08-07.md`.
@ -136,7 +139,9 @@
> dump/restore를 수행하지 않으므로 매 execute 직전 fresh custom dump SHA/size/TOC와 DB identity를 별도 결속한다.
> 자동 rollback은 image/Compose/active-state만 복구하며 적용 migration/data restore는 별도 owner 승인이 필요하다.
> 과거 `6030a677…c611`의 localhost 108/108·평문 origin UUID 24건 실패와 후속 verified rollback은 이력으로 보존한다.
> 매일 04:30 KST `vignette-e2e` 자동화는 ACTIVE다.
> 매시간 `vignette-e2e` heartbeat 자동화는 ACTIVE다. 공개/NAS 화면과 health는 읽기 전용으로 보여주고,
> 소스 material milestone에서만 전용 disposable 회기 E2E를 실행한다. runner의 clean HEAD/tree·로컬 npipe Docker·
> loopback 동적 포트·sentinel·exact cleanup 계약과 unit 10/10은 완료됐고, 첫 실제 GREEN receipt는 clean commit 뒤 남았다.
> 증거: [배포 증거](./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 응답·계량·오류 표면화를 비교하는 것이다.

View file

@ -272,7 +272,14 @@
.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 (max-width:460px){
.kpis{grid-template-columns:1fr;}
h1{font-size:22px;}
.stamp{display:none;}
.dg-note{overflow-wrap:anywhere;}
.ocols{grid-template-columns:minmax(0,1fr);}
.ocol,.ostack,.ocard{min-width:0;max-width:100%;width:auto;}
}
@media (prefers-reduced-motion:reduce){ *{transition:none!important;} }
</style>
</head>
@ -658,6 +665,7 @@
<p class="dg-note">190차 적용(2026-08-09): exact include 52개를 clean commit <code>5221f79e…1c69</code>으로 고정하고 tree <code>e4f15001…308b</code>, 2회 동일 archive <code>1109bf86…0f4b</code>를 결속했다. fresh NAS dump <code>6f4b95a7…b529f</code>(1,015,222 bytes·TOC 1,752/TABLE DATA 129) 뒤 execute는 API 921·types·typecheck·build·insecure-context 6/6·candidate DB 회기 후반까지 통과했지만 <b>110/112</b>에서 승격 전에 fail-closed했다. 실패 2건은 Vite entry <code>/src/main.tsx</code>만 차단하던 boot 진단 테스트가 production hashed entry <code>/assets/index-*.js</code>를 차단하지 못한 환경 계약 누락이었다. 두 entry를 함께 차단하도록 고쳐 Vite source 2/2와 production preview 2/2를 통과했다. NAS mutation·rollback·DB migration은 0이며, 테스트 수정의 새 clean commit을 재결속한 한 번의 execute가 남았다.</p>
<p class="dg-note">191차 적용(2026-08-09): 후속 clean commit <code>21461ab3…6fde</code>·tree <code>5c5f12a8…524e</code>·archive <code>20d49694…fa1a</code>는 candidate <b>112/112</b>를 통과하고 NAS 새 API/Web <code>d5021950…</code>/<code>376a3aa8…</code>를 올렸다. 실제 NAS-origin postdeploy G4 desktop/mobile 2건이 110/112에서 실패하자 active-state commit 전에 이전 <code>52e0e816…</code>/<code>6fdbb646…</code>로 자동 rollback했고 health·auth 401·OpenAPI 126·G0~G8 route·image ID를 재검증해 rollback <b>verified</b>로 종료했다. exact-image 평문 origin에서 문장 fingerprint의 <code>crypto.subtle.digest</code> 의존이 요청 전 예외를 내는 원인을 확인했다. 원문 외부 전송 없이 portable SHA-256 fallback을 추가하고 표준 digest를 payload에 exact 고정해 secure localhost 2/2와 Tailnet insecure origin 2/2를 통과했다. 새 clean commit의 전체 candidate+NAS-origin 0 failure 전까지 G8은 runtime REVALIDATION이다.</p>
<p class="dg-note">192차 적용(2026-08-09): G8 clean-head 최종 승격을 완료했다. source HEAD <code>61a41d1f…6af</code>·tree <code>87dec55d…3b77</code>에서 2회 동일하게 만든 archive <code>4d15d055d74f…119d4d</code>를 active release로 고정했고 candidate session E2E <b>112/112</b>와 실제 <code>http://100.116.83.60:8088</code> 평문 NAS-origin browser review <b>112/112</b>(11 specs, desktop/mobile/single-run)를 모두 통과했다. 실행 이미지는 API <code>d5021950…e4b1</code>·Web <code>9796c092…4b36</code>이며 health는 <code>status=ok·db=true·engine=true</code>, 비인증 auth 401, OpenAPI 126을 재확인했다. execute 직전 fresh custom dump <code>36ec8748…24db8</code>(1,097,100 bytes, TOC 1,738 / TABLE DATA 129)를 보존했고 이전 active <code>6030a677…c611</code>과 이전 이미지는 rollback 기준으로 남겼다. 앞선 fail-closed·verified rollback 이력은 삭제하지 않는다. 이 증거로 G8을 <b>DONE</b>으로 승격하며 남은 Outcome OS gate는 G7 external proof다.</p>
<p class="dg-note">193차 적용(2026-08-09): 학생 화면의 320×568 신뢰성 게이트를 닫았다. 처방 재연습 prestart CTA를 첫 화면에 유지하고, active 회기의 마이크·일시정지·종료를 모든 밀도 구간에서 최소 44px로 고정했으며, 대시보드의 긴 privacy token과 owner grid가 320px 문서를 384px까지 넓히던 overflow를 제거했다. typecheck, session-layout desktop/mobile <b>8/8</b>, dev-dashboard desktop/mobile <b>10/10</b>, 자기주도 focused <b>16/16</b>을 통과했다. 동시에 NAS 112건을 실 API/DB 22와 route fixture 90으로 정직하게 분리하고, clean HEAD/tree·전용 disposable stack·동일 학습자 SSE→review→G4/G5 0→1→1·exact cleanup을 강제하는 주기 runner를 추가했다. unit <b>10/10</b>과 Compose preflight는 통과했으며 첫 실제 GREEN receipt는 이 변경의 clean commit 뒤 실행한다. G7 human pack은 category·κ≥0.70·preregistration·paired completeness·결측 ITT를 캡처 전에 검증해 부적합 pack이면 마이크를 열지 않는다.</p>
<div class="dg-principles" aria-label="디자인 생성 가드레일">
<div><b>래스터만 사용</b><span>이미지 생성 도구 산출물은 PNG 기반 시안이다. SVG·벡터·와이어프레임·로고 시트로 해석하지 않는다.</span></div>
<div><b>기능 우선</b><span>메인 라우트의 실제 액션과 정보 구조를 먼저 반영한다. 장식은 기능을 가리지 않는 수준에서만 쓴다.</span></div>
@ -912,7 +920,7 @@
</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">clean 공개 runtime·인증 WSS 무마이크 rehearsal까지 닫았고, 동의 마이크·사람 평가 증거를 기다린다.</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를 통과했다. 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와 결속한다. 공개 API/cloudflared는 clean <code>b34623f3…</code>·tree <code>32cc85c7…</code>로 fresh 승격돼 OpenAPI 126·<code>/admin/voice-runtime</code>·<code>local_whisper/small</code>·<code>melotts/melotts-korean</code>·WS queue 4를 제공한다. receipt <code>9f8d1941…a21bf</code>가 passed이고 두 source-pinned task도 result 0이다. current six-suite 87/87·runtime sampler 4/4를 통과했다.</p></div><div class="kv k-warn"><b>외부 종료 GATE</b><p>30초 무마이크 rehearsal은 authenticated public WSS ready/ping/close1000, runtime 7 samples, Windows topology 7 samples를 모두 통과했고 <code>physical_capture=false</code>·UUID/email literal 0이다. 남은 것은 사용자가 선택한 장치와 실행 직전 명시 동의를 받은 물리 마이크 3,120초 양방향 soak, 같은 public host·공통 3,000초 시간창의 worker/Uvicorn queue와 process/TCP high-water, 최소 31명·held-out 50회기·150축·blind evaluator 2인의 독립 human voice-gain pack이다. 네 artifact가 <code>scripts/check-g7-external-proof.py</code> exit 0을 만들기 전에는 메인 상태를 DONE으로 바꾸지 않는다.</p></div></div>
<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를 통과했다. 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와 결속한다. 공개 API/cloudflared는 clean <code>b34623f3…</code>·tree <code>32cc85c7…</code>로 fresh 승격돼 OpenAPI 126·<code>/admin/voice-runtime</code>·<code>local_whisper/small</code>·<code>melotts/melotts-korean</code>·WS queue 4를 제공한다. receipt <code>9f8d1941…a21bf</code>가 passed이고 두 source-pinned task도 result 0이다. human pack은 category·κ·preregistration·paired completeness·결측 ITT를 캡처 전에 검증하며 관련 계약 97/97·runtime sampler 4/4를 통과했다.</p></div><div class="kv k-warn"><b>외부 종료 GATE</b><p>30초 무마이크 rehearsal은 authenticated public WSS ready/ping/close1000, runtime 7 samples, Windows topology 7 samples를 모두 통과했고 <code>physical_capture=false</code>·UUID/email literal 0이다. 남은 것은 사용자가 선택한 장치와 실행 직전 명시 동의를 받은 물리 마이크 3,120초 양방향 soak, 같은 public host·공통 3,000초 시간창의 worker/Uvicorn queue와 process/TCP high-water, 최소 31명·held-out 50회기·150축·blind evaluator 2인의 독립 human voice-gain pack이다. pack은 <code>check-g7-human-voice-gain.py</code>를 먼저 통과해야 하며 부적합하면 마이크를 열기 전에 exit 2다. 네 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 · CLEAN-HEAD NAS VERIFIED</span><span class="scard-mid"><span class="scard-title">Autonomous Content &amp; Continuous Improvement</span><span class="scard-sum">agentic worker·human gate·실제 rollback과 current clean source의 NAS-origin 112/112를 모두 닫았다.</span></span><span class="caret" aria-hidden="true"></span></button>
@ -1068,7 +1076,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-09 현재 수집은 <b>626 tests / 44 files</b>다. 이 숫자는 수집량이며 현 작업트리 전체 GREEN과 동일하지 않다. G8 clean-head release gate는 candidate 112/112와 실제 NAS-origin 112/112를 통과했다. 이전 단일 120/120과 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>627 tests / 45 files</b>다. 이 숫자는 수집량이며 현 작업트리 전체 GREEN과 동일하지 않다. G8 clean-head release gate는 candidate 112/112와 실제 NAS-origin 112/112를 통과했다. 이전 단일 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 &amp; 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>
@ -1145,7 +1153,7 @@
<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 clean source HEAD <code>61a41d1f…6af</code>·tree <code>87dec55d…3b77</code>의 archive <code>4d15d055…119d4d</code>를 전용 Compose 프로젝트·포트·네트워크·named volume에 승격했다. candidate session E2E 112/112와 실제 NAS 평문 origin browser review 112/112, health ok·db/engine true, OpenAPI 126·auth 401을 통과했고 fresh dump와 이전 active/images를 보존했다. 자동화는 ACTIVE이며 material milestone+release gate·NAS preflight+배포 SHA 변경 때만 프리뷰를 갱신한다. G7 외부 mic/provider/human 증거는 별도다.</td></tr>
<tr><td>격리 NAS 프리뷰 · 회기 E2E 자동화</td><td><code>http://100.116.83.60:8088</code> / <code>vignette-e2e</code>시간 heartbeat / <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 clean source HEAD <code>61a41d1f…6af</code>·tree <code>87dec55d…3b77</code>의 archive <code>4d15d055…119d4d</code>를 전용 Compose 프로젝트·포트·네트워크·named volume에 승격했다. candidate 112/112와 실제 NAS-origin 112/112는 통과했지만 영수증은 실 API/DB 22건과 route fixture 90건을 분리한다. 자동화는 공개/NAS 화면과 health를 읽기 전용으로 매시간 보여준다. material milestone의 동일 학습자 SSE→review→G4/G5 actual 폐루프는 clean HEAD/tree·전용 engine/DB/API/Web·보호 포트 거부·sentinel·exact cleanup runner가 소유하며 unit 10/10은 통과했다. 첫 실제 GREEN receipt는 clean commit 뒤 남았다. release gate·NAS preflight·배포 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까지 잇는다. 공개 배포·프로세스·source pin은 clean commit <code>b34623f3…</code>와 fresh receipt <code>9f8d1941…</code>로 완료했다. Cloudflare가 Python 기본 User-Agent를 403으로 거부하는 경계도 브라우저 호환 header와 회귀 4/4로 고정했다. 30초 rehearsal은 세 leg 7 samples를 모두 통과했으며 마이크를 열지 않았다. <b>남은 선행 조건:</b> 사용자의 물리 장치 선택과 명시 동의, 독립 human voice-gain pack이다.</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> runner 프로세스 exit는 canonical checker <code>exit 0</code>·<code>gate_closed=true</code>에 결속되고, browser Origin과 API transport host는 별도 allowlist로 검증된다. 세 artifact 교집합은 <code>≥3000s</code> + 120초 margin이고 Windows proof는 detached-clean HEAD/tree·tool SHA·<code>psutil==6.1.1</code>을 pin한다. clean-source 배포와 무마이크 rehearsal은 완료했다. 남은 것은 물리 마이크 3,120초, 동시 runtime/topology, 독립 human pack을 합친 4-artifact 실증뿐이다.</td></tr>
<tr><td>G8 DONE · clean-head NAS 기준선</td><td><code>vignette-preview-20260807</code> · <code>run-outcome-os-release-agent.py</code></td><td><b>상태는 DONE.</b> active archive <code>4d15d055…119d4d</code>, source HEAD/tree <code>61a41d1f…6af</code>/<code>87dec55d…3b77</code>, API/Web <code>d5021950…e4b1</code>/<code>9796c092…4b36</code> exact running. candidate 112/112와 actual NAS-origin 112/112, health ok·db/engine true·auth 401·OpenAPI 126을 통과했다. fresh dump <code>36ec8748…24db8</code> 1,097,100 bytes, TOC 1,738 / TABLE DATA 129와 previous active <code>6030a677…c611</code>을 보존했다. release agent의 DB restore 비소유·image/Compose/active-state rollback 경계는 유지한다.</td></tr>

View file

@ -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` | **필요(+시드)** | **필요** | 자동기동 | 일부만 | **필요** | 현재 수집 626 tests / 44 files · 현 작업트리 전체 GREEN 미검증 |
| Playwright E2E(전체) | `apps/web` | `npm run e2e` | **필요(+시드)** | **필요** | 자동기동 | 일부만 | **필요** | 현재 수집 627 tests / 45 files · 현 작업트리 전체 GREEN 미검증 |
핵심 원칙: **단위 테스트(pytest)와 타입체크/빌드는 외부 서비스 없이 단독 실행된다.**
**E2E만 풀스택(DB+API+웹+브라우저)을 요구한다.** 아래 각 절에서 근거와 절차를 설명한다.
@ -271,10 +271,17 @@ VITE_API_BASE=http://127.0.0.1:8000 npm run e2e # 프록시 대신 API
### 3.6 실측 테스트 개수 (현재)
2026-08-09 `npx playwright test --list` 기준 **현재 수집 626 tests / 44 files**다.
이 숫자는 수집량이지 통과량이 아니다. 현 작업트리 전체 626개 완주는 아직 증거가 없으며,
2026-08-09 `npx playwright test --list` 기준 **현재 수집 627 tests / 45 files**다.
이 숫자는 수집량이지 통과량이 아니다. 현 작업트리 전체 627개 완주는 아직 증거가 없으며,
과거 전체 GREEN 기록과 이번 focused/release gate 결과를 구분해 적는다.
NAS-origin 112건은 전부 실 API/DB 회기가 아니다. release receipt는 `session-layout`·`session-persistence`
22건을 `real_api_db_specs`, 나머지 route fixture 90건을 `route_fixture_specs`로 분리한다.
학생 동일 계정의 홈 추천→브라우저 회기 생성→실제 SSE→review ready→G4/G5 POST·reload→0→1→1은
`scripts/run-periodic-learner-e2e.py --execute`가 전용 disposable engine/DB/API/Web에서만 실행한다.
이 runner는 clean HEAD/tree, 로컬 npipe Docker, loopback 동적 포트, sentinel과 exact cleanup을 강제하며
공개 8001·DB 55432·engine 9099와 NAS를 구조적으로 거부한다. 첫 GREEN receipt 전에는 스케줄 등록하지 않는다.
- **병렬 시나리오**: 166 tests (desktop 83 + mobile 83)
- **`@single-run` 직렬 시나리오**: 49 tests (DB 영속화·세션 MVP·음성 성공경로·인증 시각 테마·회기말 평가 저장·교수자 명시 재평가 저장·교수자 턴 재평가 저장·교수자 UI 평가 재시도·교수자 사용자별 분석·source pack sync·이론모드 저장·동의 철회 후 voice 차단·브라우저 stream PII 마스킹·음성 transcript 저장 실패 UI 표면화 등)
- 2026-08-06 Outcome & Alliance OS G0 검증: `test_measurement_contract.py` + runtime schema SSOT **11 passed**,
@ -432,6 +439,10 @@ VITE_API_BASE=http://127.0.0.1:8000 npm run e2e # 프록시 대신 API
ready/ping/close1000, runtime 7 samples, Windows topology 7 samples를 모두 통과했고 `physical_capture=false`,
UUID/email literal 0이다. current G7 six-suite는 87/87, runtime sampler는 4/4다. 이 리허설은 실제 마이크
3,120초·공통 3,000초 high-water·독립 human pack·canonical checker exit 0을 대체하지 않는다.
- 2026-08-09 G7 human pack preflight: production runner는 캡처 전에 category·categorical κ≥0.70,
preregistration 선행, 50회기/150축 complete pairing, 양 조건 결측 ITT를 검증한다. 독립 검수는
`python -X utf8 -B scripts/check-g7-human-voice-gain.py --input <pack.json>`이며 `--print-schema`도 지원한다.
부적합 pack은 마이크를 열기 전에 exit 2다. API evaluator 11/11 + G7 script 86/86, 합계 97/97 통과했다.
- 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와 시간축 키보드 포커스를 수치 회귀로 고정했다. 상세는

View file

@ -90,8 +90,8 @@
PID/start/exe/command SHA/cwd safe receipt를 만든다. current six-suite 87/87과 runtime sampler 4/4를 통과했다.
무마이크 rehearsal은 완료됐지만 물리 마이크·human pack을 대체하지 않는다.
격리 NAS 프리뷰 `http://100.116.83.60:8088`은 전용 Compose 프로젝트·포트·네트워크·볼륨에 배포했고, 실제 브라우저 회기와
review API 저장 축어록 2턴을 확인했다. 기존 프로젝트 중단·재생성 명령은 실행하지 않았다. 매일 04:30 KST `Vignette 회기 E2E 정기 검증`
(automation id `vignette-e2e`)은 ACTIVE이며 material milestone+release gate·NAS preflight+배포 SHA 변경 때만 프리뷰를 갱신한다. 2026-08-07 SHA `6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611`의 localhost 108/108과 평문 origin UUID 24건 실패는 역사 기준선으로 보존한다. 실제 receipt-bound rollback은 같은 프리뷰에서 별도 helper로 두 번 실행해 종료했다([런북](./nas-preview-g8-rollback-proof-runbook.md), [기계 판독 증거](./evidence/nas-preview-g8-actual-rollback-2026-08-07.json)). 현재는 source HEAD/tree `61a41d1f…6af`/`87dec55d…3b77`의 active archive `4d15d055…119d4d`, exact API/Web `d5021950…e4b1`/`9796c092…4b36`이 실행 중이다. candidate 112/112와 실제 NAS-origin 112/112, health ok·db/engine true·auth 401·OpenAPI 126을 통과했고 fresh dump `36ec8748…24db8` 1,097,100 bytes·TOC 1,738/TABLE DATA 129와 previous `6030a677…c611`을 보존했다. 첫 예약 실행 이력은 대기 상태다. 증거: [배포 증거](./nas-preview-deployment-evidence-2026-08-07.md),
review API 저장 축어록 2턴을 확인했다. 기존 프로젝트 중단·재생성 명령은 실행하지 않았다. 매시간 `Vignette 앱 상태·회기 E2E 정기 검증`
heartbeat(automation id `vignette-e2e`)는 ACTIVE이며 공개/NAS 화면·health는 읽기 전용으로 보여주고, material milestone에서만 전용 disposable 회기 E2E를 실행한다. NAS 112건은 실 API/DB 22와 route fixture 90으로 영수증에서 분리한다. 동일 학습자 SSE→review→G4/G5 actual 폐루프 runner의 clean HEAD/tree·보호 포트 거부·sentinel·exact cleanup과 unit 10/10은 완료됐고 첫 실제 GREEN receipt는 clean commit 뒤 남았다. release gate·NAS preflight·배포 SHA 변경 때만 프리뷰를 갱신한다. 2026-08-07 SHA `6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611`의 localhost 108/108과 평문 origin UUID 24건 실패는 역사 기준선으로 보존한다. 실제 receipt-bound rollback은 같은 프리뷰에서 별도 helper로 두 번 실행해 종료했다([런북](./nas-preview-g8-rollback-proof-runbook.md), [기계 판독 증거](./evidence/nas-preview-g8-actual-rollback-2026-08-07.json)). 현재는 source HEAD/tree `61a41d1f…6af`/`87dec55d…3b77`의 active archive `4d15d055…119d4d`, exact API/Web `d5021950…e4b1`/`9796c092…4b36`이 실행 중이다. candidate 112/112와 실제 NAS-origin 112/112, health ok·db/engine true·auth 401·OpenAPI 126을 통과했고 fresh dump `36ec8748…24db8` 1,097,100 bytes·TOC 1,738/TABLE DATA 129와 previous `6030a677…c611`을 보존했다. 증거: [배포 증거](./nas-preview-deployment-evidence-2026-08-07.md),
[브라우저 증거](./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를 통과했다.
- [ ] **stable-source 재부팅 후 watchdog smoke** — watchdog·로그온 boot는 detached-clean `b34623f3…` stable

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를 닫지 않는다.