Files
Find-Word/.agent/RESULT-v0.2.3.md
T

395 lines
38 KiB
Markdown
Raw Normal View History

2026-10-14 10:01:46 +00:00
# Find-Word v0.2.3 구현·검증 결과
작성일: 2026-10-14 UTC. worktree `/home/hermes/worktrees/find-word-v0.2.3`, branch `feat/v0.2.3`.
기준 HEAD `c92660b514f7af8eb556d3fa5c0fc80e4bdd4cf5`. 지정된 TASK/ACCEPTANCE/AMBIENCE-SOURCES를 먼저 읽고 기존 코드·테스트·문서를 확인했다.
**Git commit/push/merge/tag, 외부 배포, secrets 사용은 하지 않았다.** 로컬 production preview만 사용했다.
정확한 credit **Designed by OZ at Heat-T Medical Inc.**와 private/UNLICENSED 프로젝트 권리를 보존했다.
제3자 현장음은 각각의 라이선스를 유지한다.
**현재 screening 상태:** mall=36, market=24, cafe=0의 최종 WAV는 각각 segment 0개,
`duration_after_vad=0.0`, `requiredEmptyPassed=true`다. 사람의 청취 인수와 법적/privacy clearance는 false다.
전체 unit 125개·Python 36개·sandboxed Chrome 30개 및 lint/build가 통과했고 audit 취약점은 0개다.
실행 원문은 맨 아래 **최종 privacy screening 재시도** 절에 있다.
앞선 구현/독립 리뷰 기록은 당시 결과다. 이전 실패 screening은 별도 읽기 전용 경로에 원문 보존했다.
## 최초 구현 내용 (아래 리뷰 수정 내역으로 보완)
- 정확히 세 개의 서로 다른 문제/단어를 한 단계에서 출제한다. 시작 단계 1~5 선택, mixed 선택/입력/선택,
진행률/안내/분모 3문항, 정상 완료→다음 단계의 새 3문항, 5단계 재도전과 홈 복귀를 구현했다.
- SNR을 −12~+12 dB, 1 dB 눈금으로 직접 설정한다. 범위 밖 값은 clamp, NaN/Infinity는 단계 기본값으로 복구한다.
변경은 현재 재청취·미응답 문항·이후 라운드에 적용한다. 재생 중 gain을 바꾸지 않는다.
다음 단계도 수동 값은 유지하고 홈 복귀 시 수동 override를 지운다. 시작 단계/모드/환경/전체 음량은 홈 설정으로 유지한다.
- ready/loading/playing/answer/feedback에서 `문제 풀이 종료`가 가능하다. token 무효화→엔진 정지→결과 전환으로
지연 콜백을 차단한다. 응답 수·정답 수·실제 응답 분모를 표시하고 무응답을 오답으로 세지 않는다.
- 여덟 고유 실제 현장 녹음을 fetch/decode/loop한다. 절차적 잡음 합성 코드는 제거했다.
101개 음성 및 여덟 배경은 모두 same-origin `?v=0.2.3` 정적 요청이다. 런타임 외부 API/TTS/추적은 없다.
- 음성과 배경을 각각 RMS .03으로 정규화하고 상대 gain `10^(dB/20)`을 적용한다.
안내/목표/배경의 피크 합에 공통 gain을 적용해 최악 합계 peak ≤ .8을 유지한다.
master 음량은 그 뒤 공통 적용되므로 SNR을 보존한다. 배경은 목표 ended에서 함께 정리한다.
- HTTP/decode/준비 15초 timeout/재생 길이+5초 timeout/abort 후 한국어 안내와 재시도를 유지한다.
- CHANGELOG 생성, package/lock/HTML/UI/자산 버전/README/설계/배포/오디오/단어/권리 문서를 0.2.3에 맞췄다.
상세 한국어 설계 주석을 수정 코드의 모듈·함수·클래스·상태/매개변수·경합/수치 계산에 추가했다.
## RED → GREEN 원문
각 동작/회귀는 테스트를 먼저 작성·실행한 뒤 구현했다. 아래 RED의 종료 코드는 1, GREEN은 0이다.
`-t`로 좁힌 실행의 skipped는 필터 제외이며 최종 전체 실행은 skip 0이다. 실패 원문을 성공 로그로 대체하지 않았다.
| 계약/회귀 | RED → GREEN | 원문 |
| --- | --- | --- |
| 세 문항/같은 시작 단계/혼합/점수 | 기존 10문항·자동 단계에서 3 실패 → 9 통과 | [01 RED](evidence-v0.2.3/01-round-RED.log), [01 GREEN](evidence-v0.2.3/01-round-GREEN.log) |
| SNR 정규화/미응답 전파/전 단계 종료/지연 이벤트 | 누락 reducer action 등 7 실패 → 15 통과 | [02 RED](evidence-v0.2.3/02-snr-exit-RED.log), [02 GREEN](evidence-v0.2.3/02-snr-exit-GREEN.log) |
| 시작 단계/수동 SNR/다음 라운드/5단계 경계/중도 결과/UI 초기화 | 10 실패 → 18 통과 | [03 RED](evidence-v0.2.3/03-screen-RED.log), [03 GREEN](evidence-v0.2.3/03-screen-GREEN.log) |
| 고유 녹음 URL/절차적 매개변수 제거/peak/SNR 수학 | 2 실패 → 콘텐츠 포함 19 통과 | [04 RED](evidence-v0.2.3/04-recorded-mix-RED.log), [04 GREEN](evidence-v0.2.3/04-recorded-mix-GREEN.log) |
| 실제 배경 fetch/decode/loop/abort/timeout/재시도 | 전환 중 남은 합성 호출 및 누락 녹음 경로로 9 실패 → 11 통과 | [05 RED](evidence-v0.2.3/05-playback-RED.log), [05 GREEN](evidence-v0.2.3/05-playback-GREEN.log) |
| Node 직접 import 회귀 | ERR_MODULE_NOT_FOUND → 101문장 import 통과 | [06 RED](evidence-v0.2.3/06-node-import-RED.log), [06 GREEN](evidence-v0.2.3/06-node-import-GREEN.log) |
| 8개 배포 PCM/해시/출처/고지/루프 경계 | manifest ENOENT → 전수 통과 | [07 RED](evidence-v0.2.3/07-assets-RED.log), [07 GREEN](evidence-v0.2.3/07-assets-GREEN.log) |
| 실제 FFmpeg 변환/재현/무음 거부/길이 제한 | 변환 모듈 없음 → Python 2 통과 | [변환 RED](evidence-v0.2.3/07-converter-RED.log), [변환 GREEN](evidence-v0.2.3/07-converter-GREEN.log) |
| 재생 중 SNR 변경의 실제 청취 이력 | −10을 들었다고 잘못 기록 → 34 통과 | [08 RED](evidence-v0.2.3/08-heard-snr-RED.log), [08 GREEN](evidence-v0.2.3/08-heard-snr-GREEN.log) |
| 버전/문서/변경 이력/기존 voice metadata | 이전 버전·누락 CHANGELOG → 50 통과 | [09 RED](evidence-v0.2.3/09-version-docs-RED.log), [09 GREEN](evidence-v0.2.3/09-version-docs-GREEN.log) |
| 3문항의 환경 순환 회귀 | 3개 환경만 도달 → 8개 도달, 35 통과 | [10 RED](evidence-v0.2.3/10-environment-rotation-RED.log), [10 GREEN](evidence-v0.2.3/10-environment-rotation-GREEN.log) |
| dist 현장음/manifest/notice 변조·누락·원본 혼입/고지 링크 | 5 실패 → 26 통과 | [11 RED](evidence-v0.2.3/11-bundle-notice-RED.log), [11 GREEN](evidence-v0.2.3/11-bundle-notice-GREEN.log) |
| 누락 WAV의 SPA fallback | desktop/mobile 200 HTML → 양쪽 404, 4 통과 | [13 RED](evidence-v0.2.3/13-static-404-RED.log), [13 GREEN](evidence-v0.2.3/13-static-404-GREEN.log) |
| 결과 화면의 잘못된 도움말 anchor | 없는 #howto 링크 노출 → 제거 | [15 RED](evidence-v0.2.3/15-results-anchor-RED.log), [15 GREEN](evidence-v0.2.3/15-results-anchor-GREEN.log) |
| 새 설정의 터치 크기/카드 경계 | 양쪽 23px 높이 → 44px 이상, 2 통과 | [16 RED](evidence-v0.2.3/16-controls-layout-RED.log), [16 GREEN](evidence-v0.2.3/16-controls-layout-GREEN.log) |
| 3문항의 보너스 안내 | 당시 40점 → 20점 안내; 리뷰에서 문항별 20점/합계 30점으로 재정정 | [17 RED](evidence-v0.2.3/17-bonus-copy-RED.log), [17 GREEN](evidence-v0.2.3/17-bonus-copy-GREEN.log) |
| 모바일 select 선택값 잘림 | 360px에서 글자 폭 초과 → 중복 SNR을 아래 제어에 모음 | [21 RED](evidence-v0.2.3/21-select-label-RED.log), [21 GREEN](evidence-v0.2.3/21-select-label-GREEN.log) |
| Chrome favicon 기본 404 | 내장 icon 없음/실제 콘솔 오류 → 내장 SVG | [19 RED](evidence-v0.2.3/19-favicon-RED.log), [19 GREEN](evidence-v0.2.3/19-favicon-GREEN.log) |
추가 진단 기록도 보존했다. `01-round-sampling-regression.log`의 96/100은 세 문항으로 줄인 뒤
기존 200 seed 표본이 작아진 테스트 회귀여서 1000 seed로 전체 100조합 도달성을 검사했다.
`03-screen-duplicate-description-RED.log`는 같은 SNR 설명을 중복 배치한 회귀를 잡았다.
`05-playback-fixture-regression.log` 뒤 mock AudioBuffer의 copyToChannel을 실제 복사처럼 보완했고
부동소수점 0.12000000000000001 비교를 근사 비교로 바꿨다. 실제 재생은 별도의 Chrome으로 검증한다.
`12-lint-initial.log`의 축소된 union 비교/fixture 타입 오류도 수정했다.
## 현장음 원본과 변환
지정된 원본 8개를 모두 사용했다. **대체 원본은 0개**다. 여섯 기존 원본을 재사용했고 café는
HTTP 429의 **Retry-After 600초** backoff 뒤 지정 직접 URL로 다운로드했다. rain도 지정 URL에서 받았다.
[다운로드 기록](evidence-v0.2.3/source-downloads.log), [rain 기록](evidence-v0.2.3/rain-download.log).
[원본 전수 검사](evidence-v0.2.3/source-integrity.json)는 전체 원본 SHA-256/bytes/ffprobe 정보와
FFmpeg **전체 디코드 8/8 성공, stderr 0**을 기록한다. café 19,947,952 bytes, rain 91,187,572 bytes는
Commons primary 메타데이터의 크기와 일치한다. [Commons API 근거](evidence-v0.2.3/commons-license-audit.json),
[primary 페이지/권리 재확인](evidence-v0.2.3/primary-page-verification.json).
`python3 scripts/ambience_audio.py`로 원본의 명시적 중간 구간을 선택해 전부 **12초 / 24,000Hz / mono / PCM16**으로 만들었다.
양끝 **50ms 선형 fade**, RMS 목표 **.08**, 최대 증폭 **4배**, peak ceiling **.85** 중 안전한 선형 gain을 적용했다.
저레벨 rain과 순간 peak가 큰 market은 제한 때문에 RMS 목표보다 낮다. 엔진은 재생 시 각각 다시 RMS 정규화한다.
양끝 PCM은 0, non-silent 8/8, 고유 출력 해시 8/8, 각 576,044 bytes, 합계 **4,608,352 bytes**다.
대형 OGG/MP3/FLAC 원본은 `/tmp/findword-ambience-sources`에만 있으며 Git/public/dist에 번들하지 않았다.
| 배포 자산 (`public/ambience/`) | 원본 구간 | RMS | peak | 출력 SHA-256 |
| --- | --- | --- | --- | --- |
| street.wav | 35–47s | 0.07999756 | 0.39675903 | `a8bffa201b174618955bc51b860167169125304811aabe2086b0bb35b06c3ae1` |
| mall.wav | 20–32s | 0.07999757 | 0.55468750 | `6e9171f549cacadfaeedd57a41c8e2a76b0e789f5c9a97af029a8d3558c9f145` |
| theater.wav | 45–57s | 0.07999757 | 0.49508667 | `2d6a444fb7874cdf6b2d8a741a69175299b112eb66834912c025437d8277d635` |
| department.wav | 90–102s | 0.07999758 | 0.47497559 | `aa43f3d1d3e7c2b1f8ace24ecbda45d0ea3a031db941c39506dc6c1665b8efa7` |
| market.wav | 30–42s | 0.04922208 | 0.84997559 | `4287e971a12767f70da714e0c1aff30125268f9c3faf07ebfbcc0d2904f4a312` |
| subway.wav | 65–77s | 0.07999755 | 0.42529297 | `9faf70b68617d267b367a0f3692ed90015458543ab4b0844f6c704e6a724728f` |
| cafe.wav | 120–132s | 0.07999759 | 0.39709473 | `8db06f3f09bbd8409573ba4874c7ccdd1e7e825c8a61989a4efe66206a66c2d2` |
| rain.wav | 180–192s | 0.01138463 | 0.10241699 | `f4315084e820f742b0709de5bb0d1abd3d5af1730620e08c2948b70347356dd9` |
[배포 manifest](../public/ambience/manifest.json)에 environment id/표시 이름, source page/direct URL, creator,
license/license URL, attribution, 원본/출력 SHA-256, bytes/duration/RMS/peak, 실제 FFmpeg 버전과 변환 설정을 기록했다.
[AMBIENCE_NOTICES](../public/ambience/AMBIENCE_NOTICES.md)를 dist에 포함하고 UI에서 접근할 수 있다.
시장 파생 WAV는 **CC BY-SA 4.0**, 지하철은 **CC BY 4.0**, 나머지는 CC0/PD이며 출처·변경 고지를 보존했다.
프로젝트의 UNLICENSED를 이 녹음에 적용하지 않는다. 런타임 React/ReactDOM/scheduler의 기존 MIT 전문도 그대로 배포한다.
## 보존과 구체적 회귀 수정
- **101개 neural WAV 바이트/원문/해시 변경 0**. voice manifest는 배포 version만 0.2.3으로 올리고
`generatedForVersion: 0.1.1`을 추가했으며 생성 시각·provider·voice·prosody·도구/실측값은 보존했다.
- 기존 10 mechanic/100문장, 정답·오답 규칙·점수 공식·재청취·객관식/주관식/mixed·IME 보호를 유지했다.
- **과거 .agent 추적 파일 180개 모두 Git blob hash 동일**. v0.1/v0.1.1 RESULT와 evidence 수정 0.
[보존 검사](evidence-v0.2.3/preservation.json).
- 짧아진 라운드 때문에 순환 환경이 street/mall/theater에만 갇히는 회귀를 seed 기반 환경 셔플로 수정했다.
- 환경 URL 모듈 추가로 Python 생성기의 Node 직접 import가 깨지는 회귀를 명시적 `.ts` 경로로 수정했다.
- `heardDb`를 PLAY 시점에 기록해 재생 중 조절값이 과거 청취 이력/결과를 덮어쓰지 않게 했다.
- 중복 SNR 설명, 결과 화면의 없는 도움말 anchor, 실제 라운드 보너스 상한 안내를 바로잡았다.
- Vite dev/preview의 누락 WAV HTML fallback을 차단하고 Nginx 인계에도 `/ambience/` 404 규칙을 추가했다.
- 모바일 최종 이미지에서 시작 단계 select의 긴 선택값 잘림을 찾아 글자 폭 검사로 재현하고, 중복 SNR 표기를 전용 제어로 모았다.
- 실제 Chrome의 기본 `/favicon.ico` 404 콘솔 오류를 내장 SVG로 제거했다.
## 리뷰 전 검증 기록 (리뷰 수정 후 결과는 문서 하단)
환경: Node **26.7.0**, npm **11.19.0**, Python **3.11.16**, FFmpeg **6.1.1-3ubuntu5**, system Chrome **146.0.7680.80**.
[환경](evidence-v0.2.3/environment.json). Chrome sandbox 활성화, `--no-sandbox` 0개를 실행 중 process argv로 확인했다.
[실행 sandbox 기록](evidence-v0.2.3/chrome-sandbox.json); 설정 계약 테스트도 통과했다.
| 검증 | 결과 | 원문 |
| --- | --- | --- |
| `npm run lint` | PASS, ESLint + TypeScript strict, exit 0 | [22-lint](evidence-v0.2.3/22-lint.log) |
| `npm test` | **13 files / 94 passed**, skip 0, exit 0; Python runner 포함 | [22-full-test](evidence-v0.2.3/22-full-test.log) |
| `python3 -m unittest discover -s tests/python -v` | **20 passed**, exit 0 (기존 neural 18 + 새 변환 2) | [18-python](evidence-v0.2.3/18-python.log) |
| `npm run build` | PASS, 101 voice + 8 ambience + 두 notice 검증, exit 0 | [22-build](evidence-v0.2.3/22-build.log) |
| `npm audit --json` | **취약점 0** (info/low/moderate/high/critical 모두 0), exit 0 | [audit JSON](evidence-v0.2.3/14-npm-audit.json) |
| 최종 sandboxed system Chrome desktop/mobile | **26 passed / 0 failed / 0 skipped**, exit 0; 실제 재생·접근성·무외부 요청 검증 | [22-browser](evidence-v0.2.3/22-browser.log), [machine-readable 결과](evidence-v0.2.3/browser-results.json) |
| 원본/배포 녹음 및 101개 voice 보존 | PASS | [원본](evidence-v0.2.3/source-integrity.json), [보존](evidence-v0.2.3/preservation.json) |
| 비밀정보 형식 검사 | **0 findings**, 값 노출 없이 경로/개수만 기록 | [scan](evidence-v0.2.3/secret-scan.json) |
| `git diff --check` | PASS | [최종 worktree 점검](evidence-v0.2.3/final-review.log) |
브라우저는 1280×1000 desktop / 360×800 mobile에서 같은 production build를 사용한다.
실제 디코드한 109 WAV의 response hash/bytes/24kHz/mono/duration/RMS/peak를 전수 대조하고,
앱에서 여덟 환경을 실제 loop로 재생 후 정지했다. 혼합 모드 세 문항 완주/재청취/키보드,
4단계 시작→5단계 새 라운드, 수동 SNR 유지/재청취/홈 초기화, 재생 중 종료/응답 뒤 종료,
intro/target/ambience 404 재시도, 누락 WAV의 HTML Accept 404, notice 원문 제공을 검사한다.
정상 흐름의 console/page errors·failed requests·외부 요청·axe violations가 모두 0이어야 통과한다.
브라우저 native audio 메서드를 관찰할 때 원래 메서드를 실행하며 가짜 decode/ended를 주입하지 않는다.
최종 캡처 8개와 실제 검사 JSON을 새 evidence에 보존했다. 모바일 게임/시작과 desktop 시작/결과의
레이아웃을 이미지로 확인했고, 결과 분모/현재 SNR/종료 버튼/고지 링크가 잘리지 않는다.
| 화면 | Desktop | Mobile |
| --- | --- | --- |
| 시작 | [PNG](evidence-v0.2.3/browser/desktop-start.png) | [PNG](evidence-v0.2.3/browser/mobile-start.png) |
| 게임 | [PNG](evidence-v0.2.3/browser/desktop-game.png) | [PNG](evidence-v0.2.3/browser/mobile-game.png) |
| 정상 결과 | [PNG](evidence-v0.2.3/browser/desktop-normal-results.png) | [PNG](evidence-v0.2.3/browser/mobile-normal-results.png) |
| 중도 종료 | [PNG](evidence-v0.2.3/browser/desktop-early-exit.png) | [PNG](evidence-v0.2.3/browser/mobile-early-exit.png) |
실제 109 WAV 해시/디코드/레벨 및 오류 0 집계:
[desktop JSON](evidence-v0.2.3/browser/desktop-audio-integrity.json),
[mobile JSON](evidence-v0.2.3/browser/mobile-audio-integrity.json).
세 문항→다음 단계→중도 종료→홈·재청취의 실제 소스 시작/정지 및 네트워크 집계:
[desktop runtime](evidence-v0.2.3/browser/desktop-runtime-audit.json),
[mobile runtime](evidence-v0.2.3/browser/mobile-runtime-audit.json).
초기 browser suite 24/24 통과 후 console/requestfailed 검사를 강화해 missing favicon을 찾았다.
그 실패는 `18-browser.log`에 남기고, 최종 검증은 별도 로그로 구분했다. 오류를 숨기거나 예외 목록으로 제외하지 않았다.
404/잘못된 WAV 주입 테스트의 의도적 실패 응답은 정상 흐름의 오류 0 집계와 구분한다.
## 실제 검증의 한계와 인계
- Chrome은 실제 WAV를 디코드하고 실제 AudioContext/BufferSource를 실행했다. 헤드리스 자동 검증은
스피커/헤드폰의 물리 음압, 한국어 청감, 개인의 난이도나 임상 효과를 보증하지 않는다.
- 녹음 장면은 primary 출처 설명으로 확인했고 짧은 연속 구간의 포맷/신호/해시/루프를 검증했다.
**사람이 여덟 구간을 직접 청취하여 음악·불필요하게 명료한 우연 대화가 없는지 판단하는 인수는 수행하지 않았다.**
자동 검사를 인간 청취 인증으로 주장하지 않는다. [샘플과 provenance](../docs/AUDIO_PROVENANCE.md)를 Neo가 검토할 수 있다.
- mobile은 system Chrome 360×800 viewport/touch emulation이다. 실제 Android/iOS 기기 및 Safari 검증은 별도다.
- SNR은 전체 버퍼 RMS에 대한 디지털 상대값이다. 현장음의 시간 변화 때문에 순간/주파수별 SNR은 다르다.
- 로컬 `dist`만 검증했다. production HTTPS/MIME/cache/서버 설정은 배포 후 Neo의 독립 검증 대상이다.
- 사용자 지시대로 commit/push/merge/tag/deploy는 미실행이다. acceptance의 Neo 릴리스 항목은 완료로 표시하지 않았다.
[DEPLOYMENT](../docs/DEPLOYMENT.md)에 feature push→main FF→annotated tag→fresh clone→commit-addressed 원자 배포/rollback 절차를 인계했다.
## 독립 리뷰 수정 — 2026-10-14
사용자가 요약한 두 독립 리뷰와 본 결과 문서를 읽고 release-hardening을 수행했다.
기존 uncommitted v0.2.3 작업을 이어서 수정했으며 commit/push/merge/tag/deploy는 하지 않았다.
새 실패 테스트를 먼저 실행하고 생산 코드/검증기를 수정했다. 이전 evidence 로그는 수정하지 않았다.
신규 기록은 모두 [review-fixes/](evidence-v0.2.3/review-fixes/) 아래에 있다.
### 수정 내용과 동작
1. 진행 막대는 답변 수가 아닌 **현재 문항 1/3 → 2/3 → 3/3**이다. 첫 재생·답변 전·피드백과
다음 라운드에서도 일관된다. 접근성 이름/값, native progress 위치, 실제 PNG 채움 픽셀까지 검사한다.
2. 결과/README/설계/CHANGELOG의 점수 설명은 **정답마다 단계×100점 + 연속 정답 보너스**,
**문항별 최대 20점, 라운드 합계 0+10+20=30점**이다. 1단계 세 정답의 **330점**을 실제 UI 흐름으로 검증했다.
3. 응답이 없으면 정답률은 **— / 응답 없음**이며 0%나 0/0을 보여 주지 않는다. 응답 없는 단계는 **미진행**이다.
중도 종료 제목은 **문제 풀이를 중간에 종료했어요.**, 정상 결과는 **N단계 문제 풀이를 마쳤어요.**로 구분한다.
4. 실제 Chrome RED에서 `입력`, 결과의 `만나요`/`최대` 등 어절 분리를 검출했다.
모드 설명과 결과 영역에 keep-all을 적용하고 줄바꿈을 허용했다. 320px/360px에서 어절 내부 Range 줄 분리와
요소/문서 overflow를 검사하며 시작·정상 결과·중도 결과 캡처를 남겼다.
5. README 현재 증거 경로를 `.agent/evidence-v0.2.3/review-fixes/`로 바로잡고 일본어 reducer 테스트 제목을 한국어로 바꿨다.
6. build verifier는 앱 콘텐츠의 10 patterns×10 words+intro로 **정확한 101개 이름**을 독립 구성한다.
manifest 정확히 101개, 중복 없는 예상 inventory, dist/audio의 추가/누락 파일을 모두 검사한다.
양쪽 manifest와 디렉터리를 함께 비우기/축약/중복/이름 변경해도 실패하며 정상 사본은 통과한다.
7. 승인 계획과 배포 manifest의 `verifiedLocalSource` 및 모호한 `sourceDurationSeconds`를 제거했다.
`expectedSourceSha256`, `sourceBytes`, `sourceVerification`과 **directUrl 바이트의 ffprobe format.duration**인
`directMediaDurationSeconds`를 보존한다. Freesound preview MP3의 길이와 primary 페이지 표시 길이를 혼동하지 않는다.
파생 WAV의 `duration`/`conversion.durationSeconds`는 별도로 12초다. 기존 source-integrity 증거와 대조했다.
8. 생성기는 **첫 변환 전에 여덟 원본 전부**의 승인 해시/bytes를 확인한다. 불일치/누락/중복/잘못된 목록은 실패하고,
현재 파일 값으로 승인 계획을 덮어쓰지 않는다. 실제 원본 8개도 새 승인 필드로 재검증했다.
9. 현장음 notice를 **public/ambience/AMBIENCE_NOTICES.md**로 옮겼다. 같은 파일시스템의 staging에서
8 WAV·manifest·notice와 실측 PCM을 전수 검사한 다음 Linux **renameat2(RENAME_EXCHANGE)** 한 번으로 교체한다.
최초 생성은 rename 한 번이다. overlay를 없애 stale 파일을 제거하고 정상/dangling symlink 출력을 거부한다.
모든 변환 위치의 실패/KeyboardInterrupt, manifest/notice 쓰기 실패, staged 변조, 교환 실패/미지원,
commit 전 SIGTERM에서 이전 디렉터리를 보존한다. 실제 교환 도중 디렉터리 fd로 읽은 세 파일이
같은 세대인 것도 확인했다. UI 링크·build verifier·권리·배포·재현 문서를 함께 수정했다.
10. 모든 배포 notice 항목에 **creator, primary page URL, direct download URL, license URL,
outputLicense, 명시적 Changes**가 있어야 한다. 다른 항목에 공통 URL이 남아도 누락을 감추지 못한다.
8개 항목×6개 필드 제거와 public/dist 동시 metadata 변조를 build verifier에서 거부한다.
11. CHANGELOG와 본 문서에 실제 수정/검증을 반영했다. **109 WAV 재생성 0 / 바이트 변경 0**이다.
### 새 RED → GREEN 원문
| 계약 | RED | GREEN |
| --- | --- | --- |
| 진행률·330점·보너스·무응답·종료/완료 문구·README | [01 RED](evidence-v0.2.3/review-fixes/01-ui-RED.log) | [01 GREEN-02: 27 통과](evidence-v0.2.3/review-fixes/01-ui-GREEN-02.log) |
| 실제 desktop/mobile 진행률·320/360px 어절·overflow | [02 RED: 4 실패](evidence-v0.2.3/review-fixes/02-browser-RED.log) | [02 GREEN: 4 통과](evidence-v0.2.3/review-fixes/02-browser-GREEN.log) |
| manifest/폴더 동시 축약·추가/누락 | [03 RED](evidence-v0.2.3/review-fixes/03-voice-inventory-RED.log) | [03 GREEN](evidence-v0.2.3/review-fixes/03-voice-inventory-GREEN.log) |
| durable 승인 원본 메타데이터 | [04 RED](evidence-v0.2.3/review-fixes/04-source-plan-RED.log) | [04 GREEN](evidence-v0.2.3/review-fixes/04-source-plan-GREEN.log) |
| 첫 변환 전 전수 원본 검사 | [05 RED](evidence-v0.2.3/review-fixes/05-source-guard-RED.log) | [05 GREEN](evidence-v0.2.3/review-fixes/05-source-guard-GREEN.log) |
| staging·notice·symlink·검증·원자 교환·실패/중단 보존 | [06 RED](evidence-v0.2.3/review-fixes/06-atomic-RED.log) | [06 GREEN](evidence-v0.2.3/review-fixes/06-atomic-GREEN.log) |
| notice 동시 발행 경로/UI 링크/항목 내용 | [07 RED](evidence-v0.2.3/review-fixes/07-notice-path-RED.log) | [07 GREEN](evidence-v0.2.3/review-fixes/07-notice-path-GREEN.log) |
| 항목별 필수 notice 강제 | [08 RED](evidence-v0.2.3/review-fixes/08-notice-verifier-RED.log) | [08–09 GREEN: 27 통과](evidence-v0.2.3/review-fixes/08-09-build-GREEN.log) |
| manifest 사본 동시 출처/라이선스/변경 변조 | [09 RED](evidence-v0.2.3/review-fixes/09-build-plan-RED.log) | [08–09 GREEN](evidence-v0.2.3/review-fixes/08-09-build-GREEN.log) |
RED 로그는 exit 1, 표에 연결한 GREEN은 exit 0이다. 필터 실행의 skipped는 최종 전체 실행과 구분한다.
`01-ui-GREEN.log`는 이름과 달리 **exit 1**인 중간 실행이다. 새 계약은 통과했지만 기존 통합 테스트의
이전 제목 regex가 남아 실패했고, 그것을 고친 `01-ui-GREEN-02.log`가 실제 GREEN이다. 실패 로그도 보존했다.
초기 RED/GREEN browser PNG를 사후 픽셀 분석한 [RED](evidence-v0.2.3/review-fixes/02-progress-pixels-RED.log) /
[GREEN](evidence-v0.2.3/review-fixes/02-progress-pixels-GREEN.log)도 보존했다. desktop 1096px 막대는
365→731→1096px, mobile 320px 막대는 107→213→320px 채워졌다. 같은 픽셀 검사를 최종 브라우저 테스트에도 넣었다.
추가 동시 읽기/미지원 교환 검증은 [10-atomic-additional](evidence-v0.2.3/review-fixes/10-atomic-additional.log)에 있다.
### 리뷰 수정 후 전체 검증
| 실행 | 실제 결과 | 원문 |
| --- | --- | --- |
| npm run lint | PASS, exit 0 | [lint](evidence-v0.2.3/review-fixes/11-lint.log) |
| npm test | 13 files / **118 passed**, skip 0, exit 0 | [unit](evidence-v0.2.3/review-fixes/11-unit.log) |
| python3 -m unittest discover -s tests/python -v | **32 passed** (neural 18 + ambience 14), exit 0 | [Python](evidence-v0.2.3/review-fixes/11-python.log) |
| npm run build | PASS, 101 voice + 8 ambience + runtime/ambience notices, exit 0 | [build](evidence-v0.2.3/review-fixes/11-build.log) |
| npm audit --json | **취약점 0**, exit 0 | [audit](evidence-v0.2.3/review-fixes/npm-audit.json) |
| sandboxed system Chrome desktop/mobile | **30 passed / 0 failed / 0 skipped / 0 flaky**, 4.0분, exit 0 | [browser log](evidence-v0.2.3/review-fixes/12-browser.log), [JSON](evidence-v0.2.3/review-fixes/browser-results.json) |
[보존 검사](evidence-v0.2.3/review-fixes/preservation.json): 리뷰 시작 시점의 WAV 109개와 이전 `.agent` 파일
268개 모두 해시 동일하다. Git 추적된 v0.1/v0.1.1 `.agent` 파일 180개도 HEAD와 동일하다.
[원본 재확인](evidence-v0.2.3/review-fixes/source-reverification.json): 승인 원본 8개와 배포 bundle을 검증했고 변환은 호출하지 않았다.
Chrome **146.0.7680.80**, desktop 1280×1000/mobile 360×800에 추가 320px/360px copy 검사를 수행했다.
실행 중인 이 테스트의 Chrome 프로세스 20개에 `--no-sandbox`가 없음을 [실제 process 기록](evidence-v0.2.3/review-fixes/chrome-sandbox.json)으로 확인했다.
원래 AudioContext/BufferSource를 실행한 109 WAV 해시·decode 검사와 8개 실제 ambience loop, 오류 재시도,
키보드/axe/외부 요청/콘솔 검사에 새 진행률 픽셀·한국어 어절 검사를 더했다.
[desktop 오디오](evidence-v0.2.3/review-fixes/desktop-audio-integrity.json),
[mobile 오디오](evidence-v0.2.3/review-fixes/mobile-audio-integrity.json),
[desktop runtime](evidence-v0.2.3/review-fixes/desktop-runtime-audit.json),
[mobile runtime](evidence-v0.2.3/review-fixes/mobile-runtime-audit.json).
| 최종 화면 | Desktop | Mobile |
| --- | --- | --- |
| 시작 | [PNG](evidence-v0.2.3/review-fixes/screenshots/desktop-start.png) | [PNG](evidence-v0.2.3/review-fixes/screenshots/mobile-start.png) |
| 게임 | [PNG](evidence-v0.2.3/review-fixes/screenshots/desktop-game.png) | [PNG](evidence-v0.2.3/review-fixes/screenshots/mobile-game.png) |
| 330점 정상 결과 | [PNG](evidence-v0.2.3/review-fixes/screenshots/desktop-normal-results.png) | [PNG](evidence-v0.2.3/review-fixes/screenshots/mobile-normal-results.png) |
| 무응답 중도 종료 | [PNG](evidence-v0.2.3/review-fixes/screenshots/desktop-early-exit.png) | [PNG](evidence-v0.2.3/review-fixes/screenshots/mobile-early-exit.png) |
### 리뷰 후에도 남는 한계
**사람이 여덟 현장음 구간을 청취하여 음악/우연 대화와 적절성을 판단하는 인수는 수행하지 않았다.**
자동 PCM/Chrome 재생 검증을 인간 청취 승인으로 주장하지 않는다. mobile은 Chrome viewport/touch emulation이며
실제 Android/iOS/Safari, 물리적 음압 또는 임상 효과 검증은 아니다.
원자 commit 전 실패/중단은 이전 디렉터리를 보존하며 commit 뒤에는 완성된 새 트리가 공개된다.
강제 kill은 숨은 stage를 남길 수 있다. 전원 손실 내구성이나 여러 HTTP 요청에 걸친 snapshot 격리는 검증하지 않았다.
로컬 build/preview만 확인했으며 사용자 지시대로 Git commit/push/merge/tag 및 배포는 하지 않았다.
## 최종 privacy screening 재시도 — 2026-10-14
이전 market 60초/cafe 180초의 ASR-empty 실패를 새 구간으로 수정했다.
현재 **mall=36, market=24, cafe=0**, 최종 8 WAV의 segment 수는 모두 0이다.
세 필수 환경의 `duration_after_vad=0.0`, `requiredEmptyPassed=true`를 실제 inference로 확인했다.
**humanListeningAcceptance=false / legalPrivacyClearance=false**이며 ASR만으로 청취 인수를 주장하지 않는다.
### TDD·생성·이전 증거 보존
[01-release-RED](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/01-release-RED.log)는
구현 전 정확한 새 시작점, true gate, market/cafe 바이트 변경 조건에서 3개 실패했다.
[04-release-GREEN](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/04-release-GREEN.log)은
원자 재생성·실제 ASR 이후 같은 6개 테스트를 실행한 결과다.
테스트는 VAD/decoder 설정, manifest/WAV 해시, empty segment/text와 0.0 VAD duration,
청취/법적 인수 false, 이전 실패 상태·no-VAD 해시·모든 보존 파일 해시를 강제한다.
계획의 market/cafe 시작점만 24/0으로 바꾸고 원본 해시·bytes·직접 미디어 길이·권리 정보는 유지했다.
[02-regeneration](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/02-regeneration.log)과
[regeneration.json](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/regeneration.json)은
원본 전수 확인 후 기존 하드닝된 생성기로 전체 bundle을 staging/검증/원자 교환한 근거다.
생성기는 계획을 변경하지 않았고 notice와 manifest도 같은 구간으로 발행했다.
이번에는 **market/cafe 두 WAV만 변경**, 101 voice와 다섯 unaffected ambience 및 mall WAV는 바이트 동일하다.
이전 screening 파일 89개 전체를 읽기 전용
[prior-attempt-market60-cafe180](evidence-v0.2.3/privacy-screening/prior-attempt-market60-cafe180/)에 복사·해시 고정했다.
[이전 final-asr.json](evidence-v0.2.3/privacy-screening/prior-attempt-market60-cafe180/final-asr.json)은
`requiredEmptyPassed=false`, mall 0 / market 3 / cafe 1 segment 그대로다.
SHA-256은 `01c8a92fab9f8df7a5f778c5f7e478ce96bcedc33e11a1055bd29154a08b706b`이며 테스트에도 고정했다.
이전 source plan/manifest/notices/RESULT/docs의 사본과
[시작 시점 baseline](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/baseline.json)도 남겼다.
실패 로그를 지우거나 성공으로 고쳐 쓰지 않았다.
### 정확한 최종 ASR와 한계
[현재 final-asr.json](evidence-v0.2.3/privacy-screening/final-asr.json),
[03-final-asr.log](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/03-final-asr.log):
faster-whisper 1.2.1, tiny multilingual revision `d90ca5fe260221311c53c58e660288d3deb8d356`,
CPU int8/4 threads, beam_size=5, best_of=5, 기본 Silero VAD, condition_on_previous_text=true,
자동 언어 감지, random seed 0. 이전 실패 실행과 전체 decoder/VAD 인자가 같은지 대조했다.
모델/VAD·입력 WAV·manifest·실행 script 해시, 의존성·모든 기본 설정·원문 segment/info를 기록했다.
반환 segment 삭제나 threshold 튜닝 없이 각 현재 WAV를 검사했다.
[no-vad-asr.json](evidence-v0.2.3/privacy-screening/no-vad-asr.json)의 SHA-256은
`14b42ee0b80c194caa3c0de2644f647d6d7720c60c2208374778619f47c2e969` 그대로다.
그 결과는 이전 mall 36/market 60/cafe 180초에서 나온 환각 가능 text이며 새 market/cafe 검사가 아니다.
**no-VAD 결과는 지정된 VAD gate를 대체하거나 뒤집지 않는다.** 기존 scan/raw provenance도 바이트 보존했다.
빈 VAD 결과도 말소리·음악·개인 대화의 부재를 증명하지 않고 출력 text/language도 충실한 전사가 아니다.
독립적인 사람 청취 인증과 법적/privacy clearance는 수행하지 않았다. 제품 소유자 Oz는
2026-10-14 제공된 최종 WAV 8개 검수 묶음과 자동 검사 한계를 기준으로 **현재 자산 그대로
릴리스를 진행하도록 명시적으로 승인**했다. 이 제품 결정은 제3자의 법적/privacy clearance를
대신하지 않는다.
### 현재 자산 무결성
| WAV | 원본 구간 | RMS | peak | SHA-256 |
| --- | --- | --- | --- | --- |
| street.wav | 35–47s | 0.07999756 | 0.39675903 | `a8bffa201b174618955bc51b860167169125304811aabe2086b0bb35b06c3ae1` |
| mall.wav | 36–48s | 0.07999755 | 0.71917725 | `680a769940c08f0d12c6c954c08366598dd8b5745e276f39c82b10d1a88261a1` |
| theater.wav | 45–57s | 0.07999757 | 0.49508667 | `2d6a444fb7874cdf6b2d8a741a69175299b112eb66834912c025437d8277d635` |
| department.wav | 90–102s | 0.07999758 | 0.47497559 | `aa43f3d1d3e7c2b1f8ace24ecbda45d0ea3a031db941c39506dc6c1665b8efa7` |
| market.wav | 24–36s | 0.04309857 | 0.84997559 | `d6f73d86aea98b83e2995e61baaa7315a0ea7adeebc093769d76e1569b207658` |
| subway.wav | 65–77s | 0.07999755 | 0.42529297 | `9faf70b68617d267b367a0f3692ed90015458543ab4b0844f6c704e6a724728f` |
| cafe.wav | 0–12s | 0.07999754 | 0.40573120 | `6853c121104940041c268a2891eadae032c17aa22b4c3b3ddfbc025262434995` |
| rain.wav | 180–192s | 0.01138463 | 0.10241699 | `f4315084e820f742b0709de5bb0d1abd3d5af1730620e08c2948b70347356dd9` |
### 재시도 전체 검증
| 검사 | 실제 결과 | 근거 |
| --- | --- | --- |
| lint / typecheck | PASS, exit 0 | [05-lint](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/05-lint.log) |
| 전체 unit | 13 files / **125 passed, 0 failed, 0 skipped**, exit 0 | [05-unit](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/05-unit.log) |
| 전체 Python | **36 passed**, exit 0 | [05-python](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/05-python.log) |
| build | PASS, 101 voice + 8 ambience 및 notices, exit 0 | [05-build](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/05-build.log) |
| npm audit | **취약점 0**, exit 0 | [audit](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/npm-audit.json) |
| sandboxed Chrome desktop/mobile | **30 passed / 0 failed / 0 skipped / 0 flaky**, exit 0 | [06-browser](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/06-browser.log), [JSON](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/browser-results.json) |
현재 자동 검증은 모두 통과했다. [validation-summary](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/validation-summary.json)는 위 실행들의
실제 exit code·결과를 확인한 뒤 작성했으며 사람의 인수/법적 clearance를 포함한 전체 승인으로 해석하지 않는다.
현재 ASR gate 통과를 이전 실패의 통과로 바꾸어 기록하지 않았다.
Chrome 146.0.7680.80, desktop 1280×1000 / mobile 360×800 touch emulation의 전체 suite는
240.7초 걸렸다. 이 runner의 Chrome 자손 process 4개에서
`--no-sandbox`가 없음을 [실제 process 기록](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/chrome-sandbox.json)으로 확인했다.
설정은 `chromiumSandbox=true`이고 opt-out 환경변수는 제거했다.
양쪽에서 현재 **109 WAV response SHA-256/bytes·실제 decode·길이·채널·sample rate**를 전수 확인했다.
[desktop 오디오](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/desktop-audio-integrity.json), [mobile 오디오](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/mobile-audio-integrity.json),
[독립 실측 대조](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/browser-integrity-summary.json): hash/bytes 정확히 일치,
RMS/peak 차이는 브라우저 float scaling을 고려한 PCM16 한 step 이내다.
정상 요청에서 외부 요청·console/page 오류·failed request는 모두 0이며,
실제 여덟 ambience loop, 오류 재시도, UI·키보드·axe, 320/360px 어절·진행률 픽셀 검사도 통과했다.
[desktop runtime](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/desktop-runtime-audit.json), [mobile runtime](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/mobile-runtime-audit.json).
브라우저는 첫 전체 실행에서 통과했다. 이후 artifact 수집의
[08 실패](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/08-browser-integrity.log)는
runtime attachment를 별도 파일로 예상했으나 Playwright JSON 안의 base64 body였기 때문이다.
초기 수집 script와 실패 로그를 보존했고, body 원문을 추출한
[09 성공](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/09-browser-integrity.log)에서
양쪽 109개 WAV와 runtime audit를 대조했다. 테스트 결과를 수정하거나 재분류하지 않았다.
| 현재 화면 | Desktop | Mobile |
| --- | --- | --- |
| 시작 | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/desktop-start.png) | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/mobile-start.png) |
| 게임 | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/desktop-game.png) | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/mobile-game.png) |
| 정상 결과 | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/desktop-normal-results.png) | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/mobile-normal-results.png) |
| 중도 종료 | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/desktop-early-exit.png) | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/mobile-early-exit.png) |
[보존 검사](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/preservation.json): 101 voice·다섯 unaffected ambience·mall 바이트 동일,
이전 `.agent` 파일 484개·읽기 전용 prior-attempt 사본 89개·HEAD 추적 역사 파일 180개 해시 동일.
갱신 허용된 plan/RESULT/current ASR/current summary 4개는 이전 상태를 별도 보존했다.
원본 provenance는 지정 market/cafe 시작점 외에 변경이 없고 dist는 현재 bundle과 바이트 동일하다.
**한계와 인계:** 지정된 자동 VAD acceptance를 충족했다. 사람의 청취 인수와 법적/privacy clearance는
여전히 수행하지 않았으며 empty ASR은 말소리/음악의 부재 증거가 아니다.
mobile은 Chrome viewport/touch emulation이다. Git commit/push/merge/tag/deploy는 하지 않았다.