feat: improve Korean neural voice quality for v0.1.1
@@ -0,0 +1,21 @@
|
|||||||
|
# ACCEPTANCE — Find-Word v0.1.1
|
||||||
|
|
||||||
|
- [ ] package/UI/index/README/manifest 버전 0.1.1
|
||||||
|
- [ ] 정확한 크레딧 유지
|
||||||
|
- [ ] `ko-KR-HyunsuMultilingualNeural`, edge-tts 7.3.1 기반 101개 신규 WAV
|
||||||
|
- [ ] 24kHz mono 16-bit PCM
|
||||||
|
- [ ] 모든 WAV 비무음, full-scale peak 없음, 합리적 duration
|
||||||
|
- [ ] manifest 원문·hash·bytes·duration·provider·voice·prosody·tool version
|
||||||
|
- [ ] 임시 디렉터리 전체 생성 후 원자 교체
|
||||||
|
- [ ] bounded retry와 실패 시 기존 자산 보존
|
||||||
|
- [ ] runtime 외부 TTS/API 요청 없음
|
||||||
|
- [ ] eSpeak 생성 의존 제거
|
||||||
|
- [ ] audio provenance와 생성 도구 라이선스·provider 한계 문서
|
||||||
|
- [ ] 기존 10개 mechanic/100문장/8환경/5SNR/choice+text 보존
|
||||||
|
- [ ] lint PASS
|
||||||
|
- [ ] 전체 tests PASS
|
||||||
|
- [ ] build PASS
|
||||||
|
- [ ] desktop/mobile browser tests PASS
|
||||||
|
- [ ] audit high 0
|
||||||
|
- [ ] `.agent/RESULT-v0.1.1.md`
|
||||||
|
- [ ] Codex는 commit/push/deploy 미실행
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
# 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](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를 별도로 대조한다.
|
||||||
|
[원문 로그](evidence-v0.1.1/12-independent-audio.log),
|
||||||
|
[파일별 수치 JSON](evidence-v0.1.1/audio-integrity.json).
|
||||||
|
|
||||||
|
[Chrome 전수 검사 스크립트](evidence-v0.1.1/verify_browser.mjs)는 production preview의
|
||||||
|
WAV를 실제 `OfflineAudioContext`로 디코드한다. 응답 bytes/hash, 채널, rate, duration,
|
||||||
|
RMS, peak와 UI/index 버전을 검사한다.
|
||||||
|
[Chrome 로그](evidence-v0.1.1/15-browser-audio.log),
|
||||||
|
[101개 디코드 결과 및 sandbox 상태](evidence-v0.1.1/browser-audio-integrity.json).
|
||||||
|
|
||||||
|
청취용: [intro.wav](../public/audio/intro.wav), [luck-apple.wav](../public/audio/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-v0.1.1/18-environment.log).
|
||||||
|
|
||||||
|
| 검증 | 결과 | evidence |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `npm ci` | PASS, 238 packages 설치 | [로그](evidence-v0.1.1/00-npm-ci.log) |
|
||||||
|
| `npm run lint` | PASS, ESLint + TypeScript strict | [최종 로그](evidence-v0.1.1/16-final-lint.log) |
|
||||||
|
| `npm test` | **11 files / 60 passed**, skip 0; Python 18개 계약 포함 | [로그](evidence-v0.1.1/11-full-test.log) |
|
||||||
|
| `npm run audio:generate` | **101개 신규 생성·검증·원자 교체 성공** | [로그](evidence-v0.1.1/06-audio-generate.log) |
|
||||||
|
| `npm run build` | PASS, 101개 해시·런타임 MIT notice 전문 검증 | [로그](evidence-v0.1.1/13-build.log) |
|
||||||
|
| sandboxed system Chrome desktop/mobile suite | **12 passed (2.3m)**, skip 0 | [로그](evidence-v0.1.1/14-browser.log) |
|
||||||
|
| sandboxed Chrome 101개 전수 디코드 | PASS, Namespace/PID/Network/Seccomp sandbox 활성 | [로그](evidence-v0.1.1/15-browser-audio.log) |
|
||||||
|
| `npm audit --audit-level=high` | **0 vulnerabilities**, npm 개발 의존성 포함 | [로그](evidence-v0.1.1/10-audit.log) |
|
||||||
|
| 독립 101개 원문/hash/PCM/FFmpeg 검사 | PASS | [JSON](evidence-v0.1.1/audio-integrity.json) |
|
||||||
|
| 소스·문서·manifest 비밀값 패턴 검사 | 40개 텍스트 파일, 0 matches | [기록](evidence-v0.1.1/17-security-history.json) |
|
||||||
|
| `git diff --check`·문서 링크·sandbox 실행 인자·HEAD·역사 기록 최종 검사 | PASS | [최종 무결성](evidence-v0.1.1/19-final-integrity.json) |
|
||||||
|
| 과거 evidence + RESULT 해시 대조 | **115개 전부 동일** | [baseline](evidence-v0.1.1/baseline.json), [검사](evidence-v0.1.1/17-security-history.json) |
|
||||||
|
|
||||||
|
브라우저 전체 명령:
|
||||||
|
`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 시작](evidence-v0.1.1/desktop-home.png),
|
||||||
|
[게임](evidence-v0.1.1/desktop-game.png), [결과](evidence-v0.1.1/desktop-results.png),
|
||||||
|
[mobile 시작](evidence-v0.1.1/mobile-home.png), [게임](evidence-v0.1.1/mobile-game.png),
|
||||||
|
[결과](evidence-v0.1.1/mobile-results.png).
|
||||||
|
|
||||||
|
## 변경 파일과 기록 보존
|
||||||
|
|
||||||
|
- 앱 표기: `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](evidence-v0.1.1/changed-files.txt)에 별도로 남긴다.
|
||||||
|
|
||||||
|
## 남은 청감·provider 한계
|
||||||
|
|
||||||
|
- 자동 검사와 실제 브라우저 재생은 완료했지만 **사람의 청감 평가를 수행한 것은 아니다**.
|
||||||
|
한국어 발음·유사 자모 구분·웃음·소음 혼합 난이도는 대표 샘플과 게임으로 직접 평가해야 한다.
|
||||||
|
- 원격 모델/서비스는 고정할 수 없다. pinned client로 절차를 재현해도 다음 생성의
|
||||||
|
바이트가 같다는 보장은 없다. provider 장애·제한·음성 목록 변경 시 명확히 실패한다.
|
||||||
|
- edge-tts의 LGPLv3(일부 MIT)와 FFmpeg의 빌드별 LGPL/GPL은 provider 서비스 조건 및
|
||||||
|
음성 출력의 권리와 별개다. 생성 음성의 독점 권리나 포괄적 상업 이용 허락을 주장하지 않는다.
|
||||||
|
근거 링크·확인일·조건의 한계는 [AUDIO_PROVENANCE](../docs/AUDIO_PROVENANCE.md)에 기록했다.
|
||||||
|
- 원자 교체는 Linux와 해당 파일시스템의 지원이 필요하다. SIGKILL/전원 손실 때 임시
|
||||||
|
숨김 폴더는 남을 수 있고 전원 손실 내구성까지 보장하지 않는다. 일반 실패·Ctrl-C는 정리한다.
|
||||||
|
- 실제 모바일 Safari/물리적 오디오 장치 청취는 이번 Chrome 검증 범위 밖이다.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# TASK — Find-Word v0.1.1 Korean neural voice upgrade
|
||||||
|
|
||||||
|
## 사용자 지시
|
||||||
|
|
||||||
|
현재 v0.1의 한국어 음성이 제대로 만들어지지 않았다. 음성 품질을 높이고 샘플을 다시 생성하며 버전을 0.1.1로 올린다. 모든 소스는 Gitea에 push 및 main merge할 예정이다. Codex는 구현만 하고 commit/push/deploy는 하지 않는다.
|
||||||
|
|
||||||
|
## 고정 계약
|
||||||
|
|
||||||
|
- 정확한 크레딧: `Designed by OZ at Heat-T Medical Inc.`
|
||||||
|
- canonical repository: `https://source.ibit.kr/OZ-Group/Find-Word`
|
||||||
|
- 작업 브랜치: `fix/v0.1.1-neural-voice`
|
||||||
|
- 버전: UI/package/docs/manifest 모두 `0.1.1`
|
||||||
|
- 한국어 상세 소스 주석 유지
|
||||||
|
- 기존 10개 mechanic, 100개 문장 조합, 8개 환경, 5단계 SNR, 객관식·주관식 기능 보존
|
||||||
|
|
||||||
|
## 선택한 생성 방식
|
||||||
|
|
||||||
|
- 직접 생성한 사전 샘플에서 사용 가능한 고품질 음성: `ko-KR-HyunsuMultilingualNeural`
|
||||||
|
- 생성 클라이언트: `edge-tts==7.3.1`
|
||||||
|
- 권장 prosody: rate `-6%`, pitch `-4Hz`
|
||||||
|
- 앱 런타임에는 외부 TTS/API 요청이 없어야 한다.
|
||||||
|
- 생성 시 임시 MP3를 받아 ffmpeg로 24kHz mono PCM s16 WAV로 변환한다.
|
||||||
|
- 음성 간 레벨 편차와 peak를 완화하되 발음을 훼손하는 과도한 DSP를 하지 않는다.
|
||||||
|
- 네트워크·provider 오류에 bounded retry와 명확한 실패를 제공한다.
|
||||||
|
- 부분 실패가 기존 public/audio를 훼손하지 않도록 임시 디렉터리에서 전체 생성·검증 후 원자 교체한다.
|
||||||
|
- API 키·쿠키·인증정보를 저장하지 않는다.
|
||||||
|
|
||||||
|
## 품질·재현성 요구사항
|
||||||
|
|
||||||
|
- intro 1개 + 목표 문장 100개, 총 101개를 모두 새로 생성한다.
|
||||||
|
- manifest에 version, provider, voice, rate, pitch, edge-tts version, ffmpeg version, sample rate, SHA-256, bytes, duration, 원문을 기록한다.
|
||||||
|
- 24kHz mono 16-bit PCM, 비무음, peak full-scale 미도달, 비정상적으로 짧거나 긴 파일 없음.
|
||||||
|
- `audio:generate`는 pinned venv의 Python으로 재현 가능해야 하고 README/AUDIO_PROVENANCE에 설치·생성 절차와 provider 의존성·한계를 기록한다.
|
||||||
|
- `public/audio/intro.wav`와 대표 샘플 `luck-apple.wav`가 청취용 샘플이다.
|
||||||
|
- eSpeak cache/download 경로·문서·manifest 의존성을 제거한다. 과거 evidence는 당시 기록이므로 내용 변조 없이 보존한다.
|
||||||
|
- 생성 도구의 오픈소스 라이선스와 서비스 사용 조건을 과장 없이 문서화한다. 프로젝트가 음성 자체에 독점 권리를 주장한다고 쓰지 않는다.
|
||||||
|
|
||||||
|
## TDD
|
||||||
|
|
||||||
|
1. 버전 0.1.1 계약 테스트 RED
|
||||||
|
2. neural provider/voice/manifest 계약 RED
|
||||||
|
3. 24kHz·비무음·peak·duration·101개 integrity RED
|
||||||
|
4. generator atomicity/retry/unsafe secret absence 테스트 RED
|
||||||
|
5. 기존 full suite와 browser suite GREEN
|
||||||
|
|
||||||
|
## 최종 검증
|
||||||
|
|
||||||
|
- npm ci
|
||||||
|
- npm run lint
|
||||||
|
- npm test
|
||||||
|
- npm run audio:generate 또는 명시적 생성 명령
|
||||||
|
- npm run build
|
||||||
|
- sandboxed system Chrome browser tests
|
||||||
|
- npm audit --audit-level=high
|
||||||
|
- 101개 manifest/hash/PCM 독립 검사
|
||||||
|
|
||||||
|
## 기록
|
||||||
|
|
||||||
|
`.agent/RESULT-v0.1.1.md`에 RED/GREEN, 변경 파일, 생성 provider/voice, 자산 크기, 최종 결과, 남은 청감·provider 위험을 기록한다.
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
Collecting edge-tts==7.3.1
|
||||||
|
Using cached edge_tts-7.3.1-py3-none-any.whl.metadata (6.1 kB)
|
||||||
|
Collecting aiohttp<4.0.0,>=3.8.0 (from edge-tts==7.3.1)
|
||||||
|
Using cached aiohttp-3.14.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (8.3 kB)
|
||||||
|
Collecting certifi>=2023.11.17 (from edge-tts==7.3.1)
|
||||||
|
Using cached certifi-2026.7.22-py3-none-any.whl.metadata (2.5 kB)
|
||||||
|
Collecting tabulate<1.0.0,>=0.4.4 (from edge-tts==7.3.1)
|
||||||
|
Using cached tabulate-0.10.0-py3-none-any.whl.metadata (40 kB)
|
||||||
|
Collecting typing-extensions<5.0.0,>=4.1.0 (from edge-tts==7.3.1)
|
||||||
|
Using cached typing_extensions-4.16.0-py3-none-any.whl.metadata (3.3 kB)
|
||||||
|
Collecting aiohappyeyeballs>=2.5.0 (from aiohttp<4.0.0,>=3.8.0->edge-tts==7.3.1)
|
||||||
|
Using cached aiohappyeyeballs-2.7.1-py3-none-any.whl.metadata (5.9 kB)
|
||||||
|
Collecting aiosignal>=1.4.0 (from aiohttp<4.0.0,>=3.8.0->edge-tts==7.3.1)
|
||||||
|
Using cached aiosignal-1.4.0-py3-none-any.whl.metadata (3.7 kB)
|
||||||
|
Collecting attrs>=17.3.0 (from aiohttp<4.0.0,>=3.8.0->edge-tts==7.3.1)
|
||||||
|
Using cached attrs-26.1.0-py3-none-any.whl.metadata (8.8 kB)
|
||||||
|
Collecting frozenlist>=1.1.1 (from aiohttp<4.0.0,>=3.8.0->edge-tts==7.3.1)
|
||||||
|
Using cached frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl.metadata (20 kB)
|
||||||
|
Collecting multidict<8.0,>=4.5 (from aiohttp<4.0.0,>=3.8.0->edge-tts==7.3.1)
|
||||||
|
Using cached multidict-7.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (6.2 kB)
|
||||||
|
Collecting propcache>=0.2.0 (from aiohttp<4.0.0,>=3.8.0->edge-tts==7.3.1)
|
||||||
|
Using cached propcache-0.5.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (25 kB)
|
||||||
|
Collecting yarl<2.0,>=1.25.1 (from aiohttp<4.0.0,>=3.8.0->edge-tts==7.3.1)
|
||||||
|
Using cached yarl-1.25.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (111 kB)
|
||||||
|
Collecting idna>=2.0 (from yarl<2.0,>=1.25.1->aiohttp<4.0.0,>=3.8.0->edge-tts==7.3.1)
|
||||||
|
Using cached idna-3.20-py3-none-any.whl.metadata (7.2 kB)
|
||||||
|
Using cached edge_tts-7.3.1-py3-none-any.whl (38 kB)
|
||||||
|
Using cached aiohttp-3.14.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (1.9 MB)
|
||||||
|
Using cached certifi-2026.7.22-py3-none-any.whl (136 kB)
|
||||||
|
Using cached tabulate-0.10.0-py3-none-any.whl (39 kB)
|
||||||
|
Using cached typing_extensions-4.16.0-py3-none-any.whl (45 kB)
|
||||||
|
Using cached aiohappyeyeballs-2.7.1-py3-none-any.whl (15 kB)
|
||||||
|
Using cached aiosignal-1.4.0-py3-none-any.whl (7.5 kB)
|
||||||
|
Using cached attrs-26.1.0-py3-none-any.whl (67 kB)
|
||||||
|
Using cached frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (231 kB)
|
||||||
|
Using cached multidict-7.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (480 kB)
|
||||||
|
Using cached propcache-0.5.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (235 kB)
|
||||||
|
Using cached yarl-1.25.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (117 kB)
|
||||||
|
Using cached idna-3.20-py3-none-any.whl (69 kB)
|
||||||
|
Installing collected packages: typing-extensions, tabulate, propcache, multidict, idna, frozenlist, certifi, attrs, aiohappyeyeballs, yarl, aiosignal, aiohttp, edge-tts
|
||||||
|
Successfully installed aiohappyeyeballs-2.7.1 aiohttp-3.14.5 aiosignal-1.4.0 attrs-26.1.0 certifi-2026.7.22 edge-tts-7.3.1 frozenlist-1.8.0 idna-3.20 multidict-7.1.0 propcache-0.5.4 tabulate-0.10.0 typing-extensions-4.16.0 yarl-1.25.1
|
||||||
|
|
||||||
|
[notice] A new release of pip is available: 24.0 -> 26.2.1
|
||||||
|
[notice] To update, run: python -m pip install --upgrade pip
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
added 238 packages, and audited 239 packages in 3s
|
||||||
|
|
||||||
|
56 packages are looking for funding
|
||||||
|
run `npm fund` for details
|
||||||
|
|
||||||
|
found 0 vulnerabilities
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
test_all_101_are_validated_before_single_atomic_publication (test_neural_audio.GeneratorContract.test_all_101_are_validated_before_single_atomic_publication) ... ERROR
|
||||||
|
test_atomic_exchange_keeps_old_tree_at_staging_path (test_neural_audio.GeneratorContract.test_atomic_exchange_keeps_old_tree_at_staging_path) ... ERROR
|
||||||
|
test_atomic_first_publication (test_neural_audio.GeneratorContract.test_atomic_first_publication) ... ERROR
|
||||||
|
test_cli_import_has_no_generation_or_filesystem_side_effects (test_neural_audio.GeneratorContract.test_cli_import_has_no_generation_or_filesystem_side_effects) ... FAIL
|
||||||
|
test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell (test_neural_audio.GeneratorContract.test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell) ... ERROR
|
||||||
|
test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets (test_neural_audio.GeneratorContract.test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets) ... FAIL
|
||||||
|
test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24 (test_neural_audio.GeneratorContract.test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24) ... ERROR
|
||||||
|
test_ffmpeg_failure_is_not_retried_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_ffmpeg_failure_is_not_retried_and_preserves_existing_files) ... FAIL
|
||||||
|
test_interruption_preserves_existing_files_and_cleans_temporary_data (test_neural_audio.GeneratorContract.test_interruption_preserves_existing_files_and_cleans_temporary_data) ... ERROR
|
||||||
|
test_invalid_pcm_never_reaches_publication (test_neural_audio.GeneratorContract.test_invalid_pcm_never_reaches_publication) ... ERROR
|
||||||
|
test_publication_failure_preserves_existing_files (test_neural_audio.GeneratorContract.test_publication_failure_preserves_existing_files) ... ERROR
|
||||||
|
test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) ...
|
||||||
|
test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': 'intro.wav', 'text': '안내 문장'}) ... ERROR
|
||||||
|
test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': 'intro.wav', 'text': '안내 문장'}) ... ERROR
|
||||||
|
test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': '../escape.wav', 'text': '외부'}) ... ERROR
|
||||||
|
test_timeout_is_bounded_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_timeout_is_bounded_and_preserves_existing_files) ... FAIL
|
||||||
|
test_transient_provider_failures_retry_cleanly_with_bounded_backoff (test_neural_audio.GeneratorContract.test_transient_provider_failures_retry_cleanly_with_bounded_backoff) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) ...
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'amplitude': 0}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'amplitude': 32767}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'rate': 16000}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'channels': 2}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'width': 1}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'duration': 0.2}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'duration': 21}) ... ERROR
|
||||||
|
test_valid_pcm_returns_measured_hash_bytes_duration_and_levels (test_neural_audio.PCMContract.test_valid_pcm_returns_measured_hash_bytes_duration_and_levels) ... ERROR
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_all_101_are_validated_before_single_atomic_publication (test_neural_audio.GeneratorContract.test_all_101_are_validated_before_single_atomic_publication)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 78, in test_all_101_are_validated_before_single_atomic_publication
|
||||||
|
result = self.run_generate()
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_atomic_exchange_keeps_old_tree_at_staging_path (test_neural_audio.GeneratorContract.test_atomic_exchange_keeps_old_tree_at_staging_path)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 202, in test_atomic_exchange_keeps_old_tree_at_staging_path
|
||||||
|
audio.atomic_publish(staged, self.output)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 13, in atomic_publish
|
||||||
|
raise NotImplementedError("원자 디렉터리 교체 미구현")
|
||||||
|
NotImplementedError: 원자 디렉터리 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_atomic_first_publication (test_neural_audio.GeneratorContract.test_atomic_first_publication)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 211, in test_atomic_first_publication
|
||||||
|
audio.atomic_publish(staged, output)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 13, in atomic_publish
|
||||||
|
raise NotImplementedError("원자 디렉터리 교체 미구현")
|
||||||
|
NotImplementedError: 원자 디렉터리 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell (test_neural_audio.GeneratorContract.test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 177, in test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell
|
||||||
|
audio.synthesize_edge("한국어 ; $(literal)", path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 17, in synthesize_edge
|
||||||
|
raise NotImplementedError("pinned edge-tts 실행 미구현")
|
||||||
|
NotImplementedError: pinned edge-tts 실행 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24 (test_neural_audio.GeneratorContract.test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 190, in test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24
|
||||||
|
audio.convert_mp3(mp3, output)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 21, in convert_mp3
|
||||||
|
raise NotImplementedError("ffmpeg 변환 및 선형 레벨 보정 미구현")
|
||||||
|
NotImplementedError: ffmpeg 변환 및 선형 레벨 보정 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_interruption_preserves_existing_files_and_cleans_temporary_data (test_neural_audio.GeneratorContract.test_interruption_preserves_existing_files_and_cleans_temporary_data)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 149, in test_interruption_preserves_existing_files_and_cleans_temporary_data
|
||||||
|
self.run_generate(synthesize=interrupt)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_invalid_pcm_never_reaches_publication (test_neural_audio.GeneratorContract.test_invalid_pcm_never_reaches_publication)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 132, in test_invalid_pcm_never_reaches_publication
|
||||||
|
self.run_generate(convert=invalid)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_publication_failure_preserves_existing_files (test_neural_audio.GeneratorContract.test_publication_failure_preserves_existing_files)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 155, in test_publication_failure_preserves_existing_files
|
||||||
|
self.run_generate()
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': 'intro.wav', 'text': '안내 문장'})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 163, in test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests
|
||||||
|
audio.generate(entries, self.output, synthesize=self.synthesize)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': 'intro.wav', 'text': '안내 문장'})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 163, in test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests
|
||||||
|
audio.generate(entries, self.output, synthesize=self.synthesize)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': '../escape.wav', 'text': '외부'})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 163, in test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests
|
||||||
|
audio.generate(entries, self.output, synthesize=self.synthesize)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_transient_provider_failures_retry_cleanly_with_bounded_backoff (test_neural_audio.GeneratorContract.test_transient_provider_failures_retry_cleanly_with_bounded_backoff)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 97, in test_transient_provider_failures_retry_cleanly_with_bounded_backoff
|
||||||
|
self.run_generate(synthesize=flaky)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'amplitude': 0})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 225, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'amplitude': 32767})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 225, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'rate': 16000})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 225, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'channels': 2})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 225, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'width': 1})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 225, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'duration': 0.2})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 225, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'duration': 21})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 225, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_valid_pcm_returns_measured_hash_bytes_duration_and_levels (test_neural_audio.PCMContract.test_valid_pcm_returns_measured_hash_bytes_duration_and_levels)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 231, in test_valid_pcm_returns_measured_hash_bytes_duration_and_levels
|
||||||
|
result = audio.inspect_wav(path)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
FAIL: test_cli_import_has_no_generation_or_filesystem_side_effects (test_neural_audio.GeneratorContract.test_cli_import_has_no_generation_or_filesystem_side_effects)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 172, in test_cli_import_has_no_generation_or_filesystem_side_effects
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
File "<frozen importlib._bootstrap_external>", line 940, in exec_module
|
||||||
|
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/generate_audio.py", line 27, in <module>
|
||||||
|
source = subprocess.check_output([
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/.local/share/uv/python/cpython-3.11.16-linux-x86_64-gnu/lib/python3.11/unittest/mock.py", line 1124, in __call__
|
||||||
|
return self._mock_call(*args, **kwargs)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/.local/share/uv/python/cpython-3.11.16-linux-x86_64-gnu/lib/python3.11/unittest/mock.py", line 1128, in _mock_call
|
||||||
|
return self._execute_mock_call(*args, **kwargs)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/.local/share/uv/python/cpython-3.11.16-linux-x86_64-gnu/lib/python3.11/unittest/mock.py", line 1183, in _execute_mock_call
|
||||||
|
raise effect
|
||||||
|
AssertionError: import 실행 금지
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
FAIL: test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets (test_neural_audio.GeneratorContract.test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
During handling of the above exception, another exception occurred:
|
||||||
|
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 111, in test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "target-1.wav.*3") as failure:
|
||||||
|
AssertionError: "target-1.wav.*3" does not match "neural 생성·retry·검증·원자 교체 미구현"
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
FAIL: test_ffmpeg_failure_is_not_retried_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_ffmpeg_failure_is_not_retried_and_preserves_existing_files)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
During handling of the above exception, another exception occurred:
|
||||||
|
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 138, in test_ffmpeg_failure_is_not_retried_and_preserves_existing_files
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "intro.wav.*변환"):
|
||||||
|
AssertionError: "intro.wav.*변환" does not match "neural 생성·retry·검증·원자 교체 미구현"
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
FAIL: test_timeout_is_bounded_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_timeout_is_bounded_and_preserves_existing_files)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
During handling of the above exception, another exception occurred:
|
||||||
|
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 123, in test_timeout_is_bounded_and_preserves_existing_files
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "intro.wav.*3"):
|
||||||
|
AssertionError: "intro.wav.*3" does not match "neural 생성·retry·검증·원자 교체 미구현"
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 16 tests in 0.633s
|
||||||
|
|
||||||
|
FAILED (failures=4, errors=20)
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
test_all_101_are_validated_before_single_atomic_publication (test_neural_audio.GeneratorContract.test_all_101_are_validated_before_single_atomic_publication) ... ERROR
|
||||||
|
test_atomic_exchange_keeps_old_tree_at_staging_path (test_neural_audio.GeneratorContract.test_atomic_exchange_keeps_old_tree_at_staging_path) ... ERROR
|
||||||
|
test_atomic_first_publication (test_neural_audio.GeneratorContract.test_atomic_first_publication) ... ERROR
|
||||||
|
test_cli_import_has_no_generation_or_filesystem_side_effects (test_neural_audio.GeneratorContract.test_cli_import_has_no_generation_or_filesystem_side_effects) ... FAIL
|
||||||
|
test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell (test_neural_audio.GeneratorContract.test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell) ... ERROR
|
||||||
|
test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets (test_neural_audio.GeneratorContract.test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets) ... FAIL
|
||||||
|
test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24 (test_neural_audio.GeneratorContract.test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24) ... ERROR
|
||||||
|
test_ffmpeg_failure_is_not_retried_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_ffmpeg_failure_is_not_retried_and_preserves_existing_files) ... FAIL
|
||||||
|
test_full_validation_detects_changed_earlier_file_before_publication (test_neural_audio.GeneratorContract.test_full_validation_detects_changed_earlier_file_before_publication) ... ERROR
|
||||||
|
test_interruption_preserves_existing_files_and_cleans_temporary_data (test_neural_audio.GeneratorContract.test_interruption_preserves_existing_files_and_cleans_temporary_data) ... ERROR
|
||||||
|
test_invalid_pcm_never_reaches_publication (test_neural_audio.GeneratorContract.test_invalid_pcm_never_reaches_publication) ... ERROR
|
||||||
|
test_publication_failure_preserves_existing_files (test_neural_audio.GeneratorContract.test_publication_failure_preserves_existing_files) ... ERROR
|
||||||
|
test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) ...
|
||||||
|
test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': 'intro.wav', 'text': '안내 문장'}) ... ERROR
|
||||||
|
test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': 'intro.wav', 'text': '안내 문장'}) ... ERROR
|
||||||
|
test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': '../escape.wav', 'text': '외부'}) ... ERROR
|
||||||
|
test_timeout_is_bounded_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_timeout_is_bounded_and_preserves_existing_files) ... FAIL
|
||||||
|
test_transient_provider_failures_retry_cleanly_with_bounded_backoff (test_neural_audio.GeneratorContract.test_transient_provider_failures_retry_cleanly_with_bounded_backoff) ... ERROR
|
||||||
|
test_wrong_edge_version_fails_before_network_or_asset_changes (test_neural_audio.GeneratorContract.test_wrong_edge_version_fails_before_network_or_asset_changes) ... FAIL
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) ...
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'amplitude': 0}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'amplitude': 32767}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'rate': 16000}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'channels': 2}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'width': 1}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'duration': 0.2}) ... ERROR
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'duration': 21}) ... ERROR
|
||||||
|
test_valid_pcm_returns_measured_hash_bytes_duration_and_levels (test_neural_audio.PCMContract.test_valid_pcm_returns_measured_hash_bytes_duration_and_levels) ... ERROR
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_all_101_are_validated_before_single_atomic_publication (test_neural_audio.GeneratorContract.test_all_101_are_validated_before_single_atomic_publication)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 78, in test_all_101_are_validated_before_single_atomic_publication
|
||||||
|
result = self.run_generate()
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_atomic_exchange_keeps_old_tree_at_staging_path (test_neural_audio.GeneratorContract.test_atomic_exchange_keeps_old_tree_at_staging_path)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 218, in test_atomic_exchange_keeps_old_tree_at_staging_path
|
||||||
|
audio.atomic_publish(staged, self.output)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 13, in atomic_publish
|
||||||
|
raise NotImplementedError("원자 디렉터리 교체 미구현")
|
||||||
|
NotImplementedError: 원자 디렉터리 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_atomic_first_publication (test_neural_audio.GeneratorContract.test_atomic_first_publication)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 227, in test_atomic_first_publication
|
||||||
|
audio.atomic_publish(staged, output)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 13, in atomic_publish
|
||||||
|
raise NotImplementedError("원자 디렉터리 교체 미구현")
|
||||||
|
NotImplementedError: 원자 디렉터리 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell (test_neural_audio.GeneratorContract.test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 193, in test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell
|
||||||
|
audio.synthesize_edge("한국어 ; $(literal)", path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 17, in synthesize_edge
|
||||||
|
raise NotImplementedError("pinned edge-tts 실행 미구현")
|
||||||
|
NotImplementedError: pinned edge-tts 실행 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24 (test_neural_audio.GeneratorContract.test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 206, in test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24
|
||||||
|
audio.convert_mp3(mp3, output)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 21, in convert_mp3
|
||||||
|
raise NotImplementedError("ffmpeg 변환 및 선형 레벨 보정 미구현")
|
||||||
|
NotImplementedError: ffmpeg 변환 및 선형 레벨 보정 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_full_validation_detects_changed_earlier_file_before_publication (test_neural_audio.GeneratorContract.test_full_validation_detects_changed_earlier_file_before_publication)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 164, in test_full_validation_detects_changed_earlier_file_before_publication
|
||||||
|
self.run_generate(convert=tamper)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_interruption_preserves_existing_files_and_cleans_temporary_data (test_neural_audio.GeneratorContract.test_interruption_preserves_existing_files_and_cleans_temporary_data)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 149, in test_interruption_preserves_existing_files_and_cleans_temporary_data
|
||||||
|
self.run_generate(synthesize=interrupt)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_invalid_pcm_never_reaches_publication (test_neural_audio.GeneratorContract.test_invalid_pcm_never_reaches_publication)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 132, in test_invalid_pcm_never_reaches_publication
|
||||||
|
self.run_generate(convert=invalid)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_publication_failure_preserves_existing_files (test_neural_audio.GeneratorContract.test_publication_failure_preserves_existing_files)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 155, in test_publication_failure_preserves_existing_files
|
||||||
|
self.run_generate()
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': 'intro.wav', 'text': '안내 문장'})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 179, in test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests
|
||||||
|
audio.generate(entries, self.output, synthesize=self.synthesize)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': 'intro.wav', 'text': '안내 문장'})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 179, in test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests
|
||||||
|
audio.generate(entries, self.output, synthesize=self.synthesize)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) (entries={'file': '../escape.wav', 'text': '외부'})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 179, in test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests
|
||||||
|
audio.generate(entries, self.output, synthesize=self.synthesize)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_transient_provider_failures_retry_cleanly_with_bounded_backoff (test_neural_audio.GeneratorContract.test_transient_provider_failures_retry_cleanly_with_bounded_backoff)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 97, in test_transient_provider_failures_retry_cleanly_with_bounded_backoff
|
||||||
|
self.run_generate(synthesize=flaky)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 69, in run_generate
|
||||||
|
return audio.generate(ENTRIES, self.output, synthesize=synthesize or self.synthesize,
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 5, in generate
|
||||||
|
raise NotImplementedError("neural 생성·retry·검증·원자 교체 미구현")
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'amplitude': 0})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 241, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'amplitude': 32767})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 241, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'rate': 16000})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 241, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'channels': 2})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 241, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'width': 1})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 241, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'duration': 0.2})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 241, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) (options={'duration': 21})
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 241, in test_rejects_silence_clipping_bad_format_and_abnormal_duration
|
||||||
|
audio.inspect_wav(path)
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
ERROR: test_valid_pcm_returns_measured_hash_bytes_duration_and_levels (test_neural_audio.PCMContract.test_valid_pcm_returns_measured_hash_bytes_duration_and_levels)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 247, in test_valid_pcm_returns_measured_hash_bytes_duration_and_levels
|
||||||
|
result = audio.inspect_wav(path)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/neural_audio.py", line 9, in inspect_wav
|
||||||
|
raise NotImplementedError("PCM 품질 검사 미구현")
|
||||||
|
NotImplementedError: PCM 품질 검사 미구현
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
FAIL: test_cli_import_has_no_generation_or_filesystem_side_effects (test_neural_audio.GeneratorContract.test_cli_import_has_no_generation_or_filesystem_side_effects)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 188, in test_cli_import_has_no_generation_or_filesystem_side_effects
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
File "<frozen importlib._bootstrap_external>", line 940, in exec_module
|
||||||
|
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/scripts/generate_audio.py", line 27, in <module>
|
||||||
|
source = subprocess.check_output([
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/.local/share/uv/python/cpython-3.11.16-linux-x86_64-gnu/lib/python3.11/unittest/mock.py", line 1124, in __call__
|
||||||
|
return self._mock_call(*args, **kwargs)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/.local/share/uv/python/cpython-3.11.16-linux-x86_64-gnu/lib/python3.11/unittest/mock.py", line 1128, in _mock_call
|
||||||
|
return self._execute_mock_call(*args, **kwargs)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/hermes/.local/share/uv/python/cpython-3.11.16-linux-x86_64-gnu/lib/python3.11/unittest/mock.py", line 1183, in _execute_mock_call
|
||||||
|
raise effect
|
||||||
|
AssertionError: import 실행 금지
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
FAIL: test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets (test_neural_audio.GeneratorContract.test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
During handling of the above exception, another exception occurred:
|
||||||
|
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 111, in test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "target-1.wav.*3") as failure:
|
||||||
|
AssertionError: "target-1.wav.*3" does not match "neural 생성·retry·검증·원자 교체 미구현"
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
FAIL: test_ffmpeg_failure_is_not_retried_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_ffmpeg_failure_is_not_retried_and_preserves_existing_files)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
During handling of the above exception, another exception occurred:
|
||||||
|
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 138, in test_ffmpeg_failure_is_not_retried_and_preserves_existing_files
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "intro.wav.*변환"):
|
||||||
|
AssertionError: "intro.wav.*변환" does not match "neural 생성·retry·검증·원자 교체 미구현"
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
FAIL: test_timeout_is_bounded_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_timeout_is_bounded_and_preserves_existing_files)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
During handling of the above exception, another exception occurred:
|
||||||
|
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 123, in test_timeout_is_bounded_and_preserves_existing_files
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "intro.wav.*3"):
|
||||||
|
AssertionError: "intro.wav.*3" does not match "neural 생성·retry·검증·원자 교체 미구현"
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
FAIL: test_wrong_edge_version_fails_before_network_or_asset_changes (test_neural_audio.GeneratorContract.test_wrong_edge_version_fails_before_network_or_asset_changes)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
NotImplementedError: neural 생성·retry·검증·원자 교체 미구현
|
||||||
|
|
||||||
|
During handling of the above exception, another exception occurred:
|
||||||
|
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/hermes/worktrees/find-word-v0.1.1/tests/python/test_neural_audio.py", line 169, in test_wrong_edge_version_fails_before_network_or_asset_changes
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "7.3.1"):
|
||||||
|
AssertionError: "7.3.1" does not match "neural 생성·retry·검증·원자 교체 미구현"
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 18 tests in 0.488s
|
||||||
|
|
||||||
|
FAILED (failures=5, errors=21)
|
||||||
@@ -0,0 +1,432 @@
|
|||||||
|
test_all_101_are_validated_before_single_atomic_publication (test_neural_audio.GeneratorContract.test_all_101_are_validated_before_single_atomic_publication) ... [001/101] intro.wav 1.000s
|
||||||
|
[002/101] target-0.wav 1.000s
|
||||||
|
[003/101] target-1.wav 1.000s
|
||||||
|
[004/101] target-2.wav 1.000s
|
||||||
|
[005/101] target-3.wav 1.000s
|
||||||
|
[006/101] target-4.wav 1.000s
|
||||||
|
[007/101] target-5.wav 1.000s
|
||||||
|
[008/101] target-6.wav 1.000s
|
||||||
|
[009/101] target-7.wav 1.000s
|
||||||
|
[010/101] target-8.wav 1.000s
|
||||||
|
[011/101] target-9.wav 1.000s
|
||||||
|
[012/101] target-10.wav 1.000s
|
||||||
|
[013/101] target-11.wav 1.000s
|
||||||
|
[014/101] target-12.wav 1.000s
|
||||||
|
[015/101] target-13.wav 1.000s
|
||||||
|
[016/101] target-14.wav 1.000s
|
||||||
|
[017/101] target-15.wav 1.000s
|
||||||
|
[018/101] target-16.wav 1.000s
|
||||||
|
[019/101] target-17.wav 1.000s
|
||||||
|
[020/101] target-18.wav 1.000s
|
||||||
|
[021/101] target-19.wav 1.000s
|
||||||
|
[022/101] target-20.wav 1.000s
|
||||||
|
[023/101] target-21.wav 1.000s
|
||||||
|
[024/101] target-22.wav 1.000s
|
||||||
|
[025/101] target-23.wav 1.000s
|
||||||
|
[026/101] target-24.wav 1.000s
|
||||||
|
[027/101] target-25.wav 1.000s
|
||||||
|
[028/101] target-26.wav 1.000s
|
||||||
|
[029/101] target-27.wav 1.000s
|
||||||
|
[030/101] target-28.wav 1.000s
|
||||||
|
[031/101] target-29.wav 1.000s
|
||||||
|
[032/101] target-30.wav 1.000s
|
||||||
|
[033/101] target-31.wav 1.000s
|
||||||
|
[034/101] target-32.wav 1.000s
|
||||||
|
[035/101] target-33.wav 1.000s
|
||||||
|
[036/101] target-34.wav 1.000s
|
||||||
|
[037/101] target-35.wav 1.000s
|
||||||
|
[038/101] target-36.wav 1.000s
|
||||||
|
[039/101] target-37.wav 1.000s
|
||||||
|
[040/101] target-38.wav 1.000s
|
||||||
|
[041/101] target-39.wav 1.000s
|
||||||
|
[042/101] target-40.wav 1.000s
|
||||||
|
[043/101] target-41.wav 1.000s
|
||||||
|
[044/101] target-42.wav 1.000s
|
||||||
|
[045/101] target-43.wav 1.000s
|
||||||
|
[046/101] target-44.wav 1.000s
|
||||||
|
[047/101] target-45.wav 1.000s
|
||||||
|
[048/101] target-46.wav 1.000s
|
||||||
|
[049/101] target-47.wav 1.000s
|
||||||
|
[050/101] target-48.wav 1.000s
|
||||||
|
[051/101] target-49.wav 1.000s
|
||||||
|
[052/101] target-50.wav 1.000s
|
||||||
|
[053/101] target-51.wav 1.000s
|
||||||
|
[054/101] target-52.wav 1.000s
|
||||||
|
[055/101] target-53.wav 1.000s
|
||||||
|
[056/101] target-54.wav 1.000s
|
||||||
|
[057/101] target-55.wav 1.000s
|
||||||
|
[058/101] target-56.wav 1.000s
|
||||||
|
[059/101] target-57.wav 1.000s
|
||||||
|
[060/101] target-58.wav 1.000s
|
||||||
|
[061/101] target-59.wav 1.000s
|
||||||
|
[062/101] target-60.wav 1.000s
|
||||||
|
[063/101] target-61.wav 1.000s
|
||||||
|
[064/101] target-62.wav 1.000s
|
||||||
|
[065/101] target-63.wav 1.000s
|
||||||
|
[066/101] target-64.wav 1.000s
|
||||||
|
[067/101] target-65.wav 1.000s
|
||||||
|
[068/101] target-66.wav 1.000s
|
||||||
|
[069/101] target-67.wav 1.000s
|
||||||
|
[070/101] target-68.wav 1.000s
|
||||||
|
[071/101] target-69.wav 1.000s
|
||||||
|
[072/101] target-70.wav 1.000s
|
||||||
|
[073/101] target-71.wav 1.000s
|
||||||
|
[074/101] target-72.wav 1.000s
|
||||||
|
[075/101] target-73.wav 1.000s
|
||||||
|
[076/101] target-74.wav 1.000s
|
||||||
|
[077/101] target-75.wav 1.000s
|
||||||
|
[078/101] target-76.wav 1.000s
|
||||||
|
[079/101] target-77.wav 1.000s
|
||||||
|
[080/101] target-78.wav 1.000s
|
||||||
|
[081/101] target-79.wav 1.000s
|
||||||
|
[082/101] target-80.wav 1.000s
|
||||||
|
[083/101] target-81.wav 1.000s
|
||||||
|
[084/101] target-82.wav 1.000s
|
||||||
|
[085/101] target-83.wav 1.000s
|
||||||
|
[086/101] target-84.wav 1.000s
|
||||||
|
[087/101] target-85.wav 1.000s
|
||||||
|
[088/101] target-86.wav 1.000s
|
||||||
|
[089/101] target-87.wav 1.000s
|
||||||
|
[090/101] target-88.wav 1.000s
|
||||||
|
[091/101] target-89.wav 1.000s
|
||||||
|
[092/101] target-90.wav 1.000s
|
||||||
|
[093/101] target-91.wav 1.000s
|
||||||
|
[094/101] target-92.wav 1.000s
|
||||||
|
[095/101] target-93.wav 1.000s
|
||||||
|
[096/101] target-94.wav 1.000s
|
||||||
|
[097/101] target-95.wav 1.000s
|
||||||
|
[098/101] target-96.wav 1.000s
|
||||||
|
[099/101] target-97.wav 1.000s
|
||||||
|
[100/101] target-98.wav 1.000s
|
||||||
|
[101/101] target-99.wav 1.000s
|
||||||
|
ok
|
||||||
|
test_atomic_exchange_keeps_old_tree_at_staging_path (test_neural_audio.GeneratorContract.test_atomic_exchange_keeps_old_tree_at_staging_path) ... ok
|
||||||
|
test_atomic_first_publication (test_neural_audio.GeneratorContract.test_atomic_first_publication) ... ok
|
||||||
|
test_cli_import_has_no_generation_or_filesystem_side_effects (test_neural_audio.GeneratorContract.test_cli_import_has_no_generation_or_filesystem_side_effects) ... ok
|
||||||
|
test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell (test_neural_audio.GeneratorContract.test_edge_adapter_uses_pinned_python_prosody_timeout_and_no_shell) ... ok
|
||||||
|
test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets (test_neural_audio.GeneratorContract.test_exhausted_provider_failure_preserves_existing_files_and_redacts_secrets) ... ok
|
||||||
|
test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24 (test_neural_audio.GeneratorContract.test_ffmpeg_adapter_actually_decodes_mp3_to_normalized_pcm24) ... ok
|
||||||
|
test_ffmpeg_failure_is_not_retried_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_ffmpeg_failure_is_not_retried_and_preserves_existing_files) ... ok
|
||||||
|
test_full_validation_detects_changed_earlier_file_before_publication (test_neural_audio.GeneratorContract.test_full_validation_detects_changed_earlier_file_before_publication) ... [001/101] intro.wav 1.000s
|
||||||
|
[002/101] target-0.wav 1.000s
|
||||||
|
[003/101] target-1.wav 1.000s
|
||||||
|
[004/101] target-2.wav 1.000s
|
||||||
|
[005/101] target-3.wav 1.000s
|
||||||
|
[006/101] target-4.wav 1.000s
|
||||||
|
[007/101] target-5.wav 1.000s
|
||||||
|
[008/101] target-6.wav 1.000s
|
||||||
|
[009/101] target-7.wav 1.000s
|
||||||
|
[010/101] target-8.wav 1.000s
|
||||||
|
[011/101] target-9.wav 1.000s
|
||||||
|
[012/101] target-10.wav 1.000s
|
||||||
|
[013/101] target-11.wav 1.000s
|
||||||
|
[014/101] target-12.wav 1.000s
|
||||||
|
[015/101] target-13.wav 1.000s
|
||||||
|
[016/101] target-14.wav 1.000s
|
||||||
|
[017/101] target-15.wav 1.000s
|
||||||
|
[018/101] target-16.wav 1.000s
|
||||||
|
[019/101] target-17.wav 1.000s
|
||||||
|
[020/101] target-18.wav 1.000s
|
||||||
|
[021/101] target-19.wav 1.000s
|
||||||
|
[022/101] target-20.wav 1.000s
|
||||||
|
[023/101] target-21.wav 1.000s
|
||||||
|
[024/101] target-22.wav 1.000s
|
||||||
|
[025/101] target-23.wav 1.000s
|
||||||
|
[026/101] target-24.wav 1.000s
|
||||||
|
[027/101] target-25.wav 1.000s
|
||||||
|
[028/101] target-26.wav 1.000s
|
||||||
|
[029/101] target-27.wav 1.000s
|
||||||
|
[030/101] target-28.wav 1.000s
|
||||||
|
[031/101] target-29.wav 1.000s
|
||||||
|
[032/101] target-30.wav 1.000s
|
||||||
|
[033/101] target-31.wav 1.000s
|
||||||
|
[034/101] target-32.wav 1.000s
|
||||||
|
[035/101] target-33.wav 1.000s
|
||||||
|
[036/101] target-34.wav 1.000s
|
||||||
|
[037/101] target-35.wav 1.000s
|
||||||
|
[038/101] target-36.wav 1.000s
|
||||||
|
[039/101] target-37.wav 1.000s
|
||||||
|
[040/101] target-38.wav 1.000s
|
||||||
|
[041/101] target-39.wav 1.000s
|
||||||
|
[042/101] target-40.wav 1.000s
|
||||||
|
[043/101] target-41.wav 1.000s
|
||||||
|
[044/101] target-42.wav 1.000s
|
||||||
|
[045/101] target-43.wav 1.000s
|
||||||
|
[046/101] target-44.wav 1.000s
|
||||||
|
[047/101] target-45.wav 1.000s
|
||||||
|
[048/101] target-46.wav 1.000s
|
||||||
|
[049/101] target-47.wav 1.000s
|
||||||
|
[050/101] target-48.wav 1.000s
|
||||||
|
[051/101] target-49.wav 1.000s
|
||||||
|
[052/101] target-50.wav 1.000s
|
||||||
|
[053/101] target-51.wav 1.000s
|
||||||
|
[054/101] target-52.wav 1.000s
|
||||||
|
[055/101] target-53.wav 1.000s
|
||||||
|
[056/101] target-54.wav 1.000s
|
||||||
|
[057/101] target-55.wav 1.000s
|
||||||
|
[058/101] target-56.wav 1.000s
|
||||||
|
[059/101] target-57.wav 1.000s
|
||||||
|
[060/101] target-58.wav 1.000s
|
||||||
|
[061/101] target-59.wav 1.000s
|
||||||
|
[062/101] target-60.wav 1.000s
|
||||||
|
[063/101] target-61.wav 1.000s
|
||||||
|
[064/101] target-62.wav 1.000s
|
||||||
|
[065/101] target-63.wav 1.000s
|
||||||
|
[066/101] target-64.wav 1.000s
|
||||||
|
[067/101] target-65.wav 1.000s
|
||||||
|
[068/101] target-66.wav 1.000s
|
||||||
|
[069/101] target-67.wav 1.000s
|
||||||
|
[070/101] target-68.wav 1.000s
|
||||||
|
[071/101] target-69.wav 1.000s
|
||||||
|
[072/101] target-70.wav 1.000s
|
||||||
|
[073/101] target-71.wav 1.000s
|
||||||
|
[074/101] target-72.wav 1.000s
|
||||||
|
[075/101] target-73.wav 1.000s
|
||||||
|
[076/101] target-74.wav 1.000s
|
||||||
|
[077/101] target-75.wav 1.000s
|
||||||
|
[078/101] target-76.wav 1.000s
|
||||||
|
[079/101] target-77.wav 1.000s
|
||||||
|
[080/101] target-78.wav 1.000s
|
||||||
|
[081/101] target-79.wav 1.000s
|
||||||
|
[082/101] target-80.wav 1.000s
|
||||||
|
[083/101] target-81.wav 1.000s
|
||||||
|
[084/101] target-82.wav 1.000s
|
||||||
|
[085/101] target-83.wav 1.000s
|
||||||
|
[086/101] target-84.wav 1.000s
|
||||||
|
[087/101] target-85.wav 1.000s
|
||||||
|
[088/101] target-86.wav 1.000s
|
||||||
|
[089/101] target-87.wav 1.000s
|
||||||
|
[090/101] target-88.wav 1.000s
|
||||||
|
[091/101] target-89.wav 1.000s
|
||||||
|
[092/101] target-90.wav 1.000s
|
||||||
|
[093/101] target-91.wav 1.000s
|
||||||
|
[094/101] target-92.wav 1.000s
|
||||||
|
[095/101] target-93.wav 1.000s
|
||||||
|
[096/101] target-94.wav 1.000s
|
||||||
|
[097/101] target-95.wav 1.000s
|
||||||
|
[098/101] target-96.wav 1.000s
|
||||||
|
[099/101] target-97.wav 1.000s
|
||||||
|
[100/101] target-98.wav 1.000s
|
||||||
|
[101/101] target-99.wav 1.000s
|
||||||
|
ok
|
||||||
|
test_interruption_preserves_existing_files_and_cleans_temporary_data (test_neural_audio.GeneratorContract.test_interruption_preserves_existing_files_and_cleans_temporary_data) ... ok
|
||||||
|
test_invalid_pcm_never_reaches_publication (test_neural_audio.GeneratorContract.test_invalid_pcm_never_reaches_publication) ... ok
|
||||||
|
test_publication_failure_preserves_existing_files (test_neural_audio.GeneratorContract.test_publication_failure_preserves_existing_files) ... [001/101] intro.wav 1.000s
|
||||||
|
[002/101] target-0.wav 1.000s
|
||||||
|
[003/101] target-1.wav 1.000s
|
||||||
|
[004/101] target-2.wav 1.000s
|
||||||
|
[005/101] target-3.wav 1.000s
|
||||||
|
[006/101] target-4.wav 1.000s
|
||||||
|
[007/101] target-5.wav 1.000s
|
||||||
|
[008/101] target-6.wav 1.000s
|
||||||
|
[009/101] target-7.wav 1.000s
|
||||||
|
[010/101] target-8.wav 1.000s
|
||||||
|
[011/101] target-9.wav 1.000s
|
||||||
|
[012/101] target-10.wav 1.000s
|
||||||
|
[013/101] target-11.wav 1.000s
|
||||||
|
[014/101] target-12.wav 1.000s
|
||||||
|
[015/101] target-13.wav 1.000s
|
||||||
|
[016/101] target-14.wav 1.000s
|
||||||
|
[017/101] target-15.wav 1.000s
|
||||||
|
[018/101] target-16.wav 1.000s
|
||||||
|
[019/101] target-17.wav 1.000s
|
||||||
|
[020/101] target-18.wav 1.000s
|
||||||
|
[021/101] target-19.wav 1.000s
|
||||||
|
[022/101] target-20.wav 1.000s
|
||||||
|
[023/101] target-21.wav 1.000s
|
||||||
|
[024/101] target-22.wav 1.000s
|
||||||
|
[025/101] target-23.wav 1.000s
|
||||||
|
[026/101] target-24.wav 1.000s
|
||||||
|
[027/101] target-25.wav 1.000s
|
||||||
|
[028/101] target-26.wav 1.000s
|
||||||
|
[029/101] target-27.wav 1.000s
|
||||||
|
[030/101] target-28.wav 1.000s
|
||||||
|
[031/101] target-29.wav 1.000s
|
||||||
|
[032/101] target-30.wav 1.000s
|
||||||
|
[033/101] target-31.wav 1.000s
|
||||||
|
[034/101] target-32.wav 1.000s
|
||||||
|
[035/101] target-33.wav 1.000s
|
||||||
|
[036/101] target-34.wav 1.000s
|
||||||
|
[037/101] target-35.wav 1.000s
|
||||||
|
[038/101] target-36.wav 1.000s
|
||||||
|
[039/101] target-37.wav 1.000s
|
||||||
|
[040/101] target-38.wav 1.000s
|
||||||
|
[041/101] target-39.wav 1.000s
|
||||||
|
[042/101] target-40.wav 1.000s
|
||||||
|
[043/101] target-41.wav 1.000s
|
||||||
|
[044/101] target-42.wav 1.000s
|
||||||
|
[045/101] target-43.wav 1.000s
|
||||||
|
[046/101] target-44.wav 1.000s
|
||||||
|
[047/101] target-45.wav 1.000s
|
||||||
|
[048/101] target-46.wav 1.000s
|
||||||
|
[049/101] target-47.wav 1.000s
|
||||||
|
[050/101] target-48.wav 1.000s
|
||||||
|
[051/101] target-49.wav 1.000s
|
||||||
|
[052/101] target-50.wav 1.000s
|
||||||
|
[053/101] target-51.wav 1.000s
|
||||||
|
[054/101] target-52.wav 1.000s
|
||||||
|
[055/101] target-53.wav 1.000s
|
||||||
|
[056/101] target-54.wav 1.000s
|
||||||
|
[057/101] target-55.wav 1.000s
|
||||||
|
[058/101] target-56.wav 1.000s
|
||||||
|
[059/101] target-57.wav 1.000s
|
||||||
|
[060/101] target-58.wav 1.000s
|
||||||
|
[061/101] target-59.wav 1.000s
|
||||||
|
[062/101] target-60.wav 1.000s
|
||||||
|
[063/101] target-61.wav 1.000s
|
||||||
|
[064/101] target-62.wav 1.000s
|
||||||
|
[065/101] target-63.wav 1.000s
|
||||||
|
[066/101] target-64.wav 1.000s
|
||||||
|
[067/101] target-65.wav 1.000s
|
||||||
|
[068/101] target-66.wav 1.000s
|
||||||
|
[069/101] target-67.wav 1.000s
|
||||||
|
[070/101] target-68.wav 1.000s
|
||||||
|
[071/101] target-69.wav 1.000s
|
||||||
|
[072/101] target-70.wav 1.000s
|
||||||
|
[073/101] target-71.wav 1.000s
|
||||||
|
[074/101] target-72.wav 1.000s
|
||||||
|
[075/101] target-73.wav 1.000s
|
||||||
|
[076/101] target-74.wav 1.000s
|
||||||
|
[077/101] target-75.wav 1.000s
|
||||||
|
[078/101] target-76.wav 1.000s
|
||||||
|
[079/101] target-77.wav 1.000s
|
||||||
|
[080/101] target-78.wav 1.000s
|
||||||
|
[081/101] target-79.wav 1.000s
|
||||||
|
[082/101] target-80.wav 1.000s
|
||||||
|
[083/101] target-81.wav 1.000s
|
||||||
|
[084/101] target-82.wav 1.000s
|
||||||
|
[085/101] target-83.wav 1.000s
|
||||||
|
[086/101] target-84.wav 1.000s
|
||||||
|
[087/101] target-85.wav 1.000s
|
||||||
|
[088/101] target-86.wav 1.000s
|
||||||
|
[089/101] target-87.wav 1.000s
|
||||||
|
[090/101] target-88.wav 1.000s
|
||||||
|
[091/101] target-89.wav 1.000s
|
||||||
|
[092/101] target-90.wav 1.000s
|
||||||
|
[093/101] target-91.wav 1.000s
|
||||||
|
[094/101] target-92.wav 1.000s
|
||||||
|
[095/101] target-93.wav 1.000s
|
||||||
|
[096/101] target-94.wav 1.000s
|
||||||
|
[097/101] target-95.wav 1.000s
|
||||||
|
[098/101] target-96.wav 1.000s
|
||||||
|
[099/101] target-97.wav 1.000s
|
||||||
|
[100/101] target-98.wav 1.000s
|
||||||
|
[101/101] target-99.wav 1.000s
|
||||||
|
ok
|
||||||
|
test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests (test_neural_audio.GeneratorContract.test_rejects_incomplete_or_duplicate_or_unsafe_inventory_before_requests) ... ok
|
||||||
|
test_timeout_is_bounded_and_preserves_existing_files (test_neural_audio.GeneratorContract.test_timeout_is_bounded_and_preserves_existing_files) ... intro.wav: provider 시도 1/3 실패
|
||||||
|
intro.wav: provider 시도 2/3 실패
|
||||||
|
intro.wav: provider 시도 3/3 실패
|
||||||
|
ok
|
||||||
|
test_transient_provider_failures_retry_cleanly_with_bounded_backoff (test_neural_audio.GeneratorContract.test_transient_provider_failures_retry_cleanly_with_bounded_backoff) ... intro.wav: provider 시도 1/3 실패
|
||||||
|
intro.wav: provider 시도 2/3 실패
|
||||||
|
[001/101] intro.wav 1.000s
|
||||||
|
[002/101] target-0.wav 1.000s
|
||||||
|
[003/101] target-1.wav 1.000s
|
||||||
|
[004/101] target-2.wav 1.000s
|
||||||
|
[005/101] target-3.wav 1.000s
|
||||||
|
[006/101] target-4.wav 1.000s
|
||||||
|
[007/101] target-5.wav 1.000s
|
||||||
|
[008/101] target-6.wav 1.000s
|
||||||
|
[009/101] target-7.wav 1.000s
|
||||||
|
[010/101] target-8.wav 1.000s
|
||||||
|
[011/101] target-9.wav 1.000s
|
||||||
|
[012/101] target-10.wav 1.000s
|
||||||
|
[013/101] target-11.wav 1.000s
|
||||||
|
[014/101] target-12.wav 1.000s
|
||||||
|
[015/101] target-13.wav 1.000s
|
||||||
|
[016/101] target-14.wav 1.000s
|
||||||
|
[017/101] target-15.wav 1.000s
|
||||||
|
[018/101] target-16.wav 1.000s
|
||||||
|
[019/101] target-17.wav 1.000s
|
||||||
|
[020/101] target-18.wav 1.000s
|
||||||
|
[021/101] target-19.wav 1.000s
|
||||||
|
[022/101] target-20.wav 1.000s
|
||||||
|
[023/101] target-21.wav 1.000s
|
||||||
|
[024/101] target-22.wav 1.000s
|
||||||
|
[025/101] target-23.wav 1.000s
|
||||||
|
[026/101] target-24.wav 1.000s
|
||||||
|
[027/101] target-25.wav 1.000s
|
||||||
|
[028/101] target-26.wav 1.000s
|
||||||
|
[029/101] target-27.wav 1.000s
|
||||||
|
[030/101] target-28.wav 1.000s
|
||||||
|
[031/101] target-29.wav 1.000s
|
||||||
|
[032/101] target-30.wav 1.000s
|
||||||
|
[033/101] target-31.wav 1.000s
|
||||||
|
[034/101] target-32.wav 1.000s
|
||||||
|
[035/101] target-33.wav 1.000s
|
||||||
|
[036/101] target-34.wav 1.000s
|
||||||
|
[037/101] target-35.wav 1.000s
|
||||||
|
[038/101] target-36.wav 1.000s
|
||||||
|
[039/101] target-37.wav 1.000s
|
||||||
|
[040/101] target-38.wav 1.000s
|
||||||
|
[041/101] target-39.wav 1.000s
|
||||||
|
[042/101] target-40.wav 1.000s
|
||||||
|
[043/101] target-41.wav 1.000s
|
||||||
|
[044/101] target-42.wav 1.000s
|
||||||
|
[045/101] target-43.wav 1.000s
|
||||||
|
[046/101] target-44.wav 1.000s
|
||||||
|
[047/101] target-45.wav 1.000s
|
||||||
|
[048/101] target-46.wav 1.000s
|
||||||
|
[049/101] target-47.wav 1.000s
|
||||||
|
[050/101] target-48.wav 1.000s
|
||||||
|
[051/101] target-49.wav 1.000s
|
||||||
|
[052/101] target-50.wav 1.000s
|
||||||
|
[053/101] target-51.wav 1.000s
|
||||||
|
[054/101] target-52.wav 1.000s
|
||||||
|
[055/101] target-53.wav 1.000s
|
||||||
|
[056/101] target-54.wav 1.000s
|
||||||
|
[057/101] target-55.wav 1.000s
|
||||||
|
[058/101] target-56.wav 1.000s
|
||||||
|
[059/101] target-57.wav 1.000s
|
||||||
|
[060/101] target-58.wav 1.000s
|
||||||
|
[061/101] target-59.wav 1.000s
|
||||||
|
[062/101] target-60.wav 1.000s
|
||||||
|
[063/101] target-61.wav 1.000s
|
||||||
|
[064/101] target-62.wav 1.000s
|
||||||
|
[065/101] target-63.wav 1.000s
|
||||||
|
[066/101] target-64.wav 1.000s
|
||||||
|
[067/101] target-65.wav 1.000s
|
||||||
|
[068/101] target-66.wav 1.000s
|
||||||
|
[069/101] target-67.wav 1.000s
|
||||||
|
[070/101] target-68.wav 1.000s
|
||||||
|
[071/101] target-69.wav 1.000s
|
||||||
|
[072/101] target-70.wav 1.000s
|
||||||
|
[073/101] target-71.wav 1.000s
|
||||||
|
[074/101] target-72.wav 1.000s
|
||||||
|
[075/101] target-73.wav 1.000s
|
||||||
|
[076/101] target-74.wav 1.000s
|
||||||
|
[077/101] target-75.wav 1.000s
|
||||||
|
[078/101] target-76.wav 1.000s
|
||||||
|
[079/101] target-77.wav 1.000s
|
||||||
|
[080/101] target-78.wav 1.000s
|
||||||
|
[081/101] target-79.wav 1.000s
|
||||||
|
[082/101] target-80.wav 1.000s
|
||||||
|
[083/101] target-81.wav 1.000s
|
||||||
|
[084/101] target-82.wav 1.000s
|
||||||
|
[085/101] target-83.wav 1.000s
|
||||||
|
[086/101] target-84.wav 1.000s
|
||||||
|
[087/101] target-85.wav 1.000s
|
||||||
|
[088/101] target-86.wav 1.000s
|
||||||
|
[089/101] target-87.wav 1.000s
|
||||||
|
[090/101] target-88.wav 1.000s
|
||||||
|
[091/101] target-89.wav 1.000s
|
||||||
|
[092/101] target-90.wav 1.000s
|
||||||
|
[093/101] target-91.wav 1.000s
|
||||||
|
[094/101] target-92.wav 1.000s
|
||||||
|
[095/101] target-93.wav 1.000s
|
||||||
|
[096/101] target-94.wav 1.000s
|
||||||
|
[097/101] target-95.wav 1.000s
|
||||||
|
[098/101] target-96.wav 1.000s
|
||||||
|
[099/101] target-97.wav 1.000s
|
||||||
|
[100/101] target-98.wav 1.000s
|
||||||
|
[101/101] target-99.wav 1.000s
|
||||||
|
ok
|
||||||
|
test_wrong_edge_version_fails_before_network_or_asset_changes (test_neural_audio.GeneratorContract.test_wrong_edge_version_fails_before_network_or_asset_changes) ... ok
|
||||||
|
test_rejects_silence_clipping_bad_format_and_abnormal_duration (test_neural_audio.PCMContract.test_rejects_silence_clipping_bad_format_and_abnormal_duration) ... ok
|
||||||
|
test_valid_pcm_returns_measured_hash_bytes_duration_and_levels (test_neural_audio.PCMContract.test_valid_pcm_returns_measured_hash_bytes_duration_and_levels) ... ok
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 18 tests in 2.699s
|
||||||
|
|
||||||
|
OK
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
|
||||||
|
> find-word@0.1.1 audio:generate
|
||||||
|
> .venv-audio/bin/python scripts/generate_audio.py
|
||||||
|
|
||||||
|
[001/101] intro.wav 7.056s
|
||||||
|
[002/101] luck-apple.wav 3.936s
|
||||||
|
[003/101] luck-sea.wav 3.984s
|
||||||
|
[004/101] luck-tree.wav 3.816s
|
||||||
|
[005/101] luck-hat.wav 3.984s
|
||||||
|
[006/101] luck-umbrella.wav 4.008s
|
||||||
|
[007/101] luck-market.wav 4.056s
|
||||||
|
[008/101] luck-oil.wav 3.960s
|
||||||
|
[009/101] luck-sound.wav 3.840s
|
||||||
|
[010/101] luck-meat.wav 3.912s
|
||||||
|
[011/101] luck-furniture.wav 3.888s
|
||||||
|
[012/101] password-apple.wav 4.896s
|
||||||
|
[013/101] password-sea.wav 4.848s
|
||||||
|
[014/101] password-tree.wav 4.872s
|
||||||
|
[015/101] password-hat.wav 4.896s
|
||||||
|
[016/101] password-umbrella.wav 4.992s
|
||||||
|
[017/101] password-market.wav 4.800s
|
||||||
|
[018/101] password-oil.wav 5.016s
|
||||||
|
[019/101] password-sound.wav 4.920s
|
||||||
|
[020/101] password-meat.wav 5.016s
|
||||||
|
[021/101] password-furniture.wav 4.872s
|
||||||
|
[022/101] remember-apple.wav 8.232s
|
||||||
|
[023/101] remember-sea.wav 8.328s
|
||||||
|
[024/101] remember-tree.wav 8.520s
|
||||||
|
[025/101] remember-hat.wav 8.376s
|
||||||
|
[026/101] remember-umbrella.wav 8.400s
|
||||||
|
[027/101] remember-market.wav 8.304s
|
||||||
|
[028/101] remember-oil.wav 8.328s
|
||||||
|
[029/101] remember-sound.wav 8.208s
|
||||||
|
[030/101] remember-meat.wav 8.232s
|
||||||
|
[031/101] remember-furniture.wav 8.400s
|
||||||
|
[032/101] last-apple.wav 7.416s
|
||||||
|
[033/101] last-sea.wav 7.200s
|
||||||
|
[034/101] last-tree.wav 7.968s
|
||||||
|
[035/101] last-hat.wav 7.392s
|
||||||
|
[036/101] last-umbrella.wav 7.728s
|
||||||
|
[037/101] last-market.wav 7.488s
|
||||||
|
[038/101] last-oil.wav 7.032s
|
||||||
|
[039/101] last-sound.wav 7.392s
|
||||||
|
[040/101] last-meat.wav 7.224s
|
||||||
|
[041/101] last-furniture.wav 7.440s
|
||||||
|
[042/101] first-apple.wav 7.488s
|
||||||
|
[043/101] first-sea.wav 7.272s
|
||||||
|
[044/101] first-tree.wav 7.896s
|
||||||
|
[045/101] first-hat.wav 7.440s
|
||||||
|
[046/101] first-umbrella.wav 7.632s
|
||||||
|
[047/101] first-market.wav 7.488s
|
||||||
|
[048/101] first-oil.wav 6.984s
|
||||||
|
[049/101] first-sound.wav 7.368s
|
||||||
|
[050/101] first-meat.wav 7.200s
|
||||||
|
[051/101] first-furniture.wav 7.584s
|
||||||
|
[052/101] repeat-apple.wav 8.280s
|
||||||
|
[053/101] repeat-sea.wav 7.968s
|
||||||
|
[054/101] repeat-tree.wav 8.832s
|
||||||
|
[055/101] repeat-hat.wav 8.256s
|
||||||
|
[056/101] repeat-umbrella.wav 8.544s
|
||||||
|
[057/101] repeat-market.wav 8.376s
|
||||||
|
[058/101] repeat-oil.wav 7.704s
|
||||||
|
[059/101] repeat-sound.wav 8.232s
|
||||||
|
[060/101] repeat-meat.wav 7.920s
|
||||||
|
[061/101] repeat-furniture.wav 8.280s
|
||||||
|
[062/101] correction-apple.wav 8.376s
|
||||||
|
[063/101] correction-sea.wav 8.304s
|
||||||
|
[064/101] correction-tree.wav 8.352s
|
||||||
|
[065/101] correction-hat.wav 8.304s
|
||||||
|
[066/101] correction-umbrella.wav 8.472s
|
||||||
|
[067/101] correction-market.wav 8.448s
|
||||||
|
[068/101] correction-oil.wav 8.472s
|
||||||
|
[069/101] correction-sound.wav 8.280s
|
||||||
|
[070/101] correction-meat.wav 8.400s
|
||||||
|
[071/101] correction-furniture.wav 8.304s
|
||||||
|
[072/101] middle-apple.wav 7.512s
|
||||||
|
[073/101] middle-sea.wav 7.224s
|
||||||
|
[074/101] middle-tree.wav 7.968s
|
||||||
|
[075/101] middle-hat.wav 7.368s
|
||||||
|
[076/101] middle-umbrella.wav 7.680s
|
||||||
|
[077/101] middle-market.wav 7.416s
|
||||||
|
[078/101] middle-oil.wav 6.960s
|
||||||
|
[079/101] middle-sound.wav 7.368s
|
||||||
|
[080/101] middle-meat.wav 7.128s
|
||||||
|
[081/101] middle-furniture.wav 7.392s
|
||||||
|
[082/101] message-apple.wav 10.776s
|
||||||
|
[083/101] message-sea.wav 10.776s
|
||||||
|
[084/101] message-tree.wav 11.256s
|
||||||
|
[085/101] message-hat.wav 10.800s
|
||||||
|
[086/101] message-umbrella.wav 10.896s
|
||||||
|
[087/101] message-market.wav 10.896s
|
||||||
|
[088/101] message-oil.wav 10.608s
|
||||||
|
[089/101] message-sound.wav 10.872s
|
||||||
|
[090/101] message-meat.wav 10.680s
|
||||||
|
[091/101] message-furniture.wav 10.872s
|
||||||
|
[092/101] mission-apple.wav 5.136s
|
||||||
|
[093/101] mission-sea.wav 5.064s
|
||||||
|
[094/101] mission-tree.wav 5.040s
|
||||||
|
[095/101] mission-hat.wav 5.112s
|
||||||
|
[096/101] mission-umbrella.wav 5.160s
|
||||||
|
[097/101] mission-market.wav 5.184s
|
||||||
|
[098/101] mission-oil.wav 5.040s
|
||||||
|
[099/101] mission-sound.wav 5.016s
|
||||||
|
[100/101] mission-meat.wav 5.088s
|
||||||
|
[101/101] mission-furniture.wav 5.040s
|
||||||
|
101개 신규 WAV 원자 교체 완료, 합계 34,898,524 바이트
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
tests/assets.test.ts 130ms
|
||||||
|
tests/neural-release.test.ts 12ms
|
||||||
|
tests/generator.test.ts 5ms
|
||||||
|
tests/App.test.tsx 91ms (unchanged)
|
||||||
|
tests/browser/game.spec.ts 30ms
|
||||||
|
tests/browser/ui.spec.ts 19ms
|
||||||
|
eslint.config.js 21ms (unchanged)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
> find-word@0.1.1 test
|
||||||
|
> vitest run tests/neural-release.test.ts tests/App.test.tsx -t 0.1.1|브랜드|고정 venv
|
||||||
|
|
||||||
|
|
||||||
|
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1.1
|
||||||
|
|
||||||
|
|
||||||
|
Test Files 2 passed (2)
|
||||||
|
Tests 3 passed | 12 skipped (15)
|
||||||
|
Start at 06:52:28
|
||||||
|
Duration 1.30s (environment 47%, tests 20%, setup 20%, transform 8%, import 4%, worker 1%)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
> find-word@0.1.1 lint
|
||||||
|
> eslint . && tsc --noEmit
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
found 0 vulnerabilities
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
> find-word@0.1.1 test
|
||||||
|
> vitest run
|
||||||
|
|
||||||
|
|
||||||
|
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1.1
|
||||||
|
|
||||||
|
|
||||||
|
Test Files 11 passed (11)
|
||||||
|
Tests 60 passed (60)
|
||||||
|
Start at 06:56:13
|
||||||
|
Duration 13.30s (tests 64%, environment 24%, setup 9%, transform 1%, import 1%, worker 1%)
|
||||||
|
|
||||||
|
Isolate 11 workers spawned · ~999ms startup each (spawn + environment, per file)
|
||||||
|
at least ~2.66s faster with isolate: false — reuses workers across files instead of one per file
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
PASS intro.wav: 7.056s, RMS=0.100000, peak=0.499847
|
||||||
|
PASS luck-apple.wav: 3.936s, RMS=0.100000, peak=0.401642
|
||||||
|
PASS luck-sea.wav: 3.984s, RMS=0.100000, peak=0.388702
|
||||||
|
PASS luck-tree.wav: 3.816s, RMS=0.100000, peak=0.369202
|
||||||
|
PASS luck-hat.wav: 3.984s, RMS=0.100000, peak=0.417084
|
||||||
|
PASS luck-umbrella.wav: 4.008s, RMS=0.100000, peak=0.385651
|
||||||
|
PASS luck-market.wav: 4.056s, RMS=0.100000, peak=0.402222
|
||||||
|
PASS luck-oil.wav: 3.960s, RMS=0.100000, peak=0.381714
|
||||||
|
PASS luck-sound.wav: 3.840s, RMS=0.100000, peak=0.386322
|
||||||
|
PASS luck-meat.wav: 3.912s, RMS=0.100000, peak=0.371124
|
||||||
|
PASS luck-furniture.wav: 3.888s, RMS=0.100000, peak=0.387054
|
||||||
|
PASS password-apple.wav: 4.896s, RMS=0.100000, peak=0.483429
|
||||||
|
PASS password-sea.wav: 4.848s, RMS=0.100000, peak=0.533905
|
||||||
|
PASS password-tree.wav: 4.872s, RMS=0.100000, peak=0.490387
|
||||||
|
PASS password-hat.wav: 4.896s, RMS=0.100000, peak=0.505890
|
||||||
|
PASS password-umbrella.wav: 4.992s, RMS=0.100000, peak=0.477661
|
||||||
|
PASS password-market.wav: 4.800s, RMS=0.100000, peak=0.481689
|
||||||
|
PASS password-oil.wav: 5.016s, RMS=0.100000, peak=0.494507
|
||||||
|
PASS password-sound.wav: 4.920s, RMS=0.100000, peak=0.462097
|
||||||
|
PASS password-meat.wav: 5.016s, RMS=0.100000, peak=0.497864
|
||||||
|
PASS password-furniture.wav: 4.872s, RMS=0.100000, peak=0.483643
|
||||||
|
PASS remember-apple.wav: 8.232s, RMS=0.100000, peak=0.563568
|
||||||
|
PASS remember-sea.wav: 8.328s, RMS=0.100000, peak=0.546539
|
||||||
|
PASS remember-tree.wav: 8.520s, RMS=0.100000, peak=0.548645
|
||||||
|
PASS remember-hat.wav: 8.376s, RMS=0.100000, peak=0.541779
|
||||||
|
PASS remember-umbrella.wav: 8.400s, RMS=0.100000, peak=0.557831
|
||||||
|
PASS remember-market.wav: 8.304s, RMS=0.100000, peak=0.572815
|
||||||
|
PASS remember-oil.wav: 8.328s, RMS=0.100000, peak=0.524384
|
||||||
|
PASS remember-sound.wav: 8.208s, RMS=0.100000, peak=0.537781
|
||||||
|
PASS remember-meat.wav: 8.232s, RMS=0.100000, peak=0.519287
|
||||||
|
PASS remember-furniture.wav: 8.400s, RMS=0.100000, peak=0.561890
|
||||||
|
PASS last-apple.wav: 7.416s, RMS=0.100000, peak=0.622528
|
||||||
|
PASS last-sea.wav: 7.200s, RMS=0.100000, peak=0.591034
|
||||||
|
PASS last-tree.wav: 7.968s, RMS=0.100000, peak=0.580750
|
||||||
|
PASS last-hat.wav: 7.392s, RMS=0.100000, peak=0.550079
|
||||||
|
PASS last-umbrella.wav: 7.728s, RMS=0.100000, peak=0.597656
|
||||||
|
PASS last-market.wav: 7.488s, RMS=0.100000, peak=0.600555
|
||||||
|
PASS last-oil.wav: 7.032s, RMS=0.100000, peak=0.557068
|
||||||
|
PASS last-sound.wav: 7.392s, RMS=0.100000, peak=0.564087
|
||||||
|
PASS last-meat.wav: 7.224s, RMS=0.100000, peak=0.537201
|
||||||
|
PASS last-furniture.wav: 7.440s, RMS=0.100000, peak=0.583588
|
||||||
|
PASS first-apple.wav: 7.488s, RMS=0.100000, peak=0.579773
|
||||||
|
PASS first-sea.wav: 7.272s, RMS=0.100000, peak=0.571777
|
||||||
|
PASS first-tree.wav: 7.896s, RMS=0.100000, peak=0.583344
|
||||||
|
PASS first-hat.wav: 7.440s, RMS=0.100000, peak=0.531250
|
||||||
|
PASS first-umbrella.wav: 7.632s, RMS=0.100000, peak=0.570007
|
||||||
|
PASS first-market.wav: 7.488s, RMS=0.100000, peak=0.639069
|
||||||
|
PASS first-oil.wav: 6.984s, RMS=0.100000, peak=0.534729
|
||||||
|
PASS first-sound.wav: 7.368s, RMS=0.100000, peak=0.518951
|
||||||
|
PASS first-meat.wav: 7.200s, RMS=0.100000, peak=0.513123
|
||||||
|
PASS first-furniture.wav: 7.584s, RMS=0.100000, peak=0.570038
|
||||||
|
PASS repeat-apple.wav: 8.280s, RMS=0.100000, peak=0.641449
|
||||||
|
PASS repeat-sea.wav: 7.968s, RMS=0.100000, peak=0.684326
|
||||||
|
PASS repeat-tree.wav: 8.832s, RMS=0.100000, peak=0.618744
|
||||||
|
PASS repeat-hat.wav: 8.256s, RMS=0.100000, peak=0.616730
|
||||||
|
PASS repeat-umbrella.wav: 8.544s, RMS=0.100000, peak=0.674835
|
||||||
|
PASS repeat-market.wav: 8.376s, RMS=0.100000, peak=0.682251
|
||||||
|
PASS repeat-oil.wav: 7.704s, RMS=0.100000, peak=0.623962
|
||||||
|
PASS repeat-sound.wav: 8.232s, RMS=0.100000, peak=0.611450
|
||||||
|
PASS repeat-meat.wav: 7.920s, RMS=0.100000, peak=0.592102
|
||||||
|
PASS repeat-furniture.wav: 8.280s, RMS=0.100000, peak=0.667023
|
||||||
|
PASS correction-apple.wav: 8.376s, RMS=0.100000, peak=0.490326
|
||||||
|
PASS correction-sea.wav: 8.304s, RMS=0.100000, peak=0.496155
|
||||||
|
PASS correction-tree.wav: 8.352s, RMS=0.100000, peak=0.470612
|
||||||
|
PASS correction-hat.wav: 8.304s, RMS=0.100000, peak=0.470978
|
||||||
|
PASS correction-umbrella.wav: 8.472s, RMS=0.100000, peak=0.479034
|
||||||
|
PASS correction-market.wav: 8.448s, RMS=0.100000, peak=0.491486
|
||||||
|
PASS correction-oil.wav: 8.472s, RMS=0.100000, peak=0.470795
|
||||||
|
PASS correction-sound.wav: 8.280s, RMS=0.100000, peak=0.469269
|
||||||
|
PASS correction-meat.wav: 8.400s, RMS=0.100000, peak=0.481537
|
||||||
|
PASS correction-furniture.wav: 8.304s, RMS=0.100000, peak=0.487396
|
||||||
|
PASS middle-apple.wav: 7.512s, RMS=0.100000, peak=0.553558
|
||||||
|
PASS middle-sea.wav: 7.224s, RMS=0.100000, peak=0.564941
|
||||||
|
PASS middle-tree.wav: 7.968s, RMS=0.100000, peak=0.600006
|
||||||
|
PASS middle-hat.wav: 7.368s, RMS=0.100000, peak=0.555206
|
||||||
|
PASS middle-umbrella.wav: 7.680s, RMS=0.100000, peak=0.569550
|
||||||
|
PASS middle-market.wav: 7.416s, RMS=0.100000, peak=0.594971
|
||||||
|
PASS middle-oil.wav: 6.960s, RMS=0.100000, peak=0.539917
|
||||||
|
PASS middle-sound.wav: 7.368s, RMS=0.100000, peak=0.534821
|
||||||
|
PASS middle-meat.wav: 7.128s, RMS=0.100000, peak=0.510834
|
||||||
|
PASS middle-furniture.wav: 7.392s, RMS=0.100000, peak=0.557617
|
||||||
|
PASS message-apple.wav: 10.776s, RMS=0.100000, peak=0.525360
|
||||||
|
PASS message-sea.wav: 10.776s, RMS=0.100000, peak=0.535217
|
||||||
|
PASS message-tree.wav: 11.256s, RMS=0.100000, peak=0.526245
|
||||||
|
PASS message-hat.wav: 10.800s, RMS=0.100000, peak=0.587006
|
||||||
|
PASS message-umbrella.wav: 10.896s, RMS=0.100000, peak=0.530365
|
||||||
|
PASS message-market.wav: 10.896s, RMS=0.100000, peak=0.534821
|
||||||
|
PASS message-oil.wav: 10.608s, RMS=0.100000, peak=0.518127
|
||||||
|
PASS message-sound.wav: 10.872s, RMS=0.100000, peak=0.519531
|
||||||
|
PASS message-meat.wav: 10.680s, RMS=0.100000, peak=0.514771
|
||||||
|
PASS message-furniture.wav: 10.872s, RMS=0.100000, peak=0.529327
|
||||||
|
PASS mission-apple.wav: 5.136s, RMS=0.100000, peak=0.470154
|
||||||
|
PASS mission-sea.wav: 5.064s, RMS=0.100000, peak=0.450134
|
||||||
|
PASS mission-tree.wav: 5.040s, RMS=0.100000, peak=0.458527
|
||||||
|
PASS mission-hat.wav: 5.112s, RMS=0.100000, peak=0.449829
|
||||||
|
PASS mission-umbrella.wav: 5.160s, RMS=0.100000, peak=0.465332
|
||||||
|
PASS mission-market.wav: 5.184s, RMS=0.100000, peak=0.485687
|
||||||
|
PASS mission-oil.wav: 5.040s, RMS=0.100000, peak=0.443573
|
||||||
|
PASS mission-sound.wav: 5.016s, RMS=0.100000, peak=0.454803
|
||||||
|
PASS mission-meat.wav: 5.088s, RMS=0.100000, peak=0.441864
|
||||||
|
PASS mission-furniture.wav: 5.040s, RMS=0.100000, peak=0.485229
|
||||||
|
{
|
||||||
|
"count": 101,
|
||||||
|
"newVsV01": 101,
|
||||||
|
"uniqueHashes": 101,
|
||||||
|
"bytes": 34898524,
|
||||||
|
"durationRange": [
|
||||||
|
3.816,
|
||||||
|
11.256
|
||||||
|
],
|
||||||
|
"rmsRange": [
|
||||||
|
0.09999985338851038,
|
||||||
|
0.10000004859108667
|
||||||
|
],
|
||||||
|
"peakRange": [
|
||||||
|
0.36920166015625,
|
||||||
|
0.684326171875
|
||||||
|
],
|
||||||
|
"historicalUnchanged": 115
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
> find-word@0.1.1 build
|
||||||
|
> tsc --noEmit && vite build && node scripts/check-build.mjs
|
||||||
|
|
||||||
|
vite v8.3.4 building client environment for production...
|
||||||
|
transforming...
|
||||||
|
✓ 20 modules transformed.
|
||||||
|
rendering chunks...
|
||||||
|
computing gzip size...
|
||||||
|
dist/index.html 0.89 kB │ gzip: 0.63 kB
|
||||||
|
dist/assets/index-KT6VDfPn.css 15.25 kB │ gzip: 4.39 kB
|
||||||
|
dist/assets/index-CorsjeLi.js 245.34 kB │ gzip: 77.26 kB
|
||||||
|
|
||||||
|
✓ built in 405ms
|
||||||
|
빌드 음성 자산 101개 및 런타임 notice 전문 확인
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
> find-word@0.1.1 test:browser
|
||||||
|
> playwright test
|
||||||
|
|
||||||
|
[WebServer] (node:789414) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
|
||||||
|
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
|
||||||
|
|
||||||
|
Running 12 tests using 2 workers
|
||||||
|
|
||||||
|
(node:789465) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
|
||||||
|
(Use `node --trace-warnings ...` to show where the warning was created)
|
||||||
|
(node:789464) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
|
||||||
|
(Use `node --trace-warnings ...` to show where the warning was created)
|
||||||
|
pw:browser <launching> /usr/bin/google-chrome --disable-field-trial-config --disable-background-networking --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-back-forward-cache --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-background-pages --disable-component-update --no-default-browser-check --disable-default-apps --disable-dev-shm-usage --disable-edgeupdater --disable-extensions --disable-features=AvoidUnnecessaryBeforeUnloadCheckSync,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,BlockOriginHeaderModificationOnRedirect,Translate,AutoDeElevate,OptimizationHints,NetworkTimeServiceQuerying,AimEnabled,msForceBrowserSignIn,msEdgeUpdateLaunchServicesPreferredVersion --enable-features=CDPScreenshotNewSurface --allow-pre-commit-input --disable-hang-monitor --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-backgrounding --disable-updater-scheduler --force-color-profile=srgb --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --no-service-autorun --export-tagged-pdf --disable-search-engine-choice-screen --unsafely-disable-devtools-self-xss-warnings --edge-skip-compat-layer-relaunch --disable-infobars --disable-search-engine-choice-screen --disable-sync --enable-unsafe-swiftshader --headless --hide-scrollbars --mute-audio --blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4 --user-data-dir=/tmp/playwright_chromiumdev_profile-usRFeJ --remote-debugging-pipe --no-startup-window +0ms
|
||||||
|
pw:browser <launching> /usr/bin/google-chrome --disable-field-trial-config --disable-background-networking --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-back-forward-cache --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-background-pages --disable-component-update --no-default-browser-check --disable-default-apps --disable-dev-shm-usage --disable-edgeupdater --disable-extensions --disable-features=AvoidUnnecessaryBeforeUnloadCheckSync,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,BlockOriginHeaderModificationOnRedirect,Translate,AutoDeElevate,OptimizationHints,NetworkTimeServiceQuerying,AimEnabled,msForceBrowserSignIn,msEdgeUpdateLaunchServicesPreferredVersion --enable-features=CDPScreenshotNewSurface --allow-pre-commit-input --disable-hang-monitor --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-backgrounding --disable-updater-scheduler --force-color-profile=srgb --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --no-service-autorun --export-tagged-pdf --disable-search-engine-choice-screen --unsafely-disable-devtools-self-xss-warnings --edge-skip-compat-layer-relaunch --disable-infobars --disable-search-engine-choice-screen --disable-sync --enable-unsafe-swiftshader --headless --hide-scrollbars --mute-audio --blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4 --user-data-dir=/tmp/playwright_chromiumdev_profile-tgmIGX --remote-debugging-pipe --no-startup-window +0ms
|
||||||
|
pw:browser <launched> pid=789488 +15ms
|
||||||
|
pw:browser <launched> pid=789490 +14ms
|
||||||
|
pw:browser [pid=789488][err] [789574:789574:1014/065656.619803:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +253ms
|
||||||
|
pw:browser [pid=789490][err] [789572:789572:1014/065656.641388:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +270ms
|
||||||
|
pw:browser [pid=789488][err] [789657:7:1014/065657.003307:ERROR:gpu/ipc/client/command_buffer_proxy_impl.cc:287] ContextResult::kTransientFailure: Failed to send GpuControl.CreateCommandBuffer. +394ms
|
||||||
|
pw:browser [pid=789490][err] [789697:789697:1014/065657.581581:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +937ms
|
||||||
|
pw:browser [pid=789488][err] [789721:789721:1014/065659.123843:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +2s
|
||||||
|
pw:browser [pid=789490][err] [789490:789531:1014/065701.312194:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +4s
|
||||||
|
pw:browser [pid=789488][err] [789488:789553:1014/065702.399765:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +3s
|
||||||
|
✓ 2 [desktop] › tests/browser/game.spec.ts:114:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (14.7s)
|
||||||
|
✓ 3 [desktop] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (309ms)
|
||||||
|
✓ 4 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.6s)
|
||||||
|
✓ 5 [desktop] › tests/browser/ui.spec.ts:52:1 › 난이도 설명은 모바일에서도 보인다 (1.0s)
|
||||||
|
pw:browser [pid=789490][err] [789490:789531:1014/065725.821501:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +25s
|
||||||
|
pw:browser [pid=789488][err] [789488:789553:1014/065728.796251:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: QUOTA_EXCEEDED +26s
|
||||||
|
✓ 6 [desktop] › tests/browser/ui.spec.ts:68:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (16.7s)
|
||||||
|
pw:browser [pid=789490] <gracefully close start> +5s
|
||||||
|
pw:browser [pid=789490] <process did exit: exitCode=0, signal=null> +53ms
|
||||||
|
pw:browser [pid=789490] starting temporary directories cleanup +0ms
|
||||||
|
pw:browser [pid=789490] finished temporary directories cleanup +9ms
|
||||||
|
pw:browser [pid=789490] <gracefully close end> +0ms
|
||||||
|
(node:789845) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
|
||||||
|
(Use `node --trace-warnings ...` to show where the warning was created)
|
||||||
|
pw:browser <launching> /usr/bin/google-chrome --disable-field-trial-config --disable-background-networking --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-back-forward-cache --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-background-pages --disable-component-update --no-default-browser-check --disable-default-apps --disable-dev-shm-usage --disable-edgeupdater --disable-extensions --disable-features=AvoidUnnecessaryBeforeUnloadCheckSync,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,BlockOriginHeaderModificationOnRedirect,Translate,AutoDeElevate,OptimizationHints,NetworkTimeServiceQuerying,AimEnabled,msForceBrowserSignIn,msEdgeUpdateLaunchServicesPreferredVersion --enable-features=CDPScreenshotNewSurface --allow-pre-commit-input --disable-hang-monitor --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-backgrounding --disable-updater-scheduler --force-color-profile=srgb --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --no-service-autorun --export-tagged-pdf --disable-search-engine-choice-screen --unsafely-disable-devtools-self-xss-warnings --edge-skip-compat-layer-relaunch --disable-infobars --disable-search-engine-choice-screen --disable-sync --enable-unsafe-swiftshader --headless --hide-scrollbars --mute-audio --blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4 --user-data-dir=/tmp/playwright_chromiumdev_profile-WzJ5B4 --remote-debugging-pipe --no-startup-window +0ms
|
||||||
|
pw:browser <launched> pid=789857 +13ms
|
||||||
|
pw:browser [pid=789857][err] [789900:789900:1014/065731.887141:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +138ms
|
||||||
|
pw:browser [pid=789857][err] [789972:789972:1014/065733.402085:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +2s
|
||||||
|
pw:browser [pid=789857][err] [789857:789885:1014/065736.698476:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +3s
|
||||||
|
pw:browser [pid=789857][err] [789857:789885:1014/065757.253598:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +21s
|
||||||
|
✓ 1 [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (1.6m)
|
||||||
|
pw:browser [pid=789488] <gracefully close start> +1m
|
||||||
|
pw:browser [pid=789488] <process did exit: exitCode=0, signal=null> +49ms
|
||||||
|
pw:browser [pid=789488] starting temporary directories cleanup +1ms
|
||||||
|
pw:browser [pid=789488] finished temporary directories cleanup +10ms
|
||||||
|
pw:browser [pid=789488] <gracefully close end> +0ms
|
||||||
|
(node:790149) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
|
||||||
|
(Use `node --trace-warnings ...` to show where the warning was created)
|
||||||
|
pw:browser <launching> /usr/bin/google-chrome --disable-field-trial-config --disable-background-networking --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-back-forward-cache --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-background-pages --disable-component-update --no-default-browser-check --disable-default-apps --disable-dev-shm-usage --disable-edgeupdater --disable-extensions --disable-features=AvoidUnnecessaryBeforeUnloadCheckSync,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,BlockOriginHeaderModificationOnRedirect,Translate,AutoDeElevate,OptimizationHints,NetworkTimeServiceQuerying,AimEnabled,msForceBrowserSignIn,msEdgeUpdateLaunchServicesPreferredVersion --enable-features=CDPScreenshotNewSurface --allow-pre-commit-input --disable-hang-monitor --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-backgrounding --disable-updater-scheduler --force-color-profile=srgb --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --no-service-autorun --export-tagged-pdf --disable-search-engine-choice-screen --unsafely-disable-devtools-self-xss-warnings --edge-skip-compat-layer-relaunch --disable-infobars --disable-search-engine-choice-screen --disable-sync --enable-unsafe-swiftshader --headless --hide-scrollbars --mute-audio --blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4 --user-data-dir=/tmp/playwright_chromiumdev_profile-F4j9tO --remote-debugging-pipe --no-startup-window +0ms
|
||||||
|
pw:browser <launched> pid=790161 +5ms
|
||||||
|
pw:browser [pid=790161][err] [790203:790203:1014/065834.688015:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +137ms
|
||||||
|
pw:browser [pid=790161][err] [790242:7:1014/065834.832706:ERROR:gpu/ipc/client/command_buffer_proxy_impl.cc:287] ContextResult::kTransientFailure: Failed to send GpuControl.CreateCommandBuffer. +143ms
|
||||||
|
pw:browser [pid=790161][err] [790262:790262:1014/065835.186112:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +352ms
|
||||||
|
pw:browser [pid=789857][err] [789857:789885:1014/065837.765817:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +41s
|
||||||
|
pw:browser [pid=790161][err] [790161:790189:1014/065838.519301:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: QUOTA_EXCEEDED +3s
|
||||||
|
✓ 8 [mobile] › tests/browser/game.spec.ts:114:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (16.9s)
|
||||||
|
✓ 9 [mobile] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (292ms)
|
||||||
|
✓ 10 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (938ms)
|
||||||
|
✓ 11 [mobile] › tests/browser/ui.spec.ts:52:1 › 난이도 설명은 모바일에서도 보인다 (921ms)
|
||||||
|
✓ 12 [mobile] › tests/browser/ui.spec.ts:68:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (16.7s)
|
||||||
|
pw:browser [pid=790161] <gracefully close start> +32s
|
||||||
|
pw:browser [pid=790161] <process did exit: exitCode=0, signal=null> +51ms
|
||||||
|
pw:browser [pid=790161] starting temporary directories cleanup +0ms
|
||||||
|
pw:browser [pid=790161] finished temporary directories cleanup +9ms
|
||||||
|
pw:browser [pid=790161] <gracefully close end> +0ms
|
||||||
|
✓ 7 [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (1.7m)
|
||||||
|
pw:browser [pid=789857] <gracefully close start> +34s
|
||||||
|
pw:browser [pid=789857] <process did exit: exitCode=0, signal=null> +107ms
|
||||||
|
pw:browser [pid=789857] starting temporary directories cleanup +1ms
|
||||||
|
pw:browser [pid=789857] finished temporary directories cleanup +18ms
|
||||||
|
pw:browser [pid=789857] <gracefully close end> +1ms
|
||||||
|
|
||||||
|
12 passed (2.3m)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
2026-10-14T06:56:54.062Z pw:browser <launching> /usr/bin/google-chrome --disable-field-trial-config --disable-background-networking --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-back-forward-cache --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-background-pages --disable-component-update --no-default-browser-check --disable-default-apps --disable-dev-shm-usage --disable-edgeupdater --disable-extensions --disable-features=AvoidUnnecessaryBeforeUnloadCheckSync,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,BlockOriginHeaderModificationOnRedirect,Translate,AutoDeElevate,OptimizationHints,NetworkTimeServiceQuerying,AimEnabled,msForceBrowserSignIn,msEdgeUpdateLaunchServicesPreferredVersion --enable-features=CDPScreenshotNewSurface --allow-pre-commit-input --disable-hang-monitor --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-backgrounding --disable-updater-scheduler --force-color-profile=srgb --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --no-service-autorun --export-tagged-pdf --disable-search-engine-choice-screen --unsafely-disable-devtools-self-xss-warnings --edge-skip-compat-layer-relaunch --disable-infobars --disable-search-engine-choice-screen --disable-sync --enable-unsafe-swiftshader --headless --hide-scrollbars --mute-audio --blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4 --user-data-dir=/tmp/playwright_chromiumdev_profile-jPkOZ2 --remote-debugging-pipe --no-startup-window
|
||||||
|
2026-10-14T06:56:54.067Z pw:browser <launched> pid=789288
|
||||||
|
2026-10-14T06:56:54.238Z pw:browser [pid=789288][err] [789341:789341:1014/065654.236997:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7
|
||||||
|
2026-10-14T06:56:54.428Z pw:browser [pid=789288][err] [789379:7:1014/065654.427988:ERROR:gpu/ipc/client/command_buffer_proxy_impl.cc:287] ContextResult::kTransientFailure: Failed to send GpuControl.CreateCommandBuffer.
|
||||||
|
SANDBOX STATUS
|
||||||
|
Sandbox Status
|
||||||
|
Layer 1 Sandbox Namespace
|
||||||
|
PID namespaces Yes
|
||||||
|
Network namespaces Yes
|
||||||
|
Seccomp-BPF sandbox Yes
|
||||||
|
Seccomp-BPF sandbox supports TSYNC Yes
|
||||||
|
Ptrace Protection with Yama LSM (Broker) Yes
|
||||||
|
Ptrace Protection with Yama LSM (Non-broker) No
|
||||||
|
|
||||||
|
You are adequately sandboxed.
|
||||||
|
PASS: sandboxed Chrome decoded 101/101 WAVs at 24kHz mono; hashes, bytes, duration, RMS, peak verified; external requests=0, JS errors=0.
|
||||||
|
2026-10-14T06:56:57.008Z pw:browser [pid=789288] <gracefully close start>
|
||||||
|
2026-10-14T06:56:57.243Z pw:browser [pid=789288] <process did exit: exitCode=0, signal=null>
|
||||||
|
2026-10-14T06:56:57.243Z pw:browser [pid=789288] starting temporary directories cleanup
|
||||||
|
2026-10-14T06:56:57.285Z pw:browser [pid=789288] finished temporary directories cleanup
|
||||||
|
2026-10-14T06:56:57.286Z pw:browser [pid=789288] <gracefully close end>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
> find-word@0.1.1 lint
|
||||||
|
> eslint . && tsc --noEmit
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"historicalFilesUnchanged": 115,
|
||||||
|
"gitHeadUnchanged": "1c0069c367f6e108d3f67a2a0d044399dd0ef2bd",
|
||||||
|
"branch": "fix/v0.1.1-neural-voice",
|
||||||
|
"secretScanTextFiles": 40,
|
||||||
|
"secretMatches": 0,
|
||||||
|
"publicStageDirectories": 0,
|
||||||
|
"pythonVenvVersion": "7.3.1"
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Linux-6.8.0-142-generic-x86_64-with-glibc2.39
|
||||||
|
Generated at: 2026-10-14T06:55:41.751382+00:00
|
||||||
|
$ node --version
|
||||||
|
v26.7.0
|
||||||
|
$ npm --version
|
||||||
|
11.19.0
|
||||||
|
$ .venv-audio/bin/python --version
|
||||||
|
Python 3.11.16
|
||||||
|
$ .venv-audio/bin/python -m pip check
|
||||||
|
No broken requirements found.
|
||||||
|
$ google-chrome --version
|
||||||
|
Google Chrome 146.0.7680.80
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"historicalFilesUnchanged": 115,
|
||||||
|
"gitHeadUnchanged": "1c0069c367f6e108d3f67a2a0d044399dd0ef2bd",
|
||||||
|
"branch": "fix/v0.1.1-neural-voice",
|
||||||
|
"chromeLaunchesWithSandbox": 5,
|
||||||
|
"browserTestsPassed": 12,
|
||||||
|
"browserRuntimeSandbox": [
|
||||||
|
"Namespace",
|
||||||
|
"PID namespaces",
|
||||||
|
"Network namespaces",
|
||||||
|
"Seccomp-BPF"
|
||||||
|
],
|
||||||
|
"documentLinksChecked": 56,
|
||||||
|
"gameMechanicsAndRuntimeAudioUnchanged": true,
|
||||||
|
"distContainsOnlyExpectedArtifacts": true,
|
||||||
|
"diffCheck": "PASS"
|
||||||
|
}
|
||||||
@@ -0,0 +1,738 @@
|
|||||||
|
{
|
||||||
|
"historical": {
|
||||||
|
".agent/evidence/01-content-green.log": "5585acd257b49225218835acd69a1d240c7737d12dba62949f31a5b7c1c9ed2a",
|
||||||
|
".agent/evidence/01-content-red.log": "0f28fe2ae2c4669a52ba2fc15bfa6285e4922474dc2b2571098b522679948e12",
|
||||||
|
".agent/evidence/02-game-green.log": "5ca5895f8a09f4e9de7a846bf9ab37fb3715623e8e7654dab095505c097fc0c7",
|
||||||
|
".agent/evidence/02-game-red.log": "78468ee5c25c1685f3613926f636b6fb4569227533feda3547afc6b8123d6452",
|
||||||
|
".agent/evidence/03-dsp-green.log": "d3d08683a1426b845a2e987572861b82b3ac978b8c352991950c0f32a473d3de",
|
||||||
|
".agent/evidence/03-dsp-intermediate.log": "084a52294f718718256eb1516048a672d045a5f7dcb5d11c646092d29efbab58",
|
||||||
|
".agent/evidence/03-dsp-red.log": "bacf42e5c808d32702b9bcd30523d1fcf17d5ff75b281233d9e84f33b6e42589",
|
||||||
|
".agent/evidence/04-audio-green.log": "d48c93fa7d32315ed4f719dee19222e7398bc4c4bceb8cf345e0e221a9ac89d8",
|
||||||
|
".agent/evidence/04-audio-red.log": "58bbf8eae572f83e408dd101eac738a5f6963550e21c8265f42a7eeba18be7a7",
|
||||||
|
".agent/evidence/05-assets-red.log": "04388acb098f7dabe9a35783b496075ee7c39757d3fde5361c9ac080188ac371",
|
||||||
|
".agent/evidence/06-ui-05-assets-green.log": "ced2ad738cda673b24308dcb9d99d59e3ae918709eeb32de359d6ab1022133e2",
|
||||||
|
".agent/evidence/06-ui-intermediate.log": "3295b3ae691dadb8feb7f0aaf604a1aa56cbfae27b5dbdd486cb5645f5edabe9",
|
||||||
|
".agent/evidence/06-ui-red.log": "1f1ed6bd440af22f5abf59e4be194f969bee1d115d436a155d879d8cffddb747",
|
||||||
|
".agent/evidence/07-build-green.log": "ec98087d20a68c5b81c6c23406316f1e5f19c6124498e1fc005d04bb78894a57",
|
||||||
|
".agent/evidence/07-build-red.log": "138802de7bc67c1535668efe7a915424a70d5d7994acb7f366a139e89c1da77f",
|
||||||
|
".agent/evidence/08-rule-green.log": "91b7ce3fda181fbe51cbe4e6debb61dd44dd55226dcfa81ed73559ba76811f3f",
|
||||||
|
".agent/evidence/08-rule-red.log": "2cc1854cf6a4b9e8d53a7a36b1dde1fb6c6970b89f841b9e8f404e6e42bb929e",
|
||||||
|
".agent/evidence/09-ime-green.log": "e5d5d1cfa2b6db724ded97d5f3309a517bcf637d9b2b417e9110dc9da0a48d72",
|
||||||
|
".agent/evidence/09-ime-red.log": "10e5f9f99609044c768cd338df0d574a06a7442e0debe0e61ca16a6b5873adf3",
|
||||||
|
".agent/evidence/10-ime-reset-green.log": "e12761b3a2814a79d1448e7452474a086bf371921287718eb72dd9b0f6b8fc82",
|
||||||
|
".agent/evidence/10-ime-reset-red.log": "f6cb7c1724b8f9c410385e9970051577c03527fb7e719f43a77c967ca08ef18f",
|
||||||
|
".agent/evidence/11-provenance-green.log": "54dcc417b97f82422c5c3e0ae11caa1f15a60412853ea1dc0152d15031bcd935",
|
||||||
|
".agent/evidence/11-provenance-red.log": "e69fa7989223e659c16b8030f8668e562ad5b0daa060fbb1c9eff41a37fd56fd",
|
||||||
|
".agent/evidence/12-ime-replay-green.log": "a93d9f16c7d81ed5e9a7c0b70ffee1e393d2943ddf3149a5bb115c5517327c44",
|
||||||
|
".agent/evidence/12-ime-replay-red.log": "8cf5dab06d93cc544c21276ee633829f8b137b4b6d7c5fe47aadcad6fc2afb71",
|
||||||
|
".agent/evidence/13-heading-build-after.log": "19829ac264f97aa023baf7d5e1fd429343272f3bd952e8b6dfb9eec0f594b453",
|
||||||
|
".agent/evidence/13-heading-build-before.log": "646d197d2ef93c19fec4719e20297de1b22eff54f3e6155ba2125fd3dd372c46",
|
||||||
|
".agent/evidence/13-heading-green.log": "d59fa8546083e9d371c7a552287f27bf3acd572bd0cce3da8eba860da5c05880",
|
||||||
|
".agent/evidence/13-heading-red.log": "33acc2838786a87b8ecc170a1ac0a9d7521daf8e5b9aed2195e76e45ea327f76",
|
||||||
|
".agent/evidence/14-snr-browser-green.log": "fcb82296e9af4b96112fae0bbb77c6ba41230c117021243e85916ae1130a462d",
|
||||||
|
".agent/evidence/14-snr-browser-red.log": "36298e5bb1f45a69b0ca75e5cbce85ee1455c4485759670268fbbcf55bacd4d4",
|
||||||
|
".agent/evidence/14-snr-build.log": "1e9ce36a48904a58195f9d1c2d9ca6cf2895fcb8bd0b5ab9b72f76e92c6b2585",
|
||||||
|
".agent/evidence/14-snr-green.log": "ae74e57cea20841299a8c1ee5eaf60efad34bda697339f45527cdec83578e6df",
|
||||||
|
".agent/evidence/14-snr-red.log": "b39bdbd8815d0d9cfcedc41c61124afece2011ea6c6c250b0ee0139eae7e9edd",
|
||||||
|
".agent/evidence/15-volume-browser-green.log": "aeccd5f13eb9db4062cbf2c70eee332d0c39540d580e113d0a79a899b2ef02de",
|
||||||
|
".agent/evidence/15-volume-browser-red.log": "84954280c1729703f9e9c2680c8933c3b0752b19fb9c3562e7dbf2ea1b4a548c",
|
||||||
|
".agent/evidence/15-volume-build.log": "2767b794124804b3447f529eef025e2530975fcc506e1cbe8994acf8243e6ba8",
|
||||||
|
".agent/evidence/15-volume-green.log": "0f9a90923f4fdc477c8f2436653eb2bf0e4b251bcb52e795701309ba14f23c7d",
|
||||||
|
".agent/evidence/15-volume-red.log": "9beae6305080cd0d869bf3fd2093beb6e2e25e5424223aa7e4ad4db96954f37e",
|
||||||
|
".agent/evidence/16-copy-green.log": "dacbdbab5d4af2b6e19eb4e0502a70720aac9fb6cf3b9d6dfdc73a5bf7edabfe",
|
||||||
|
".agent/evidence/16-copy-red.log": "ebe3d7c86938d33d7f1a4818b18c7e94ab8feb18bee099fcb535a93d8083861b",
|
||||||
|
".agent/evidence/17-spacing-green.log": "8ab85362ceadcdfecf4f7b1e95abfb4237e15018692e14f937be90265c351b11",
|
||||||
|
".agent/evidence/17-spacing-red.log": "12db8083cd8627a81a52cd32358780a3c2257cb3bd702350b190b39226f6550f",
|
||||||
|
".agent/evidence/18-literal-green.log": "878a8d788a8cedbf652fe0e819fe482099249a52ae7b5cc3d8826e64b32c6377",
|
||||||
|
".agent/evidence/18-literal-red.log": "ad23ae7be3d9aab83907c12849012a01f9d1b12e5a174c3acede89817a74d2d2",
|
||||||
|
".agent/evidence/19-notices-green.log": "efc51aca0738f3cb5264ec3c3ad0d17bb150d685f5fdb5e200bc1eedc0e3c02b",
|
||||||
|
".agent/evidence/19-notices-red.log": "8f34c583cb8563803828d72fe56622d84bcf59a9541d983d6c0b705225955794",
|
||||||
|
".agent/evidence/20-audio-generate.log": "f864fc266f7ca50c600457399f198a77949e9ee1601b0d2fc19b64942a2030cb",
|
||||||
|
".agent/evidence/20-audio-green.log": "edb9b9f0aa1af11835960eee9bb6e8c013341aa528d2857bac0b89ac38457528",
|
||||||
|
".agent/evidence/20-audio-red.log": "c1cbd5b585e8a70e23b88bfcee0c6479f85fdec0d1e06d13c81c689a140c0ac0",
|
||||||
|
".agent/evidence/20-mechanics-green.log": "9776c68143f668e35df8b2e19884b25d2369e4800b4c264e635d0c260818c5f1",
|
||||||
|
".agent/evidence/20-mechanics-red.log": "d25a1c844366e14fe1db9bb7c243fe0adb6857ee5b4548cf2865de96589af647",
|
||||||
|
".agent/evidence/21-node-green.log": "3c36e9e45b6844ae2c184f943b61195482d9de53ceab06221093c8281d210d73",
|
||||||
|
".agent/evidence/21-node-red.log": "ee25556037bdbd6b099dbb087a522284e8df00bd573dfcf0e42c734e31e09e84",
|
||||||
|
".agent/evidence/22-sandbox-green.log": "e8a1a17e49c22dea81d7f85111c744473427466791ddeda75047cd0082e7faba",
|
||||||
|
".agent/evidence/22-sandbox-red.log": "c8a97270f0f68367d6edb3e5ee10f34bbda1127f8dae2c5639afd482198e7bcd",
|
||||||
|
".agent/evidence/23-delivery-browser-red.log": "180a61bbacf85db9e52f721b50da683103b719bc350454cc99d4c8954b2ae86e",
|
||||||
|
".agent/evidence/24-retry-audio-budget.log": "b9241872b4ecd80265df4c6c2be90b31e9de4d0754772655e50b7fb4f05e2d3b",
|
||||||
|
".agent/evidence/24-retry-final-browser.log": "68029aae89adf86fd60ee56a20a1e2f769cb4f894f27ae23963ca5b5b58c62c4",
|
||||||
|
".agent/evidence/24-retry-final-build.log": "702c20554872e437137097ca59a80d911188477f018bc48c4b49a770b73d5a84",
|
||||||
|
".agent/evidence/24-retry-final-lint.log": "8682ca1f55e3ec58f13321cc339c8647ef72c1934d74ea9b980aee717e528819",
|
||||||
|
".agent/evidence/24-retry-final-test.log": "b71bb1375a039bcc3d7b3c5070da85ece3551f32ae1b8bb3a2abc5ef1edd57b8",
|
||||||
|
".agent/evidence/24-retry-integrity.log": "a188fe54dce05f6caa868b3d9f2c22a852f6a8cdc462355673316578be623a08",
|
||||||
|
".agent/evidence/24-retry-targeted-green.log": "4e53f2de988040540f5075b2e4a483ca53b671486c35e864fbc3c2c9c5361c61",
|
||||||
|
".agent/evidence/audio-reproduce.log": "5f5726f3ed6845882c7a6d6f9a8d898c2b73bf61fa6328e8bbafd25f61ed4268",
|
||||||
|
".agent/evidence/browser-final.log": "9273c91613160e239027452894b3735f9657ccad97a52412eb889290aebd5adf",
|
||||||
|
".agent/evidence/browser-first.log": "f3087028d76aae36d61d7a14da488bd809079a9e121a2d8fb01fa034f99d4f05",
|
||||||
|
".agent/evidence/browser-second.log": "2ff1d16f51b9384cf431d5923ad87d773510fe4b89bdf62f09cbda2efae20e3b",
|
||||||
|
".agent/evidence/browser-third.log": "1b6c264930da8934e7f1c8da6c998543f35e68b1c3159dad722cfe23a8e5244c",
|
||||||
|
".agent/evidence/changed-files.txt": "2b609231e5c7becd581f79413a4f7443312e71f0099ac6a51b2c5f0dfdee576f",
|
||||||
|
".agent/evidence/desktop-game.png": "42702e89264357257db8bf54c2b3743e3212e62cfd3476a8c25bad83fb59ff81",
|
||||||
|
".agent/evidence/desktop-home.png": "bf2f515fcd483cd5ca9a3b408f484d3556b75b1be0bb70360b7fd5f7c3404993",
|
||||||
|
".agent/evidence/desktop-results.png": "003df3aeb08ce66618775377d9b2003809d696629f61cf54b7b86e7a0ed63593",
|
||||||
|
".agent/evidence/final-audit.log": "6d8c5c8f3d7684adb070417bd608d01ae90aa3dc26a65af03ffda4955f38d9a3",
|
||||||
|
".agent/evidence/final-browser.log": "5855691def462b23667a9d658338a0458a53d984b5b5f7e11b45f9a63092abcd",
|
||||||
|
".agent/evidence/final-build.log": "565a95a51a10f78bc08f256755afce5ef183440dcdea5e75b3aefde8d7c5b837",
|
||||||
|
".agent/evidence/final-lint.log": "d2354c241320f11f07890cbf32f99a5d88f1c7f4f236d860e2cd26db739badd8",
|
||||||
|
".agent/evidence/final-security.log": "c585566624a4596e97327fb1864b1ac2a3189c0794b1e25a9d6ff1abb86046cb",
|
||||||
|
".agent/evidence/final-test.log": "af03027cb22a090ecd107c10e3fc7e2f9ceb394122b0b73caa7d362310504170",
|
||||||
|
".agent/evidence/mobile-game.png": "20a3f02f70a28b2faae1b33d0d4d6fea10b77caa426e4bf9faa5ef6d4474a846",
|
||||||
|
".agent/evidence/mobile-home.png": "7f84bd54183fcdd03fe136b41145146ddfb323041c96a8b9e6b670c9a923033a",
|
||||||
|
".agent/evidence/mobile-results.png": "ec2938c03ac03da8e33e25f043522b317b60c16be1c29f9dd325a81ae84513a8",
|
||||||
|
".agent/evidence/neo-before-spacing-browser.log": "e5c214f14bd93ef1bb731f87ecfd3a02d299c9f9e5a788233f6eda5eb8946b4f",
|
||||||
|
".agent/evidence/neo-desktop-game.png": "f5966688ba46f14fab5125452f935e55f6e6dc4355c876416729866129daea70",
|
||||||
|
".agent/evidence/neo-desktop-heading.png": "db609f2f54af0e6b1bb96bce9bf839454403d6e2787e6520a2928fad31beb021",
|
||||||
|
".agent/evidence/neo-desktop-home.png": "826c5da794c4ea3c15bd6c13e81d1c021fbeb8a902fc4908b78f9c457f091c0f",
|
||||||
|
".agent/evidence/neo-desktop-results.png": "98a68ca050a4e66c17dcc3f1bd5f0e86bfc88c2ebe9a75284766d17e10426632",
|
||||||
|
".agent/evidence/neo-final-browser.log": "582e7cbb643201e4b197b566b021322448abaae0b383967ad0e816d0c351e96b",
|
||||||
|
".agent/evidence/neo-final-build.log": "f30138e53b8c12e70e80e2cd5f4bdfcf8d1f6a095216a4f9ade6687bca1f0488",
|
||||||
|
".agent/evidence/neo-final-lint.log": "d2354c241320f11f07890cbf32f99a5d88f1c7f4f236d860e2cd26db739badd8",
|
||||||
|
".agent/evidence/neo-final-test.log": "f718e2acda255fcba67b272c7fa625af450714b384bb67a72f09328bf6964f0b",
|
||||||
|
".agent/evidence/neo-intermediate-build.log": "9d8a1f0586d58196e03e45330b6e8ff5522fdd3c27870aae2fb052199f8cf5cb",
|
||||||
|
".agent/evidence/neo-intermediate-lint.log": "5e94ea847c3a092f4ff8e97ac5b61ebef805b3c859ad38461f4a437045d1b5b9",
|
||||||
|
".agent/evidence/neo-mobile-game.png": "1a6733a3293204e4841ec8722a1ffe37033943097c691bcfadbcd907fde024ef",
|
||||||
|
".agent/evidence/neo-mobile-heading.png": "ae07d0a1cb6f835525c7e0ef31655a543eaa9e10183596c2d409dead6faccf15",
|
||||||
|
".agent/evidence/neo-mobile-home.png": "9b3ba8b0a9f7b321882429e31ac4a0472fcc13a998dfadfc930c273bc37558d4",
|
||||||
|
".agent/evidence/neo-mobile-results.png": "d49fdf6ae9566d7a2003db3f252d84ed88ac4d73a0390ce404b47e7bab9c60df",
|
||||||
|
".agent/evidence/release-desktop-game.png": "7300a06a507f651a587ed3deb36cc5bb728f4a36c30b7b1cf409a3b209ba564c",
|
||||||
|
".agent/evidence/release-desktop-heading.png": "de454d77e29737e23c573646404489d5682004521deabfa01b439d607faa32ca",
|
||||||
|
".agent/evidence/release-desktop-home.png": "f28f744e11b66373ee8db6eda2f7bdc384da4f7e763fbb467a4cda30090e869b",
|
||||||
|
".agent/evidence/release-desktop-results.png": "2ebb4056d0ecdadea8a3469500801065ae158c17cd2db27817e813e53adc71f3",
|
||||||
|
".agent/evidence/release-docs-final.log": "3529acce5befee536ec7d8e9d22cbbf27182297bb07408ab6e31551aaee76ed3",
|
||||||
|
".agent/evidence/release-final-audit.log": "6d8c5c8f3d7684adb070417bd608d01ae90aa3dc26a65af03ffda4955f38d9a3",
|
||||||
|
".agent/evidence/release-final-browser.log": "2a9881cef8c379f41abf5d69dd5ad21881988f78c0579c95e463121dc37f6e09",
|
||||||
|
".agent/evidence/release-final-build.log": "3fba8356382341db9c562d985ca2229fd0b81969532746b0146bc489d54c321e",
|
||||||
|
".agent/evidence/release-final-lint.log": "d2354c241320f11f07890cbf32f99a5d88f1c7f4f236d860e2cd26db739badd8",
|
||||||
|
".agent/evidence/release-final-test.log": "46d5d0fc0180adc00a734b64db4acb4cbaa37d12567d0224836306312213870c",
|
||||||
|
".agent/evidence/release-format.log": "57658c36cb3161b53cdf934017e4b7a733a273509ace184e0a2a6eaa046c742b",
|
||||||
|
".agent/evidence/release-integrity.log": "cc77121b43a6b24a358999cf1a3bbf701d467fcee9730095f318303c2e80602b",
|
||||||
|
".agent/evidence/release-intermediate-lint.log": "ee597e984ebdf11ea247c8cb1551cda446580cbd7a3dc708f85fa78bb72700ad",
|
||||||
|
".agent/evidence/release-mobile-game.png": "83a918e5ff2142e540cd12b85806b3e69a71bc66df5d241f4b2f5abdaf493f82",
|
||||||
|
".agent/evidence/release-mobile-heading.png": "d5c92a3b62f749d5f064af12efa5a1a2ad13b177270ec01dd10a549a8437899d",
|
||||||
|
".agent/evidence/release-mobile-home.png": "c7fc37a247bbadba60de9dff3c8d87930a2e6ebb868f687a4135b3fd035ed1c3",
|
||||||
|
".agent/evidence/release-mobile-results.png": "587956c9ad5eca054e4bb4b44748a2c637655e407427ba5b4c6ad7bf5a74539a",
|
||||||
|
".agent/RESULT.md": "eef6d73a67a8a7e90cd7b4747debc5976cc51571bc268328634413f698d0e311"
|
||||||
|
},
|
||||||
|
"audio": {
|
||||||
|
"generator": "eSpeak NG ko, speed 145, pitch 45, amplitude 100; FFmpeg PCM 16kHz mono s16",
|
||||||
|
"toolVersions": {
|
||||||
|
"espeak": "eSpeak NG text-to-speech: 1.51",
|
||||||
|
"ffmpeg": "ffmpeg version 6.1.1-3ubuntu5 Copyright (c) 2000-2023 the FFmpeg developers"
|
||||||
|
},
|
||||||
|
"source": "https://github.com/espeak-ng/espeak-ng",
|
||||||
|
"method": "자체 작성 문장의 오프라인 포먼트 합성; 외부 녹음·네트워크 TTS 없음",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"file": "intro.wav",
|
||||||
|
"text": "으하하하~~ 나는 에스다. 널 위한 행운의 단어를 불러주마, 잘 찾아보거라.",
|
||||||
|
"sha256": "4c271551d99cd9dffc01f35c176e98b54b4879549543ff8e2c79c6af8d4dc53a",
|
||||||
|
"bytes": 259900
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-apple.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘사과’입니다.",
|
||||||
|
"sha256": "51e6cf35b219f13c0d72ec9633f0c108c3393bc5ef6ae6aca6aa5d00153b4177",
|
||||||
|
"bytes": 125658
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-sea.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘바다’입니다.",
|
||||||
|
"sha256": "6585cb8d50eea173395417fc5fb357f55901665fe45dd07d942f79e202729111",
|
||||||
|
"bytes": 123366
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-tree.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘나무’입니다.",
|
||||||
|
"sha256": "6d188188d1478d0298536e0e03a58883629459dee56c6ae8ab88b4d7170c1def",
|
||||||
|
"bytes": 121224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-hat.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘모자’입니다.",
|
||||||
|
"sha256": "be8d4287ebce15e221a8908f6b2d7478e789ecc51ba95d65fdf34f130954e7e5",
|
||||||
|
"bytes": 121644
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-umbrella.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘우산’입니다.",
|
||||||
|
"sha256": "188e24932e5e68fb0de8270e5b48128d486da956ad4069afbbcd0ad9af5913ce",
|
||||||
|
"bytes": 122178
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-market.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘시장’입니다.",
|
||||||
|
"sha256": "e58cdfd5a43074179e65edaf5f223dd15ad1accdd2fd547a6d3311de57b78eca",
|
||||||
|
"bytes": 124352
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-oil.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘기름’입니다.",
|
||||||
|
"sha256": "8b1070414191ff87b9531caf0782b13458b246a1f07b51f7ddcff2cd613930f7",
|
||||||
|
"bytes": 120286
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-sound.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘소리’입니다.",
|
||||||
|
"sha256": "052e2c2724c2fcf9d4c68ba3b746a430af0cfc74b42ed6f8317ae2cc5af1c2ad",
|
||||||
|
"bytes": 122660
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-meat.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘고기’입니다.",
|
||||||
|
"sha256": "dcf3d1c84a9ee00ea8a1ea5552ac69b3dc46b9c4160ee3375a4afe38426c0b8d",
|
||||||
|
"bytes": 121754
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "luck-furniture.wav",
|
||||||
|
"text": "오늘의 행운을 담은 단어는 ‘가구’입니다.",
|
||||||
|
"sha256": "6265e3807ea10eb82e9bb15a7e72dcab5e148277bc49bacfdb2663817d151ddf",
|
||||||
|
"bytes": 122164
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-apple.wav",
|
||||||
|
"text": "연습 암호는 ‘다과’, 진짜 암호는 ‘사과’입니다.",
|
||||||
|
"sha256": "14b7c8c0253784f319a04b568da594d8e46077d03931a36a23e8a7689dd4b928",
|
||||||
|
"bytes": 161688
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-sea.wav",
|
||||||
|
"text": "연습 암호는 ‘사다’, 진짜 암호는 ‘바다’입니다.",
|
||||||
|
"sha256": "832178b1eee4fe29653ca8dfda5bfb356cab83f4f880a704ee9eab937443f872",
|
||||||
|
"bytes": 160256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-tree.wav",
|
||||||
|
"text": "연습 암호는 ‘나루’, 진짜 암호는 ‘나무’입니다.",
|
||||||
|
"sha256": "ef25aa09f89acadcbb2c43a1f18d0c2da292fae4d87d052868942d08b3825a8d",
|
||||||
|
"bytes": 156642
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-hat.wav",
|
||||||
|
"text": "연습 암호는 ‘포자’, 진짜 암호는 ‘모자’입니다.",
|
||||||
|
"sha256": "aef3f595e1b2d36212d4611f9998d841303e96ee5385639094aebcc984ff8a94",
|
||||||
|
"bytes": 158616
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-umbrella.wav",
|
||||||
|
"text": "연습 암호는 ‘부산’, 진짜 암호는 ‘우산’입니다.",
|
||||||
|
"sha256": "6bc1bf13c3fd1739bc9e490525ecd5839b9a4032faecb9f101a2c68824d57b82",
|
||||||
|
"bytes": 162668
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-market.wav",
|
||||||
|
"text": "연습 암호는 ‘지장’, 진짜 암호는 ‘시장’입니다.",
|
||||||
|
"sha256": "33036e08a06dd33b6aa86e5b78352d2f90e35ac1c7f9f202af2a32e2ceffcfc1",
|
||||||
|
"bytes": 166360
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-oil.wav",
|
||||||
|
"text": "연습 암호는 ‘이름’, 진짜 암호는 ‘기름’입니다.",
|
||||||
|
"sha256": "d88914ab7302112beecb6b2edbe114c3c119ee375985eca9716bf239aea94038",
|
||||||
|
"bytes": 157396
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-sound.wav",
|
||||||
|
"text": "연습 암호는 ‘오리’, 진짜 암호는 ‘소리’입니다.",
|
||||||
|
"sha256": "d3df570902e1f1fb5b2a7c573d57857bf4da547fb97ebdf27f04fbbf29c1170f",
|
||||||
|
"bytes": 155298
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-meat.wav",
|
||||||
|
"text": "연습 암호는 ‘보기’, 진짜 암호는 ‘고기’입니다.",
|
||||||
|
"sha256": "59c422dbf3c2ed7a397e7bf3455a2bf59132f85477765c6ec13673633cdb1e8c",
|
||||||
|
"bytes": 157754
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "password-furniture.wav",
|
||||||
|
"text": "연습 암호는 ‘하구’, 진짜 암호는 ‘가구’입니다.",
|
||||||
|
"sha256": "c349bd57598eec8543ce488dd86a4c4ddd4ec661ac9e8d7f42d914d545f38f61",
|
||||||
|
"bytes": 158998
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-apple.wav",
|
||||||
|
"text": "‘다과’. 이 단어는 잊으세요. 이제 ‘사과’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "3123d84c546ff4e630a8b93d59bd5e668a5de30ea651e56e134d47690aead28b",
|
||||||
|
"bytes": 205094
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-sea.wav",
|
||||||
|
"text": "‘사다’. 이 단어는 잊으세요. 이제 ‘바다’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "d802055500865e38b9beffe0213b70314aa59ec181cb1af1897dd010bb5875e3",
|
||||||
|
"bytes": 204654
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-tree.wav",
|
||||||
|
"text": "‘나루’. 이 단어는 잊으세요. 이제 ‘나무’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "1e3f340609eb9844b7fc727f0c17a7f9f60245d4e00df2330d734efa97d26a33",
|
||||||
|
"bytes": 201206
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-hat.wav",
|
||||||
|
"text": "‘포자’. 이 단어는 잊으세요. 이제 ‘모자’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "8a3cd1ef975d8a6782c018bbe23d9e69b4876fe8e64dcb9971f5025bb7072a4c",
|
||||||
|
"bytes": 202530
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-umbrella.wav",
|
||||||
|
"text": "‘부산’. 이 단어는 잊으세요. 이제 ‘우산’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "5c5ab3c30f02d3a3b8188f1d81f4d8f617a9e9783c18da38bf6d4bee9d03709e",
|
||||||
|
"bytes": 209448
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-market.wav",
|
||||||
|
"text": "‘지장’. 이 단어는 잊으세요. 이제 ‘시장’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "eb582873b55043a61b80e34b25302f8d52ae9c7d641032ce65ddca8fb9bfb181",
|
||||||
|
"bytes": 212988
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-oil.wav",
|
||||||
|
"text": "‘이름’. 이 단어는 잊으세요. 이제 ‘기름’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "d56d8639bf462b1c341c07252666975ff6f3428de74f0bef886019fe0085be57",
|
||||||
|
"bytes": 204388
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-sound.wav",
|
||||||
|
"text": "‘오리’. 이 단어는 잊으세요. 이제 ‘소리’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "c3d8e3e75086fd52a38806ea2122542d4fe495e381d4aa4c36e72277449451bf",
|
||||||
|
"bytes": 200100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-meat.wav",
|
||||||
|
"text": "‘보기’. 이 단어는 잊으세요. 이제 ‘고기’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "be4cc7dc1af803a23eb4319d076ecf4c5ab37b56913287f49c3b3c96eb4a75ce",
|
||||||
|
"bytes": 201730
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "remember-furniture.wav",
|
||||||
|
"text": "‘하구’. 이 단어는 잊으세요. 이제 ‘가구’, 이 단어를 기억하세요.",
|
||||||
|
"sha256": "acc3efbba9ef6bf53a3992a669946b6c58e90c9821e2fb5ef8e8c4a738f8bdf7",
|
||||||
|
"bytes": 202972
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-apple.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘다과’, ‘사고’, ‘사과’.",
|
||||||
|
"sha256": "5f583e9af9213cd5dc78abb68e5fcba3dbb7c65d7d8131a53210d8326e5cbb87",
|
||||||
|
"bytes": 187732
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-sea.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘사다’, ‘보다’, ‘바다’.",
|
||||||
|
"sha256": "21e589a6f7c262cca5533a8599a9d4fb257028a6e0c42732f6147209ad35ecb6",
|
||||||
|
"bytes": 190468
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-tree.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘나루’, ‘너무’, ‘나무’.",
|
||||||
|
"sha256": "915249a9860aa29c36149fc57b04057b2fa6ab9253fdfa1e0837236f84cb7aab",
|
||||||
|
"bytes": 186820
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-hat.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘포자’, ‘모조’, ‘모자’.",
|
||||||
|
"sha256": "0ce70943449ec5fb18c57d4ba7ea09a6e995f4bc991db07c70c9a846e16679a1",
|
||||||
|
"bytes": 189216
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-umbrella.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘부산’, ‘오산’, ‘우산’.",
|
||||||
|
"sha256": "8e5a3a57fc55a7deae5df5be5425a970b0a4926b74844f0f6fba74d882d8ea73",
|
||||||
|
"bytes": 192394
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-market.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘지장’, ‘사장’, ‘시장’.",
|
||||||
|
"sha256": "2419dbb03e18d3528a615b4977405699ab5285ea4b392e45164b625ebb5ff0db",
|
||||||
|
"bytes": 201286
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-oil.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘이름’, ‘거름’, ‘기름’.",
|
||||||
|
"sha256": "8a7108a6b23dddb5f2ae1c72edf6c488335ccd322acdb6d96a371e7613d47eb2",
|
||||||
|
"bytes": 191000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-sound.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘오리’, ‘수리’, ‘소리’.",
|
||||||
|
"sha256": "c2b77970b477fd05671c833daaf2fb48aace796e1da54a1f9286fca0a8b7f484",
|
||||||
|
"bytes": 185296
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-meat.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘보기’, ‘고개’, ‘고기’.",
|
||||||
|
"sha256": "f1bbcef87d1b2fda6d36ecc484c62c6f1a4a182c12e2e0ac8bb58f494809ddf8",
|
||||||
|
"bytes": 185540
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "last-furniture.wav",
|
||||||
|
"text": "세 단어 중 마지막을 고르세요. ‘하구’, ‘가교’, ‘가구’.",
|
||||||
|
"sha256": "5e625527e69a3f9a9c508cb9da26252ab621949d7ae9ff41bdd9336627234de0",
|
||||||
|
"bytes": 185132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-apple.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘사과’, ‘다과’, ‘사고’.",
|
||||||
|
"sha256": "d539d03875121570650cac2a2bf800cc67e0a72effaf755e02c870f9569e5028",
|
||||||
|
"bytes": 203904
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-sea.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘바다’, ‘사다’, ‘보다’.",
|
||||||
|
"sha256": "2d789349b36dab9bbe784e2fcb8412704f2dd15b22ad63937bad9948720aa5fb",
|
||||||
|
"bytes": 207002
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-tree.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘나무’, ‘나루’, ‘너무’.",
|
||||||
|
"sha256": "af4ad056b18af0d376a1df7dda3d7b8419e76cda6ffe57dd71054692aa653fd9",
|
||||||
|
"bytes": 203354
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-hat.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘모자’, ‘포자’, ‘모조’.",
|
||||||
|
"sha256": "e34fd5edff5323e45f8621a2adb79ed871d1ce671bd3ac8c4a49a8c703878811",
|
||||||
|
"bytes": 205388
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-umbrella.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘우산’, ‘부산’, ‘오산’.",
|
||||||
|
"sha256": "4be32c725583f1579b15be4bbe110b4ab42705221a63621125aa1aa98f7f061c",
|
||||||
|
"bytes": 210768
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-market.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘시장’, ‘지장’, ‘사장’.",
|
||||||
|
"sha256": "37925d65ce12429905a1b185824722a34af8d77b15eb7bec3b52665b5c1fbc7c",
|
||||||
|
"bytes": 217794
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-oil.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘기름’, ‘이름’, ‘거름’.",
|
||||||
|
"sha256": "0991123aee956c26ca658382102c9b6d0a164fbf2dea45f07a978d2cc1fc6b80",
|
||||||
|
"bytes": 205784
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-sound.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘소리’, ‘오리’, ‘수리’.",
|
||||||
|
"sha256": "5b3063285cddd8f46a2c2cbbee1ecc9b64ecdbd287df76df65dec854679ec884",
|
||||||
|
"bytes": 201840
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-meat.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘고기’, ‘보기’, ‘고개’.",
|
||||||
|
"sha256": "acf07b6db586616e67e2841a6f122120a5b0b18543047271bd5b8c55e33786e3",
|
||||||
|
"bytes": 201712
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "first-furniture.wav",
|
||||||
|
"text": "세 단어 중 첫 번째를 고르세요. ‘가구’, ‘하구’, ‘가교’.",
|
||||||
|
"sha256": "acba37fc2641c7073210855d294eb7c6e5f3328652c606186b1b0e569e2d0bf6",
|
||||||
|
"bytes": 203872
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-apple.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘사과’, ‘다과’, ‘사과’, ‘사고’.",
|
||||||
|
"sha256": "5fcef242c63dc7e86c02a1a5813e409e6392511aad5834510696b0d0499a5ef1",
|
||||||
|
"bytes": 215994
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-sea.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘바다’, ‘사다’, ‘바다’, ‘보다’.",
|
||||||
|
"sha256": "ef675bfa38f4eb4abac04cc64aa2c3ec416d405f9c67646356c9a8f39fe17d21",
|
||||||
|
"bytes": 211180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-tree.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘나무’, ‘나루’, ‘나무’, ‘너무’.",
|
||||||
|
"sha256": "b6a1d2923d90f9dd35b324aea419daf3891b76cc8c5c9551fe5ec1140c2ab0bf",
|
||||||
|
"bytes": 206504
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-hat.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘모자’, ‘포자’, ‘모자’, ‘모조’.",
|
||||||
|
"sha256": "32cbc9823f5af5f897416bec2c377a8559c82fd3fae66273152826dd000cdc4c",
|
||||||
|
"bytes": 208872
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-umbrella.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘우산’, ‘부산’, ‘우산’, ‘오산’.",
|
||||||
|
"sha256": "4830662f39bc0c62ee9be89de80a44a837c50063fad8fb63129b865673de5525",
|
||||||
|
"bytes": 220408
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-market.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘시장’, ‘지장’, ‘시장’, ‘사장’.",
|
||||||
|
"sha256": "0e146387377dc38fc10435e06e8eb8cd30a595092887bf8ee4c9d0e7b577650d",
|
||||||
|
"bytes": 232742
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-oil.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘기름’, ‘이름’, ‘기름’, ‘거름’.",
|
||||||
|
"sha256": "94de2ef7f7d7f9132f3646c3dc09399de23b81909f3edf4bbd1c5b01fad4d8fe",
|
||||||
|
"bytes": 216502
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-sound.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘소리’, ‘오리’, ‘소리’, ‘수리’.",
|
||||||
|
"sha256": "7f12b6f49a7b4f64af796aa4ad8cfffc85a6b39e2bc6698f74b420b74af6c7c8",
|
||||||
|
"bytes": 205404
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-meat.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘고기’, ‘보기’, ‘고기’, ‘고개’.",
|
||||||
|
"sha256": "ba8beeb33bcbe4771aaea9b3832a4c25868f6e34e37b3e3d026e891c07b8e71a",
|
||||||
|
"bytes": 203544
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "repeat-furniture.wav",
|
||||||
|
"text": "두 번 들린 단어를 고르세요. ‘가구’, ‘하구’, ‘가구’, ‘가교’.",
|
||||||
|
"sha256": "581a94726b08b6cfd3c688f08838a05787ed902ab6d1d4a038e0ff6b34f63c3a",
|
||||||
|
"bytes": 208274
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-apple.wav",
|
||||||
|
"text": "정답은 ‘다과’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘사과’입니다.",
|
||||||
|
"sha256": "53d1c1b207607e610964e68134a3d35ff521b569e228366ec4eb7c03691724bb",
|
||||||
|
"bytes": 256484
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-sea.wav",
|
||||||
|
"text": "정답은 ‘사다’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘바다’입니다.",
|
||||||
|
"sha256": "df61f91374b9fbb5817249ae3b6cadfae914d3e97ee2c6d07366ef82080a11db",
|
||||||
|
"bytes": 255204
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-tree.wav",
|
||||||
|
"text": "정답은 ‘나루’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘나무’입니다.",
|
||||||
|
"sha256": "837c24b18f63a73e135511b2edd48ba12593317de3be4d225622e6d11b1a57da",
|
||||||
|
"bytes": 251316
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-hat.wav",
|
||||||
|
"text": "정답은 ‘포자’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘모자’입니다.",
|
||||||
|
"sha256": "00d7c21caccbbb6c80ec6218415be1b0a6683a2d509b60821d3556e43353b5dc",
|
||||||
|
"bytes": 253208
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-umbrella.wav",
|
||||||
|
"text": "정답은 ‘부산’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘우산’입니다.",
|
||||||
|
"sha256": "9bb3ce5b00ccc1632c37638550f329cbb6cda01864433e068f85eb7ccab86370",
|
||||||
|
"bytes": 255400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-market.wav",
|
||||||
|
"text": "정답은 ‘지장’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘시장’입니다.",
|
||||||
|
"sha256": "c8152db42c956701cd5e21c588ef62aedaefa922658d4bc0fecf8243548d7054",
|
||||||
|
"bytes": 257262
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-oil.wav",
|
||||||
|
"text": "정답은 ‘이름’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘기름’입니다.",
|
||||||
|
"sha256": "228759e87b1c80be72ea34ff5b77cb5e4557afca1b73434ca8f6f1c2fedaa836",
|
||||||
|
"bytes": 247864
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-sound.wav",
|
||||||
|
"text": "정답은 ‘오리’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘소리’입니다.",
|
||||||
|
"sha256": "24f2d52bb865c71fc4735a4aebd69ce0e429a9f68646e73e77adababbe34af93",
|
||||||
|
"bytes": 250170
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-meat.wav",
|
||||||
|
"text": "정답은 ‘보기’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘고기’입니다.",
|
||||||
|
"sha256": "821caf897ddab107df8435b00b686bf9223e043c91a311edcaa0c3c5eb8c6511",
|
||||||
|
"bytes": 253432
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "correction-furniture.wav",
|
||||||
|
"text": "정답은 ‘하구’입니다. 아니, 잘못 말했어요. 정정한 답은 ‘가구’입니다.",
|
||||||
|
"sha256": "5d69426f3319fa435ee60b234540dd8d9a07f00900177f03da2770f44511ade8",
|
||||||
|
"bytes": 254342
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-apple.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘다과’, ‘사과’, ‘사고’.",
|
||||||
|
"sha256": "0ecc7a7d27508f73c9064a35609c922bbdd6fce8ebfc155e74f7b78d4e897dae",
|
||||||
|
"bytes": 191056
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-sea.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘사다’, ‘바다’, ‘보다’.",
|
||||||
|
"sha256": "c7828af4e64745d89a6f23fda6c86efb76febe379bab0901c2972a4569947604",
|
||||||
|
"bytes": 194176
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-tree.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘나루’, ‘나무’, ‘너무’.",
|
||||||
|
"sha256": "756d13453c7e13dcfaa65c8cb3497ac8d3a5d12bedb56dcd74e7f1c022392416",
|
||||||
|
"bytes": 190516
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-hat.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘포자’, ‘모자’, ‘모조’.",
|
||||||
|
"sha256": "6ad5ec1e2c2c0de568c55a0c1f0e8361885725a2515cf396c8fe5d2106ae22d0",
|
||||||
|
"bytes": 192548
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-umbrella.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘부산’, ‘우산’, ‘오산’.",
|
||||||
|
"sha256": "a507896dd50f98909c6a9fc0a4bfddfde5c975836e494cd9b7f40b69bc8ec33b",
|
||||||
|
"bytes": 196102
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-market.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘지장’, ‘시장’, ‘사장’.",
|
||||||
|
"sha256": "900024616e512ea6777d08f80e3e2c83815ea1f03b6eeb537d212995474e1048",
|
||||||
|
"bytes": 204956
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-oil.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘이름’, ‘기름’, ‘거름’.",
|
||||||
|
"sha256": "c42389a5ccd3b39a5877a38e647a6abbec6ff6a7eabf3d4c95c0507fa65d6d2f",
|
||||||
|
"bytes": 194772
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-sound.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘오리’, ‘소리’, ‘수리’.",
|
||||||
|
"sha256": "b05ae2042bc973c3d1873fe15b5bc7f2af7e803794315023486efa788048342c",
|
||||||
|
"bytes": 188984
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-meat.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘보기’, ‘고기’, ‘고개’.",
|
||||||
|
"sha256": "d54d6f9b4bd90b840443b9e0b0390a93909eeb81c37af6a9b57ce545bede532d",
|
||||||
|
"bytes": 188870
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "middle-furniture.wav",
|
||||||
|
"text": "세 단어 중 가운데를 고르세요. ‘하구’, ‘가구’, ‘가교’.",
|
||||||
|
"sha256": "d9318ef33ae1c60630ddda5abeefcbe43626d8f8a9abd5d62efd132eeb77c8fc",
|
||||||
|
"bytes": 191024
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-apple.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘다과’, ‘사과’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "3867563d5acd2530c72cbe6f98ca2b463ec0c92285069c99c8ba084c45aabfd6",
|
||||||
|
"bytes": 316834
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-sea.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘사다’, ‘바다’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "b7d8d89d93247eca9aec124f80bf6328d6d88511d05b513ce6d9fa32b3211848",
|
||||||
|
"bytes": 317816
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-tree.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘나루’, ‘나무’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "9539e5ee87068e241598db2e4fbacb240daa011a2238c9864da8ffde6b644918",
|
||||||
|
"bytes": 315930
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-hat.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘포자’, ‘모자’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "ccbba6a22c9cbc1231f45fbd7420c6257ac22ac72de8e8c0c97f1cc31bb2b16e",
|
||||||
|
"bytes": 317258
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-umbrella.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘부산’, ‘우산’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "e980f4476f7275cc5ae24e7ce0e6d9c1e6db027f7cbd1eb39cc5bc4bc07cbf6e",
|
||||||
|
"bytes": 320532
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-market.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘지장’, ‘시장’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "35432a3ed8dd1e15894ace5aad6f5a66cf28509ba92b8ee47bcaae424cb25ba1",
|
||||||
|
"bytes": 325564
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-oil.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘이름’, ‘기름’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "814fc749006ed040de58b5236a8d92f0d79683c8eea3b9d095386344f41a47bd",
|
||||||
|
"bytes": 317786
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-sound.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘오리’, ‘소리’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "e25c867e6c18d906c010816475253d2b10aa3cc4192b5551ba78055d65c461c8",
|
||||||
|
"bytes": 313270
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-meat.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘보기’, ‘고기’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "f1863e1527b7714ae64f5d752b542e8768b8ed89b5885012283f7ca20742ee00",
|
||||||
|
"bytes": 314464
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "message-furniture.wav",
|
||||||
|
"text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘하구’, ‘가구’. 지금은 빨간 불입니다.",
|
||||||
|
"sha256": "43f87d0e3d92bd132f9ea46e024b9a940736b7399e899866ed42af367a550f78",
|
||||||
|
"bytes": 316152
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-apple.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘사과’, 왼쪽 표지는 ‘다과’입니다.",
|
||||||
|
"sha256": "36697151a1a57645f5acd9fcca2d13bffa21937e1bbcde08ab2d8d485730145a",
|
||||||
|
"bytes": 166540
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-sea.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘바다’, 왼쪽 표지는 ‘사다’입니다.",
|
||||||
|
"sha256": "2741f404e39ec64af7f73452f82c68b3ae1d377e9f40a586034f9b147fbfb114",
|
||||||
|
"bytes": 165122
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-tree.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘나무’, 왼쪽 표지는 ‘나루’입니다.",
|
||||||
|
"sha256": "8a7bb2c795d8877433be2621b69a48c2b6c5c1e192c094c01257fb93eb5d9449",
|
||||||
|
"bytes": 160834
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-hat.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘모자’, 왼쪽 표지는 ‘포자’입니다.",
|
||||||
|
"sha256": "26dfa26bd8dcb9d63c231dfc37bf25970f4a8dadcaa40347dd15974887e948b1",
|
||||||
|
"bytes": 163102
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-umbrella.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘우산’, 왼쪽 표지는 ‘부산’입니다.",
|
||||||
|
"sha256": "79be98918c3fb4e5e4e2e51b2bfa5e5aad6c600478ae1a34c055b16ce71d5264",
|
||||||
|
"bytes": 167480
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-market.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘시장’, 왼쪽 표지는 ‘지장’입니다.",
|
||||||
|
"sha256": "8f18144a91072be37dc31a161fe6e9bb1980ab36af0edee8d7a359397b8bb8ac",
|
||||||
|
"bytes": 170724
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-oil.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘기름’, 왼쪽 표지는 ‘이름’입니다.",
|
||||||
|
"sha256": "1ceabc58f445f7bd5609b2f9de80b309ad64d11c888c7261cf84315a7637a748",
|
||||||
|
"bytes": 161758
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-sound.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘소리’, 왼쪽 표지는 ‘오리’입니다.",
|
||||||
|
"sha256": "6651b14caf4e54069730f137e5d8c3c1e5a9c66aa72f15623cc442edb967cadb",
|
||||||
|
"bytes": 160162
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-meat.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘고기’, 왼쪽 표지는 ‘보기’입니다.",
|
||||||
|
"sha256": "5d2ab3071fd27e850d99c50ad37905096ef28267b660a35992559bdc126c4931",
|
||||||
|
"bytes": 162626
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "mission-furniture.wav",
|
||||||
|
"text": "오른쪽 표지는 ‘가구’, 왼쪽 표지는 ‘하구’입니다.",
|
||||||
|
"sha256": "080307bb46bb47378c0c03437d9ef44118a5d4ea17b2b6d70e5cbc710c71ba78",
|
||||||
|
"bytes": 163884
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"head": "1c0069c367f6e108d3f67a2a0d044399dd0ef2bd",
|
||||||
|
"branch": "fix/v0.1.1-neural-voice"
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
ADDED .agent/RESULT-v0.1.1.md
|
||||||
|
ADDED .agent/evidence-v0.1.1/00-audio-venv.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/00-npm-ci.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/01-version-docs-red.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/02-assets-red.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/03-generator-red.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/04-generator-additional-red.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/05-generator-green.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/06-audio-generate.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/07-format.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/08-version-docs-green.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/09-lint.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/10-audit.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/11-full-test.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/12-independent-audio.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/13-build.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/14-browser.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/15-browser-audio.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/16-final-lint.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/17-security-history.json
|
||||||
|
ADDED .agent/evidence-v0.1.1/18-environment.log
|
||||||
|
ADDED .agent/evidence-v0.1.1/19-final-integrity.json
|
||||||
|
ADDED .agent/evidence-v0.1.1/audio-integrity.json
|
||||||
|
ADDED .agent/evidence-v0.1.1/baseline.json
|
||||||
|
ADDED .agent/evidence-v0.1.1/browser-audio-integrity.json
|
||||||
|
ADDED .agent/evidence-v0.1.1/changed-files.txt
|
||||||
|
ADDED .agent/evidence-v0.1.1/desktop-game.png
|
||||||
|
ADDED .agent/evidence-v0.1.1/desktop-heading.png
|
||||||
|
ADDED .agent/evidence-v0.1.1/desktop-home.png
|
||||||
|
ADDED .agent/evidence-v0.1.1/desktop-results.png
|
||||||
|
ADDED .agent/evidence-v0.1.1/mobile-game.png
|
||||||
|
ADDED .agent/evidence-v0.1.1/mobile-heading.png
|
||||||
|
ADDED .agent/evidence-v0.1.1/mobile-home.png
|
||||||
|
ADDED .agent/evidence-v0.1.1/mobile-results.png
|
||||||
|
ADDED .agent/evidence-v0.1.1/verify_audio.py
|
||||||
|
ADDED .agent/evidence-v0.1.1/verify_browser.mjs
|
||||||
|
ADDED requirements-audio.txt
|
||||||
|
ADDED scripts/neural_audio.py
|
||||||
|
ADDED tests/generator.test.ts
|
||||||
|
ADDED tests/neural-release.test.ts
|
||||||
|
ADDED tests/python/test_neural_audio.py
|
||||||
|
MODIFIED .gitignore
|
||||||
|
MODIFIED LICENSE
|
||||||
|
MODIFIED README.md
|
||||||
|
MODIFIED docs/ARCHITECTURE.md
|
||||||
|
MODIFIED docs/AUDIO_PROVENANCE.md
|
||||||
|
MODIFIED docs/DEPLOYMENT.md
|
||||||
|
MODIFIED eslint.config.js
|
||||||
|
MODIFIED index.html
|
||||||
|
MODIFIED package-lock.json
|
||||||
|
MODIFIED package.json
|
||||||
|
MODIFIED public/audio/correction-apple.wav
|
||||||
|
MODIFIED public/audio/correction-furniture.wav
|
||||||
|
MODIFIED public/audio/correction-hat.wav
|
||||||
|
MODIFIED public/audio/correction-market.wav
|
||||||
|
MODIFIED public/audio/correction-meat.wav
|
||||||
|
MODIFIED public/audio/correction-oil.wav
|
||||||
|
MODIFIED public/audio/correction-sea.wav
|
||||||
|
MODIFIED public/audio/correction-sound.wav
|
||||||
|
MODIFIED public/audio/correction-tree.wav
|
||||||
|
MODIFIED public/audio/correction-umbrella.wav
|
||||||
|
MODIFIED public/audio/first-apple.wav
|
||||||
|
MODIFIED public/audio/first-furniture.wav
|
||||||
|
MODIFIED public/audio/first-hat.wav
|
||||||
|
MODIFIED public/audio/first-market.wav
|
||||||
|
MODIFIED public/audio/first-meat.wav
|
||||||
|
MODIFIED public/audio/first-oil.wav
|
||||||
|
MODIFIED public/audio/first-sea.wav
|
||||||
|
MODIFIED public/audio/first-sound.wav
|
||||||
|
MODIFIED public/audio/first-tree.wav
|
||||||
|
MODIFIED public/audio/first-umbrella.wav
|
||||||
|
MODIFIED public/audio/intro.wav
|
||||||
|
MODIFIED public/audio/last-apple.wav
|
||||||
|
MODIFIED public/audio/last-furniture.wav
|
||||||
|
MODIFIED public/audio/last-hat.wav
|
||||||
|
MODIFIED public/audio/last-market.wav
|
||||||
|
MODIFIED public/audio/last-meat.wav
|
||||||
|
MODIFIED public/audio/last-oil.wav
|
||||||
|
MODIFIED public/audio/last-sea.wav
|
||||||
|
MODIFIED public/audio/last-sound.wav
|
||||||
|
MODIFIED public/audio/last-tree.wav
|
||||||
|
MODIFIED public/audio/last-umbrella.wav
|
||||||
|
MODIFIED public/audio/luck-apple.wav
|
||||||
|
MODIFIED public/audio/luck-furniture.wav
|
||||||
|
MODIFIED public/audio/luck-hat.wav
|
||||||
|
MODIFIED public/audio/luck-market.wav
|
||||||
|
MODIFIED public/audio/luck-meat.wav
|
||||||
|
MODIFIED public/audio/luck-oil.wav
|
||||||
|
MODIFIED public/audio/luck-sea.wav
|
||||||
|
MODIFIED public/audio/luck-sound.wav
|
||||||
|
MODIFIED public/audio/luck-tree.wav
|
||||||
|
MODIFIED public/audio/luck-umbrella.wav
|
||||||
|
MODIFIED public/audio/manifest.json
|
||||||
|
MODIFIED public/audio/message-apple.wav
|
||||||
|
MODIFIED public/audio/message-furniture.wav
|
||||||
|
MODIFIED public/audio/message-hat.wav
|
||||||
|
MODIFIED public/audio/message-market.wav
|
||||||
|
MODIFIED public/audio/message-meat.wav
|
||||||
|
MODIFIED public/audio/message-oil.wav
|
||||||
|
MODIFIED public/audio/message-sea.wav
|
||||||
|
MODIFIED public/audio/message-sound.wav
|
||||||
|
MODIFIED public/audio/message-tree.wav
|
||||||
|
MODIFIED public/audio/message-umbrella.wav
|
||||||
|
MODIFIED public/audio/middle-apple.wav
|
||||||
|
MODIFIED public/audio/middle-furniture.wav
|
||||||
|
MODIFIED public/audio/middle-hat.wav
|
||||||
|
MODIFIED public/audio/middle-market.wav
|
||||||
|
MODIFIED public/audio/middle-meat.wav
|
||||||
|
MODIFIED public/audio/middle-oil.wav
|
||||||
|
MODIFIED public/audio/middle-sea.wav
|
||||||
|
MODIFIED public/audio/middle-sound.wav
|
||||||
|
MODIFIED public/audio/middle-tree.wav
|
||||||
|
MODIFIED public/audio/middle-umbrella.wav
|
||||||
|
MODIFIED public/audio/mission-apple.wav
|
||||||
|
MODIFIED public/audio/mission-furniture.wav
|
||||||
|
MODIFIED public/audio/mission-hat.wav
|
||||||
|
MODIFIED public/audio/mission-market.wav
|
||||||
|
MODIFIED public/audio/mission-meat.wav
|
||||||
|
MODIFIED public/audio/mission-oil.wav
|
||||||
|
MODIFIED public/audio/mission-sea.wav
|
||||||
|
MODIFIED public/audio/mission-sound.wav
|
||||||
|
MODIFIED public/audio/mission-tree.wav
|
||||||
|
MODIFIED public/audio/mission-umbrella.wav
|
||||||
|
MODIFIED public/audio/password-apple.wav
|
||||||
|
MODIFIED public/audio/password-furniture.wav
|
||||||
|
MODIFIED public/audio/password-hat.wav
|
||||||
|
MODIFIED public/audio/password-market.wav
|
||||||
|
MODIFIED public/audio/password-meat.wav
|
||||||
|
MODIFIED public/audio/password-oil.wav
|
||||||
|
MODIFIED public/audio/password-sea.wav
|
||||||
|
MODIFIED public/audio/password-sound.wav
|
||||||
|
MODIFIED public/audio/password-tree.wav
|
||||||
|
MODIFIED public/audio/password-umbrella.wav
|
||||||
|
MODIFIED public/audio/remember-apple.wav
|
||||||
|
MODIFIED public/audio/remember-furniture.wav
|
||||||
|
MODIFIED public/audio/remember-hat.wav
|
||||||
|
MODIFIED public/audio/remember-market.wav
|
||||||
|
MODIFIED public/audio/remember-meat.wav
|
||||||
|
MODIFIED public/audio/remember-oil.wav
|
||||||
|
MODIFIED public/audio/remember-sea.wav
|
||||||
|
MODIFIED public/audio/remember-sound.wav
|
||||||
|
MODIFIED public/audio/remember-tree.wav
|
||||||
|
MODIFIED public/audio/remember-umbrella.wav
|
||||||
|
MODIFIED public/audio/repeat-apple.wav
|
||||||
|
MODIFIED public/audio/repeat-furniture.wav
|
||||||
|
MODIFIED public/audio/repeat-hat.wav
|
||||||
|
MODIFIED public/audio/repeat-market.wav
|
||||||
|
MODIFIED public/audio/repeat-meat.wav
|
||||||
|
MODIFIED public/audio/repeat-oil.wav
|
||||||
|
MODIFIED public/audio/repeat-sea.wav
|
||||||
|
MODIFIED public/audio/repeat-sound.wav
|
||||||
|
MODIFIED public/audio/repeat-tree.wav
|
||||||
|
MODIFIED public/audio/repeat-umbrella.wav
|
||||||
|
MODIFIED scripts/generate_audio.py
|
||||||
|
MODIFIED src/App.tsx
|
||||||
|
MODIFIED tests/App.test.tsx
|
||||||
|
MODIFIED tests/assets.test.ts
|
||||||
|
MODIFIED tests/browser/game.spec.ts
|
||||||
|
MODIFIED tests/browser/ui.spec.ts
|
||||||
|
USER INPUT .agent/ACCEPTANCE-v0.1.1.md
|
||||||
|
USER INPUT .agent/TASK-v0.1.1.md
|
||||||
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 59 KiB |
@@ -0,0 +1,71 @@
|
|||||||
|
"""생성기 함수를 재사용하지 않는 v0.1.1 인수 검사: 원문·이전 해시·PCM·FFmpeg."""
|
||||||
|
from array import array
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import wave
|
||||||
|
|
||||||
|
root = Path(__file__).resolve().parents[2]
|
||||||
|
evidence = root / ".agent/evidence-v0.1.1"
|
||||||
|
manifest = json.loads((root / "public/audio/manifest.json").read_text())
|
||||||
|
baseline = json.loads((evidence / "baseline.json").read_text())
|
||||||
|
old = {entry["file"]: entry for entry in baseline["audio"]["files"]}
|
||||||
|
expected = json.loads(subprocess.check_output([
|
||||||
|
"node", "--experimental-strip-types", "--input-type=module", "-e",
|
||||||
|
"import {INTRO,PATTERNS,WORDS,renderSentence} from './src/content.ts'; "
|
||||||
|
"console.log(JSON.stringify([{file:'intro.wav',text:INTRO},"
|
||||||
|
"...PATTERNS.flatMap(p=>WORDS.map(w=>({file:p.id+'-'+w.id+'.wav',text:renderSentence(p,w)})))]));"
|
||||||
|
], cwd=root, text=True))
|
||||||
|
assert manifest["version"] == "0.1.1"
|
||||||
|
assert manifest["provider"] == "Microsoft Edge online TTS"
|
||||||
|
assert manifest["voice"] == "ko-KR-HyunsuMultilingualNeural"
|
||||||
|
assert manifest["rate"] == "-6%" and manifest["pitch"] == "-4Hz"
|
||||||
|
assert manifest["toolVersions"]["edge-tts"] == "7.3.1"
|
||||||
|
assert [{"file": e["file"], "text": e["text"]} for e in manifest["files"]] == expected
|
||||||
|
assert len(expected) == 101
|
||||||
|
assert {p.name for p in (root / "public/audio").iterdir()} == {e["file"] for e in expected} | {"manifest.json"}
|
||||||
|
rows = []
|
||||||
|
for entry in manifest["files"]:
|
||||||
|
path = root / "public/audio" / entry["file"]
|
||||||
|
data = path.read_bytes()
|
||||||
|
digest = hashlib.sha256(data).hexdigest()
|
||||||
|
assert digest == entry["sha256"] and digest != old[entry["file"]]["sha256"]
|
||||||
|
assert len(data) == entry["bytes"]
|
||||||
|
with wave.open(str(path), "rb") as stream:
|
||||||
|
assert (stream.getframerate(), stream.getnchannels(), stream.getsampwidth(), stream.getcomptype()) == (24000, 1, 2, "NONE")
|
||||||
|
duration = stream.getnframes()/24000
|
||||||
|
assert 1 <= duration <= 20 and duration == entry["duration"]
|
||||||
|
samples = array("h", stream.readframes(stream.getnframes()))
|
||||||
|
if sys.byteorder != "little":
|
||||||
|
samples.byteswap()
|
||||||
|
rms = math.sqrt(sum(x*x for x in samples)/len(samples))/32768
|
||||||
|
peak = max(abs(x) for x in samples)/32768
|
||||||
|
assert rms > 100/32768 and peak < 32767/32768
|
||||||
|
assert peak/rms*0.03*2 < 0.8
|
||||||
|
assert abs(entry["rms"]-rms) < 1e-12 and abs(entry["peak"]-peak) < 1e-12
|
||||||
|
probe = json.loads(subprocess.check_output([
|
||||||
|
"ffprobe", "-v", "error", "-show_entries",
|
||||||
|
"stream=codec_name,sample_fmt,sample_rate,channels,bits_per_sample,duration", "-of", "json", str(path)
|
||||||
|
], text=True))["streams"]
|
||||||
|
assert len(probe) == 1
|
||||||
|
stream = probe[0]
|
||||||
|
assert (stream["codec_name"], stream["sample_fmt"], stream["sample_rate"], stream["channels"], stream["bits_per_sample"]) == ("pcm_s16le", "s16", "24000", 1, 16)
|
||||||
|
assert abs(float(stream["duration"])-duration) < 1e-5
|
||||||
|
subprocess.run(["ffmpeg", "-v", "error", "-xerror", "-i", str(path), "-f", "null", "-"], check=True, capture_output=True)
|
||||||
|
rows.append({"file": path.name, "sha256": digest, "bytes": len(data), "duration": duration,
|
||||||
|
"rms": rms, "peak": peak, "ffprobe": stream, "newVsV01": True, "ffmpegDecoded": True})
|
||||||
|
print(f"PASS {path.name}: {duration:.3f}s, RMS={rms:.6f}, peak={peak:.6f}", flush=True)
|
||||||
|
assert len({r["sha256"] for r in rows}) == 101
|
||||||
|
assert sum(r["bytes"] for r in rows) < 48*1024*1024
|
||||||
|
for path, digest in baseline["historical"].items():
|
||||||
|
assert hashlib.sha256((root / path).read_bytes()).hexdigest() == digest, path
|
||||||
|
summary = {"count": len(rows), "newVsV01": 101, "uniqueHashes": 101, "bytes": sum(r["bytes"] for r in rows),
|
||||||
|
"durationRange": [min(r["duration"] for r in rows), max(r["duration"] for r in rows)],
|
||||||
|
"rmsRange": [min(r["rms"] for r in rows), max(r["rms"] for r in rows)],
|
||||||
|
"peakRange": [min(r["peak"] for r in rows), max(r["peak"] for r in rows)],
|
||||||
|
"historicalUnchanged": len(baseline["historical"]), "files": rows}
|
||||||
|
(evidence / "audio-integrity.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2)+"\n")
|
||||||
|
print(json.dumps({k:v for k,v in summary.items() if k != "files"}, indent=2))
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/* global OfflineAudioContext */
|
||||||
|
// 실제 Chrome의 WAV 디코더로 101개를 검증한다. 재생·UI 흐름은 전체
|
||||||
|
// Playwright suite가 별도 담당하며 이 인수 검사는 모의 AudioContext를 쓰지 않는다.
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { writeFileSync } from 'node:fs';
|
||||||
|
import { spawn } from 'node:child_process';
|
||||||
|
import { chromium } from '@playwright/test';
|
||||||
|
|
||||||
|
const server = spawn(process.execPath, ['node_modules/vite/bin/vite.js', 'preview', '--host', '127.0.0.1', '--port', '4174', '--strictPort'], { stdio: 'ignore' });
|
||||||
|
const base = 'http://127.0.0.1:4174';
|
||||||
|
let browser;
|
||||||
|
try {
|
||||||
|
for (let i = 0; ; i++) {
|
||||||
|
try { if ((await fetch(base)).ok) break; } catch { /* localhost 준비만 기다린다. */ }
|
||||||
|
assert(i < 100, 'preview startup failed');
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
}
|
||||||
|
browser = await chromium.launch({ executablePath: '/usr/bin/google-chrome', chromiumSandbox: true });
|
||||||
|
const sandbox = await browser.newPage();
|
||||||
|
await sandbox.goto('chrome://sandbox');
|
||||||
|
const sandboxStatus = await sandbox.locator('body').innerText();
|
||||||
|
console.log('SANDBOX STATUS\n' + sandboxStatus);
|
||||||
|
assert.match(sandboxStatus, /Seccomp-BPF sandbox\s+Yes/);
|
||||||
|
await sandbox.close();
|
||||||
|
const page = await browser.newPage();
|
||||||
|
const external = [], errors = [];
|
||||||
|
page.on('request', r => { if (!r.url().startsWith(base + '/')) external.push(r.url()); });
|
||||||
|
page.on('pageerror', e => errors.push(e.message));
|
||||||
|
await page.goto(base);
|
||||||
|
assert.equal(await page.title(), '단어를 맞춰라 · Find-Word v0.1.1');
|
||||||
|
assert.equal(await page.locator('.version').innerText(), 'v0.1.1');
|
||||||
|
const result = await page.evaluate(async () => {
|
||||||
|
const manifest = await (await fetch('/audio/manifest.json')).json();
|
||||||
|
const context = new OfflineAudioContext(1, 1, 24000);
|
||||||
|
const rows = [];
|
||||||
|
for (const entry of manifest.files) {
|
||||||
|
const response = await fetch('/audio/' + entry.file);
|
||||||
|
if (!response.ok) throw Error(entry.file + ': HTTP failure');
|
||||||
|
const bytes = await response.arrayBuffer();
|
||||||
|
const digest = Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', bytes)), b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
if (digest !== entry.sha256 || bytes.byteLength !== entry.bytes) throw Error(entry.file + ': integrity failure');
|
||||||
|
const buffer = await context.decodeAudioData(bytes);
|
||||||
|
let power = 0, peak = 0;
|
||||||
|
for (const x of buffer.getChannelData(0)) { power += x*x; peak = Math.max(peak, Math.abs(x)); }
|
||||||
|
if (buffer.sampleRate !== 24000 || buffer.numberOfChannels !== 1 || Math.abs(buffer.duration-entry.duration)>1e-9)
|
||||||
|
throw Error(entry.file + ': decode format/duration mismatch');
|
||||||
|
const rms = Math.sqrt(power/buffer.length);
|
||||||
|
if (rms <= 100/32768 || peak >= 32767/32768) throw Error(entry.file + ': signal failure');
|
||||||
|
rows.push({ file: entry.file, sha256: digest, bytes: entry.bytes, sampleRate: buffer.sampleRate,
|
||||||
|
channels: buffer.numberOfChannels, duration: buffer.duration, rms, peak });
|
||||||
|
}
|
||||||
|
return { version: manifest.version, voice: manifest.voice, files: rows };
|
||||||
|
});
|
||||||
|
assert.equal(result.version, '0.1.1');
|
||||||
|
assert.equal(result.voice, 'ko-KR-HyunsuMultilingualNeural');
|
||||||
|
assert.equal(result.files.length, 101);
|
||||||
|
assert.deepEqual(external, []);
|
||||||
|
assert.deepEqual(errors, []);
|
||||||
|
writeFileSync('.agent/evidence-v0.1.1/browser-audio-integrity.json', JSON.stringify({ chrome: browser.version(), sandboxStatus, external, errors, ...result }, null, 2)+'\n');
|
||||||
|
console.log('PASS: sandboxed Chrome decoded 101/101 WAVs at 24kHz mono; hashes, bytes, duration, RMS, peak verified; external requests=0, JS errors=0.');
|
||||||
|
} finally {
|
||||||
|
await browser?.close();
|
||||||
|
server.kill('SIGTERM');
|
||||||
|
}
|
||||||
@@ -5,3 +5,5 @@ dist/
|
|||||||
test-results/
|
test-results/
|
||||||
playwright-report/
|
playwright-report/
|
||||||
*.local
|
*.local
|
||||||
|
.venv-audio/
|
||||||
|
__pycache__/
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
Find-Word v0.1
|
Find-Word v0.1.1
|
||||||
Private / proprietary. All rights reserved.
|
Private / proprietary. All rights reserved.
|
||||||
|
|
||||||
No license is granted to use, copy, modify, publish, distribute, sublicense,
|
No license is granted to use, copy, modify, publish, distribute, sublicense,
|
||||||
or sell the original project code, documentation, or assets without prior
|
or sell the original project code or documentation without prior
|
||||||
written permission from the respective rights holders.
|
written permission from the respective rights holders.
|
||||||
|
|
||||||
Third-party components remain subject to their own licenses. Their notices
|
Third-party components remain subject to their own licenses. Their notices
|
||||||
are reproduced in THIRD_PARTY_NOTICES.md and accompany the distribution.
|
are reproduced in THIRD_PARTY_NOTICES.md and accompany the distribution.
|
||||||
|
|
||||||
|
This project does not claim exclusive rights to generated speech.
|
||||||
|
Provider terms and any third-party rights remain applicable to generated audio;
|
||||||
|
the project notice does not grant or warrant rights in that output.
|
||||||
|
See docs/AUDIO_PROVENANCE.md.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 단어를 맞춰라 · Find-Word v0.1
|
# 단어를 맞춰라 · Find-Word v0.1.1
|
||||||
|
|
||||||
소음 속에 숨은 한국어 단어를 듣고 찾는 반응형 웹 게임입니다.
|
소음 속에 숨은 한국어 단어를 듣고 찾는 반응형 웹 게임입니다.
|
||||||
|
|
||||||
@@ -79,17 +79,35 @@ Chromium sandbox는 기본으로 켭니다. sandbox를 지원하지 않는 격
|
|||||||
|
|
||||||
브라우저 테스트는 기존 `dist/`를 사용하므로 소스 변경 후 먼저 빌드하세요.
|
브라우저 테스트는 기존 `dist/`를 사용하므로 소스 변경 후 먼저 빌드하세요.
|
||||||
실제 음성 재생이 끝나기를 기다려 약 2–3분이 걸립니다. 로컬 4173 포트를
|
실제 음성 재생이 끝나기를 기다려 약 2–3분이 걸립니다. 로컬 4173 포트를
|
||||||
테스트 중에만 열고 종료합니다. 스크린샷은 `.agent/evidence/`에 남습니다.
|
테스트 중에만 열고 종료합니다. v0.1.1 스크린샷은 `.agent/evidence-v0.1.1/`에 남습니다.
|
||||||
|
|
||||||
## 음성과 문서
|
## 음성과 문서
|
||||||
|
|
||||||
한국어 WAV는 직접 쓴 문장을 로컬 eSpeak NG로 합성했습니다. 환경음은
|
한국어 WAV 101개는 직접 쓴 문장을 Microsoft Edge online TTS의
|
||||||
브라우저에서 고정 seed로 합성합니다. 외부 음원이나 신경망 음성 모델을 사용하지 않습니다.
|
`ko-KR-HyunsuMultilingualNeural`로 새로 생성했습니다. edge-tts **7.3.1**,
|
||||||
|
rate `-6%`, pitch `-4Hz`이며 FFmpeg로 **24kHz mono PCM16**으로 변환합니다.
|
||||||
|
생성 시 네트워크가 필요하며 앱은 저장된 WAV만 재생합니다. 환경음은 브라우저에서
|
||||||
|
고정 seed로 합성합니다. 생성 음성의 독점 권리를 주장하지 않습니다.
|
||||||
|
|
||||||
|
Linux에서 Python 3.11 이상과 FFmpeg를 준비한 뒤 다음 명령을 사용합니다.
|
||||||
|
클라이언트 버전을 고정해도 원격 모델 변경으로 동일 바이트 재생성은 보장하지 않습니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m venv .venv-audio
|
||||||
|
.venv-audio/bin/python -m pip install -r requirements-audio.txt
|
||||||
|
.venv-audio/bin/python -m pip check
|
||||||
|
npm run audio:generate
|
||||||
|
```
|
||||||
|
|
||||||
|
고정 venv의 Python이 101개 모두 임시 생성·검증하고 디렉터리를 원자 교체합니다.
|
||||||
|
요청별 60초 제한과 최대 3회 시도를 적용하며, 실패 시 기존 자산을 보존합니다.
|
||||||
|
청취 샘플: [안내](public/audio/intro.wav), [행운의 사과](public/audio/luck-apple.wav).
|
||||||
생성 방법, 도구 라이선스, 합성 음질의 한계는 [AUDIO_PROVENANCE.md](docs/AUDIO_PROVENANCE.md)를
|
생성 방법, 도구 라이선스, 합성 음질의 한계는 [AUDIO_PROVENANCE.md](docs/AUDIO_PROVENANCE.md)를
|
||||||
참고하세요. 실제 음량은 출력 장치에 따라 달라지며 표기 dB는 디지털 상대값입니다.
|
참고하세요. 실제 음량은 출력 장치에 따라 달라지며 표기 dB는 디지털 상대값입니다.
|
||||||
|
|
||||||
- [설계와 상태 전이](docs/ARCHITECTURE.md)
|
- [설계와 상태 전이](docs/ARCHITECTURE.md)
|
||||||
- [Neo를 위한 정적 배포 안내](docs/DEPLOYMENT.md)
|
- [Neo를 위한 정적 배포 안내](docs/DEPLOYMENT.md)
|
||||||
- [RED/GREEN 및 실제 검증 결과](.agent/RESULT.md)
|
- [v0.1.1 RED/GREEN 및 실제 검증 결과](.agent/RESULT-v0.1.1.md)
|
||||||
|
- [v0.1 역사 기록](.agent/RESULT.md)
|
||||||
|
|
||||||
이번 작업에서는 서버 배포와 Git commit/push를 실행하지 않습니다.
|
이번 작업에서는 서버 배포와 Git commit/push를 실행하지 않습니다.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 설계
|
# Find-Word v0.1.1 설계
|
||||||
|
|
||||||
## 데이터와 출제
|
## 데이터와 출제
|
||||||
|
|
||||||
@@ -67,6 +67,11 @@ Promise를 무시한다. 질문과 보기는 재생 완료 후 DOM에 생성되
|
|||||||
|
|
||||||
## 오디오
|
## 오디오
|
||||||
|
|
||||||
|
101개 저장 WAV는 edge-tts 7.3.1의 `ko-KR-HyunsuMultilingualNeural`로
|
||||||
|
빌드 전에 생성한다. 24kHz mono PCM16이며 원문·도구·해시는 manifest에 남긴다.
|
||||||
|
생성기는 임시 디렉터리에서 전체 검증 후 원자 교체한다. 생성 절차는
|
||||||
|
[AUDIO_PROVENANCE](AUDIO_PROVENANCE.md)에 있으며 앱은 provider에 연결하지 않는다.
|
||||||
|
|
||||||
`src/audio/dsp.ts`는 순수 수치 함수이며 장치가 없어도 테스트할 수 있다.
|
`src/audio/dsp.ts`는 순수 수치 함수이며 장치가 없어도 테스트할 수 있다.
|
||||||
정수 seed 잡음에 저역 통과 필터, 사인파, 진폭 변조를 조합하고 양 끝을 페이드한다.
|
정수 seed 잡음에 저역 통과 필터, 사인파, 진폭 변조를 조합하고 양 끝을 페이드한다.
|
||||||
8개 프리셋은 실제 현장 녹음이 아니라 환경을 추상적으로 표현한 합성 신호다.
|
8개 프리셋은 실제 현장 녹음이 아니라 환경을 추상적으로 표현한 합성 신호다.
|
||||||
|
|||||||
@@ -1,83 +1,103 @@
|
|||||||
# 오디오 출처와 재현
|
# Find-Word v0.1.1 오디오 출처와 재현
|
||||||
|
|
||||||
## 포함 자산
|
## 포함 자산과 생성 설정
|
||||||
|
|
||||||
| 대상 | 생성 방식 | 원문/설정 | 배포 방식 |
|
| 대상 | 생성 방식 | 원문/설정 | 배포 방식 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `public/audio/intro.wav` | 로컬 eSpeak NG 한국어 포먼트 합성 | `src/content.ts`의 INTRO | PCM WAV |
|
| `public/audio/intro.wav` | Microsoft Edge online TTS neural 음성 | `src/content.ts`의 INTRO | 24kHz mono PCM16 WAV |
|
||||||
| 목표 음성 100개 | 같은 로컬 엔진 | 10 패턴 × 10 단어 | PCM WAV |
|
| 목표 음성 100개 | 같은 provider·voice | 10 패턴 × 10 단어, `renderSentence` | 같은 WAV |
|
||||||
| 길거리·쇼핑몰·극장·백화점·시장·지하철·카페·비 오는 거리 | 직접 작성한 결정론적 DSP | `ENVIRONMENTS`, 고정 seed | 브라우저 메모리에서 합성 |
|
| 8개 배경 환경 | 직접 작성한 결정론적 DSP | `ENVIRONMENTS`, 고정 seed | 브라우저 메모리에서 합성 |
|
||||||
| 화면 파형·S 캐릭터 | 직접 작성한 CSS | `App.tsx`, `styles.css` | CSS, 외부 이미지 없음 |
|
| 화면 파형·S 캐릭터 | 직접 작성한 CSS | `App.tsx`, `styles.css` | CSS |
|
||||||
|
|
||||||
모든 한국어 문장은 이번 앱을 위해 직접 작성했다. 다른 사람의 녹음, 노래,
|
voice는 **`ko-KR-HyunsuMultilingualNeural`**, rate는 **`-6%`**, pitch는
|
||||||
샘플 라이브러리, 음성 복제, 신경망 모델을 포함하지 않는다. 웃음도 INTRO의
|
**`-4Hz`**, 생성 클라이언트는 **edge-tts 7.3.1**이다. 직접 작성한 한국어
|
||||||
‘으하하하~~’ 문자열을 동일 엔진으로 합성한 것이다. 효과를 위해 타인 음원을
|
문장을 온라인 provider에 보내 MP3 응답을 받은 뒤 FFmpeg로 PCM을 만든다.
|
||||||
다운로드하지 않았다. 런타임에 speechSynthesis 또는 네트워크 TTS도 호출하지 않는다.
|
음성 복제나 사람의 녹음 샘플을 사용하지 않는다. 웃음은 INTRO의 ‘으하하하~~’를
|
||||||
|
같은 voice로 합성한다. 앱은 저장한 WAV만 같은 출처에서 가져오며 런타임 외부
|
||||||
|
TTS/API, speechSynthesis, 인증정보가 필요하지 않다.
|
||||||
|
|
||||||
WAV는 16kHz, mono, signed 16-bit little-endian PCM이다. 101개 합계는
|
청취용 샘플은 [intro.wav](../public/audio/intro.wav)와
|
||||||
**20,497,222바이트**다. 파일별 원문·바이트 수·SHA-256은
|
[luck-apple.wav](../public/audio/luck-apple.wav)이다. 101개 전체 생성 결과의
|
||||||
`public/audio/manifest.json`에 있다. 생성 시 실행 도구에서 읽은 eSpeak NG 1.51,
|
크기·길이 범위와 독립 검증은 [v0.1.1 RESULT](../.agent/RESULT-v0.1.1.md)에 기록한다.
|
||||||
FFmpeg 6.1.1-3ubuntu5의 실제 버전도 기록했다. `tests/assets.test.ts`는 원문 대응, PCM
|
`public/audio/manifest.json`은 version, provider, voice, rate, pitch, sampleRate,
|
||||||
포맷, 비무음 신호, 20MiB 이내 크기, gain 적용 후 피크 여유를 검사한다.
|
생성 시각, 실측 edge-tts/FFmpeg 버전, 파일별 원문·SHA-256·bytes·duration·RMS·peak를 담는다.
|
||||||
빌드 마지막에는 `dist/audio` 파일의 해시도 확인한다.
|
|
||||||
청취 연산 변경에 맞춰 101개 WAV와 manifest를 모두 재생성했다. 정답과 경쟁
|
|
||||||
단어는 `renderSentence`로 채우며, 순서·반복·정정·조건·방향 단서가 실제 음성에 들어간다.
|
|
||||||
|
|
||||||
## 사용 도구와 라이선스
|
## 설치와 생성
|
||||||
|
|
||||||
- [eSpeak NG 공식 저장소](https://github.com/espeak-ng/espeak-ng): GPL-3.0-or-later.
|
생성 환경은 Linux (기존 디렉터리 교체에 `renameat2(RENAME_EXCHANGE)` 필요),
|
||||||
이번에는 Ubuntu 24.04 패키지 `1.51+dfsg-12build1`, `ko` 포먼트 음성을 사용했다.
|
Python 3.11 이상, FFmpeg, Node.js 22.22.2 이상이다. **이미 포함된 WAV를
|
||||||
[공식 언어 목록](https://github.com/espeak-ng/espeak-ng/blob/master/docs/languages.md)에 한국어가 있다.
|
빌드·실행할 때는 Python/FFmpeg/provider 연결이 필요 없다.** 생성기 계약 테스트는
|
||||||
- [FFmpeg 공식 라이선스 안내](https://ffmpeg.org/legal.html): 빌드 구성에 따라
|
Python과 FFmpeg를 사용하되 네트워크 없이 동작한다.
|
||||||
LGPL/GPL. 생성 과정에서 PCM 리샘플링만 사용했으며 실행 파일을 배포하지 않는다.
|
|
||||||
- 합성 엔진의 프로그램 라이선스와 그 출력물은 구분한다.
|
|
||||||
[GPLv3 §2](https://www.gnu.org/licenses/gpl-3.0.html#section2) 및
|
|
||||||
[GNU 출력물 설명](https://www.gnu.org/licenses/gpl-faq.html#GPLOutput)에 따르면
|
|
||||||
출력이 그 프로그램을 바탕으로 한 저작물인 경우에만 프로그램 라이선스가
|
|
||||||
출력에 적용된다. 이 작업의 산출물은 직접 작성한 텍스트의 포먼트 합성 파형이며,
|
|
||||||
프로그램 코드·외부 녹음·모델을 포함하지 않는 자체 생성 오디오로 취급한다.
|
|
||||||
- 엔진·라이브러리·다운로드한 Debian 패키지는 `.cache/`에서만 생성 도구로
|
|
||||||
사용하며 Git 대상과 `dist/`에 들어가지 않는다. 별도 외부 음원 라이선스는 없다.
|
|
||||||
프로젝트 자체의 소스·문서·자체 생성 자산은 private / all-rights-reserved다.
|
|
||||||
[루트 LICENSE](../LICENSE)를 따른다. 별도로 번들에 포함되는 React, ReactDOM,
|
|
||||||
scheduler의 MIT 전문은 [THIRD_PARTY_NOTICES.md](../THIRD_PARTY_NOTICES.md)에
|
|
||||||
있으며 `dist/THIRD_PARTY_NOTICES.md`로 함께 배포한다.
|
|
||||||
|
|
||||||
## 생성 절차
|
|
||||||
|
|
||||||
일반적인 Ubuntu 개발 환경에는 `espeak-ng`, `ffmpeg`, Python 3, Node.js가
|
|
||||||
필요하다. **이미 포함된 WAV로 빌드·실행할 때는 이 도구가 필요 없다.**
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
python3 -m venv .venv-audio
|
||||||
|
.venv-audio/bin/python -m pip install -r requirements-audio.txt
|
||||||
|
.venv-audio/bin/python -m pip check
|
||||||
npm run audio:generate
|
npm run audio:generate
|
||||||
npm test -- tests/assets.test.ts
|
npm test
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
현재 작업에서는 시스템 설치 없이 다음 명령으로 worktree 내부에 도구를 풀었다.
|
`audio:generate`는 `.venv-audio/bin/python scripts/generate_audio.py`로 고정되어
|
||||||
이 명령은 문서화용이며 서버에서 실행한 이력이 없다.
|
전역 Python으로 대체되지 않는다. `requirements-audio.txt`는 edge-tts==7.3.1과
|
||||||
|
전이 의존성 버전을 고정한다. 생성기는 실제 설치 버전을 조회하고 7.3.1이 아니면
|
||||||
|
외부 요청 전에 실패한다. `.venv-audio/`는 Git과 배포물에서 제외한다.
|
||||||
|
|
||||||
```bash
|
원문을 Node type stripping으로 앱 데이터에서 읽고, 101개 모두 같은 파일시스템의
|
||||||
mkdir -p .cache/espeak
|
`public/.audio-stage-*/`에 순차 생성한다. 각 provider 요청은 최대 60초, 최대
|
||||||
cd .cache/espeak
|
3회 시도이며 실패 사이 1초/2초를 기다린다. FFmpeg도 60초 제한이 있다.
|
||||||
apt download espeak-ng espeak-ng-data libespeak-ng1 libpcaudio0 libsonic0
|
재시도 전 불완전 MP3를 삭제하고 변환·검증 실패는 즉시 중단한다. 로그에는
|
||||||
for pkg in *.deb; do dpkg-deb -x "$pkg" root; done
|
파일명·시도 횟수만 남기고 provider stderr/응답·쿠키·인증 헤더를 저장하지 않는다.
|
||||||
cd ../..
|
|
||||||
npm run audio:generate
|
|
||||||
```
|
|
||||||
|
|
||||||
`scripts/generate_audio.py`가 시스템 엔진 또는 `.cache/espeak/root/usr`를
|
전체 WAV와 manifest를 검증한 뒤 기존 `public/audio`와 완성 디렉터리를 단일
|
||||||
자동 선택한다. `ko`, 속도 145, 피치 45, 진폭 100을 고정하고 FFmpeg로 16kHz
|
원자 교환으로 바꾼다. 실패·Ctrl-C는 기존 디렉터리의 바이트를 그대로 보존한다.
|
||||||
PCM을 만든 뒤 Python wave 모듈로 메타데이터 없는 WAV를 쓴다. 런타임 데이터는
|
원자 교환을 지원하지 않는 파일시스템에서는 실패하며 비원자 방식으로 우회하지 않는다.
|
||||||
Node의 type stripping으로 읽어 텍스트 중복을 피한다. 같은 엔진·버전·플랫폼에서는
|
성공 후 이전 자산과 임시 MP3를 정리한다. 강제 종료(SIGKILL)·전원 손실 때는
|
||||||
해시로 재현 여부를 비교할 수 있다. 다른 버전은 새 출처와 해시를 검토해야 한다.
|
숨김 staging 폴더가 남을 수 있으나 공개 디렉터리는 교환 전 또는 후의 전체 집합이다.
|
||||||
|
이는 전원 손실 시 저장장치 내구성 보장을 뜻하지 않는다.
|
||||||
|
|
||||||
## 음량·품질 한계
|
고정 클라이언트와 설정으로 **생성 절차**를 재현할 수 있다. 원격 모델·서비스 버전은
|
||||||
|
고정할 수 없어 재생성 WAV의 바이트 동일성은 보장하지 않는다. 새 실행의 manifest를
|
||||||
|
검토해야 한다. 빌드는 이미 생성한 WAV의 해시를 보존하고 101개 모두 포함됐는지 확인한다.
|
||||||
|
|
||||||
보이스와 배경은 같은 디지털 RMS로 정규화되고 음성 경로만 dB gain을 적용한다.
|
## 레벨 처리와 검증
|
||||||
안내의 gain은 1.8에서 0.12로 감소한다. 기본 전체 음량은 35%이다.
|
|
||||||
물리적 음압을 측정하거나 장치 출력을 보정하지 않는다.
|
|
||||||
|
|
||||||
포먼트 합성 특성상 한국어 발음·억양과 웃음이 기계적으로 들린다. 배경도 실제
|
MP3를 24kHz mono signed 16-bit little-endian PCM으로 디코드한다. 파일 전체에
|
||||||
장소 녹음처럼 세밀하지 않다. 객관식의 자모 차이와 단어 친숙도, 발음의 자연스러움은
|
동일한 선형 gain을 적용해 RMS 0.10을 목표로 하며 증폭은 최대 2배, peak는
|
||||||
검수자가 직접 들어 평가해야 한다. 현재 단계 수치는 임상 검증이나 청력 판정용이 아니다.
|
0.89로 제한한다. 동적 압축·노이즈 게이트·침묵 제거·시간 변형은 하지 않는다.
|
||||||
|
원래 신호의 자음과 억양을 보존하면서 파일 간 레벨 차이와 피크를 완화한다.
|
||||||
|
|
||||||
|
생성기와 독립 자산 테스트는 101개 파일의 원문·집합·SHA-256·bytes·duration,
|
||||||
|
24kHz mono PCM16, 1–20초 길이, RMS > 100/32768, full-scale 미도달,
|
||||||
|
총 48MiB 미만을 검사한다. 런타임 RMS 0.03과 최대 +6dB에서도 peak < 0.8을
|
||||||
|
요구한다. 기존 앱은 Web Audio에서 배경·음성을 RMS 정규화한 뒤 상대 SNR을 적용한다.
|
||||||
|
물리적 음압이나 주파수별 청감을 보정한 값은 아니다.
|
||||||
|
|
||||||
|
## 도구 라이선스와 서비스 조건
|
||||||
|
|
||||||
|
- [edge-tts 7.3.1 LICENSE](https://github.com/rany2/edge-tts/blob/7.3.1/LICENSE):
|
||||||
|
대부분의 코드에 LGPLv3, `srt_composer.py`에만 MIT가 적용된다.
|
||||||
|
[공식 프로젝트](https://github.com/rany2/edge-tts)는 Microsoft Edge의 온라인
|
||||||
|
TTS를 이용하는 클라이언트다. 클라이언트 라이선스가 provider 사용·음성 출력의
|
||||||
|
재배포 권한을 대신 부여하지 않는다. 이 프로젝트는 클라이언트/모델을 번들하지 않는다.
|
||||||
|
- [FFmpeg 공식 라이선스](https://ffmpeg.org/legal.html): 빌드 옵션에 따라 LGPL/GPL이
|
||||||
|
적용된다. 실제 사용 버전은 manifest에 남기며 실행 파일은 배포하지 않는다.
|
||||||
|
- [Microsoft Services Agreement](https://www.microsoft.com/en-us/servicesagreement)의
|
||||||
|
AI Services 등 해당 서비스 조건은 도구의 오픈소스 라이선스와 별개다.
|
||||||
|
위 문서 §13(s)(vi)는 출력의 권리·사용 가능성을 이용자가 판단하도록 설명한다.
|
||||||
|
API 키 없이 호출된다는 사실이 상업 사용·재배포의 포괄적 허락이나 SLA를 뜻하지 않는다.
|
||||||
|
배포자는 자신의 이용 상황에 적용되는 최신 조건을 확인해야 한다.
|
||||||
|
- 프로젝트는 생성 음성 자체에 **독점 권리를 주장하지 않는다**. [LICENSE](../LICENSE)의
|
||||||
|
소스·문서 권리 유보와 provider 및 제3자의 권리는 구분한다. 런타임 React,
|
||||||
|
ReactDOM, scheduler의 MIT 전문은 [THIRD_PARTY_NOTICES.md](../THIRD_PARTY_NOTICES.md)에 있다.
|
||||||
|
|
||||||
|
문서 확인일: 2026-10-14 UTC. provider의 음성 목록, rate/pitch 지원, 가용성,
|
||||||
|
접근 제한, 조건은 바뀔 수 있다. 온라인 생성이 중단되어도 포함된 WAV의 빌드·재생에는
|
||||||
|
영향이 없다. 실패 시 다른 voice로 자동 대체하지 않는다.
|
||||||
|
|
||||||
|
## 청감과 평가의 한계
|
||||||
|
|
||||||
|
자동 검증은 PCM 신호·무결성·재생 흐름을 확인한다. 한국어 발음, 유사 자모의
|
||||||
|
구분, 웃음의 자연스러움과 실제 사용자의 난이도는 청취 평가가 필요하다.
|
||||||
|
대표 샘플과 게임 내 소음 혼합 상태를 직접 들어 인수 판단한다. 배경은 장소를
|
||||||
|
추상화한 합성 신호다. 이 게임의 수치는 임상 검증·치료·청력 판정용이 아니다.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 정적 배포 인계
|
# Find-Word v0.1.1 정적 배포 인계
|
||||||
|
|
||||||
이 문서는 **Neo가 검토 후 수행할 절차**다. Codex는 서버, DNS, 인증정보에
|
이 문서는 **Neo가 검토 후 수행할 절차**다. Codex는 서버, DNS, 인증정보에
|
||||||
접근하지 않았고 배포·서비스 생성·Git commit/push도 하지 않았다.
|
접근하지 않았고 배포·서비스 생성·Git commit/push도 하지 않았다.
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
Node.js 최소 **22.22.2** (`>=22.22.2`)의 지원 LTS 환경에서 `npm ci`, `npm run lint`, `npm test`,
|
Node.js 최소 **22.22.2** (`>=22.22.2`)의 지원 LTS 환경에서 `npm ci`, `npm run lint`, `npm test`,
|
||||||
`npm run build`를 실행한다. `dist/` 전체를 하나의 릴리스로 전달한다.
|
`npm run build`를 실행한다. `dist/` 전체를 하나의 릴리스로 전달한다.
|
||||||
`dist/audio/`의 WAV 101개와 manifest가 반드시 포함되어야 한다. 런타임은
|
`dist/audio/`의 24kHz neural WAV 101개와 version 0.1.1 manifest가 반드시 포함되어야 한다. 런타임은
|
||||||
Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·API 키는 필요 없다.
|
Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·API 키는 필요 없다.
|
||||||
잠긴 jsdom 계열 의존성 때문에 24.x는 24.15.0 이상, 26.x는 26.0.0 이상이어야 한다.
|
잠긴 jsdom 계열 의존성 때문에 24.x는 24.15.0 이상, 26.x는 26.0.0 이상이어야 한다.
|
||||||
브라우저 검증의 Chromium sandbox는 기본 활성화다. sandbox를 지원하지 않는
|
브라우저 검증의 Chromium sandbox는 기본 활성화다. sandbox를 지원하지 않는
|
||||||
@@ -15,6 +15,8 @@ Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·A
|
|||||||
|
|
||||||
Canonical repository는 [OZ-Group/Find-Word](https://source.ibit.kr/OZ-Group/Find-Word)다.
|
Canonical repository는 [OZ-Group/Find-Word](https://source.ibit.kr/OZ-Group/Find-Word)다.
|
||||||
프로젝트 자체는 private / all-rights-reserved이며 [LICENSE](../LICENSE)를 따른다.
|
프로젝트 자체는 private / all-rights-reserved이며 [LICENSE](../LICENSE)를 따른다.
|
||||||
|
생성 음성은 프로젝트의 독점 권리 주장 대상이 아니며 provider 조건은
|
||||||
|
[AUDIO_PROVENANCE](AUDIO_PROVENANCE.md)에 기록한다.
|
||||||
React, ReactDOM, scheduler의 MIT 전문을 담은 `dist/THIRD_PARTY_NOTICES.md`도
|
React, ReactDOM, scheduler의 MIT 전문을 담은 `dist/THIRD_PARTY_NOTICES.md`도
|
||||||
반드시 배포한다. `build` 검증기는 고지 파일이 없거나 축약·변조되면 실패한다.
|
반드시 배포한다. `build` 검증기는 고지 파일이 없거나 축약·변조되면 실패한다.
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export default tseslint.config(
|
|||||||
"dist/**",
|
"dist/**",
|
||||||
"node_modules/**",
|
"node_modules/**",
|
||||||
".cache/**",
|
".cache/**",
|
||||||
|
".venv-audio/**",
|
||||||
"test-results/**",
|
"test-results/**",
|
||||||
"playwright-report/**",
|
"playwright-report/**",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
name="description"
|
name="description"
|
||||||
content="소음 속 작은 한국어 단어를 찾는 청각적 주의·식별 게임. 에스와 함께 나만의 속도로 소리를 탐험하세요."
|
content="소음 속 작은 한국어 단어를 찾는 청각적 주의·식별 게임. 에스와 함께 나만의 속도로 소리를 탐험하세요."
|
||||||
/>
|
/>
|
||||||
<title>단어를 맞춰라 · Find-Word v0.1</title>
|
<title>단어를 맞춰라 · Find-Word v0.1.1</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "find-word",
|
"name": "find-word",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "find-word",
|
"name": "find-word",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.3.0",
|
"react": "^19.3.0",
|
||||||
"react-dom": "^19.3.0"
|
"react-dom": "^19.3.0"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "find-word",
|
"name": "find-word",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.22.2"
|
"node": ">=22.22.2"
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"build": "tsc --noEmit && vite build && node scripts/check-build.mjs",
|
"build": "tsc --noEmit && vite build && node scripts/check-build.mjs",
|
||||||
"preview": "vite preview --host 127.0.0.1",
|
"preview": "vite preview --host 127.0.0.1",
|
||||||
"audio:generate": "python3 scripts/generate_audio.py",
|
"audio:generate": ".venv-audio/bin/python scripts/generate_audio.py",
|
||||||
"test:browser": "playwright test"
|
"test:browser": "playwright test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||