sample rate·생성 시각·실측 도구 버전을 남겼다. 인증 헤더·쿠키·API 키는 저장하지 않는다.
- 기존 생성 도구의 캐시/다운로드/문서/manifest 의존을 제거했다. 기존 v0.1 기록은 보존했다.
- 10개 mechanic, 100문장, 8환경, 5SNR, 객관식·주관식, IME·재생 취소·재청취·
실패 복구 흐름은 기존 소스와 테스트를 보존했다. 앱은 로컬 WAV만 요청한다.
## 구현 전에 확인한 실제 RED → GREEN
RED를 모두 실행한 뒤 구현했다. 생성기의 새 함수 계약은 `NotImplementedError`만
발생시키는 비동작 stub으로 선언했고, 기존 명령 진입점은 RED 때 그대로 두어 import
부작용도 실패로 확인했다. stub에 성공 경로를 미리 구현하지 않았다.
RED는 각 명령 종료 코드 **1**, GREEN은 **0**이다. 실패 로그를 덮어쓰지 않았다.
| 계약 | 실제 RED | 실제 GREEN | 원문 evidence |
| --- | --- | --- | --- |
| UI/package/lock/index/문서 버전 및 pinned 생성·권리 문서 | `npm test -- tests/neural-release.test.ts tests/App.test.tsx -t '0.1.1\|브랜드\|고정 venv'`: 3 failed, 기존 12개는 필터로 제외 | 동일 명령 3 passed | [RED](evidence-v0.1.1/01-version-docs-red.log), [GREEN](evidence-v0.1.1/08-version-docs-green.log) |
| neural provider/voice/prosody/실측 버전, 101개 PCM·원문·hash·bytes·duration | `npm test -- tests/assets.test.ts`: 2 failed. 기존 16kHz·누락 metadata/duration을 실제 검출 | 최종 전체 suite에서 두 계약 모두 통과 | [RED](evidence-v0.1.1/02-assets-red.log), [GREEN](evidence-v0.1.1/11-full-test.log) |
| 원자 교체·초기 공개·bounded retry·timeout·기존 자산 보존·secret redaction·실제 MP3 변환·PCM 검증·import 부작용 | Python unittest 16개 모두 RED (subtest 포함 failures=4, errors=20) | 아래 추가 계약을 포함한 18개 통과 | [RED](evidence-v0.1.1/03-generator-red.log), [GREEN](evidence-v0.1.1/05-generator-green.log) |
| 앞서 생성한 파일 손상 감지, 다른 edge-tts 버전의 외부 요청 전 거부 | 구현 전 전체 18개 RED (subtest 포함 failures=5, errors=21) | `python3 -m unittest discover -s tests/python -v`: 18 passed | [추가 RED](evidence-v0.1.1/04-generator-additional-red.log), [GREEN](evidence-v0.1.1/05-generator-green.log) |
Python 계약은 fake provider/작은 PCM fixture로 실패를 강제한다. 원자 디렉터리 교체와
FFmpeg MP3 변환은 테스트에서도 실제 실행한다. 이 fixture는 public 음성에 사용하지
않았다. 실제 provider의 101개 생성은 다음 별도 로그에 있으며 실패/재시도는 0회였다.
전체 `npm test`는 Python 계약 runner도 포함하며 **skip 없이** 통과했다.
## 실제 음성 생성·전수 독립 검사
명령: `npm run audio:generate`.
생성 완료: **2026-10-14T06:55:41.751382+00:00**.
[101개 실제 생성 로그](evidence-v0.1.1/06-audio-generate.log).
| 항목 | 실제 결과 |
| --- | --- |
| WAV 개수 | 101개, 101개 해시 모두 고유하고 v0.1의 같은 파일과 모두 다름 |
| WAV 합계 | **34,898,524바이트** (약 33.282MiB), 48MiB 예산 이내 |
| PCM | 전부 **24,000Hz · mono · signed 16-bit PCM** |
| duration | **3.816–11.256초**, 안내 7.056초 |
| RMS 범위 | **0.0999998534–0.1000000486** |
| peak 범위 | **0.3692016602–0.6843261719**, full-scale 0개 |
| 원문·bytes·duration·SHA-256 | 101/101 일치 |
| FFprobe 포맷 확인 + FFmpeg 전체 디코드 | 101/101 성공 |
| Chrome 실제 WAV 디코드·해시·레벨 검사 | 101/101 성공, 외부 페이지 요청 0·JS 예외 0 |
| 남은 임시 MP3/공개 staging 폴더 | 0개 |
[독립 검사 스크립트](evidence-v0.1.1/verify_audio.py)는 생성기의 검증 함수를
import하지 않는다. Python wave·해시·앱 원문과 FFprobe/FFmpeg를 별도로 대조한다.
`AUDIO_ASSET_VERSION`과 `audioAssetUrl`을 출제·오디오 엔진이 함께 사용한다.
WAV/manifest 및 package/lock은 변경하지 않았다.
발견 원인은 **음성 내용 교체 후에도 요청 URL이 같았던 것**이다. 사용자 보고의
배포 Nginx `/audio` 7일 캐시에서 기존 사용자는 이전 음성을 계속 받을 수 있었다.
기존 배포 문서의 재검증 가정과 실제 설정이 달랐으며, 브라우저 테스트의
전체 URL `.endsWith('.wav')`와 404 glob도 query string을 놓쳤다.
원격 서버에는 접속하지 않고 소스·로컬 production build에서 재현했다.
앱 구현을 수정하기 전에 unit/browser 계약을 작성하고 실제 RED를 확인했다.
RED 때 앱 소스가 그대로였다는 기록은 [04-red-state.json](evidence-v0.1.1/cache-regression/04-red-state.json)에 있다.
| 계약/검증 | 결과 | 새 evidence |
| --- | --- | --- |
| 목표 100개 버전·intro/target fetch·각각 404 retry·query pathname 판정 | RED 5 failed / 12 passed → GREEN 17 passed | [RED](evidence-v0.1.1/cache-regression/01-unit-red.log), [GREEN](evidence-v0.1.1/cache-regression/05-unit-green.log) |
| 실제 브라우저 요청/정답 mapping·intro/target 404·101개 URL | 구현 전 desktop RED 4 failed → 전체 desktop/mobile GREEN 16 passed (2.6m) | [RED](evidence-v0.1.1/cache-regression/03-browser-red.log), [GREEN](evidence-v0.1.1/cache-regression/09-browser-green.log) |
| `npm run lint` | PASS | [로그](evidence-v0.1.1/cache-regression/06-lint.log) |
| `npm test` | 12 files / 65 passed, skip 0; Python 18개 계약 포함 | [로그](evidence-v0.1.1/cache-regression/07-full-test.log) |