Files
Find-Word/.agent/evidence-v0.2.3/privacy-screening/retry-market24-cafe0/before-result.md
T

409 lines
39 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자 현장음은 각각의 라이선스를 유지한다.
**현재 최종 상태:** privacy excerpt 구현/재생성 완료, **ASR zero-segment acceptance 미달**. 단위 123 통과 / 1 실패이며 실패는 새 ASR acceptance다.
lint/build·Python 36개·sandboxed Chrome 30개 PASS, npm audit 0건. 최종 Chrome 결과와 상세 근거는 맨 아래 **최종 privacy excerpt refinement** 절에 있다.
앞선 구현/독립 리뷰의 기록은 당시 결과로 보존하며 최신 acceptance 성공으로 오인하지 않는다.
## 최초 구현 내용 (아래 리뷰 수정 내역으로 보완)
- 정확히 세 개의 서로 다른 문제/단어를 한 단계에서 출제한다. 시작 단계 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 excerpt refinement — 2026-10-14
**요청한 ASR-empty 조건은 아직 충족하지 못했다.** 지정된 새 구간과 provenance, generator,
전체 원자 재생성은 반영했지만 최종 normalized WAV의 ASR에 market/cafe candidate segments가 남았다.
해당 acceptance test를 완화하거나 skip하지 않았으며 현재 `npm test`는 **123 통과 / 1 실패**다.
정확한 이전 ASR 설정이 제공되지 않아 Neo의 scan과 동등한 방법을 재현했다고 주장하지 않는다.
commit/push/merge/tag/deploy는 하지 않았다.
### 변경과 원자 재생성
- `.agent/AMBIENCE-SOURCES-v0.2.3.json`의 8개 항목에 `selectedSegmentStartSeconds`를 추가했다.
street=35, mall=36, theater=45, department=90, market=60, subway=65, cafe=180, rain=180.
원본 hash/bytes/직접 미디어 길이/권리 정보는 바꾸지 않았다. generator는 별도 시작점 map을 제거하고
계획 값을 읽는다. 누락/비수치/bool/비유한값/음수/12초 범위 초과는 첫 변환 전에 실패한다.
- 임의의 fractional start와 정확한 source 끝 경계를 주는 테스트로 hidden hardcoded map이 없음을 확인한다.
성공/실패 시 계획을 변경하지 않고 manifest의 선택값/실제 conversion 시작점/notice 구간이 일치해야 한다.
build verifier는 public/dist manifest와 notice를 함께 변조한 시작점·길이도 거부한다.
- 첫 변환 전에 8개 원본 SHA-256·bytes·ffprobe duration을 전수 대조했고 generator의 preflight도 다시 수행했다.
같은 파일시스템 staging에서 전체 8 WAV·manifest·notice를 검증한 뒤 원자 디렉터리 교환했다.
[재생성 실측](evidence-v0.2.3/privacy-screening/regeneration.json), [성공 로그](evidence-v0.2.3/privacy-screening/09-regeneration.log).
- 실제 Ogg 입력 seek에서 market은 287,958, cafe는 287,809 frame으로 짧아져 최초 생성이 중단됐다.
output-side seek 재시도만으로 cafe는 해결되지 않았다. 각 실패와 [기존 bundle 보존](evidence-v0.2.3/privacy-screening/failed-regeneration-preservation.json)을 남겼다.
최종 fallback은 처음부터 디코드한 24kHz PCM에서 승인 시작 sample부터 288,000개를 선택하고
sample 수로 output PTS를 재설정한다. 패딩/시간 변형 없이 실제 `seekMethod`를 manifest에 기록한다.
진짜 원본 부족은 여전히 거부한다. [진단](evidence-v0.2.3/privacy-screening/seek-diagnostics.json),
[추가 진단](evidence-v0.2.3/privacy-screening/seek-diagnostics-followup.json).
- 기존 입력 seek가 완전한 다섯 WAV는 기존 byte를 재현했다. 모든 최종 WAV는 12초/24kHz/mono/PCM16,
576,044 bytes이며 양끝 50ms fade와 동일 bounded normalization을 유지한다.
| 최종 자산 | 원본 구간 | 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 | 60–72s | 0.07999756 | 0.50723267 | `7a7a980845a4aade2a7e8a108eae746220a4688e92792ee93d3deb76b2a762f6` |
| subway.wav | 65–77s | 0.07999755 | 0.42529297 | `9faf70b68617d267b367a0f3692ed90015458543ab4b0844f6c704e6a724728f` |
| cafe.wav | 180–192s | 0.07999757 | 0.44708252 | `802a956e91b85a2fdd0e1f161d75bfada7a53499bcb57ba7f578411480ded216` |
| rain.wav | 180–192s | 0.01138463 | 0.10241699 | `f4315084e820f742b0709de5bb0d1abd3d5af1730620e08c2948b70347356dd9` |
위 표가 최신 바이트다. 문서 상단의 최초 구현 표와 앞선 리뷰 기록은 당시 값으로 그대로 보존했다.
### ASR 증거와 미충족 조건
[final-asr.json](evidence-v0.2.3/privacy-screening/final-asr.json)은 **정확한 최종 8 WAV 전수 검사**다.
faster-whisper 1.2.1, Systran faster-whisper-tiny multilingual revision
`d90ca5fe260221311c53c58e660288d3deb8d356`, CPU int8/4 threads, beam 5, random seed 0,
자동 language detection, package 기본 Silero VAD/decoding threshold를 사용했다.
모델/VAD 파일 해시·의존성 버전·전체 설정·변환 옵션·각 WAV/source hash와 반환 segment/info를 보존했다.
VAD의 무한대 기본 옵션은 JSON 호환 `{"nonFiniteFloat":"Infinity"}`로 의미를 명시해 저장했다.
| 환경 | VAD 활성 최종 segment 수 | 별도 VAD 미사용 segment 수 |
| --- | --- | --- |
| street | 0 | 0 |
| mall | 0 | 1 |
| theater | 0 | 1 |
| department | 0 | 1 |
| market | 3 | 1 |
| subway | 0 | 1 |
| cafe | 1 | 1 |
| rain | 0 | 1 |
mall은 비었지만 **market 3개, cafe 1개가 남아 `requiredEmptyPassed: false`**다.
[별도 VAD 미사용 결과](evidence-v0.2.3/privacy-screening/no-vad-asr.json)는 세 변경 구간 모두에서 text가 나왔다.
VAD 비교는 방법의 영향을 확인하기 위한 추가 검사이며 이전 설정과 같다는 증거는 없다.
threshold 튜닝이나 반환 segment 삭제로 조건을 충족시키지 않았다. 원본 구간을 검사한 scan의 빈 결과를
정규화된 최종 WAV의 빈 결과로 치환하지 않는다. 정확한 Neo 명령/설정 확인을 요청했으며 최종 파생본 재검토가 필요하다.
**Tiny-model ASR은 screening signal일 뿐, 사람의 청취 인수나 법적/개인정보 clearance가 아니다.**
빈 segment도 말소리/음악/개인 대화의 부재를 증명하지 않으며 출력 text/language도 환각일 수 있다.
충실한 transcript나 실제 화자의 발언이라고 주장하지 않는다. 사람의 최종 청취 인수는 미실행이다.
[기존 후보 scan](evidence-v0.2.3/privacy-screening/candidate-scan-original.json)과
[기존 파생본 ASR](evidence-v0.2.3/privacy-screening/prior-derivatives-asr-original.json)을 `/tmp` 원문 그대로 보존했다.
[provenance](evidence-v0.2.3/privacy-screening/earlier-asr-provenance.json)는 원래 경로·크기·hash·수정 시각,
Neo 제공이라는 맥락, 당시 계획/manifest와의 연관을 기록한다. raw JSON 자체에는 모델 revision/decoding 설정/
입력 hash가 없어 독립적으로 검증한 연결이라고 주장하지 않는다. 휴리스틱 text를 편집하거나 전사로 확정하지 않았다.
### 새 RED/GREEN과 실패 기록
모든 새 기록은 `evidence-v0.2.3/privacy-screening/`에 있다. 이전 로그를 덮어쓰지 않았다.
| 계약/실행 | RED/실패 | GREEN/현재 결과 |
| --- | --- | --- |
| 계획 provenance·manifest·notice·build 변조 | [01 RED](evidence-v0.2.3/privacy-screening/01-provenance-RED.log): 4 실패 | [11 GREEN](evidence-v0.2.3/privacy-screening/11-provenance-GREEN.log): 관련 24 통과 |
| plan 직접 사용·immutability·범위 검사 | [02 RED](evidence-v0.2.3/privacy-screening/02-generator-RED.log) | [02 GREEN](evidence-v0.2.3/privacy-screening/02-generator-GREEN.log): 17 통과 |
| served manifest·notice·plan 일치 | [03 browser RED](evidence-v0.2.3/privacy-screening/03-browser-RED.log): desktop/mobile 실패 | 아래 전체 Chrome 결과 |
| ASR/바이트 보존 evidence 계약 | [04 RED](evidence-v0.2.3/privacy-screening/04-screening-RED.log) | 보존 PASS; ASR-empty는 여전히 FAIL |
| 짧은 seek와 full decode fallback | [06 RED](evidence-v0.2.3/privacy-screening/06-seek-RED.log), [08 RED](evidence-v0.2.3/privacy-screening/08-sample-trim-RED.log) | [08 GREEN](evidence-v0.2.3/privacy-screening/08-sample-trim-GREEN.log) |
| 실제 변환의 짧은 PCM | [05 실패](evidence-v0.2.3/privacy-screening/05-regeneration.log), [07 실패](evidence-v0.2.3/privacy-screening/07-regeneration.log) | [09 성공](evidence-v0.2.3/privacy-screening/09-regeneration.log) |
| ASR exact results·zero-segment 조건 | [10 ungated 실패](evidence-v0.2.3/privacy-screening/10-final-asr.log), [13 criterion RED](evidence-v0.2.3/privacy-screening/13-screening-criterion-RED.log) | [16 VAD 결과](evidence-v0.2.3/privacy-screening/16-final-asr-vad-json.log): criterion FAIL |
`14-final-asr-vad.log`는 ASR을 완료했지만 VAD 기본 Infinity를 strict JSON으로 저장하다 실패한 중간 실행이다.
그 로그·초기 스크립트·빈 출력 artifact도 보존하고, tagged value 직렬화로 다시 실행한 16번 결과가 최종 JSON이다.
ASR가 실패한 사실과 JSON 기록 문제를 분리한다.
### 최종 검증과 보존
| 검사 | 실제 결과 | 근거 |
| --- | --- | --- |
| lint | PASS | [18-final-lint](evidence-v0.2.3/privacy-screening/18-final-lint.log) |
| 모든 unit tests | **123 PASS / 1 FAIL**, skip 0; 유일한 실패는 ASR-empty acceptance | [17-unit](evidence-v0.2.3/privacy-screening/17-unit.log) |
| 모든 Python tests | **36 PASS**, skip 0 | [12-python](evidence-v0.2.3/privacy-screening/12-python.log) |
| build | PASS, 101 voice + 8 ambience 및 notices | [12-build](evidence-v0.2.3/privacy-screening/12-build.log) |
| npm audit | **취약점 0**, exit 0 | [audit JSON](evidence-v0.2.3/privacy-screening/npm-audit.json) |
[보존 전수 검사](evidence-v0.2.3/privacy-screening/preservation.json): **101 voice + 다섯 ambience WAV 바이트 동일**,
변경 WAV는 mall/market/cafe뿐이다. 시작 당시 399 `.agent` 파일 중 요청으로 갱신한 plan/RESULT를 제외한
**397개는 SHA-256 동일**하다. HEAD에 추적된 과거 `.agent` 180개도 Git blob hash가 같다.
계획/manifest/notice/RESULT의 변경 전 사본과 모든 새 실패/성공 로그를 별도로 보존했다.
최종 sandboxed system Chrome **30 PASS / 0 FAIL / 0 SKIP / 0 FLAKY**, 4.0분:
[실행 로그](evidence-v0.2.3/privacy-screening/15-browser.log),
[JSON 결과](evidence-v0.2.3/privacy-screening/browser-results.json).
Chrome 146.0.7680.80, desktop 1280×1000 / mobile 360×800 touch emulation에서
최종 served manifest/notice의 승인 구간을 대조하고 109개 WAV response hash·실제 decode·RMS·peak를 확인했다.
[desktop 무결성](evidence-v0.2.3/privacy-screening/desktop-audio-integrity.json),
[mobile 무결성](evidence-v0.2.3/privacy-screening/mobile-audio-integrity.json).
SHA-256/bytes는 정확히 일치하며 Chrome float decode와 PCM/32768 실측 RMS·peak의 차이는
[독립 대조](evidence-v0.2.3/privacy-screening/browser-integrity-summary.json)에서 PCM16 한 step 이내다.
두 기기의 정상 요청에서 외부 요청/console/page 오류/failed request는 모두 0이다.
실제 8 ambience loop·재시도·UI·axe 검증과 PNG는 새 `browser-final/` 아래 보존했다.
이 suite runner의 Chrome 자손 process 21개에서 `--no-sandbox` token이 없음을
[process 근거](evidence-v0.2.3/privacy-screening/chrome-sandbox.json)로 확인했다.
**인계:** 요청한 세 시작점은 구현했으나 market/cafe의 ASR-empty 요구가 미충족이므로 최종 인수 완료가 아니다.
정확한 이전 ASR 설정/명령을 확보해 방법 차이를 확인하고 최종 WAV를 검토해야 한다.
기본 threshold와 반환 결과를 조작하지 않았다. 사람의 청취 인수·법적/privacy clearance는 수행하지 않았고,
원격 전송·Git commit/push/merge/tag/deploy도 하지 않았다. 역사 evidence와 보호 WAV는 그대로다.