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

11 KiB
Raw Blame History

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 repository https://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/*.wav 101개와 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 검증 범위 밖이다.