11 KiB
Find-Word v0.1.1 — 한국어 neural voice 업그레이드 결과
작성일: 2026-10-14 UTC. worktree: /home/hermes/worktrees/find-word-v0.1.1.
브랜치: fix/v0.1.1-neural-voice.
입력 .agent/TASK-v0.1.1.md, .agent/ACCEPTANCE-v0.1.1.md 및 기존 맥락을 읽었다.
입력 문서는 수정하지 않았다. Git commit/push, main merge, 서버 배포는 실행하지 않았다.
구현 결과
- package·lock·UI·index·README·LICENSE·현재 버전 문서·manifest를 0.1.1로 맞췄다.
- 정확한 크레딧
Designed by OZ at Heat-T Medical Inc.와 canonical repositoryhttps://source.ibit.kr/OZ-Group/Find-Word를 유지했다. - Microsoft Edge online TTS, ko-KR-HyunsuMultilingualNeural, edge-tts 7.3.1, rate -6%, pitch -4Hz로 안내 1개·목표 100개를 모두 실제로 새로 생성했다. 기존 WAV를 재포장하거나 재사용하지 않았다.
- pinned
.venv-audio/bin/python과requirements-audio.txt로 생성 절차를 고정했다. 전역 Python의 다른 버전으로 fallback하지 않는다. FFmpeg 실측 버전은6.1.1-3ubuntu5이다. - 임시 MP3를 FFmpeg로 24kHz mono PCM16으로 디코드하고 선형 gain만 적용한다. 파일별 RMS 목표 0.10, 증폭 최대 2배, peak ceiling 0.89. 압축·게이트·음절 편집은 없다.
- 임시 디렉터리에서 101개 전체 검증 후 Linux
renameat2(RENAME_EXCHANGE)로 단일 원자 교환한다. provider 최대 3회, 1/2초 backoff, 시도당 60초 제한. 변환 실패·검증 실패·중단·원자 교체 실패는 기존 자산을 보존한다. - manifest에 원문·SHA-256·bytes·duration·RMS·peak와 provider·voice·prosody·버전· 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, GREEN |
| neural provider/voice/prosody/실측 버전, 101개 PCM·원문·hash·bytes·duration | npm test -- tests/assets.test.ts: 2 failed. 기존 16kHz·누락 metadata/duration을 실제 검출 |
최종 전체 suite에서 두 계약 모두 통과 | RED, GREEN |
| 원자 교체·초기 공개·bounded retry·timeout·기존 자산 보존·secret redaction·실제 MP3 변환·PCM 검증·import 부작용 | Python unittest 16개 모두 RED (subtest 포함 failures=4, errors=20) | 아래 추가 계약을 포함한 18개 통과 | RED, GREEN |
| 앞서 생성한 파일 손상 감지, 다른 edge-tts 버전의 외부 요청 전 거부 | 구현 전 전체 18개 RED (subtest 포함 failures=5, errors=21) | python3 -m unittest discover -s tests/python -v: 18 passed |
추가 RED, GREEN |
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개 실제 생성 로그.
| 항목 | 실제 결과 |
|---|---|
| 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개 |
독립 검사 스크립트는 생성기의 검증 함수를 import하지 않는다. Python wave·해시·앱 원문과 FFprobe/FFmpeg를 별도로 대조한다. 원문 로그, 파일별 수치 JSON.
Chrome 전수 검사 스크립트는 production preview의
WAV를 실제 OfflineAudioContext로 디코드한다. 응답 bytes/hash, 채널, rate, duration,
RMS, peak와 UI/index 버전을 검사한다.
Chrome 로그,
101개 디코드 결과 및 sandbox 상태.
청취용: intro.wav, luck-apple.wav.
최종 검증
환경: Node 26.7.0, npm 11.19.0, Python 3.11.16, Chrome 146.0.7680.80, Linux 6.8.0, glibc 2.39. 실측 환경과 pip check.
| 검증 | 결과 | evidence |
|---|---|---|
npm ci |
PASS, 238 packages 설치 | 로그 |
npm run lint |
PASS, ESLint + TypeScript strict | 최종 로그 |
npm test |
11 files / 60 passed, skip 0; Python 18개 계약 포함 | 로그 |
npm run audio:generate |
101개 신규 생성·검증·원자 교체 성공 | 로그 |
npm run build |
PASS, 101개 해시·런타임 MIT notice 전문 검증 | 로그 |
| sandboxed system Chrome desktop/mobile suite | 12 passed (2.3m), skip 0 | 로그 |
| sandboxed Chrome 101개 전수 디코드 | PASS, Namespace/PID/Network/Seccomp sandbox 활성 | 로그 |
npm audit --audit-level=high |
0 vulnerabilities, npm 개발 의존성 포함 | 로그 |
| 독립 101개 원문/hash/PCM/FFmpeg 검사 | PASS | JSON |
| 소스·문서·manifest 비밀값 패턴 검사 | 40개 텍스트 파일, 0 matches | 기록 |
git diff --check·문서 링크·sandbox 실행 인자·HEAD·역사 기록 최종 검사 |
PASS | 최종 무결성 |
| 과거 evidence + RESULT 해시 대조 | 115개 전부 동일 | baseline, 검사 |
브라우저 전체 명령:
env -u PLAYWRIGHT_NO_SANDBOX CHROME_PATH=/usr/bin/google-chrome DEBUG=pw:browser npm run test:browser.
두 viewport(1280×1000, 360×800)에서 실제 음성으로 10문제 완주·재청취·404 재시도·
키보드·레이아웃·reduced motion·axe·크레딧·MIT HTTP 응답이 모두 통과했다.
시작·게임·결과 axe 위반 0, 외부 페이지 요청 0, 페이지 JS 예외 0이다.
로컬 preview만 테스트 중 열었으며 서버 배포는 하지 않았다.
Chrome 전수 검사에서는 chrome://sandbox의 Namespace·Seccomp 상태도 실제 확인했다.
headless 브라우저는 실제 파일을 디코드하고 Web Audio를 실행하지만 물리적 스피커
청취를 대신하지 않는다. Chrome stderr의 시스템 GPU/GCM 진단과 페이지 JS 예외는 구분한다.
스크린샷: desktop 시작, 게임, 결과, mobile 시작, 게임, 결과.
변경 파일과 기록 보존
- 앱 표기:
src/App.tsx,index.html,package.json,package-lock.json. - 생성:
scripts/generate_audio.py, 신규scripts/neural_audio.py,requirements-audio.txt,.gitignore,eslint.config.js의 venv 제외. - 자산:
public/audio/*.wav101개와manifest.json. - 계약 테스트:
tests/assets.test.ts,tests/App.test.tsx, 신규tests/neural-release.test.ts,tests/generator.test.ts,tests/python/test_neural_audio.py. - 기존 browser tests는 새 스크린샷 경로와 v0.1.1 실측 길이 주석만 변경했다.
tests/browser/game.spec.ts,tests/browser/ui.spec.ts. 기존 assertion/timeout은 유지했다. - 문서:
README.md,LICENSE,docs/AUDIO_PROVENANCE.md,docs/ARCHITECTURE.md,docs/DEPLOYMENT.md. - 이번 기록:
.agent/RESULT-v0.1.1.md,.agent/evidence-v0.1.1/.
이전 .agent/RESULT.md, .agent/evidence/의 파일은 수정하지 않았다.
스크린샷도 새 경로에만 저장한다. 입력 TASK/ACCEPTANCE의 시작 상태는 untracked였으며
Codex가 새로 작성한 입력 문서가 아니다. 전체 파일 목록은
changed-files.txt에 별도로 남긴다.
남은 청감·provider 한계
- 자동 검사와 실제 브라우저 재생은 완료했지만 사람의 청감 평가를 수행한 것은 아니다. 한국어 발음·유사 자모 구분·웃음·소음 혼합 난이도는 대표 샘플과 게임으로 직접 평가해야 한다.
- 원격 모델/서비스는 고정할 수 없다. pinned client로 절차를 재현해도 다음 생성의 바이트가 같다는 보장은 없다. provider 장애·제한·음성 목록 변경 시 명확히 실패한다.
- edge-tts의 LGPLv3(일부 MIT)와 FFmpeg의 빌드별 LGPL/GPL은 provider 서비스 조건 및 음성 출력의 권리와 별개다. 생성 음성의 독점 권리나 포괄적 상업 이용 허락을 주장하지 않는다. 근거 링크·확인일·조건의 한계는 AUDIO_PROVENANCE에 기록했다.
- 원자 교체는 Linux와 해당 파일시스템의 지원이 필요하다. SIGKILL/전원 손실 때 임시 숨김 폴더는 남을 수 있고 전원 손실 내구성까지 보장하지 않는다. 일반 실패·Ctrl-C는 정리한다.
- 실제 모바일 Safari/물리적 오디오 장치 청취는 이번 Chrome 검증 범위 밖이다.