3 Commits
Author SHA1 Message Date
oz 812540c743 feat: release Find-Word v0.2.3 2026-10-14 10:01:46 +00:00
oz c92660b514 fix: version neural audio requests for v0.1.1 2026-10-14 07:31:47 +00:00
oz a92b2ba1e9 feat: improve Korean neural voice quality for v0.1.1 2026-10-14 07:15:07 +00:00
701 changed files with 120736 additions and 737 deletions
+21
View File
@@ -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 미실행
+62
View File
@@ -0,0 +1,62 @@
# Find-Word v0.2.3 수용 기준
## 버전·문서
- [ ] package.json/package-lock.json/index.html/UI/README가 `0.2.3`이다.
- [ ] `CHANGELOG.md`가 존재하고 v0.2.3의 모든 요구사항·오류 수정·자산·검증 변경을 설명한다.
- [ ] 정확한 `Designed by OZ at Heat-T Medical Inc.`를 보존한다.
- [ ] README, ARCHITECTURE, DEPLOYMENT, 오디오 provenance가 새 동작과 일치한다.
- [ ] 직접 변경한 모듈·함수·클래스·핵심 변수/매개변수·비자명한 로직에 목적 중심의 상세 한국어 주석이 있다.
## 3문항·난이도
- [ ] createQuestions는 라운드당 정확히 3개의 서로 다른 문제를 반환한다.
- [ ] 시작 난이도 1~5를 명시적으로 받으며 3문항이 모두 같은 level/db로 생성된다.
- [ ] 정상 완료 후 다음 단계 3문항을 시작할 수 있고 5단계 경계를 넘지 않는다.
- [ ] 진행률, 안내문, 결과 분모에 하드코딩된 10이 남지 않는다.
- [ ] mixed 모드가 3문항에서도 choice와 text를 모두 포함한다.
## 수준/SNR 직접 조절
- [ ] 시작 화면에서 시작 난이도 1~5를 선택할 수 있다.
- [ ] 사용자가 배경 대비 목표 음성 SNR dB를 직접 조절할 수 있다.
- [ ] 허용 범위 밖·NaN 값은 안전한 범위/기본값으로 정규화한다.
- [ ] 변경 값은 현재 문제의 재청취와 남은 라운드 문항에 일관되게 적용된다.
- [ ] 화면에 양/0/음수 dB 의미와 현재 값을 정확히 설명한다.
## 중도 종료
- [ ] loading/playing/answer/feedback 어느 단계에서도 종료할 수 있다.
- [ ] 종료 즉시 fetch/Web Audio source/timer가 취소되고 늦은 완료 이벤트가 상태를 되살리지 않는다.
- [ ] 중도 종료 결과가 정답 수/응답 수를 표시하고 무응답을 오답으로 세지 않는다.
- [ ] 결과에서 초기 화면으로 돌아가며 새 라운드에 이전 답·오디오·난이도 override가 새지 않는다.
## 실제 배경음
- [ ] 8개 Environment 각각 고유 same-origin 정적 WAV URL을 가진다.
- [ ] 정확히 8개 실제 현장 녹음 WAV가 빌드에 포함된다.
- [ ] 8개 모두 24,000Hz mono PCM16이며 non-silent이고 hash가 서로 다르다.
- [ ] manifest/provenance에 environment id, 표시 이름, source page/direct URL, creator, license, attribution, conversion, source SHA-256, output SHA-256, bytes, duration, RMS, peak가 있다.
- [ ] 라이선스가 수정·공개 재배포를 허용하며 attribution/notice가 dist에도 포함된다.
- [ ] 실제 엔진은 procedural synth가 아니라 선택한 environment 파일을 fetch/decode/loop한다.
- [ ] 404/decode/timeout/abort 오류가 복구 가능한 한국어 메시지로 처리된다.
- [ ] 배경과 목표는 각각 RMS 정규화 후 `gain = 10^(dB/20)`의 상대 비율을 사용하고 clipping headroom을 유지한다.
- [ ] 외부 runtime 요청이 0이며 누락 배경/음성 경로는 SPA HTML이 아니라 404다.
## 회귀·품질
- [ ] 기존 10개 mechanic/100개 목표 문장/101개 neural voice assets를 보존한다.
- [ ] 객관식·주관식·mixed, 채점, 점수, 재청취, IME 보호, 볼륨, 접근성이 동작한다.
- [ ] lint/typecheck, unit/component/Python tests, build verifier, npm audit가 통과한다.
- [ ] sandboxed system Chrome desktop/mobile browser suite가 실제 목표·배경 WAV를 decode/play한다.
- [ ] 브라우저에서 3문항 완료 → 다음 난이도, 시작 난이도 변경, SNR 변경, 재생 중 종료, 답변 후 종료, 초기화 흐름을 검증한다.
- [ ] console/page errors, failed requests, 외부 페이지 요청, axe 위반이 0이다.
- [ ] desktop/mobile start/game/normal-results/early-exit screenshots를 생성한다.
- [ ] 비밀정보 검사 0, production build에 runtime dependency 및 ambience attribution notice가 포함된다.
## 릴리스
- [ ] Neo 독립 검증 후 feature branch를 push한다.
- [ ] `main` fast-forward merge, annotated tag `v0.2.3`, fresh-clone readback을 완료한다.
- [ ] findword.heart-m.com에 commit-addressed release로 원자 배포하고 기존 release를 rollback 대상으로 보존한다.
- [ ] 공개 서비스에서 버전, 배경/목표 MIME·hash, cache-busting, 3문항·다음 단계·중도 종료를 실제 Chrome으로 검증한다.
+180
View File
@@ -0,0 +1,180 @@
{
"version": "0.2.3",
"policy": {
"runtime": "All ambience files are bundled same-origin static assets; no runtime requests to source sites.",
"conversion": "Select a representative segment, remove unsuitable intelligible/incidental content where practical, trim, fade for loop safety, downmix, resample and normalize to 24000 Hz mono PCM16 WAV.",
"rights": "CC0/public-domain assets retain suggested credits; CC BY and CC BY-SA derivatives retain exact attribution, modification notice and license. The proprietary project does not claim exclusive rights over third-party recordings.",
"verified": "All eight approved direct-media files were verified on 2026-10-14 by SHA-256, byte count, ffprobe and full FFmpeg decode. The expected hashes/bytes are immutable release inputs, never learned or updated by the converter.",
"durationFields": "directMediaDurationSeconds is ffprobe format.duration of the exact directUrl bytes (including Freesound preview MP3s), not a rounded primary-page recording duration. Output duration and conversion.durationSeconds describe the 12-second WAV excerpt.",
"segmentSelection": "selectedSegmentStartSeconds is immutable per-asset release provenance: the start in exact direct-media seconds of the 12-second derivative. The generator reads it without a separate start map, inference or mutation and rejects segments outside directMediaDurationSeconds before conversion.",
"privacyScreening": "The mall 36–48s, market 24–36s and cafe 0–12s selections follow Neo's rescan of the hash-pinned sources through scripts/ambience_audio.convert with faster-whisper 1.2.1 tiny multilingual CPU int8, beam_size=5, best_of=5, default VAD and condition_on_previous_text=true. Exact final-WAV screening is recorded in .agent/evidence-v0.2.3/privacy-screening/final-asr.json; the release test requires requiredEmptyPassed=true. The rejected market 60s/cafe 180s attempt is preserved under prior-attempt-market60-cafe180. ASR is only a screening signal, not a verified transcript, proof of absence of speech/music, human listening acceptance or legal/privacy clearance. Supplementary no-VAD evidence is retained and does not override the VAD gate."
},
"assets": [
{
"environmentId": "street",
"displayName": "길거리",
"pageUrl": "https://commons.wikimedia.org/wiki/File:Sunday_in_the_city_street_noise2.ogg",
"directUrl": "https://upload.wikimedia.org/wikipedia/commons/2/24/Sunday_in_the_city_street_noise2.ogg",
"creator": "cori (Cori Samuel)",
"license": "Public domain (PD-author)",
"licenseUrl": "https://commons.wikimedia.org/wiki/Template:PD-author",
"attribution": "Suggested: Sunday, 4th May 2008 5.05pm - sounds outside my window, by cori (Cori Samuel), via Wikimedia Commons; public domain.",
"expectedSourceSha256": "0ddb0be7eb3427f781c8268bcbc74b8ad7e1fbf75b9f6966e65274dbe85f3d9d",
"sourceBytes": 3414104,
"directMediaDurationSeconds": 264.254694,
"sourceVerification": {
"status": "verified",
"dateUtc": "2026-10-14",
"method": "SHA-256, byte count, ffprobe format.duration, full FFmpeg decode",
"evidence": ".agent/evidence-v0.2.3/source-integrity.json"
},
"selectedSegmentStartSeconds": 35
},
{
"environmentId": "mall",
"displayName": "쇼핑몰",
"pageUrl": "https://commons.wikimedia.org/wiki/File:1_minute_at_the_alexa_mall_in_berlin.ogg",
"directUrl": "https://upload.wikimedia.org/wikipedia/commons/f/fd/1_minute_at_the_alexa_mall_in_berlin.ogg",
"creator": "thore (Thore)",
"license": "Public domain (PD-author)",
"licenseUrl": "https://commons.wikimedia.org/wiki/Template:PD-author",
"attribution": "Suggested: 1 minute at the Alexa mall in Berlin, by thore, via Wikimedia Commons; public domain.",
"expectedSourceSha256": "bb066e130e5d665db57cb5f88e820d053c724d8f777b275213998d2074c6529a",
"sourceBytes": 1666896,
"directMediaDurationSeconds": 60.05551,
"sourceVerification": {
"status": "verified",
"dateUtc": "2026-10-14",
"method": "SHA-256, byte count, ffprobe format.duration, full FFmpeg decode",
"evidence": ".agent/evidence-v0.2.3/source-integrity.json"
},
"selectedSegmentStartSeconds": 36
},
{
"environmentId": "theater",
"displayName": "극장",
"pageUrl": "https://freesound.org/people/ElevatorFan2020/sounds/853576/",
"directUrl": "https://cdn.freesound.org/previews/853/853576_14269391-lq.mp3",
"creator": "ElevatorFan2020",
"license": "CC0 1.0 Universal",
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
"attribution": "Suggested: Crowd Ambience - Theater, by ElevatorFan2020, from Freesound, CC0 1.0.",
"expectedSourceSha256": "05c3ece7c088f34700b7610f791c0ad848a24c77be2c32fd1503a78356739a43",
"sourceBytes": 1485015,
"directMediaDurationSeconds": 176.901224,
"sourceVerification": {
"status": "verified",
"dateUtc": "2026-10-14",
"method": "SHA-256, byte count, ffprobe format.duration, full FFmpeg decode",
"evidence": ".agent/evidence-v0.2.3/source-integrity.json"
},
"selectedSegmentStartSeconds": 45
},
{
"environmentId": "department",
"displayName": "백화점",
"pageUrl": "https://freesound.org/people/icmusic/sounds/268941/",
"directUrl": "https://cdn.freesound.org/previews/268/268941_40107-lq.mp3",
"creator": "icmusic",
"license": "CC0 1.0 Universal",
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
"attribution": "Suggested: US Department store - checkouts, by icmusic, from Freesound, CC0 1.0.",
"expectedSourceSha256": "261fda28722fba623483a90b32e7a2ec388c59887f56a5247156b53f1ffb6ccd",
"sourceBytes": 2445024,
"directMediaDurationSeconds": 292.872,
"sourceVerification": {
"status": "verified",
"dateUtc": "2026-10-14",
"method": "SHA-256, byte count, ffprobe format.duration, full FFmpeg decode",
"evidence": ".agent/evidence-v0.2.3/source-integrity.json"
},
"selectedSegmentStartSeconds": 90
},
{
"environmentId": "market",
"displayName": "시장",
"pageUrl": "https://commons.wikimedia.org/wiki/File:Italia_Palermo_mercato-Ballaro_20161225_122421.ogg",
"directUrl": "https://upload.wikimedia.org/wikipedia/commons/e/e8/Italia_Palermo_mercato-Ballaro_20161225_122421.ogg",
"creator": "Marie Flutre (Wikimedia user Timflutre)",
"license": "CC BY-SA 4.0",
"licenseUrl": "https://creativecommons.org/licenses/by-sa/4.0/",
"attribution": "Italia Palermo mercato-Ballaro 20161225 122421, by Marie Flutre, CC BY-SA 4.0, via Wikimedia Commons. Modified by excerpting, trimming, fades, normalization, downmixing, resampling and PCM WAV conversion. The derivative audio remains CC BY-SA 4.0.",
"expectedSourceSha256": "fc0075dce8ad05b56460c9f18754c27b2021f21f1c780a595ccda648d33c941d",
"sourceBytes": 622200,
"directMediaDurationSeconds": 76.696961,
"sourceVerification": {
"status": "verified",
"dateUtc": "2026-10-14",
"method": "SHA-256, byte count, ffprobe format.duration, full FFmpeg decode",
"evidence": ".agent/evidence-v0.2.3/source-integrity.json"
},
"selectedSegmentStartSeconds": 24
},
{
"environmentId": "subway",
"displayName": "지하철",
"pageUrl": "https://commons.wikimedia.org/wiki/File:TTC_Subway_Line_1_Ambience_-_Museum_to_Union_(Freesound).ogg",
"directUrl": "https://upload.wikimedia.org/wikipedia/commons/d/d3/TTC_Subway_Line_1_Ambience_-_Museum_to_Union_%28Freesound%29.ogg",
"creator": "PascalHD",
"license": "CC BY 4.0",
"licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
"attribution": "TTC Subway Line 1 Ambience - Museum to Union, by PascalHD, CC BY 4.0, via Wikimedia Commons/Freesound. Modified by excerpting, trimming, fades, normalization, downmixing, resampling and PCM WAV conversion.",
"expectedSourceSha256": "41fcf064cde53cef1528fe98721347851e60c070d8ff9f213f354025cf272d4c",
"sourceBytes": 3343901,
"directMediaDurationSeconds": 425.024,
"sourceVerification": {
"status": "verified",
"dateUtc": "2026-10-14",
"method": "SHA-256, byte count, ffprobe format.duration, full FFmpeg decode",
"evidence": ".agent/evidence-v0.2.3/source-integrity.json"
},
"selectedSegmentStartSeconds": 65
},
{
"environmentId": "cafe",
"displayName": "카페",
"pageUrl": "https://commons.wikimedia.org/wiki/File:Cafe_ambiance.ogg",
"directUrl": "https://upload.wikimedia.org/wikipedia/commons/5/54/Cafe_ambiance.ogg",
"creator": "Marble Toast",
"license": "CC0 1.0 Universal",
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
"attribution": "Suggested: Cafe ambiance, by Marble Toast, via Wikimedia Commons, CC0 1.0.",
"expectedSourceSha256": "6360791fdcee1f64638f0fc9abd6d2c7a1aa75fd3d2b80033170a02d025099dc",
"sourceBytes": 19947952,
"directMediaDurationSeconds": 1200.000998,
"sourceVerification": {
"status": "verified",
"dateUtc": "2026-10-14",
"method": "SHA-256, byte count, ffprobe format.duration, full FFmpeg decode",
"evidence": ".agent/evidence-v0.2.3/source-integrity.json"
},
"selectedSegmentStartSeconds": 0
},
{
"environmentId": "rain",
"displayName": "비 오는 거리",
"pageUrl": "https://commons.wikimedia.org/wiki/File:Urban_Street_on_a_Rainy_Afternoon.flac",
"directUrl": "https://upload.wikimedia.org/wikipedia/commons/8/8c/Urban_Street_on_a_Rainy_Afternoon.flac",
"creator": "Extemporalist",
"license": "CC0 1.0 Universal",
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
"attribution": "Suggested: Urban Street on a Rainy Afternoon, by Extemporalist, via Wikimedia Commons, CC0 1.0.",
"expectedSourceSha256": "ef0511593b1bb0ba47d5c5dafd83eb07ba854b5a65fd5d41b8c76a1b3ff2c22a",
"sourceBytes": 91187572,
"directMediaDurationSeconds": 1800.0,
"sourceVerification": {
"status": "verified",
"dateUtc": "2026-10-14",
"method": "SHA-256, byte count, ffprobe format.duration, full FFmpeg decode",
"evidence": ".agent/evidence-v0.2.3/source-integrity.json"
},
"selectedSegmentStartSeconds": 180
}
],
"releaseRisks": [
"Audition chosen excerpts to avoid music and unnecessarily intelligible bystander speech.",
"The market derivative must stay CC BY-SA 4.0 with source, creator, change notice and license link.",
"The subway derivative needs CC BY 4.0 attribution and change notice.",
"Freesound direct URLs are preview MP3 files; preserve local source hashes and page/license evidence."
]
}
+189
View File
@@ -0,0 +1,189 @@
# 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 검증 범위 밖이다.
## 2026-10-14 추가 — 배포 후 오디오 캐시 회귀 수정
제품 버전 **0.1.1**을 유지하면서 intro와 목표 100개 WAV 모두
`?v=0.1.1`을 붙이도록 수정했다. `src/audio/assets.ts`의
`AUDIO_ASSET_VERSION`과 `audioAssetUrl`을 출제·오디오 엔진이 함께 사용한다.
WAV/manifest 및 package/lock은 변경하지 않았다.
발견 원인은 **음성 내용 교체 후에도 요청 URL이 같았던 것**이다. 사용자 보고의
배포 Nginx `/audio` 7일 캐시에서 기존 사용자는 이전 음성을 계속 받을 수 있었다.
기존 배포 문서의 재검증 가정과 실제 설정이 달랐으며, 브라우저 테스트의
전체 URL `.endsWith('.wav')`와 404 glob도 query string을 놓쳤다.
원격 서버에는 접속하지 않고 소스·로컬 production build에서 재현했다.
앱 구현을 수정하기 전에 unit/browser 계약을 작성하고 실제 RED를 확인했다.
RED 때 앱 소스가 그대로였다는 기록은 [04-red-state.json](evidence-v0.1.1/cache-regression/04-red-state.json)에 있다.
| 계약/검증 | 결과 | 새 evidence |
| --- | --- | --- |
| 목표 100개 버전·intro/target fetch·각각 404 retry·query pathname 판정 | RED 5 failed / 12 passed → GREEN 17 passed | [RED](evidence-v0.1.1/cache-regression/01-unit-red.log), [GREEN](evidence-v0.1.1/cache-regression/05-unit-green.log) |
| 실제 브라우저 요청/정답 mapping·intro/target 404·101개 URL | 구현 전 desktop RED 4 failed → 전체 desktop/mobile GREEN 16 passed (2.6m) | [RED](evidence-v0.1.1/cache-regression/03-browser-red.log), [GREEN](evidence-v0.1.1/cache-regression/09-browser-green.log) |
| `npm run lint` | PASS | [로그](evidence-v0.1.1/cache-regression/06-lint.log) |
| `npm test` | 12 files / 65 passed, skip 0; Python 18개 계약 포함 | [로그](evidence-v0.1.1/cache-regression/07-full-test.log) |
| `npm run build` | PASS, 101개 WAV·notice 검증 | [로그](evidence-v0.1.1/cache-regression/08-build.log) |
| 실제 Chrome sandbox | Namespace·PID·Network·Seccomp-BPF 활성, `--no-sandbox` 없음 | [로그](evidence-v0.1.1/cache-regression/11-sandbox-green.log) |
| query URL 전수 Chrome 해시·bytes·24kHz mono 디코드·duration·RMS·peak | 두 viewport 각각 101/101, 외부 요청 0·JS 예외 0 | [desktop](evidence-v0.1.1/cache-regression/artifacts/desktop-browser-audio-integrity.json), [mobile](evidence-v0.1.1/cache-regression/artifacts/mobile-browser-audio-integrity.json) |
| 과거 evidence·자산·제품 버전 파일 보존 및 HEAD·diff 검사 | 254개 해시 동일, 과거 evidence 149개 포함 | [최종 무결성](evidence-v0.1.1/cache-regression/13-final-integrity.json) |
전체 브라우저 명령은
`env -u PLAYWRIGHT_NO_SANDBOX CHROME_PATH=/usr/bin/google-chrome DEBUG=pw:browser npm run test:browser`이다.
실제 재생·재청취·10문제 완주·404 복구 후 정답·접근성 검증을 유지했다.
101개 query URL 검사를 `tests/browser/audio.spec.ts`로 정규 suite에 편입했고,
URL 판정 helper의 query 순서·가짜 WAV 접미사도 단위 테스트로 고정했다.
런타임 외부 요청이나 manifest 조회는 추가하지 않았다.
브라우저 스크린샷은 이제 `test-results/`를 사용하며 이번 결과는
[새 artifacts 경로](evidence-v0.1.1/cache-regression/artifacts/)에 복사했다.
과거 evidence와 기존 이 보고서 본문은 보존하고 이번 기록만 추가했다.
보조 sandbox probe의 최초 라벨 판정 오류와 수정 결과도
[원인·RED/GREEN·최종 결과 로그](evidence-v0.1.1/cache-regression/12-cache-regression-result.log)에 기록했다.
README·ARCHITECTURE·DEPLOYMENT에 자산 버전 갱신 규칙과 7일 캐시 예시를 반영했다.
이 변경의 검증 대상은 로컬 build이며 **Git commit/push/tag/merge와 서버 배포는 실행하지 않았다.**
+394
View File
@@ -0,0 +1,394 @@
# Find-Word v0.2.3 구현·검증 결과
작성일: 2026-10-14 UTC. worktree `/home/hermes/worktrees/find-word-v0.2.3`, branch `feat/v0.2.3`.
기준 HEAD `c92660b514f7af8eb556d3fa5c0fc80e4bdd4cf5`. 지정된 TASK/ACCEPTANCE/AMBIENCE-SOURCES를 먼저 읽고 기존 코드·테스트·문서를 확인했다.
**Git commit/push/merge/tag, 외부 배포, secrets 사용은 하지 않았다.** 로컬 production preview만 사용했다.
정확한 credit **Designed by OZ at Heat-T Medical Inc.**와 private/UNLICENSED 프로젝트 권리를 보존했다.
제3자 현장음은 각각의 라이선스를 유지한다.
**현재 screening 상태:** mall=36, market=24, cafe=0의 최종 WAV는 각각 segment 0개,
`duration_after_vad=0.0`, `requiredEmptyPassed=true`다. 사람의 청취 인수와 법적/privacy clearance는 false다.
전체 unit 125개·Python 36개·sandboxed Chrome 30개 및 lint/build가 통과했고 audit 취약점은 0개다.
실행 원문은 맨 아래 **최종 privacy screening 재시도** 절에 있다.
앞선 구현/독립 리뷰 기록은 당시 결과다. 이전 실패 screening은 별도 읽기 전용 경로에 원문 보존했다.
## 최초 구현 내용 (아래 리뷰 수정 내역으로 보완)
- 정확히 세 개의 서로 다른 문제/단어를 한 단계에서 출제한다. 시작 단계 1~5 선택, mixed 선택/입력/선택,
진행률/안내/분모 3문항, 정상 완료→다음 단계의 새 3문항, 5단계 재도전과 홈 복귀를 구현했다.
- SNR을 −12~+12 dB, 1 dB 눈금으로 직접 설정한다. 범위 밖 값은 clamp, NaN/Infinity는 단계 기본값으로 복구한다.
변경은 현재 재청취·미응답 문항·이후 라운드에 적용한다. 재생 중 gain을 바꾸지 않는다.
다음 단계도 수동 값은 유지하고 홈 복귀 시 수동 override를 지운다. 시작 단계/모드/환경/전체 음량은 홈 설정으로 유지한다.
- ready/loading/playing/answer/feedback에서 `문제 풀이 종료`가 가능하다. token 무효화→엔진 정지→결과 전환으로
지연 콜백을 차단한다. 응답 수·정답 수·실제 응답 분모를 표시하고 무응답을 오답으로 세지 않는다.
- 여덟 고유 실제 현장 녹음을 fetch/decode/loop한다. 절차적 잡음 합성 코드는 제거했다.
101개 음성 및 여덟 배경은 모두 same-origin `?v=0.2.3` 정적 요청이다. 런타임 외부 API/TTS/추적은 없다.
- 음성과 배경을 각각 RMS .03으로 정규화하고 상대 gain `10^(dB/20)`을 적용한다.
안내/목표/배경의 피크 합에 공통 gain을 적용해 최악 합계 peak ≤ .8을 유지한다.
master 음량은 그 뒤 공통 적용되므로 SNR을 보존한다. 배경은 목표 ended에서 함께 정리한다.
- HTTP/decode/준비 15초 timeout/재생 길이+5초 timeout/abort 후 한국어 안내와 재시도를 유지한다.
- CHANGELOG 생성, package/lock/HTML/UI/자산 버전/README/설계/배포/오디오/단어/권리 문서를 0.2.3에 맞췄다.
상세 한국어 설계 주석을 수정 코드의 모듈·함수·클래스·상태/매개변수·경합/수치 계산에 추가했다.
## RED → GREEN 원문
각 동작/회귀는 테스트를 먼저 작성·실행한 뒤 구현했다. 아래 RED의 종료 코드는 1, GREEN은 0이다.
`-t`로 좁힌 실행의 skipped는 필터 제외이며 최종 전체 실행은 skip 0이다. 실패 원문을 성공 로그로 대체하지 않았다.
| 계약/회귀 | RED → GREEN | 원문 |
| --- | --- | --- |
| 세 문항/같은 시작 단계/혼합/점수 | 기존 10문항·자동 단계에서 3 실패 → 9 통과 | [01 RED](evidence-v0.2.3/01-round-RED.log), [01 GREEN](evidence-v0.2.3/01-round-GREEN.log) |
| SNR 정규화/미응답 전파/전 단계 종료/지연 이벤트 | 누락 reducer action 등 7 실패 → 15 통과 | [02 RED](evidence-v0.2.3/02-snr-exit-RED.log), [02 GREEN](evidence-v0.2.3/02-snr-exit-GREEN.log) |
| 시작 단계/수동 SNR/다음 라운드/5단계 경계/중도 결과/UI 초기화 | 10 실패 → 18 통과 | [03 RED](evidence-v0.2.3/03-screen-RED.log), [03 GREEN](evidence-v0.2.3/03-screen-GREEN.log) |
| 고유 녹음 URL/절차적 매개변수 제거/peak/SNR 수학 | 2 실패 → 콘텐츠 포함 19 통과 | [04 RED](evidence-v0.2.3/04-recorded-mix-RED.log), [04 GREEN](evidence-v0.2.3/04-recorded-mix-GREEN.log) |
| 실제 배경 fetch/decode/loop/abort/timeout/재시도 | 전환 중 남은 합성 호출 및 누락 녹음 경로로 9 실패 → 11 통과 | [05 RED](evidence-v0.2.3/05-playback-RED.log), [05 GREEN](evidence-v0.2.3/05-playback-GREEN.log) |
| Node 직접 import 회귀 | ERR_MODULE_NOT_FOUND → 101문장 import 통과 | [06 RED](evidence-v0.2.3/06-node-import-RED.log), [06 GREEN](evidence-v0.2.3/06-node-import-GREEN.log) |
| 8개 배포 PCM/해시/출처/고지/루프 경계 | manifest ENOENT → 전수 통과 | [07 RED](evidence-v0.2.3/07-assets-RED.log), [07 GREEN](evidence-v0.2.3/07-assets-GREEN.log) |
| 실제 FFmpeg 변환/재현/무음 거부/길이 제한 | 변환 모듈 없음 → Python 2 통과 | [변환 RED](evidence-v0.2.3/07-converter-RED.log), [변환 GREEN](evidence-v0.2.3/07-converter-GREEN.log) |
| 재생 중 SNR 변경의 실제 청취 이력 | −10을 들었다고 잘못 기록 → 34 통과 | [08 RED](evidence-v0.2.3/08-heard-snr-RED.log), [08 GREEN](evidence-v0.2.3/08-heard-snr-GREEN.log) |
| 버전/문서/변경 이력/기존 voice metadata | 이전 버전·누락 CHANGELOG → 50 통과 | [09 RED](evidence-v0.2.3/09-version-docs-RED.log), [09 GREEN](evidence-v0.2.3/09-version-docs-GREEN.log) |
| 3문항의 환경 순환 회귀 | 3개 환경만 도달 → 8개 도달, 35 통과 | [10 RED](evidence-v0.2.3/10-environment-rotation-RED.log), [10 GREEN](evidence-v0.2.3/10-environment-rotation-GREEN.log) |
| dist 현장음/manifest/notice 변조·누락·원본 혼입/고지 링크 | 5 실패 → 26 통과 | [11 RED](evidence-v0.2.3/11-bundle-notice-RED.log), [11 GREEN](evidence-v0.2.3/11-bundle-notice-GREEN.log) |
| 누락 WAV의 SPA fallback | desktop/mobile 200 HTML → 양쪽 404, 4 통과 | [13 RED](evidence-v0.2.3/13-static-404-RED.log), [13 GREEN](evidence-v0.2.3/13-static-404-GREEN.log) |
| 결과 화면의 잘못된 도움말 anchor | 없는 #howto 링크 노출 → 제거 | [15 RED](evidence-v0.2.3/15-results-anchor-RED.log), [15 GREEN](evidence-v0.2.3/15-results-anchor-GREEN.log) |
| 새 설정의 터치 크기/카드 경계 | 양쪽 23px 높이 → 44px 이상, 2 통과 | [16 RED](evidence-v0.2.3/16-controls-layout-RED.log), [16 GREEN](evidence-v0.2.3/16-controls-layout-GREEN.log) |
| 3문항의 보너스 안내 | 당시 40점 → 20점 안내; 리뷰에서 문항별 20점/합계 30점으로 재정정 | [17 RED](evidence-v0.2.3/17-bonus-copy-RED.log), [17 GREEN](evidence-v0.2.3/17-bonus-copy-GREEN.log) |
| 모바일 select 선택값 잘림 | 360px에서 글자 폭 초과 → 중복 SNR을 아래 제어에 모음 | [21 RED](evidence-v0.2.3/21-select-label-RED.log), [21 GREEN](evidence-v0.2.3/21-select-label-GREEN.log) |
| Chrome favicon 기본 404 | 내장 icon 없음/실제 콘솔 오류 → 내장 SVG | [19 RED](evidence-v0.2.3/19-favicon-RED.log), [19 GREEN](evidence-v0.2.3/19-favicon-GREEN.log) |
추가 진단 기록도 보존했다. `01-round-sampling-regression.log`의 96/100은 세 문항으로 줄인 뒤
기존 200 seed 표본이 작아진 테스트 회귀여서 1000 seed로 전체 100조합 도달성을 검사했다.
`03-screen-duplicate-description-RED.log`는 같은 SNR 설명을 중복 배치한 회귀를 잡았다.
`05-playback-fixture-regression.log` 뒤 mock AudioBuffer의 copyToChannel을 실제 복사처럼 보완했고
부동소수점 0.12000000000000001 비교를 근사 비교로 바꿨다. 실제 재생은 별도의 Chrome으로 검증한다.
`12-lint-initial.log`의 축소된 union 비교/fixture 타입 오류도 수정했다.
## 현장음 원본과 변환
지정된 원본 8개를 모두 사용했다. **대체 원본은 0개**다. 여섯 기존 원본을 재사용했고 café는
HTTP 429의 **Retry-After 600초** backoff 뒤 지정 직접 URL로 다운로드했다. rain도 지정 URL에서 받았다.
[다운로드 기록](evidence-v0.2.3/source-downloads.log), [rain 기록](evidence-v0.2.3/rain-download.log).
[원본 전수 검사](evidence-v0.2.3/source-integrity.json)는 전체 원본 SHA-256/bytes/ffprobe 정보와
FFmpeg **전체 디코드 8/8 성공, stderr 0**을 기록한다. café 19,947,952 bytes, rain 91,187,572 bytes는
Commons primary 메타데이터의 크기와 일치한다. [Commons API 근거](evidence-v0.2.3/commons-license-audit.json),
[primary 페이지/권리 재확인](evidence-v0.2.3/primary-page-verification.json).
`python3 scripts/ambience_audio.py`로 원본의 명시적 중간 구간을 선택해 전부 **12초 / 24,000Hz / mono / PCM16**으로 만들었다.
양끝 **50ms 선형 fade**, RMS 목표 **.08**, 최대 증폭 **4배**, peak ceiling **.85** 중 안전한 선형 gain을 적용했다.
저레벨 rain과 순간 peak가 큰 market은 제한 때문에 RMS 목표보다 낮다. 엔진은 재생 시 각각 다시 RMS 정규화한다.
양끝 PCM은 0, non-silent 8/8, 고유 출력 해시 8/8, 각 576,044 bytes, 합계 **4,608,352 bytes**다.
대형 OGG/MP3/FLAC 원본은 `/tmp/findword-ambience-sources`에만 있으며 Git/public/dist에 번들하지 않았다.
| 배포 자산 (`public/ambience/`) | 원본 구간 | RMS | peak | 출력 SHA-256 |
| --- | --- | --- | --- | --- |
| street.wav | 35–47s | 0.07999756 | 0.39675903 | `a8bffa201b174618955bc51b860167169125304811aabe2086b0bb35b06c3ae1` |
| mall.wav | 20–32s | 0.07999757 | 0.55468750 | `6e9171f549cacadfaeedd57a41c8e2a76b0e789f5c9a97af029a8d3558c9f145` |
| theater.wav | 45–57s | 0.07999757 | 0.49508667 | `2d6a444fb7874cdf6b2d8a741a69175299b112eb66834912c025437d8277d635` |
| department.wav | 90–102s | 0.07999758 | 0.47497559 | `aa43f3d1d3e7c2b1f8ace24ecbda45d0ea3a031db941c39506dc6c1665b8efa7` |
| market.wav | 30–42s | 0.04922208 | 0.84997559 | `4287e971a12767f70da714e0c1aff30125268f9c3faf07ebfbcc0d2904f4a312` |
| subway.wav | 65–77s | 0.07999755 | 0.42529297 | `9faf70b68617d267b367a0f3692ed90015458543ab4b0844f6c704e6a724728f` |
| cafe.wav | 120–132s | 0.07999759 | 0.39709473 | `8db06f3f09bbd8409573ba4874c7ccdd1e7e825c8a61989a4efe66206a66c2d2` |
| rain.wav | 180–192s | 0.01138463 | 0.10241699 | `f4315084e820f742b0709de5bb0d1abd3d5af1730620e08c2948b70347356dd9` |
[배포 manifest](../public/ambience/manifest.json)에 environment id/표시 이름, source page/direct URL, creator,
license/license URL, attribution, 원본/출력 SHA-256, bytes/duration/RMS/peak, 실제 FFmpeg 버전과 변환 설정을 기록했다.
[AMBIENCE_NOTICES](../public/ambience/AMBIENCE_NOTICES.md)를 dist에 포함하고 UI에서 접근할 수 있다.
시장 파생 WAV는 **CC BY-SA 4.0**, 지하철은 **CC BY 4.0**, 나머지는 CC0/PD이며 출처·변경 고지를 보존했다.
프로젝트의 UNLICENSED를 이 녹음에 적용하지 않는다. 런타임 React/ReactDOM/scheduler의 기존 MIT 전문도 그대로 배포한다.
## 보존과 구체적 회귀 수정
- **101개 neural WAV 바이트/원문/해시 변경 0**. voice manifest는 배포 version만 0.2.3으로 올리고
`generatedForVersion: 0.1.1`을 추가했으며 생성 시각·provider·voice·prosody·도구/실측값은 보존했다.
- 기존 10 mechanic/100문장, 정답·오답 규칙·점수 공식·재청취·객관식/주관식/mixed·IME 보호를 유지했다.
- **과거 .agent 추적 파일 180개 모두 Git blob hash 동일**. v0.1/v0.1.1 RESULT와 evidence 수정 0.
[보존 검사](evidence-v0.2.3/preservation.json).
- 짧아진 라운드 때문에 순환 환경이 street/mall/theater에만 갇히는 회귀를 seed 기반 환경 셔플로 수정했다.
- 환경 URL 모듈 추가로 Python 생성기의 Node 직접 import가 깨지는 회귀를 명시적 `.ts` 경로로 수정했다.
- `heardDb`를 PLAY 시점에 기록해 재생 중 조절값이 과거 청취 이력/결과를 덮어쓰지 않게 했다.
- 중복 SNR 설명, 결과 화면의 없는 도움말 anchor, 실제 라운드 보너스 상한 안내를 바로잡았다.
- Vite dev/preview의 누락 WAV HTML fallback을 차단하고 Nginx 인계에도 `/ambience/` 404 규칙을 추가했다.
- 모바일 최종 이미지에서 시작 단계 select의 긴 선택값 잘림을 찾아 글자 폭 검사로 재현하고, 중복 SNR 표기를 전용 제어로 모았다.
- 실제 Chrome의 기본 `/favicon.ico` 404 콘솔 오류를 내장 SVG로 제거했다.
## 리뷰 전 검증 기록 (리뷰 수정 후 결과는 문서 하단)
환경: Node **26.7.0**, npm **11.19.0**, Python **3.11.16**, FFmpeg **6.1.1-3ubuntu5**, system Chrome **146.0.7680.80**.
[환경](evidence-v0.2.3/environment.json). Chrome sandbox 활성화, `--no-sandbox` 0개를 실행 중 process argv로 확인했다.
[실행 sandbox 기록](evidence-v0.2.3/chrome-sandbox.json); 설정 계약 테스트도 통과했다.
| 검증 | 결과 | 원문 |
| --- | --- | --- |
| `npm run lint` | PASS, ESLint + TypeScript strict, exit 0 | [22-lint](evidence-v0.2.3/22-lint.log) |
| `npm test` | **13 files / 94 passed**, skip 0, exit 0; Python runner 포함 | [22-full-test](evidence-v0.2.3/22-full-test.log) |
| `python3 -m unittest discover -s tests/python -v` | **20 passed**, exit 0 (기존 neural 18 + 새 변환 2) | [18-python](evidence-v0.2.3/18-python.log) |
| `npm run build` | PASS, 101 voice + 8 ambience + 두 notice 검증, exit 0 | [22-build](evidence-v0.2.3/22-build.log) |
| `npm audit --json` | **취약점 0** (info/low/moderate/high/critical 모두 0), exit 0 | [audit JSON](evidence-v0.2.3/14-npm-audit.json) |
| 최종 sandboxed system Chrome desktop/mobile | **26 passed / 0 failed / 0 skipped**, exit 0; 실제 재생·접근성·무외부 요청 검증 | [22-browser](evidence-v0.2.3/22-browser.log), [machine-readable 결과](evidence-v0.2.3/browser-results.json) |
| 원본/배포 녹음 및 101개 voice 보존 | PASS | [원본](evidence-v0.2.3/source-integrity.json), [보존](evidence-v0.2.3/preservation.json) |
| 비밀정보 형식 검사 | **0 findings**, 값 노출 없이 경로/개수만 기록 | [scan](evidence-v0.2.3/secret-scan.json) |
| `git diff --check` | PASS | [최종 worktree 점검](evidence-v0.2.3/final-review.log) |
브라우저는 1280×1000 desktop / 360×800 mobile에서 같은 production build를 사용한다.
실제 디코드한 109 WAV의 response hash/bytes/24kHz/mono/duration/RMS/peak를 전수 대조하고,
앱에서 여덟 환경을 실제 loop로 재생 후 정지했다. 혼합 모드 세 문항 완주/재청취/키보드,
4단계 시작→5단계 새 라운드, 수동 SNR 유지/재청취/홈 초기화, 재생 중 종료/응답 뒤 종료,
intro/target/ambience 404 재시도, 누락 WAV의 HTML Accept 404, notice 원문 제공을 검사한다.
정상 흐름의 console/page errors·failed requests·외부 요청·axe violations가 모두 0이어야 통과한다.
브라우저 native audio 메서드를 관찰할 때 원래 메서드를 실행하며 가짜 decode/ended를 주입하지 않는다.
최종 캡처 8개와 실제 검사 JSON을 새 evidence에 보존했다. 모바일 게임/시작과 desktop 시작/결과의
레이아웃을 이미지로 확인했고, 결과 분모/현재 SNR/종료 버튼/고지 링크가 잘리지 않는다.
| 화면 | Desktop | Mobile |
| --- | --- | --- |
| 시작 | [PNG](evidence-v0.2.3/browser/desktop-start.png) | [PNG](evidence-v0.2.3/browser/mobile-start.png) |
| 게임 | [PNG](evidence-v0.2.3/browser/desktop-game.png) | [PNG](evidence-v0.2.3/browser/mobile-game.png) |
| 정상 결과 | [PNG](evidence-v0.2.3/browser/desktop-normal-results.png) | [PNG](evidence-v0.2.3/browser/mobile-normal-results.png) |
| 중도 종료 | [PNG](evidence-v0.2.3/browser/desktop-early-exit.png) | [PNG](evidence-v0.2.3/browser/mobile-early-exit.png) |
실제 109 WAV 해시/디코드/레벨 및 오류 0 집계:
[desktop JSON](evidence-v0.2.3/browser/desktop-audio-integrity.json),
[mobile JSON](evidence-v0.2.3/browser/mobile-audio-integrity.json).
세 문항→다음 단계→중도 종료→홈·재청취의 실제 소스 시작/정지 및 네트워크 집계:
[desktop runtime](evidence-v0.2.3/browser/desktop-runtime-audit.json),
[mobile runtime](evidence-v0.2.3/browser/mobile-runtime-audit.json).
초기 browser suite 24/24 통과 후 console/requestfailed 검사를 강화해 missing favicon을 찾았다.
그 실패는 `18-browser.log`에 남기고, 최종 검증은 별도 로그로 구분했다. 오류를 숨기거나 예외 목록으로 제외하지 않았다.
404/잘못된 WAV 주입 테스트의 의도적 실패 응답은 정상 흐름의 오류 0 집계와 구분한다.
## 실제 검증의 한계와 인계
- Chrome은 실제 WAV를 디코드하고 실제 AudioContext/BufferSource를 실행했다. 헤드리스 자동 검증은
스피커/헤드폰의 물리 음압, 한국어 청감, 개인의 난이도나 임상 효과를 보증하지 않는다.
- 녹음 장면은 primary 출처 설명으로 확인했고 짧은 연속 구간의 포맷/신호/해시/루프를 검증했다.
**사람이 여덟 구간을 직접 청취하여 음악·불필요하게 명료한 우연 대화가 없는지 판단하는 인수는 수행하지 않았다.**
자동 검사를 인간 청취 인증으로 주장하지 않는다. [샘플과 provenance](../docs/AUDIO_PROVENANCE.md)를 Neo가 검토할 수 있다.
- mobile은 system Chrome 360×800 viewport/touch emulation이다. 실제 Android/iOS 기기 및 Safari 검증은 별도다.
- SNR은 전체 버퍼 RMS에 대한 디지털 상대값이다. 현장음의 시간 변화 때문에 순간/주파수별 SNR은 다르다.
- 로컬 `dist`만 검증했다. production HTTPS/MIME/cache/서버 설정은 배포 후 Neo의 독립 검증 대상이다.
- 사용자 지시대로 commit/push/merge/tag/deploy는 미실행이다. acceptance의 Neo 릴리스 항목은 완료로 표시하지 않았다.
[DEPLOYMENT](../docs/DEPLOYMENT.md)에 feature push→main FF→annotated tag→fresh clone→commit-addressed 원자 배포/rollback 절차를 인계했다.
## 독립 리뷰 수정 — 2026-10-14
사용자가 요약한 두 독립 리뷰와 본 결과 문서를 읽고 release-hardening을 수행했다.
기존 uncommitted v0.2.3 작업을 이어서 수정했으며 commit/push/merge/tag/deploy는 하지 않았다.
새 실패 테스트를 먼저 실행하고 생산 코드/검증기를 수정했다. 이전 evidence 로그는 수정하지 않았다.
신규 기록은 모두 [review-fixes/](evidence-v0.2.3/review-fixes/) 아래에 있다.
### 수정 내용과 동작
1. 진행 막대는 답변 수가 아닌 **현재 문항 1/3 → 2/3 → 3/3**이다. 첫 재생·답변 전·피드백과
다음 라운드에서도 일관된다. 접근성 이름/값, native progress 위치, 실제 PNG 채움 픽셀까지 검사한다.
2. 결과/README/설계/CHANGELOG의 점수 설명은 **정답마다 단계×100점 + 연속 정답 보너스**,
**문항별 최대 20점, 라운드 합계 0+10+20=30점**이다. 1단계 세 정답의 **330점**을 실제 UI 흐름으로 검증했다.
3. 응답이 없으면 정답률은 **— / 응답 없음**이며 0%나 0/0을 보여 주지 않는다. 응답 없는 단계는 **미진행**이다.
중도 종료 제목은 **문제 풀이를 중간에 종료했어요.**, 정상 결과는 **N단계 문제 풀이를 마쳤어요.**로 구분한다.
4. 실제 Chrome RED에서 `입력`, 결과의 `만나요`/`최대` 등 어절 분리를 검출했다.
모드 설명과 결과 영역에 keep-all을 적용하고 줄바꿈을 허용했다. 320px/360px에서 어절 내부 Range 줄 분리와
요소/문서 overflow를 검사하며 시작·정상 결과·중도 결과 캡처를 남겼다.
5. README 현재 증거 경로를 `.agent/evidence-v0.2.3/review-fixes/`로 바로잡고 일본어 reducer 테스트 제목을 한국어로 바꿨다.
6. build verifier는 앱 콘텐츠의 10 patterns×10 words+intro로 **정확한 101개 이름**을 독립 구성한다.
manifest 정확히 101개, 중복 없는 예상 inventory, dist/audio의 추가/누락 파일을 모두 검사한다.
양쪽 manifest와 디렉터리를 함께 비우기/축약/중복/이름 변경해도 실패하며 정상 사본은 통과한다.
7. 승인 계획과 배포 manifest의 `verifiedLocalSource` 및 모호한 `sourceDurationSeconds`를 제거했다.
`expectedSourceSha256`, `sourceBytes`, `sourceVerification`과 **directUrl 바이트의 ffprobe format.duration**인
`directMediaDurationSeconds`를 보존한다. Freesound preview MP3의 길이와 primary 페이지 표시 길이를 혼동하지 않는다.
파생 WAV의 `duration`/`conversion.durationSeconds`는 별도로 12초다. 기존 source-integrity 증거와 대조했다.
8. 생성기는 **첫 변환 전에 여덟 원본 전부**의 승인 해시/bytes를 확인한다. 불일치/누락/중복/잘못된 목록은 실패하고,
현재 파일 값으로 승인 계획을 덮어쓰지 않는다. 실제 원본 8개도 새 승인 필드로 재검증했다.
9. 현장음 notice를 **public/ambience/AMBIENCE_NOTICES.md**로 옮겼다. 같은 파일시스템의 staging에서
8 WAV·manifest·notice와 실측 PCM을 전수 검사한 다음 Linux **renameat2(RENAME_EXCHANGE)** 한 번으로 교체한다.
최초 생성은 rename 한 번이다. overlay를 없애 stale 파일을 제거하고 정상/dangling symlink 출력을 거부한다.
모든 변환 위치의 실패/KeyboardInterrupt, manifest/notice 쓰기 실패, staged 변조, 교환 실패/미지원,
commit 전 SIGTERM에서 이전 디렉터리를 보존한다. 실제 교환 도중 디렉터리 fd로 읽은 세 파일이
같은 세대인 것도 확인했다. UI 링크·build verifier·권리·배포·재현 문서를 함께 수정했다.
10. 모든 배포 notice 항목에 **creator, primary page URL, direct download URL, license URL,
outputLicense, 명시적 Changes**가 있어야 한다. 다른 항목에 공통 URL이 남아도 누락을 감추지 못한다.
8개 항목×6개 필드 제거와 public/dist 동시 metadata 변조를 build verifier에서 거부한다.
11. CHANGELOG와 본 문서에 실제 수정/검증을 반영했다. **109 WAV 재생성 0 / 바이트 변경 0**이다.
### 새 RED → GREEN 원문
| 계약 | RED | GREEN |
| --- | --- | --- |
| 진행률·330점·보너스·무응답·종료/완료 문구·README | [01 RED](evidence-v0.2.3/review-fixes/01-ui-RED.log) | [01 GREEN-02: 27 통과](evidence-v0.2.3/review-fixes/01-ui-GREEN-02.log) |
| 실제 desktop/mobile 진행률·320/360px 어절·overflow | [02 RED: 4 실패](evidence-v0.2.3/review-fixes/02-browser-RED.log) | [02 GREEN: 4 통과](evidence-v0.2.3/review-fixes/02-browser-GREEN.log) |
| manifest/폴더 동시 축약·추가/누락 | [03 RED](evidence-v0.2.3/review-fixes/03-voice-inventory-RED.log) | [03 GREEN](evidence-v0.2.3/review-fixes/03-voice-inventory-GREEN.log) |
| durable 승인 원본 메타데이터 | [04 RED](evidence-v0.2.3/review-fixes/04-source-plan-RED.log) | [04 GREEN](evidence-v0.2.3/review-fixes/04-source-plan-GREEN.log) |
| 첫 변환 전 전수 원본 검사 | [05 RED](evidence-v0.2.3/review-fixes/05-source-guard-RED.log) | [05 GREEN](evidence-v0.2.3/review-fixes/05-source-guard-GREEN.log) |
| staging·notice·symlink·검증·원자 교환·실패/중단 보존 | [06 RED](evidence-v0.2.3/review-fixes/06-atomic-RED.log) | [06 GREEN](evidence-v0.2.3/review-fixes/06-atomic-GREEN.log) |
| notice 동시 발행 경로/UI 링크/항목 내용 | [07 RED](evidence-v0.2.3/review-fixes/07-notice-path-RED.log) | [07 GREEN](evidence-v0.2.3/review-fixes/07-notice-path-GREEN.log) |
| 항목별 필수 notice 강제 | [08 RED](evidence-v0.2.3/review-fixes/08-notice-verifier-RED.log) | [08–09 GREEN: 27 통과](evidence-v0.2.3/review-fixes/08-09-build-GREEN.log) |
| manifest 사본 동시 출처/라이선스/변경 변조 | [09 RED](evidence-v0.2.3/review-fixes/09-build-plan-RED.log) | [08–09 GREEN](evidence-v0.2.3/review-fixes/08-09-build-GREEN.log) |
RED 로그는 exit 1, 표에 연결한 GREEN은 exit 0이다. 필터 실행의 skipped는 최종 전체 실행과 구분한다.
`01-ui-GREEN.log`는 이름과 달리 **exit 1**인 중간 실행이다. 새 계약은 통과했지만 기존 통합 테스트의
이전 제목 regex가 남아 실패했고, 그것을 고친 `01-ui-GREEN-02.log`가 실제 GREEN이다. 실패 로그도 보존했다.
초기 RED/GREEN browser PNG를 사후 픽셀 분석한 [RED](evidence-v0.2.3/review-fixes/02-progress-pixels-RED.log) /
[GREEN](evidence-v0.2.3/review-fixes/02-progress-pixels-GREEN.log)도 보존했다. desktop 1096px 막대는
365→731→1096px, mobile 320px 막대는 107→213→320px 채워졌다. 같은 픽셀 검사를 최종 브라우저 테스트에도 넣었다.
추가 동시 읽기/미지원 교환 검증은 [10-atomic-additional](evidence-v0.2.3/review-fixes/10-atomic-additional.log)에 있다.
### 리뷰 수정 후 전체 검증
| 실행 | 실제 결과 | 원문 |
| --- | --- | --- |
| npm run lint | PASS, exit 0 | [lint](evidence-v0.2.3/review-fixes/11-lint.log) |
| npm test | 13 files / **118 passed**, skip 0, exit 0 | [unit](evidence-v0.2.3/review-fixes/11-unit.log) |
| python3 -m unittest discover -s tests/python -v | **32 passed** (neural 18 + ambience 14), exit 0 | [Python](evidence-v0.2.3/review-fixes/11-python.log) |
| npm run build | PASS, 101 voice + 8 ambience + runtime/ambience notices, exit 0 | [build](evidence-v0.2.3/review-fixes/11-build.log) |
| npm audit --json | **취약점 0**, exit 0 | [audit](evidence-v0.2.3/review-fixes/npm-audit.json) |
| sandboxed system Chrome desktop/mobile | **30 passed / 0 failed / 0 skipped / 0 flaky**, 4.0분, exit 0 | [browser log](evidence-v0.2.3/review-fixes/12-browser.log), [JSON](evidence-v0.2.3/review-fixes/browser-results.json) |
[보존 검사](evidence-v0.2.3/review-fixes/preservation.json): 리뷰 시작 시점의 WAV 109개와 이전 `.agent` 파일
268개 모두 해시 동일하다. Git 추적된 v0.1/v0.1.1 `.agent` 파일 180개도 HEAD와 동일하다.
[원본 재확인](evidence-v0.2.3/review-fixes/source-reverification.json): 승인 원본 8개와 배포 bundle을 검증했고 변환은 호출하지 않았다.
Chrome **146.0.7680.80**, desktop 1280×1000/mobile 360×800에 추가 320px/360px copy 검사를 수행했다.
실행 중인 이 테스트의 Chrome 프로세스 20개에 `--no-sandbox`가 없음을 [실제 process 기록](evidence-v0.2.3/review-fixes/chrome-sandbox.json)으로 확인했다.
원래 AudioContext/BufferSource를 실행한 109 WAV 해시·decode 검사와 8개 실제 ambience loop, 오류 재시도,
키보드/axe/외부 요청/콘솔 검사에 새 진행률 픽셀·한국어 어절 검사를 더했다.
[desktop 오디오](evidence-v0.2.3/review-fixes/desktop-audio-integrity.json),
[mobile 오디오](evidence-v0.2.3/review-fixes/mobile-audio-integrity.json),
[desktop runtime](evidence-v0.2.3/review-fixes/desktop-runtime-audit.json),
[mobile runtime](evidence-v0.2.3/review-fixes/mobile-runtime-audit.json).
| 최종 화면 | Desktop | Mobile |
| --- | --- | --- |
| 시작 | [PNG](evidence-v0.2.3/review-fixes/screenshots/desktop-start.png) | [PNG](evidence-v0.2.3/review-fixes/screenshots/mobile-start.png) |
| 게임 | [PNG](evidence-v0.2.3/review-fixes/screenshots/desktop-game.png) | [PNG](evidence-v0.2.3/review-fixes/screenshots/mobile-game.png) |
| 330점 정상 결과 | [PNG](evidence-v0.2.3/review-fixes/screenshots/desktop-normal-results.png) | [PNG](evidence-v0.2.3/review-fixes/screenshots/mobile-normal-results.png) |
| 무응답 중도 종료 | [PNG](evidence-v0.2.3/review-fixes/screenshots/desktop-early-exit.png) | [PNG](evidence-v0.2.3/review-fixes/screenshots/mobile-early-exit.png) |
### 리뷰 후에도 남는 한계
**사람이 여덟 현장음 구간을 청취하여 음악/우연 대화와 적절성을 판단하는 인수는 수행하지 않았다.**
자동 PCM/Chrome 재생 검증을 인간 청취 승인으로 주장하지 않는다. mobile은 Chrome viewport/touch emulation이며
실제 Android/iOS/Safari, 물리적 음압 또는 임상 효과 검증은 아니다.
원자 commit 전 실패/중단은 이전 디렉터리를 보존하며 commit 뒤에는 완성된 새 트리가 공개된다.
강제 kill은 숨은 stage를 남길 수 있다. 전원 손실 내구성이나 여러 HTTP 요청에 걸친 snapshot 격리는 검증하지 않았다.
로컬 build/preview만 확인했으며 사용자 지시대로 Git commit/push/merge/tag 및 배포는 하지 않았다.
## 최종 privacy screening 재시도 — 2026-10-14
이전 market 60초/cafe 180초의 ASR-empty 실패를 새 구간으로 수정했다.
현재 **mall=36, market=24, cafe=0**, 최종 8 WAV의 segment 수는 모두 0이다.
세 필수 환경의 `duration_after_vad=0.0`, `requiredEmptyPassed=true`를 실제 inference로 확인했다.
**humanListeningAcceptance=false / legalPrivacyClearance=false**이며 ASR만으로 청취 인수를 주장하지 않는다.
### TDD·생성·이전 증거 보존
[01-release-RED](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/01-release-RED.log)는
구현 전 정확한 새 시작점, true gate, market/cafe 바이트 변경 조건에서 3개 실패했다.
[04-release-GREEN](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/04-release-GREEN.log)은
원자 재생성·실제 ASR 이후 같은 6개 테스트를 실행한 결과다.
테스트는 VAD/decoder 설정, manifest/WAV 해시, empty segment/text와 0.0 VAD duration,
청취/법적 인수 false, 이전 실패 상태·no-VAD 해시·모든 보존 파일 해시를 강제한다.
계획의 market/cafe 시작점만 24/0으로 바꾸고 원본 해시·bytes·직접 미디어 길이·권리 정보는 유지했다.
[02-regeneration](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/02-regeneration.log)과
[regeneration.json](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/regeneration.json)은
원본 전수 확인 후 기존 하드닝된 생성기로 전체 bundle을 staging/검증/원자 교환한 근거다.
생성기는 계획을 변경하지 않았고 notice와 manifest도 같은 구간으로 발행했다.
이번에는 **market/cafe 두 WAV만 변경**, 101 voice와 다섯 unaffected ambience 및 mall WAV는 바이트 동일하다.
이전 screening 파일 89개 전체를 읽기 전용
[prior-attempt-market60-cafe180](evidence-v0.2.3/privacy-screening/prior-attempt-market60-cafe180/)에 복사·해시 고정했다.
[이전 final-asr.json](evidence-v0.2.3/privacy-screening/prior-attempt-market60-cafe180/final-asr.json)은
`requiredEmptyPassed=false`, mall 0 / market 3 / cafe 1 segment 그대로다.
SHA-256은 `01c8a92fab9f8df7a5f778c5f7e478ce96bcedc33e11a1055bd29154a08b706b`이며 테스트에도 고정했다.
이전 source plan/manifest/notices/RESULT/docs의 사본과
[시작 시점 baseline](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/baseline.json)도 남겼다.
실패 로그를 지우거나 성공으로 고쳐 쓰지 않았다.
### 정확한 최종 ASR와 한계
[현재 final-asr.json](evidence-v0.2.3/privacy-screening/final-asr.json),
[03-final-asr.log](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/03-final-asr.log):
faster-whisper 1.2.1, tiny multilingual revision `d90ca5fe260221311c53c58e660288d3deb8d356`,
CPU int8/4 threads, beam_size=5, best_of=5, 기본 Silero VAD, condition_on_previous_text=true,
자동 언어 감지, random seed 0. 이전 실패 실행과 전체 decoder/VAD 인자가 같은지 대조했다.
모델/VAD·입력 WAV·manifest·실행 script 해시, 의존성·모든 기본 설정·원문 segment/info를 기록했다.
반환 segment 삭제나 threshold 튜닝 없이 각 현재 WAV를 검사했다.
[no-vad-asr.json](evidence-v0.2.3/privacy-screening/no-vad-asr.json)의 SHA-256은
`14b42ee0b80c194caa3c0de2644f647d6d7720c60c2208374778619f47c2e969` 그대로다.
그 결과는 이전 mall 36/market 60/cafe 180초에서 나온 환각 가능 text이며 새 market/cafe 검사가 아니다.
**no-VAD 결과는 지정된 VAD gate를 대체하거나 뒤집지 않는다.** 기존 scan/raw provenance도 바이트 보존했다.
빈 VAD 결과도 말소리·음악·개인 대화의 부재를 증명하지 않고 출력 text/language도 충실한 전사가 아니다.
독립적인 사람 청취 인증과 법적/privacy clearance는 수행하지 않았다. 제품 소유자 Oz는
2026-10-14 제공된 최종 WAV 8개 검수 묶음과 자동 검사 한계를 기준으로 **현재 자산 그대로
릴리스를 진행하도록 명시적으로 승인**했다. 이 제품 결정은 제3자의 법적/privacy clearance를
대신하지 않는다.
### 현재 자산 무결성
| WAV | 원본 구간 | RMS | peak | SHA-256 |
| --- | --- | --- | --- | --- |
| street.wav | 35–47s | 0.07999756 | 0.39675903 | `a8bffa201b174618955bc51b860167169125304811aabe2086b0bb35b06c3ae1` |
| mall.wav | 36–48s | 0.07999755 | 0.71917725 | `680a769940c08f0d12c6c954c08366598dd8b5745e276f39c82b10d1a88261a1` |
| theater.wav | 45–57s | 0.07999757 | 0.49508667 | `2d6a444fb7874cdf6b2d8a741a69175299b112eb66834912c025437d8277d635` |
| department.wav | 90–102s | 0.07999758 | 0.47497559 | `aa43f3d1d3e7c2b1f8ace24ecbda45d0ea3a031db941c39506dc6c1665b8efa7` |
| market.wav | 24–36s | 0.04309857 | 0.84997559 | `d6f73d86aea98b83e2995e61baaa7315a0ea7adeebc093769d76e1569b207658` |
| subway.wav | 65–77s | 0.07999755 | 0.42529297 | `9faf70b68617d267b367a0f3692ed90015458543ab4b0844f6c704e6a724728f` |
| cafe.wav | 0–12s | 0.07999754 | 0.40573120 | `6853c121104940041c268a2891eadae032c17aa22b4c3b3ddfbc025262434995` |
| rain.wav | 180–192s | 0.01138463 | 0.10241699 | `f4315084e820f742b0709de5bb0d1abd3d5af1730620e08c2948b70347356dd9` |
### 재시도 전체 검증
| 검사 | 실제 결과 | 근거 |
| --- | --- | --- |
| lint / typecheck | PASS, exit 0 | [05-lint](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/05-lint.log) |
| 전체 unit | 13 files / **125 passed, 0 failed, 0 skipped**, exit 0 | [05-unit](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/05-unit.log) |
| 전체 Python | **36 passed**, exit 0 | [05-python](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/05-python.log) |
| build | PASS, 101 voice + 8 ambience 및 notices, exit 0 | [05-build](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/05-build.log) |
| npm audit | **취약점 0**, exit 0 | [audit](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/npm-audit.json) |
| sandboxed Chrome desktop/mobile | **30 passed / 0 failed / 0 skipped / 0 flaky**, exit 0 | [06-browser](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/06-browser.log), [JSON](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/browser-results.json) |
현재 자동 검증은 모두 통과했다. [validation-summary](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/validation-summary.json)는 위 실행들의
실제 exit code·결과를 확인한 뒤 작성했으며 사람의 인수/법적 clearance를 포함한 전체 승인으로 해석하지 않는다.
현재 ASR gate 통과를 이전 실패의 통과로 바꾸어 기록하지 않았다.
Chrome 146.0.7680.80, desktop 1280×1000 / mobile 360×800 touch emulation의 전체 suite는
240.7초 걸렸다. 이 runner의 Chrome 자손 process 4개에서
`--no-sandbox`가 없음을 [실제 process 기록](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/chrome-sandbox.json)으로 확인했다.
설정은 `chromiumSandbox=true`이고 opt-out 환경변수는 제거했다.
양쪽에서 현재 **109 WAV response SHA-256/bytes·실제 decode·길이·채널·sample rate**를 전수 확인했다.
[desktop 오디오](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/desktop-audio-integrity.json), [mobile 오디오](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/mobile-audio-integrity.json),
[독립 실측 대조](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/browser-integrity-summary.json): hash/bytes 정확히 일치,
RMS/peak 차이는 브라우저 float scaling을 고려한 PCM16 한 step 이내다.
정상 요청에서 외부 요청·console/page 오류·failed request는 모두 0이며,
실제 여덟 ambience loop, 오류 재시도, UI·키보드·axe, 320/360px 어절·진행률 픽셀 검사도 통과했다.
[desktop runtime](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/desktop-runtime-audit.json), [mobile runtime](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/mobile-runtime-audit.json).
브라우저는 첫 전체 실행에서 통과했다. 이후 artifact 수집의
[08 실패](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/08-browser-integrity.log)는
runtime attachment를 별도 파일로 예상했으나 Playwright JSON 안의 base64 body였기 때문이다.
초기 수집 script와 실패 로그를 보존했고, body 원문을 추출한
[09 성공](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/09-browser-integrity.log)에서
양쪽 109개 WAV와 runtime audit를 대조했다. 테스트 결과를 수정하거나 재분류하지 않았다.
| 현재 화면 | Desktop | Mobile |
| --- | --- | --- |
| 시작 | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/desktop-start.png) | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/mobile-start.png) |
| 게임 | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/desktop-game.png) | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/mobile-game.png) |
| 정상 결과 | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/desktop-normal-results.png) | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/mobile-normal-results.png) |
| 중도 종료 | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/desktop-early-exit.png) | [PNG](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/screenshots/mobile-early-exit.png) |
[보존 검사](evidence-v0.2.3/privacy-screening/retry-market24-cafe0/preservation.json): 101 voice·다섯 unaffected ambience·mall 바이트 동일,
이전 `.agent` 파일 484개·읽기 전용 prior-attempt 사본 89개·HEAD 추적 역사 파일 180개 해시 동일.
갱신 허용된 plan/RESULT/current ASR/current summary 4개는 이전 상태를 별도 보존했다.
원본 provenance는 지정 market/cafe 시작점 외에 변경이 없고 dist는 현재 bundle과 바이트 동일하다.
**한계와 인계:** 지정된 자동 VAD acceptance를 충족했다. 사람의 청취 인수와 법적/privacy clearance는
여전히 수행하지 않았으며 empty ASR은 말소리/음악의 부재 증거가 아니다.
mobile은 Chrome viewport/touch emulation이다. Git commit/push/merge/tag/deploy는 하지 않았다.
+59
View File
@@ -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 위험을 기록한다.
+35
View File
@@ -0,0 +1,35 @@
# Find-Word v0.2.3 구현 작업
## 기준
- canonical repository: `https://source.ibit.kr/OZ-Group/Find-Word`
- 기준 커밋: `c92660b514f7af8eb556d3fa5c0fc80e4bdd4cf5`
- 작업 브랜치: `feat/v0.2.3`
- 제품 버전: `0.2.3`
- 배포 대상: `https://findword.heart-m.com/`
- 정확한 credit: `Designed by OZ at Heat-T Medical Inc.`
- 프로젝트 라이선스: private / all rights reserved / `UNLICENSED`
## 사용자 요구사항의 실행 해석
1. 각 라운드는 정확히 3문항이다.
2. 한 라운드의 자동 난이도는 3문항 내내 동일하다. 정상 완료 결과 화면에서 다음 난이도 버튼을 누르면 바로 다음 단계의 새 3문항 라운드가 시작된다. 5단계에서 완료하면 같은 단계 재도전 또는 처음 화면으로 돌아간다.
3. 시작 화면에서 시작 난이도(1~5)를 선택한다.
4. 사용자가 배경 대비 목표 음성 비율을 직접 조정할 수 있도록 SNR dB 제어를 제공한다. 게임 중 변경은 현재 재생 전/재청취와 이후 라운드 문항에 적용하며 자동으로 문항마다 변하지 않는다. 범위와 step은 안전하고 명시적으로 제한한다.
5. 진행 중 `문제 풀이 종료`를 누르면 현재 오디오를 즉시 중단하고, 지금까지 답한 문항 수와 정답 수를 보여 주는 중도 종료 결과 화면을 표시한다. 사용자가 `처음 화면으로`를 눌러 초기 화면으로 돌아간다. 무응답 문항을 오답으로 세지 않는다.
6. 기존 procedural white-noise 환경음을 제거하고, 길거리·쇼핑몰·극장·백화점·시장·지하철·카페·비 오는 거리의 실제 현장 녹음 8개를 정적 자산으로 번들한다. 환경마다 서로 다른 녹음을 사용한다.
7. 현장음은 공개·수정·재배포가 허용된 라이선스만 사용한다. 원본 URL, 직접 다운로드 URL, 저작자, 라이선스, 변환, 원본/배포 해시와 attribution을 manifest와 문서에 기록한다. NC/ND/불명확한 자산은 금지한다.
8. 배경음은 24kHz mono PCM16 WAV로 통일하고, 재생 시 RMS를 정규화한다. 목표 음성과의 상대 SNR이 사용자의 dB 설정을 따른다. 짧은 자산은 click 없이 loop하고 목표 음성 종료 때 정리한다.
9. 앱 런타임은 same-origin 정적 자산만 요청하며 외부 네트워크, 분석, 추적, runtime TTS를 추가하지 않는다.
10. `CHANGELOG.md`를 버전 히스토리 파일로 생성하고 v0.2.3의 모든 변경, 오류 수정, 자산/라이선스, 테스트와 배포 관련 변경을 기록한다. 이전 v0.1.1/v0.1.0의 핵심 이력도 보존한다.
11. 버전 문자열을 package, lock, HTML title, UI, README, manifest/문서와 빌드에 일관되게 `0.2.3`으로 반영한다.
12. 직접 작성·수정한 소스 코드에는 모듈, 함수, 클래스, 핵심 변수·매개변수, 비자명한 로직의 목적과 이유를 설명하는 상세한 한국어 주석을 단다. 타입 선언·자명한 JSX 태그마다 형식적 주석을 반복하지 말고 설계 의도를 설명한다.
## 개발 방식
- strict TDD: 각 동작의 실패하는 테스트를 먼저 실행해 RED를 남긴 뒤 최소 구현으로 GREEN을 확인한다.
- 과거 `.agent/evidence-v0.1.1`과 `.agent/RESULT*.md`는 수정하지 않는다.
- 새 evidence는 `.agent/evidence-v0.2.3/`, 결과는 `.agent/RESULT-v0.2.3.md`에 기록한다.
- production 코드 변경 전 테스트를 작성한다.
- 기존 100개 목표 음성과 게임 콘텐츠, 개인정보 미수집, 접근성, 정확한 credit을 보존한다.
- Codex는 commit, push, merge, tag, production 배포를 하지 않는다. Neo가 독립 검증 후 실행한다.
+44
View File
@@ -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
+7
View File
@@ -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
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+305
View File
@@ -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 바이트
+7
View File
@@ -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%)
+3
View File
@@ -0,0 +1,3 @@
> find-word@0.1.1 lint
> eslint . && tsc --noEmit
+1
View File
@@ -0,0 +1 @@
found 0 vulnerabilities
+15
View File
@@ -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
}
+15
View File
@@ -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 전문 확인
+77
View File
@@ -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>
+3
View File
@@ -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"
}
+12
View File
@@ -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"
}
File diff suppressed because it is too large Load Diff
+738
View File
@@ -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"
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,259 @@
{
"head": "a92b2ba1e973c65a57e6e34cc52568cd6b208c1e",
"files": {
".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/evidence-v0.1.1/00-audio-venv.log": "365f1b3cf48e7f6de9c24d28e4326a245d3a54eb329af4b41a9441ec546dabdc",
".agent/evidence-v0.1.1/00-npm-ci.log": "14a6e676c84fa9b2cc39a251aef0c593cda9903f9e5f2d9581bc4153c9f69e09",
".agent/evidence-v0.1.1/01-version-docs-red.log": "edb26b949d0dc28340989601e781df3582cf784220235d19c16a46aeee97d302",
".agent/evidence-v0.1.1/02-assets-red.log": "ccb8a354e2975c944fb2bde24492fe06a6c1b71add6ce65259d840c66836a1db",
".agent/evidence-v0.1.1/03-generator-red.log": "d78b7c9237c18ba8a87a77a116ea7561ae79af6e88718f9c087c1d8e2c70b1f1",
".agent/evidence-v0.1.1/04-generator-additional-red.log": "9a519598915fbdd98108ee1b364f634905e29a5a01bfdd7ffec22a8b359e9217",
".agent/evidence-v0.1.1/05-generator-green.log": "414464526a325b424e5c5305e3066b79c55527fb67b11a3b9eb3bb33dcecea93",
".agent/evidence-v0.1.1/06-audio-generate.log": "7b605184e89fd033378e6869236553a3d6e040087de23e8ae165f1e304f3b067",
".agent/evidence-v0.1.1/07-format.log": "ce1a102d13a7dfd78130216cea192e3230efa3612e999cb8a6bfd55e8eb6d471",
".agent/evidence-v0.1.1/08-version-docs-green.log": "671c2889a69c83ad24293d8e92d0076255bd00c5100c2a9eb764a570e1344edd",
".agent/evidence-v0.1.1/09-lint.log": "43d5238bc8e59b93cf3a9bc0f444f0165697b5584078973bd92efce056136c1e",
".agent/evidence-v0.1.1/10-audit.log": "6d8c5c8f3d7684adb070417bd608d01ae90aa3dc26a65af03ffda4955f38d9a3",
".agent/evidence-v0.1.1/11-full-test.log": "65721fcf2b03f153274c9cc1da13c6a1f265ab6f084be8b5921a898fbfd6b40b",
".agent/evidence-v0.1.1/12-independent-audio.log": "5ffd6173941d75deb028741cdba549705b652218f418c6d40ceb7f4b216f2d9d",
".agent/evidence-v0.1.1/13-build.log": "42ac41188359699f5f24c7f86de9d7ca6503d714a7be9e277ac2fd01be21614c",
".agent/evidence-v0.1.1/14-browser.log": "657ddc9f60ffa35684110766b2e459842cd1ab10a90b0002256a945f227a16b7",
".agent/evidence-v0.1.1/15-browser-audio.log": "cb16ec7009a88f911632d0bef64692066873d81372e65fea8dac800d59304214",
".agent/evidence-v0.1.1/16-final-lint.log": "43d5238bc8e59b93cf3a9bc0f444f0165697b5584078973bd92efce056136c1e",
".agent/evidence-v0.1.1/17-security-history.json": "5ba66683c3ae29e01f25cfae2ed832e33b07f866bfccd0e1d331a8f102d3d3c7",
".agent/evidence-v0.1.1/18-environment.log": "19a1da4298ba5a51f8dce5e6669205987bb48438e5c98f3ddcbc347ce0f91e46",
".agent/evidence-v0.1.1/19-final-integrity.json": "38f5af8927d95e9dae780decf146eb3d5feb610629202ef9ee67bf223647132f",
".agent/evidence-v0.1.1/audio-integrity.json": "d82b192602b1f054fce5f61479832666f237d7da2698afe84b09186dac90f321",
".agent/evidence-v0.1.1/baseline.json": "085be97acb81dcc83477ebc5f42dd2fd5729d9f5821cbc659df9c00b648ceb34",
".agent/evidence-v0.1.1/browser-audio-integrity.json": "2c0f59ed178285c0d338171328232351aab2b55e22371b3652453afa34849b65",
".agent/evidence-v0.1.1/changed-files.txt": "4877f3d1003372868b5a9f72326ccf0870b22daf35a71b60b67dc761342fa50f",
".agent/evidence-v0.1.1/desktop-game.png": "02c59fa37c4d4f19e5c74e56fc889105b4d280a0a4cf2c14c1838947859a3a8b",
".agent/evidence-v0.1.1/desktop-heading.png": "920d3d998d93f463be9ac6a682c1f7d5d9ac36944b52b715345c865ae46f566d",
".agent/evidence-v0.1.1/desktop-home.png": "9c6f92b67db249b2a1d3e752052678d7ae0af6be426d976d12237d84c105bf76",
".agent/evidence-v0.1.1/desktop-results.png": "3811eae6f5827760499c9c64053a2cb2ecbb1d87ed2ddf11b757eb44e56c64bd",
".agent/evidence-v0.1.1/mobile-game.png": "bb0dec7ec2c99b55caafd729e2f9c9c01875e3b6dd577cc377a76bbfd5c95a7e",
".agent/evidence-v0.1.1/mobile-heading.png": "d61f2d1b01c654a5cfe841e49e244af4e232b5fc9c899e77523b336411e99316",
".agent/evidence-v0.1.1/mobile-home.png": "7c4a4643a5222f45c6492ebf78780a317e26d423bf244fc16ea24a186a76f506",
".agent/evidence-v0.1.1/mobile-results.png": "f2a095ab1d2a3a95a1a275b652fcd31275a5803a926e01b5fcd1e9a9e127cfb9",
".agent/evidence-v0.1.1/verify_audio.py": "7751c03b83dbbd3b222f6d5a68eb1d78dac2bbf82c0fd79345ccca86a15fcb69",
".agent/evidence-v0.1.1/verify_browser.mjs": "53f0f6317274a91017a4f508ef9237e022f7d333ef4243f324720f8d7f85abeb",
"public/audio/correction-apple.wav": "ccd7327a3fb54d3320d049ffdced3bc17595861cee69429c0ee97182d454195b",
"public/audio/correction-furniture.wav": "5c004a7b2f5a4a918c9f689f1a2dcb219a620ed14313f40a9655b36dbf64948d",
"public/audio/correction-hat.wav": "f9f98c91cfbcfcb106eef7d5d3795a9cf3c3aab7e19a60f13d7dffe284d19f86",
"public/audio/correction-market.wav": "b8d25b867497adcd0fe7c8b6a8a8d2e7a52965956738a83e53f58735708773f9",
"public/audio/correction-meat.wav": "48e91b3932d3798e852041a854b3ee08c72127e092c62083a47af6b99adff241",
"public/audio/correction-oil.wav": "f6eff0cdddcad9790351491e60c53cb2a70d222033a583652f2de0b8cd6abcc7",
"public/audio/correction-sea.wav": "9233431d78ab4b144e3c33ad0cf0d49fc0fb91b29514343230ba32987b794bb3",
"public/audio/correction-sound.wav": "5a603bc69979603afddcaeb6edf500a7c540b72405daeb33f5335d746b327da1",
"public/audio/correction-tree.wav": "b8e8bbe6b8b14727efd953fae9d4aed2caeb004055d52f611c6378322847edbe",
"public/audio/correction-umbrella.wav": "92b54f04884ab915783945077efd8f3bd443849a1720f72168b6348cae1f8e85",
"public/audio/first-apple.wav": "7c1dbcb3082055df2b8f087f7e906ec867c92f679de0894c34740c05e61a4bc1",
"public/audio/first-furniture.wav": "29f007b385b7182db1d8aeccad4fc3c260df5e66e14e52a46b0ef0fcd0ff3a0a",
"public/audio/first-hat.wav": "17c8cde90567e7574b0c0702663bd70713054089ee920241b5444d9b7869eef5",
"public/audio/first-market.wav": "1e00b8f70a796cdebb0feb00c605fece7488b06915e6e036f54206f05e201e0c",
"public/audio/first-meat.wav": "f51a37c1722fef581f7d642a4dcae7fcb9953b3c318dce971bf616cd1a81fecc",
"public/audio/first-oil.wav": "f415195e0cfc7ee711d09de7519e88eab62cc8b58d70772c83c3f5c27c267819",
"public/audio/first-sea.wav": "0f0b39314c5f7d7168d4854003c0c284e7bc3438cdc4d1d1c5adab7cb5ed30e5",
"public/audio/first-sound.wav": "46f6adeed928a16af0700f1e8626cf8049f124632a04a5d10604426fb06f9596",
"public/audio/first-tree.wav": "3178230fd5850b6277a2153b438ec5289720e3db7ac929914cc213964f004bd5",
"public/audio/first-umbrella.wav": "878e0bd2e6e4105f175aef51a028f6b141f3a2072be20f783193f3ba897a3ee0",
"public/audio/intro.wav": "90d9f46e6e3f9c8a0c1a781096df54d7809ef4ad38b289508ef7b8c0411465ce",
"public/audio/last-apple.wav": "4cb2a8579fa3a7b91bb36363826ce8ca0d6d86e96e16a479e226a2802edd82f7",
"public/audio/last-furniture.wav": "5ad4f776e35439d408abed0e66567d6b5cdc80fc54d579165472e474f1238776",
"public/audio/last-hat.wav": "bdaf8fbc54df8061e9e58aa8c14b3044a6e863dc54973ee3ee6ae2c5ae1a28f1",
"public/audio/last-market.wav": "7857e87e9dbfe8fe849cc0b18c1d45b60916cb337328099c7a87a42c82e2821e",
"public/audio/last-meat.wav": "10f8ad4259ed8945c9da831e80b3902303c09caef96d400298599168fb1efed4",
"public/audio/last-oil.wav": "b5ce34950b8b31b1baf6ce55410797f0d8aa5478a670d6eace87cf76d134d6d3",
"public/audio/last-sea.wav": "f755452e1da44674aa1df7d12edffa988091b89a394e7eb6d4a00eb721f07cbf",
"public/audio/last-sound.wav": "ec9c49802aebeb6a8b898a1c0d8ad1daa127d2d64c905c197ed49a8e0c4eb64a",
"public/audio/last-tree.wav": "589e56d940a3c9e80ec02ae0b2dd93463fdc85ef019a753fcd0fded0752055f9",
"public/audio/last-umbrella.wav": "c801293a34013a8d93d3289a2cc87cd3eed83f97ba4647f5060efa00b117a9ad",
"public/audio/luck-apple.wav": "7c8f413f4b7e164f090e508ba090448f1376c6012649bbb6a917d45851313883",
"public/audio/luck-furniture.wav": "52cb92cdb8ee92b6f581c9d68dbc346f180c470564809ba57185429c9eb84127",
"public/audio/luck-hat.wav": "4ff43dd042f4b5451e9fdeb3edd59f1e389a7d1dd488094587a3a9fc7dfffa44",
"public/audio/luck-market.wav": "356af25332bf2000ed349671f3ff39767929e1f4599af622c021f907d11fb7bf",
"public/audio/luck-meat.wav": "d36dfc395a5c53fbfa4abe9980db7bb59adfdac59e40318c4aca365365e3f2ab",
"public/audio/luck-oil.wav": "3fa1e662a3af8f2726b17b2baa5bec2934406e6595ebc243c8380dd16e190977",
"public/audio/luck-sea.wav": "47aa31d757c9fb49c9b53a1c052d90a5ced4be57549d649850d9b48f8864045b",
"public/audio/luck-sound.wav": "34e4d9758d2ed448646fda364610e34779c84794403d9051ab3d8c1d33e2c80c",
"public/audio/luck-tree.wav": "122f496834fb16d6da47f801e50e46e272e4e934af959467d1db4e490f79be7a",
"public/audio/luck-umbrella.wav": "8d3e8e8bc97e5fe3f3362184c4abb12f01a882a1f1174d2644f0f70aac677c0a",
"public/audio/manifest.json": "ec4c6f3c86ba5083989a2da51150476a37b0a2d3152f202d1c82ed10a7aaa34b",
"public/audio/message-apple.wav": "d85e6f47d871369f00de2274139e5ad6b4b5686b5e20e248df0ed6b9a320d942",
"public/audio/message-furniture.wav": "26c331b7e12311236f4bb48f88918dfbf53cccd2f50b55741ea6ff4473e71dd8",
"public/audio/message-hat.wav": "5a713a8522b231bb8276805c014b385b9150b79f58ff2618446607e2b442495e",
"public/audio/message-market.wav": "9242d4cd6e0030e6b073e4d2360f84aa8a454d1749711cdb2d64c14f28a7ea20",
"public/audio/message-meat.wav": "ce19735df079b54c171e775b6b365385a80160b9033442abab95b2b9c8505e1a",
"public/audio/message-oil.wav": "a2a71a7e5e07c81871a25c6cb7540c39aa0b32fc44f78cda66b5f34ace98d805",
"public/audio/message-sea.wav": "28a461fbe03bffe2560030dea8b60715f7c0fe262dcf85b5b7f7daf483836382",
"public/audio/message-sound.wav": "1e682147dae8026ba78dc8f256c7f24acb3a6743bfd88a0fcc86096930eef078",
"public/audio/message-tree.wav": "5d684aa710a63d36ed8dcc2a8bfbdd5c71b0b1c63dad2a324adecb76591e85b8",
"public/audio/message-umbrella.wav": "41d5866dae66600df4694a8312b531571d44cece6dba0e423e7c43d09d184819",
"public/audio/middle-apple.wav": "8832f1b793ab3eedbe663875ea8dbeabb122c329bd5879437c71230b0418c3c8",
"public/audio/middle-furniture.wav": "b45b1afc8d9b534ff3e0a2a2ecb99efc1f4e40d41f89f55c4ed8ba94ad6d76d4",
"public/audio/middle-hat.wav": "b01ef9762ac84e3eb757243bc9286c62f7d45444a821ae35ac36a8b095e1598a",
"public/audio/middle-market.wav": "c855b449e0d4ace74681bfe68e5e7f0b063568827ae872017234dbe8ae119e4c",
"public/audio/middle-meat.wav": "d416107cc05e7c5b92ca27100a10a8a9689de913346179945d5542de4a6bac0c",
"public/audio/middle-oil.wav": "7a6b122b11c07da86c95fb78e2db2a70681b5219c63d6441fe56da1979ac1dc3",
"public/audio/middle-sea.wav": "b6dbe0af143ea775f05bd59a3d6af348bcc770ab378ac91ea75a56e06c326edf",
"public/audio/middle-sound.wav": "4d00aa6114327f450a00dacbc7e7b572ebf1de4ce9dc2fe4b5505766cca71ce9",
"public/audio/middle-tree.wav": "341888c75275c13eb31058f53a1d4276be0daa0a1b5a3a45522036928b83b0c1",
"public/audio/middle-umbrella.wav": "399ea2f681f755918f1117c8baba42739ceca64b307b999342f457a9235050b6",
"public/audio/mission-apple.wav": "3ef42e276ff3500c3e197d149f29f79d6a0c1d5516ed1d5a2c57210333fded9c",
"public/audio/mission-furniture.wav": "af82e03b686635c3356e706fe5b05c1231aeaaeab33f275aa5b8714988c65d43",
"public/audio/mission-hat.wav": "308278ec2bd27d4224976a55632d1f7fa1edabb0792126d9d858a699c287aee4",
"public/audio/mission-market.wav": "cefa0e95649ea6bd1df4c8dfccd2b74b4f4c028714420ce1087b61db2f2a8d47",
"public/audio/mission-meat.wav": "94f8a4f9e116a787e5ed052661d6de9ecf3ad8629db476661dd59ad03014b0c9",
"public/audio/mission-oil.wav": "5fb63e04520c312aa6b7b48b70c2f7c140b5ab480e5c6992edbc2ba85b9a06e7",
"public/audio/mission-sea.wav": "8838d6a1ee8a790427dc5aa761e70b3d8872af62086efeed743297ff7f8b8a06",
"public/audio/mission-sound.wav": "659eb0e2b06e6b165b46744769743e032e2282519f8486c57bc45e0f7d07a069",
"public/audio/mission-tree.wav": "00d8e77ec0371971d632724d91f8cf8d947bfb83c582a513b6421be4c457a1da",
"public/audio/mission-umbrella.wav": "4b4c858a25659540f9dbc524664496fc6de61ab8c1627f3cfee8c9c744e11d5b",
"public/audio/password-apple.wav": "b6f5de3fe571fe3a7fdb722fe49250bcfeedef49475271e0ec76a7630907edd5",
"public/audio/password-furniture.wav": "fb2b631dd4e12120d08a84cce6450f9cd9cd68105d13d7001d808e7deaa1df4a",
"public/audio/password-hat.wav": "e91bfc97df90a1675b6cd856880ac15c54de32cfc3b23603bdd1c2c5ac3ce6b8",
"public/audio/password-market.wav": "02e325fb753ef7a3ee585a03d2ca2aa3f1fbc58523b062222ae1136eeed7a95c",
"public/audio/password-meat.wav": "b009caadd56350d6a641ff17f0973e2b2724c183e1f45ae0bf3c483e237fa99e",
"public/audio/password-oil.wav": "b472957b27ff98ea7b8894fa1ff12653f7e9f66f77870faa9e52a1c1bf116c41",
"public/audio/password-sea.wav": "03f45b8cb5b3dc72faf5c6e3d0e2a10cea6b60301ea400d0a4453596d2f0b337",
"public/audio/password-sound.wav": "044aafff08a19e39f21e58b963644715a987cd155896998769ef8e9ce6449628",
"public/audio/password-tree.wav": "67da8147296e35134ec254bc82b58317630061b2c64d2d63c4be197ce8692d50",
"public/audio/password-umbrella.wav": "ab3ca6398c879971dc793eb25b472fcc0db72d76e386f11ef4d32c639f37c69d",
"public/audio/remember-apple.wav": "1bf2aef9a9f030af1a8fd7dee8f44aa420a397b3ba99b575feeb3dc7a63ec191",
"public/audio/remember-furniture.wav": "442bdcb956f120c270e1f321feae0415c897de0183e468980fcc119b898b2a67",
"public/audio/remember-hat.wav": "31a6a4270bf368cda419094d87a303ac3cd5c22539786cb165f8044b0b8e9d6e",
"public/audio/remember-market.wav": "d89792da51c83ac49d27e55aa6d822a338899a5ee487b34b552748201e1522f6",
"public/audio/remember-meat.wav": "9183ef90b64f5ceaf3bb0178c689231ff3e9981d6ce889124a15f2a6e2b7a344",
"public/audio/remember-oil.wav": "3ca98682ff06d1d6b8af02c0fd5b2124f42266ac989884223eff4a9bbde1287a",
"public/audio/remember-sea.wav": "5c91a36496e274d2be9c6fa8d9b506cd04f2a1b7ceb05ed1b7bb03fe7fca99d9",
"public/audio/remember-sound.wav": "98387ccc7aab566f6487d4c250b2128082cc527e013a6f7b7bc4d0266ac295df",
"public/audio/remember-tree.wav": "bc8e54f5407e8866bf1ca1ddfd4f6c63d85fee29bd9e430c7dd33e87974fc5b8",
"public/audio/remember-umbrella.wav": "c8988e604ba05d52597e561389541e8f9cda86e052279f1862bbf8399ead314e",
"public/audio/repeat-apple.wav": "cb7da4e9a1288cac64d168b241f27d7d2bc2dfecace58744f9bbf2792dfad04e",
"public/audio/repeat-furniture.wav": "bf84dc6cff1f227924a3a62158d2fc18769fb0ca40b91418fa19e534dbf92f00",
"public/audio/repeat-hat.wav": "6e089a42fd7e65e82eec7186d369c926fb7fdac794365b2bd7658822fd92437c",
"public/audio/repeat-market.wav": "68a5b899115a66ab225d610ec47b4de56cc8793657362bc38f533c06f741e4cc",
"public/audio/repeat-meat.wav": "6599d2fac73ea616a793151b4ce6f51af673988d630bac6219a77e78ae6cb34e",
"public/audio/repeat-oil.wav": "55b2c7d6aa8bbce04edf8661882c9781da81c57f56217f99b80457a3bb895da0",
"public/audio/repeat-sea.wav": "db2f235bee26ac804b5362e8e013b8f40c8da24fd3a34e0fb3b43c6d264feb36",
"public/audio/repeat-sound.wav": "e9d636ff7f4777f38b1ff15bebdcec6fbde95b4b4b05625a7415b4cfa0025b59",
"public/audio/repeat-tree.wav": "8735c3d9672561ca237fa87317beec2878492eb9063d6048d5b214423561acc1",
"public/audio/repeat-umbrella.wav": "5e59ef39113c3d5e106862356d715f4cef2862ef8ee02654a97e532675c1ac6d",
".agent/RESULT.md": "eef6d73a67a8a7e90cd7b4747debc5976cc51571bc268328634413f698d0e311",
"package.json": "41ba1d6c1e9f6350cf3bbc627b921839d67f7f9a20809d082f0c112ce7dbf631",
"package-lock.json": "9c64fc6a305196b84e39ac52405210b926c1431055724d82b41eafd21d542e5c"
}
}
@@ -0,0 +1,235 @@
UTC: 2026-10-14T07:20:59.789143+00:00
COMMAND: npm test -- tests/audio.test.ts tests/game.test.ts tests/audio-requests.test.ts
> find-word@0.1.1 test
> vitest run tests/audio.test.ts tests/game.test.ts tests/audio-requests.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1.1
❯ tests/audio-requests.test.ts (1 test | 1 failed) 28ms
× WAV 요청 판정은 query의 버전·순서와 무관하게 pathname을 사용한다 26ms
❯ tests/game.test.ts (8 tests | 1 failed) 92ms
❯ 출제와 정답 판정 (5)
× 목표 WAV 100개 모두 같은 명시적 cache-busting 버전과 정답 mapping을 유지한다 50ms
❯ tests/audio.test.ts (8 tests | 3 failed) 357ms
❯ 오디오 재생 수명과 믹서 (8)
× intro와 목표 실제 fetch URL에 같은 명시적 cache-busting 버전을 붙인다 101ms
× intro 404 후 intro·목표를 동일한 versioned URL로 다시 요청한다 47ms
× target 404 후 intro·목표를 동일한 versioned URL로 다시 요청한다 47ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 5 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/audio-requests.test.ts > WAV 요청 판정은 query의 버전·순서와 무관하게 pathname을 사용한다
AssertionError: /audio/intro.wav?v=0.1.1: expected false to be true // Object.is equality
- Expected
+ Received
- true
+ false
❯ tests/audio-requests.test.ts:12:74
10| "/audio/luck-apple.wav",
11| ]) {
12| expect.soft(isAudioRequest(new URL(path, "http://localhost")), pat…
| ^
13| }
14| for (const path of [
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/11]⎯
FAIL tests/audio-requests.test.ts > WAV 요청 판정은 query의 버전·순서와 무관하게 pathname을 사용한다
AssertionError: /audio/luck-apple.wav?v=0.1.1: expected false to be true // Object.is equality
- Expected
+ Received
- true
+ false
❯ tests/audio-requests.test.ts:12:74
10| "/audio/luck-apple.wav",
11| ]) {
12| expect.soft(isAudioRequest(new URL(path, "http://localhost")), pat…
| ^
13| }
14| for (const path of [
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/11]⎯
FAIL tests/audio-requests.test.ts > WAV 요청 판정은 query의 버전·순서와 무관하게 pathname을 사용한다
AssertionError: /audio/luck-apple.wav?retry=1&v=0.1.1: expected false to be true // Object.is equality
- Expected
+ Received
- true
+ false
❯ tests/audio-requests.test.ts:12:74
10| "/audio/luck-apple.wav",
11| ]) {
12| expect.soft(isAudioRequest(new URL(path, "http://localhost")), pat…
| ^
13| }
14| for (const path of [
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/11]⎯
FAIL tests/audio-requests.test.ts > WAV 요청 판정은 query의 버전·순서와 무관하게 pathname을 사용한다
AssertionError: /audio/luck-apple.wav?v=0.1.1&retry=1: expected false to be true // Object.is equality
- Expected
+ Received
- true
+ false
❯ tests/audio-requests.test.ts:12:74
10| "/audio/luck-apple.wav",
11| ]) {
12| expect.soft(isAudioRequest(new URL(path, "http://localhost")), pat…
| ^
13| }
14| for (const path of [
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/11]⎯
FAIL tests/audio-requests.test.ts > WAV 요청 판정은 query의 버전·순서와 무관하게 pathname을 사용한다
AssertionError: /audio/manifest.json?file=intro.wav: expected true to be false // Object.is equality
- Expected
+ Received
- false
+ true
❯ tests/audio-requests.test.ts:20:74
18| "/audio/intro.wav.txt?v=0.1.1",
19| ]) {
20| expect.soft(isAudioRequest(new URL(path, "http://localhost")), pat…
| ^
21| }
22| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/11]⎯
FAIL tests/audio-requests.test.ts > WAV 요청 판정은 query의 버전·순서와 무관하게 pathname을 사용한다
AssertionError: /other/intro.wav: expected true to be false // Object.is equality
- Expected
+ Received
- false
+ true
❯ tests/audio-requests.test.ts:20:74
18| "/audio/intro.wav.txt?v=0.1.1",
19| ]) {
20| expect.soft(isAudioRequest(new URL(path, "http://localhost")), pat…
| ^
21| }
22| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/11]⎯
FAIL tests/audio-requests.test.ts > WAV 요청 판정은 query의 버전·순서와 무관하게 pathname을 사용한다
AssertionError: /audio-backup/intro.wav: expected true to be false // Object.is equality
- Expected
+ Received
- false
+ true
❯ tests/audio-requests.test.ts:20:74
18| "/audio/intro.wav.txt?v=0.1.1",
19| ]) {
20| expect.soft(isAudioRequest(new URL(path, "http://localhost")), pat…
| ^
21| }
22| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/11]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > intro와 목표 실제 fetch URL에 같은 명시적 cache-busting 버전을 붙인다
AssertionError: expected [ '/audio/intro.wav', …(1) ] to deeply equal [ '/audio/intro.wav?v=0.1.1', …(1) ]
- Expected
+ Received
[
- "/audio/intro.wav?v=0.1.1",
- "/audio/correction-market.wav?v=0.1.1",
+ "/audio/intro.wav",
+ "/audio/correction-market.wav",
]
❯ tests/audio.test.ts:97:54
95| f.sources.at(-1)!.onended?.();
96| await done;
97| expect(f.fetcher.mock.calls.map(([url]) => url)).toEqual([
| ^
98| "/audio/intro.wav?v=0.1.1",
99| `/audio/${question.id}.wav?v=0.1.1`,
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[8/11]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > intro 404 후 intro·목표를 동일한 versioned URL로 다시 요청한다
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > target 404 후 intro·목표를 동일한 versioned URL로 다시 요청한다
AssertionError: expected [ '/audio/intro.wav', …(3) ] to deeply equal [ '/audio/intro.wav?v=0.1.1', …(3) ]
- Expected
+ Received
[
- "/audio/intro.wav?v=0.1.1",
- "/audio/correction-market.wav?v=0.1.1",
- "/audio/intro.wav?v=0.1.1",
- "/audio/correction-market.wav?v=0.1.1",
+ "/audio/intro.wav",
+ "/audio/correction-market.wav",
+ "/audio/intro.wav",
+ "/audio/correction-market.wav",
]
❯ tests/audio.test.ts:119:54
117| f.sources.at(-1)!.onended?.();
118| await done;
119| expect(f.fetcher.mock.calls.map(([url]) => url)).toEqual([
| ^
120| "/audio/intro.wav?v=0.1.1",
121| `/audio/${question.id}.wav?v=0.1.1`,
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[9/11]⎯
FAIL tests/game.test.ts > 출제와 정답 판정 > 목표 WAV 100개 모두 같은 명시적 cache-busting 버전과 정답 mapping을 유지한다
AssertionError: expected Set{ null } to deeply equal Set{ '0.1.1' }
- Expected
+ Received
Set {
- "0.1.1",
+ null,
}
❯ tests/game.test.ts:30:22
28| versions.add(url.searchParams.get("v"));
29| }
30| expect(versions).toEqual(new Set(["0.1.1"]));
| ^
31| });
32| it("실제 보기에 있는 경쟁 단어를 문장에 넣고 청취 연산별 순서를 유지한다", () => {
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[10/11]⎯
Test Files 3 failed (3)
Tests 5 failed | 12 passed (17)
Start at 07:21:00
Duration 1.91s (environment 66%, setup 13%, tests 11%, transform 6%, worker 2%, import 2%)
EXIT_CODE: 1
@@ -0,0 +1,19 @@
UTC: 2026-10-14T07:21:00.984991+00:00
COMMAND: npm run build
> 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 177ms
빌드 음성 자산 101개 및 런타임 notice 전문 확인
EXIT_CODE: 0
@@ -0,0 +1,148 @@
UTC: 2026-10-14T07:21:27.811844+00:00
COMMAND: env -u PLAYWRIGHT_NO_SANDBOX CHROME_PATH=/usr/bin/google-chrome npm run test:browser -- tests/browser/game.spec.ts tests/browser/audio.spec.ts --project=desktop
> find-word@0.1.1 test:browser
> playwright test tests/browser/game.spec.ts tests/browser/audio.spec.ts --project=desktop
[WebServer] (node:796454) 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 4 tests using 2 workers
(node:796474) 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:796475) 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)
✘ 1 [desktop] › tests/browser/audio.spec.ts:7:1 › intro·목표 101개 versioned WAV를 실제 Chrome에서 해시·디코드 검증한다 (1.8s)
(node:796732) 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)
✘ 2 [desktop] › tests/browser/game.spec.ts:8:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (3.0s)
✘ 3 [desktop] › tests/browser/game.spec.ts:124:3 › intro WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (805ms)
(node:796861) 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)
✘ 4 [desktop] › tests/browser/game.spec.ts:124:3 › target WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (666ms)
1) [desktop] › tests/browser/audio.spec.ts:7:1 › intro·목표 101개 versioned WAV를 실제 Chrome에서 해시·디코드 검증한다
Error: expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 1
Set {
- "0.1.1",
+ null,
}
30 | expect(targetUrls).toHaveLength(100);
31 | const urls = [introUrl, ...targetUrls.map((url) => new URL(url, page.url()).href)];
> 32 | expect(new Set(urls.map((url) => new URL(url).searchParams.get("v")))).toEqual(new Set(["0.1.1"]));
| ^
33 | expect(urls.every((url) => new URL(url).origin === new URL(page.url()).origin)).toBe(true);
34 | const manifest = JSON.parse(readFileSync("public/audio/manifest.json", "utf8")) as {
35 | version: string;
at /home/hermes/worktrees/find-word-v0.1.1/tests/browser/audio.spec.ts:32:74
Error Context: test-results/audio-intro·목표-101개-versioned-WAV를-실제-Chrome에서-해시·디코드-검증한다-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/audio-intro·목표-101개-versioned-WAV를-실제-Chrome에서-해시·디코드-검증한다-desktop/trace.zip
Usage:
npx playwright show-trace test-results/audio-intro·목표-101개-versioned-WAV를-실제-Chrome에서-해시·디코드-검증한다-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2) [desktop] › tests/browser/game.spec.ts:8:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ───────────────
Error: expect(received).toEqual(expected) // deep equality
- Expected - 2
+ Received + 2
Array [
- "0.1.1",
- "0.1.1",
+ null,
+ null,
]
42 | await page.getByRole("button", { name: "게임 시작 · 소리 듣기" }).click();
43 | await expect.poll(() => audioUrls.length).toBe(2);
> 44 | expect(audioUrls.map((url) => url.searchParams.get("v"))).toEqual(["0.1.1", "0.1.1"]);
| ^
45 | await expect(page.getByRole("button", { name: /듣는 중/ })).toBeDisabled();
46 | await expect(page.locator(".sound-wave.active i").first()).toHaveCSS(
47 | "animation-name",
at /home/hermes/worktrees/find-word-v0.1.1/tests/browser/game.spec.ts:44:61
Error Context: test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/trace.zip
Usage:
npx playwright show-trace test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
3) [desktop] › tests/browser/game.spec.ts:124:3 › intro WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다
Error: expect(received).toBe(expected) // Object.is equality
Expected: "0.1.1"
Received: null
138 | await expect(page.getByRole("alert")).toContainText("소리를 재생하지 못했어요");
139 | expect(missing).toHaveLength(1);
> 140 | expect(new URL(missing[0]!).searchParams.get("v")).toBe("0.1.1");
| ^
141 | expect(audioUrls).toHaveLength(2);
142 | await page.unroute(matchesFailure);
143 | await page.getByRole("button", { name: "다시 시도" }).click();
at /home/hermes/worktrees/find-word-v0.1.1/tests/browser/game.spec.ts:140:56
Error Context: test-results/game-intro-WAV-query-URL의-404를-표시하고-같은-URL로-재시도·정답-mapping한다-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/game-intro-WAV-query-URL의-404를-표시하고-같은-URL로-재시도·정답-mapping한다-desktop/trace.zip
Usage:
npx playwright show-trace test-results/game-intro-WAV-query-URL의-404를-표시하고-같은-URL로-재시도·정답-mapping한다-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
4) [desktop] › tests/browser/game.spec.ts:124:3 › target WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다
Error: expect(received).toBe(expected) // Object.is equality
Expected: "0.1.1"
Received: null
138 | await expect(page.getByRole("alert")).toContainText("소리를 재생하지 못했어요");
139 | expect(missing).toHaveLength(1);
> 140 | expect(new URL(missing[0]!).searchParams.get("v")).toBe("0.1.1");
| ^
141 | expect(audioUrls).toHaveLength(2);
142 | await page.unroute(matchesFailure);
143 | await page.getByRole("button", { name: "다시 시도" }).click();
at /home/hermes/worktrees/find-word-v0.1.1/tests/browser/game.spec.ts:140:56
Error Context: test-results/game-target-WAV-query-URL의-8e3ab-하고-같은-URL로-재시도·정답-mapping한다-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/game-target-WAV-query-URL의-8e3ab-하고-같은-URL로-재시도·정답-mapping한다-desktop/trace.zip
Usage:
npx playwright show-trace test-results/game-target-WAV-query-URL의-8e3ab-하고-같은-URL로-재시도·정답-mapping한다-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
4 failed
[desktop] › tests/browser/audio.spec.ts:7:1 › intro·목표 101개 versioned WAV를 실제 Chrome에서 해시·디코드 검증한다
[desktop] › tests/browser/game.spec.ts:8:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ────────────────
[desktop] › tests/browser/game.spec.ts:124:3 › intro WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다
[desktop] › tests/browser/game.spec.ts:124:3 › target WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다
EXIT_CODE: 1
@@ -0,0 +1,22 @@
{
"utc": "2026-10-14T07:22:25.563434+00:00",
"productionSourceDiff": "",
"unitRed": {
"failed": 5,
"passed": 12,
"exitCode": 1
},
"browserRed": {
"failed": 4,
"exitCode": 1
},
"sources": {
"src/App.tsx": "1ebde5bd2aa8038e5e003e4b9c84071bd9571dc2deff334b848720761d43df19",
"src/audio/dsp.ts": "e51176acc381b7e9d1fc19d7f3fdeb87f6b653dad13a332470104e957e973ecc",
"src/audio/engine.ts": "2d2272c8690d1b791fb51e111e1408e1a9688f8b6abb49f7ccf7bef5e8091543",
"src/content.ts": "f7363aaa6dd99f56e733843c569e96018a9c570708bea25857af59548f45e2c5",
"src/game.ts": "7aac9bccb6c322288d22966ee672da17d6745205260c8e87e009a3704ea8bd89",
"src/main.tsx": "eeab7184c76db4d03f45af69697f19affaaf318de056e36c8782000807d5e88e",
"src/styles.css": "09a32cc57163e1f517075525d98ffcf783b18e872135163f7572ac60ace25b9f"
}
}
@@ -0,0 +1,17 @@
UTC: 2026-10-14T07:22:34.296917+00:00
COMMAND: npm test -- tests/audio.test.ts tests/game.test.ts tests/audio-requests.test.ts
> find-word@0.1.1 test
> vitest run tests/audio.test.ts tests/game.test.ts tests/audio-requests.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1.1
Test Files 3 passed (3)
Tests 17 passed (17)
Start at 07:22:34
Duration 1.57s (environment 72%, setup 13%, tests 7%, transform 6%, worker 1%, import 1%)
EXIT_CODE: 0
@@ -0,0 +1,8 @@
UTC: 2026-10-14T07:23:10.118081+00:00
COMMAND: npm run lint
> find-word@0.1.1 lint
> eslint . && tsc --noEmit
EXIT_CODE: 0
@@ -0,0 +1,20 @@
UTC: 2026-10-14T07:23:10.132588+00:00
COMMAND: npm test
> find-word@0.1.1 test
> vitest run
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1.1
Test Files 12 passed (12)
Tests 65 passed (65)
Start at 07:23:10
Duration 14.73s (tests 71%, environment 20%, setup 7%, import 1%)
Isolate 12 workers spawned · ~883ms startup each (spawn + environment, per file)
at least ~2.65s faster with isolate: false — reuses workers across files instead of one per file
EXIT_CODE: 0
@@ -0,0 +1,19 @@
UTC: 2026-10-14T07:23:10.144409+00:00
COMMAND: npm run build
> 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...
✓ 21 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-CNSv_hb8.js 245.39 kB │ gzip: 77.28 kB
✓ built in 654ms
빌드 음성 자산 101개 및 런타임 notice 전문 확인
EXIT_CODE: 0
@@ -0,0 +1,84 @@
UTC: 2026-10-14T07:23:35.351749+00:00
COMMAND: env -u PLAYWRIGHT_NO_SANDBOX CHROME_PATH=/usr/bin/google-chrome DEBUG=pw:browser npm run test:browser
> find-word@0.1.1 test:browser
> playwright test
[WebServer] (node:797635) 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 16 tests using 2 workers
(node:797656) 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:797657) 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-VLndm1 --remote-debugging-pipe --no-startup-window +0ms
pw:browser <launched> pid=797676 +6ms
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-E3SrLp --remote-debugging-pipe --no-startup-window +0ms
pw:browser <launched> pid=797720 +11ms
pw:browser [pid=797676][err] [797729:797729:1014/072337.395281:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +151ms
pw:browser [pid=797720][err] [797806:797806:1014/072337.559739:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +228ms
pw:browser [pid=797676][err] [797822:7:1014/072337.636786:ERROR:gpu/ipc/client/command_buffer_proxy_impl.cc:287] ContextResult::kTransientFailure: Failed to send GpuControl.CreateCommandBuffer. +236ms
pw:browser [pid=797676][err] [797884:797884:1014/072338.237452:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +600ms
pw:browser [pid=797720][err] [797908:797908:1014/072339.957896:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +2s
✓ 1 [desktop] › tests/browser/audio.spec.ts:7:1 › intro·목표 101개 versioned WAV를 실제 Chrome에서 해시·디코드 검증한다 (3.5s)
pw:browser [pid=797720][err] [797720:797786:1014/072343.442681:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: QUOTA_EXCEEDED +3s
pw:browser [pid=797676][err] [797676:797709:1014/072344.514117:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +6s
✓ 3 [desktop] › tests/browser/game.spec.ts:130:3 › intro WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (15.7s)
✓ 4 [desktop] › tests/browser/game.spec.ts:130:3 › target WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (13.7s)
✓ 5 [desktop] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (312ms)
✓ 6 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.2s)
✓ 7 [desktop] › tests/browser/ui.spec.ts:52:1 › 난이도 설명은 모바일에서도 보인다 (852ms)
pw:browser [pid=797676][err] [797676:797709:1014/072414.236741:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +30s
✓ 8 [desktop] › tests/browser/ui.spec.ts:68:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (16.7s)
pw:browser [pid=797676] <gracefully close start> +15s
pw:browser [pid=797676] <process did exit: exitCode=0, signal=null> +63ms
pw:browser [pid=797676] starting temporary directories cleanup +1ms
pw:browser [pid=797676] finished temporary directories cleanup +8ms
pw:browser [pid=797676] <gracefully close end> +0ms
(node:798432) 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-98ggzR --remote-debugging-pipe --no-startup-window +0ms
pw:browser <launched> pid=798444 +6ms
pw:browser [pid=798444][err] [798487:798487:1014/072430.332265:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +129ms
pw:browser [pid=798444][err] [798525:7:1014/072430.474467:ERROR:gpu/ipc/client/command_buffer_proxy_impl.cc:287] ContextResult::kTransientFailure: Failed to send GpuControl.CreateCommandBuffer. +141ms
pw:browser [pid=798444][err] [798546:798546:1014/072430.822117:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +347ms
✓ 9 [mobile] › tests/browser/audio.spec.ts:7:1 › intro·목표 101개 versioned WAV를 실제 Chrome에서 해시·디코드 검증한다 (2.6s)
pw:browser [pid=798444][err] [798444:798472:1014/072437.625010:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: QUOTA_EXCEEDED +7s
✓ 2 [desktop] › tests/browser/game.spec.ts:8:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (1.7m)
pw:browser [pid=797720] <gracefully close start> +2m
pw:browser [pid=797720] <process did exit: exitCode=0, signal=null> +58ms
pw:browser [pid=797720] starting temporary directories cleanup +0ms
pw:browser [pid=797720] finished temporary directories cleanup +9ms
pw:browser [pid=797720] <gracefully close end> +0ms
(node:798715) 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-n2iw8E --remote-debugging-pipe --no-startup-window +0ms
pw:browser <launched> pid=798727 +6ms
pw:browser [pid=798727][err] [798770:798770:1014/072519.143522:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +114ms
pw:browser [pid=798727][err] [798809:7:1014/072519.291872:ERROR:gpu/ipc/client/command_buffer_proxy_impl.cc:287] ContextResult::kTransientFailure: Failed to send GpuControl.CreateCommandBuffer. +146ms
pw:browser [pid=798727][err] [798831:798831:1014/072519.654215:ERROR:base/memory/shared_memory_switch.cc:289] Failed global descriptor lookup: 7 +362ms
pw:browser [pid=798727][err] [798727:798755:1014/072523.099951:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +3s
✓ 11 [mobile] › tests/browser/game.spec.ts:130:3 › intro WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (17.1s)
pw:browser [pid=798727][err] [798727:798755:1014/072550.990604:ERROR:google_apis/gcm/engine/registration_request.cc:291] Registration response error message: DEPRECATED_ENDPOINT +28s
✓ 12 [mobile] › tests/browser/game.spec.ts:130:3 › target WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (15.9s)
✓ 13 [mobile] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (308ms)
✓ 14 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (976ms)
✓ 15 [mobile] › tests/browser/ui.spec.ts:52:1 › 난이도 설명은 모바일에서도 보인다 (772ms)
✓ 16 [mobile] › tests/browser/ui.spec.ts:68:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (16.8s)
pw:browser [pid=798727] <gracefully close start> +20s
pw:browser [pid=798727] <process did exit: exitCode=0, signal=null> +45ms
pw:browser [pid=798727] starting temporary directories cleanup +0ms
pw:browser [pid=798727] finished temporary directories cleanup +8ms
pw:browser [pid=798727] <gracefully close end> +0ms
✓ 10 [mobile] › tests/browser/game.spec.ts:8:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (1.7m)
pw:browser [pid=798444] <gracefully close start> +2m
pw:browser [pid=798444] <process did exit: exitCode=0, signal=null> +57ms
pw:browser [pid=798444] starting temporary directories cleanup +0ms
pw:browser [pid=798444] finished temporary directories cleanup +10ms
pw:browser [pid=798444] <gracefully close end> +0ms
16 passed (2.6m)
EXIT_CODE: 0
@@ -0,0 +1,40 @@
UTC: 2026-10-14T07:23:54.730130+00:00
COMMAND: env -u PLAYWRIGHT_NO_SANDBOX node .cache/verify-cache-sandbox.mjs
node:internal/modules/run_main:107
triggerUncaughtException(
^
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /Namespace sandbox\s+Yes/. Input:
'Sandbox Status\n' +
'Layer 1 Sandbox\tNamespace\n' +
'PID namespaces\tYes\n' +
'Network namespaces\tYes\n' +
'Seccomp-BPF sandbox\tYes\n' +
'Seccomp-BPF sandbox supports TSYNC\tYes\n' +
'Ptrace Protection with Yama LSM (Broker)\tYes\n' +
'Ptrace Protection with Yama LSM (Non-broker)\tNo\n' +
'\n' +
'You are adequately sandboxed.'
at file:///home/hermes/worktrees/find-word-v0.1.1/.cache/verify-cache-sandbox.mjs:8:10 {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'Sandbox Status\n' +
'Layer 1 Sandbox\tNamespace\n' +
'PID namespaces\tYes\n' +
'Network namespaces\tYes\n' +
'Seccomp-BPF sandbox\tYes\n' +
'Seccomp-BPF sandbox supports TSYNC\tYes\n' +
'Ptrace Protection with Yama LSM (Broker)\tYes\n' +
'Ptrace Protection with Yama LSM (Non-broker)\tNo\n' +
'\n' +
'You are adequately sandboxed.',
expected: /Namespace sandbox\s+Yes/,
operator: 'match',
diff: 'simple'
}
Node.js v26.7.0
EXIT_CODE: 1
@@ -0,0 +1,10 @@
UTC: 2026-10-14T07:24:19.461259+00:00
COMMAND: env -u PLAYWRIGHT_NO_SANDBOX node .cache/verify-cache-sandbox.mjs
{
"node": "v26.7.0",
"chrome": "146.0.7680.80",
"sandboxStatus": "Sandbox Status\nLayer 1 Sandbox\tNamespace\nPID namespaces\tYes\nNetwork namespaces\tYes\nSeccomp-BPF sandbox\tYes\nSeccomp-BPF sandbox supports TSYNC\tYes\nPtrace Protection with Yama LSM (Broker)\tYes\nPtrace Protection with Yama LSM (Non-broker)\tNo\n\nYou are adequately sandboxed.",
"commandLine": "/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-1fbWzM --remote-debugging-pipe --no-startup-window --noerrdialogs --ozone-platform=headless --ozone-override-screen-size=800,600 --use-angle=swiftshader-webgl --flag-switches-begin --flag-switches-end --enable-unsafe-swiftshader"
}
EXIT_CODE: 0
@@ -0,0 +1,55 @@
2026-10-14 UTC — 배포 후 오디오 캐시 회귀 수정
발견 원인
- v0.1 → v0.1.1에서 intro 1개·목표 100개 WAV 내용이 바뀌었지만 파일명은 동일하다.
- src/game.ts와 src/audio/engine.ts가 버전 없는 /audio/*.wav URL을 요청했다.
- 사용자 보고에 따르면 배포 Nginx는 /audio를 7일 캐시한다. 기존 URL의 캐시가
유효한 사용자는 이전 음성을 계속 받는다. 이 작업은 원격 서버에 접속하지 않았다.
- 배포 문서는 expires -1을 가정했고, 브라우저 요청 관찰의 URL.endsWith('.wav')와
404 route의 **/audio/*.wav glob도 query string을 처리하지 못했다.
실제 RED → GREEN 순서
1. 앱 소스를 그대로 두고 unit/browser 계약 테스트를 먼저 작성했다.
기존 endsWith 판정을 공용 테스트 helper로 추출했으며 RED까지 동작은 그대로다.
2. 01-unit-red.log: 5 failed / 12 passed / exit 1.
목표 100개 버전, intro·target fetch, intro/target 각각 404 retry,
query 순서와 무관한 pathname 판정 및 가짜 .wav query 배제를 검출했다.
3. 02-build-before-fix.log: 수정 전 앱 production build 성공.
4. 03-browser-red.log: sandboxed Chrome desktop 4 failed / exit 1.
101개 URL, 실제 앱 요청/정답 mapping, intro/target 404 계약이 모두
v=null 대신 v=0.1.1을 기대하는 assertion에서 실패했다.
5. 04-red-state.json: 위 RED 완료 시점의 production source diff가 비어 있음을 기록.
6. 그 뒤 src/audio/assets.ts 한 곳에 AUDIO_ASSET_VERSION="0.1.1"과
audioAssetUrl을 정의하고 intro·target에 함께 적용했다.
테스트 요청 관찰·404 route·정답 mapping·101개 manifest 대응은 pathname 기준이다.
7. 05-unit-green.log: 같은 대상 17 passed / exit 0.
8. 09-browser-green.log: desktop/mobile 전체 16 passed (2.6m) / exit 0.
최종 검증
- npm run lint: PASS (06-lint.log, ESLint + TypeScript).
- npm test: PASS, 12 files / 65 tests, skip 0 (07-full-test.log).
Python 생성기 18개 계약도 기존 npm test runner를 통해 통과했다.
- npm run build: PASS, WAV 101개 hash 및 runtime notice 전문 (08-build.log).
- env -u PLAYWRIGHT_NO_SANDBOX CHROME_PATH=/usr/bin/google-chrome DEBUG=pw:browser
npm run test:browser: PASS, desktop/mobile 전체 16개, skip 0.
- 두 viewport 모두 실제 음성으로 10문제 완주·재청취·정답 mapping·intro/target
각각 404 복구·키보드·레이아웃·axe를 통과했다.
- 두 viewport 각각 101/101 query URL 요청·SHA-256·bytes·24kHz mono decode·
duration·RMS·peak 통과. 외부 페이지 요청 0, 페이지 JS 예외 0.
- Node 26.7.0 / npm 11.19.0 / system Chrome 146.0.7680.80.
- 11-sandbox-green.log: Layer 1 Namespace, PID/Network namespaces,
Seccomp-BPF/TSYNC 활성. 실제 실행 인자 --no-sandbox 없음.
10-sandbox.log는 보조 검사에서 Chrome 상태 라벨을 'Namespace sandbox Yes'로
잘못 가정한 실패 기록이다. 실제 표시 'Layer 1 Sandbox Namespace'와
PID/Network Yes를 검사하도록 보조 probe를 고친 뒤 11번에서 통과했다.
앱·Playwright sandbox 설정은 바꾸지 않았다.
기록 및 범위
- 제품 0.1.1 유지. WAV/manifest 재생성 및 package/lock 변경 없음.
- 런타임 버전 조회·외부 TTS/API 호출 없음. 저장 WAV의 상대 URL query만 추가했다.
- 스크린샷은 test-results/에 저장 후 이번 artifacts/로 복사했다.
과거 verify_browser.mjs, 로그, 스크린샷은 수정하거나 다시 실행해 덮어쓰지 않았다.
- 00-baseline.json의 기존 evidence 149개, 오디오 102개, 기존 RESULT.md,
package/lock 포함 총 254개 파일은 최종 13-final-integrity.json에서 해시 대조한다.
- .agent/RESULT-v0.1.1.md에는 이번 결과를 뒤에 추가한다.
- Git commit/push/tag/merge 및 서버 배포 실행 없음. 로컬 production preview만 사용했다.
@@ -0,0 +1,50 @@
{
"utc": "2026-10-14T07:27:57.445449+00:00",
"status": "PASS",
"productVersion": "0.1.1",
"preservedFiles": 254,
"preservedHistoricalEvidenceFiles": 149,
"preservedAudioFiles": 102,
"changedHistoricalFiles": [],
"headBefore": "a92b2ba1e973c65a57e6e34cc52568cd6b208c1e",
"headAfter": "a92b2ba1e973c65a57e6e34cc52568cd6b208c1e",
"previousResultContentPreservedAsPrefix": true,
"gitDiffCheck": "PASS",
"unit": {
"files": 12,
"tests": 65,
"skipped": 0
},
"browser": {
"tests": 16,
"skipped": 0,
"launchesWithoutNoSandbox": 4,
"projects": {
"desktop": {
"wavFiles": 101,
"cacheVersion": "0.1.1",
"externalRequests": 0,
"pageErrors": 0
},
"mobile": {
"wavFiles": 101,
"cacheVersion": "0.1.1",
"externalRequests": 0,
"pageErrors": 0
}
}
},
"sourceSha256": {
"src/audio/assets.ts": "beb8da92ab1ae6dae6bc3b4db37afefc72a07bbf089fe4ea5948043a9eead79a",
"src/audio/engine.ts": "971bbe451c984cb4209e9f0338c3b9b422035e9b1339484effb5d3c2a7e6352f",
"src/game.ts": "3d0fd88ebeebcd4f25696168dd07a669bf3161d620091a67bb81882aa87232aa",
"tests/audio.test.ts": "58ebce8a48b83dae6f45d92175b37ff1b5e654ea067d0b2658bc879162b0d488",
"tests/game.test.ts": "bcb03d0c2d84177664b5320d53fb5bf2b3865dbbdc3bb281caf47199a57620e1",
"tests/audio-requests.test.ts": "9496b5515982dfb2eb24d3e4bf99da11eac28812164eaa557443fdf716bd9dd3",
"tests/browser/audio-requests.ts": "3dff162cbc5a9b735fb8f911ed83ca04d646ba011511c4f922faf712f8094295",
"tests/browser/audio.spec.ts": "f26ea575d9e5471b5746471e6704a0694e1d0a4cde34f8c5faf66ff2d9df9d16",
"tests/browser/game.spec.ts": "64306590d39c90cc8fe208ff758b63ba889ca1581ee081c520dfd43e97d7df1e",
"tests/browser/ui.spec.ts": "a342a49f83a2ef7f1b78145be08a457463baf53a8fdfeac28649bcd7bc14bbbf"
},
"gitCommitPushTagMergeDeploymentPerformed": false
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

+161
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

+71
View File
@@ -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))
+64
View File
@@ -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');
}
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 1 passed (1)
Tests 9 passed | 6 skipped (15)
Start at 07:57:16
Duration 914ms (environment 77%, setup 12%, transform 6%, tests 4%, import 1%, worker 1%)
+110
View File
@@ -0,0 +1,110 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/game.test.ts (9 tests | 3 failed) 41ms
❯ 출제와 정답 판정 (5)
× 세 문제를 한 단계로 출제하고 혼합 응답 방식과 고유 문제를 보장한다 8ms
❯ 게임 상태·점수·경합 방지 (3)
× 세 문제를 끝내면 정답률·연속 정답·단계별 결과를 집계한다 2ms
× 시작 단계 1~5를 명시하면 세 문항 모두 같은 level/db이고 범위 밖 단계도 안전하다 2ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 3 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/game.test.ts > 출제와 정답 판정 > 세 문제를 한 단계로 출제하고 혼합 응답 방식과 고유 문제를 보장한다
AssertionError: expected [ …(10) ] to have a length of 3 but got 10
- Expected
+ Received
- 3
+ 10
❯ tests/game.test.ts:51:23
49| it("세 문제를 한 단계로 출제하고 혼합 응답 방식과 고유 문제를 보장한다", () => {
50| const questions = createQuestions(42, "mixed");
51| expect(questions).toHaveLength(3);
| ^
52| expect(new Set(questions.map((q) => q.pattern.id)).size).toBe(3);
53| expect(new Set(questions.map((q) => q.environment.id)).size).toBe(…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/3]⎯
FAIL tests/game.test.ts > 게임 상태·점수·경합 방지 > 세 문제를 끝내면 정답률·연속 정답·단계별 결과를 집계한다
AssertionError: expected 'ready' to be 'results' // Object.is equality
Expected: "results"
Received: "ready"
❯ tests/game.test.ts:153:25
151| state = gameReducer(state, { type: "NEXT" });
152| }
153| expect(state.phase).toBe("results");
| ^
154| expect(state.bestStreak).toBe(2);
155| expect(state.streak).toBe(0);
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/3]⎯
FAIL tests/game.test.ts > 시작 단계 1~5를 명시하면 세 문항 모두 같은 level/db이고 범위 밖 단계도 안전하다
AssertionError: expected [ [ 1, 6 ], [ 1, 6 ], [ 2, 3 ], …(7) ] to deeply equal [ [ 1, 6 ], [ 1, 6 ], [ 1, 6 ] ]
- Expected
+ Received
@@ -6,9 +6,37 @@
[
1,
6,
],
[
- 1,
- 6,
+ 2,
+ 3,
+ ],
+ [
+ 2,
+ 3,
+ ],
+ [
+ 3,
+ 0,
+ ],
+ [
+ 3,
+ 0,
+ ],
+ [
+ 4,
+ -3,
+ ],
+ [
+ 4,
+ -3,
+ ],
+ [
+ 5,
+ -6,
+ ],
+ [
+ 5,
+ -6,
],
]
❯ tests/game.test.ts:175:49
173| for (const level of LEVELS) {
174| const questions = createQuestions(42, "mixed", "rotate", level.id);
175| expect(questions.map(q => [q.level, q.db])).toEqual(Array(3).fill(…
| ^
176| }
177| for (const [input, expected] of [[NaN, 1], [-4, 1], [99, 5], [2.7, 3…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/3]⎯
Test Files 1 failed (1)
Tests 3 failed | 6 passed (9)
Start at 07:56:19
Duration 1.22s (environment 82%, setup 9%, transform 4%, tests 4%, worker 1%, import 1%)
@@ -0,0 +1,34 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/game.test.ts (9 tests | 1 failed) 26ms
❯ 출제와 정답 판정 (5)
× 목표 WAV 100개 모두 같은 명시적 cache-busting 버전과 정답 mapping을 유지한다 13ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/game.test.ts > 출제와 정답 판정 > 목표 WAV 100개 모두 같은 명시적 cache-busting 버전과 정답 mapping을 유지한다
AssertionError: expected 96 to be 100 // Object.is equality
- Expected
+ Received
- 100
+ 96
❯ tests/game.test.ts:21:28
19| .map((q) => [q.id, q]),
20| );
21| expect(questions.size).toBe(100);
| ^
22| const versions = new Set<string | null>();
23| for (const q of questions.values()) {
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 8 passed (9)
Start at 07:56:50
Duration 861ms (environment 76%, setup 13%, transform 6%, tests 4%, worker 1%, import 1%)
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 1 passed (1)
Tests 15 passed (15)
Start at 07:57:18
Duration 879ms (environment 75%, setup 13%, transform 5%, tests 4%, import 1%, worker 1%)
@@ -0,0 +1,74 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/game.test.ts (15 tests | 7 failed) 30ms
❯ 출제와 정답 판정 (5)
× 목표 WAV 100개 모두 같은 명시적 cache-busting 버전과 정답 mapping을 유지한다 12ms
× SNR을 정수 -12~12 dB로 제한하고 현재/남은 문항에 일관되게 적용한다 1ms
× readyで中止しても無応答を加算せず終了結果を保つ 3ms
× loadingで中止しても無応答を加算せず終了結果を保つ 1ms
× playingで中止しても無応答を加算せず終了結果を保つ 1ms
× answerで中止しても無応答を加算せず終了結果を保つ 1ms
× feedbackで中止しても無応答を加算せず終了結果を保つ 1ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 7 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/game.test.ts > 출제와 정답 판정 > 목표 WAV 100개 모두 같은 명시적 cache-busting 버전과 정답 mapping을 유지한다
AssertionError: expected 96 to be 100 // Object.is equality
- Expected
+ Received
- 100
+ 96
❯ tests/game.test.ts:21:28
19| .map((q) => [q.id, q]),
20| );
21| expect(questions.size).toBe(100);
| ^
22| const versions = new Set<string | null>();
23| for (const q of questions.values()) {
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/7]⎯
FAIL tests/game.test.ts > SNR을 정수 -12~12 dB로 제한하고 현재/남은 문항에 일관되게 적용한다
TypeError: Cannot read properties of undefined (reading 'questions')
❯ tests/game.test.ts:186:21
184| for (const [input, expected] of [[-99, -12], [99, 12], [NaN, 3], [1.…
185| const adjusted = gameReducer(state, { type: "SNR", db: input! });
186| expect(adjusted.questions.map(q => q.db)).toEqual([expected, expec…
| ^
187| }
188| for (const type of ["PLAY", "STARTED", "ENDED"] as const) state = ga…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/7]⎯
FAIL tests/game.test.ts > readyで中止しても無応答を加算せず終了結果を保つ
FAIL tests/game.test.ts > loadingで中止しても無応答を加算せず終了結果を保つ
FAIL tests/game.test.ts > playingで中止しても無応答を加算せず終了結果を保つ
FAIL tests/game.test.ts > answerで中止しても無応答を加算せず終了結果を保つ
FAIL tests/game.test.ts > feedbackで中止しても無応答を加算せず終了結果を保つ
AssertionError: expected undefined to match object { phase: 'results', …(2) }
- Expected:
{
"answers": [],
"endedEarly": true,
"phase": "results",
}
+ Received:
undefined
❯ tests/game.test.ts:199:17
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/7]⎯
Test Files 1 failed (1)
Tests 7 failed | 8 passed (15)
Start at 07:56:51
Duration 918ms (environment 74%, setup 14%, transform 6%, tests 4%, import 1%, worker 1%)
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 1 passed (1)
Tests 18 passed (18)
Start at 07:59:18
Duration 4.22s (tests 80%, environment 14%, setup 2%, transform 2%, import 1%)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,461 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/App.test.tsx (18 tests | 1 failed) 3085ms
❯ 시작부터 결과까지 사용자 흐름 (13)
× 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다 112ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
TestingLibraryElementError: Found multiple elements with the text: 음성이 배경보다 6 dB 커요.
Here are the matching elements:
Ignored nodes: comments, script, style
<p
class="db-note"
>
음성이 배경보다 6 dB 커요.
<br />
소리에 익숙해지기
</p>
Ignored nodes: comments, script, style
<p
id="snr-help"
>
음성이 배경보다 6 dB 커요.
 범위 −12~+12 dB · 1 dB 간격. 양수는 음성이 더 크게, 0은 같게, 음수는 더 작게 들려요. 변경은 다음 재생·재청취부터 적용돼요.
</p>
(If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)).
Ignored nodes: comments, script, style
<body>
<div>
<a
class="skip-link"
href="#main"
>
본문으로 건너뛰기
</a>
<header
class="header wrap"
>
<div
class="brand"
>
<span
aria-hidden="true"
class="brand-mark"
>
<i />
<i />
<i />
<i />
</span>
<h1>
단어를 맞춰라
</h1>
<span
class="version"
>
v0.1.1
</span>
</div>
<div
class="header-actions"
>
<button
class="text-button"
>
문제 풀이 종료
</button>
<span
class="little-dot"
/>
소리에 집중하는 시간
</div>
</header>
<main
class="wrap"
id="main"
tabindex="-1"
>
<section
aria-label="단어 찾기 게임"
class="game-layout"
>
<div
class="game-top"
>
<span
class="eyebrow"
>
나의 소리 탐험
</span>
<span>
문제 
<b>
01
</b>
 / 
3
</span>
</div>
<progress
aria-label="전체 진행률"
max="3"
value="0"
/>
<div
class="game-columns"
>
<aside
class="environment-panel"
>
<p
class="eyebrow"
>
지금 듣고 있는 공간
</p>
<h2>
길거리
</h2>
<p>
지나가는 차와 도시의 웅성임
</p>
<div
aria-hidden="true"
class="mascot small"
>
<span
class="ear left"
/>
<span
class="ear right"
/>
<span
class="eye left"
/>
<span
class="eye right"
/>
<span
class="smile"
/>
<b>
S
</b>
</div>
<div
aria-hidden="true"
class="sound-wave "
>
<i
style="height: 10px; animation-delay: 0s;"
/>
<i
style="height: 33.799955450859244px; animation-delay: 0.05s;"
/>
<i
style="height: 16.132986448643948px; animation-delay: 0.1s;"
/>
<i
style="height: 32.219552375865575px; animation-delay: 0.15000000000000002s;"
/>
<i
style="height: 21.858720427326595px; animation-delay: 0.2s;"
/>
<i
style="height: 29.163690702963766px; animation-delay: 0.25s;"
/>
<i
style="height: 26.796992502245015px; animation-delay: 0.30000000000000004s;"
/>
<i
style="height: 24.835290693688798px; animation-delay: 0.35000000000000003s;"
/>
<i
style="height: 30.619883556555898px; animation-delay: 0.4s;"
/>
<i
style="height: 35.39139668035928px; animation-delay: 0.45s;"
/>
<i
style="height: 71.52943948029164px; animation-delay: 0.5s;"
/>
<i
style="height: 19.53593765210872px; animation-delay: 0.55s;"
/>
<i
style="height: 73.98674560913082px; animation-delay: 0.6000000000000001s;"
/>
<i
style="height: 16.952742107140878px; animation-delay: 0.65s;"
/>
<i
style="height: 72.19510052760725px; animation-delay: 0.7000000000000001s;"
/>
<i
style="height: 32.979734657418774px; animation-delay: 0.75s;"
/>
<i
style="height: 66.27347594564635px; animation-delay: 0.8s;"
/>
<i
style="height: 47.480789758355044px; animation-delay: 0.8500000000000001s;"
/>
<i
style="height: 56.615089141222136px; animation-delay: 0.9s;"
/>
<i
style="height: 59.492984932238116px; animation-delay: 0.9500000000000001s;"
/>
<i
style="height: 43.861291911681526px; animation-delay: 1s;"
/>
<i
style="height: 68.21866699734562px; animation-delay: 1.05s;"
/>
<i
style="height: 28.858982496016484px; animation-delay: 1.1s;"
/>
<i
style="height: 73.07841912260051px; animation-delay: 1.1500000000000001s;"
/>
<i
style="height: 12.60436847039135px; animation-delay: 1.2000000000000002s;"
/>
<i
style="height: 73.74953619965402px; animation-delay: 1.25s;"
/>
<i
style="height: 23.8231850360459px; animation-delay: 1.3s;"
/>
<i
style="height: 32.57029511072814px; animation-delay: 1.35s;"
/>
<i
style="height: 20.99981090780238px; animation-delay: 1.4000000000000001s;"
/>
<i
style="height: 29.735764963203792px; animation-delay: 1.4500000000000002s;"
/>
<i
style="height: 26.085500220240995px; animation-delay: 1.5s;"
/>
<i
style="height: 25.59070868046883px; animation-delay: 1.55s;"
/>
<i
style="height: 30.10305417152866px; animation-delay: 1.6s;"
/>
<i
style="height: 20.410372983416416px; animation-delay: 1.6500000000000001s;"
/>
<i
style="height: 32.785692656480826px; animation-delay: 1.7000000000000002s;"
/>
</div>
<div
class="level-tag"
>
1
단계 · 
SNR +6 dB
</div>
<p
class="db-note"
>
음성이 배경보다 6 dB 커요.
<br />
소리에 익숙해지기
</p>
<div
class="snr-control"
>
<label
for="snr"
>
목표 음성 SNR
</label>
<input
aria-describedby="snr-help"
id="snr"
max="12"
min="-12"
step="1"
type="range"
value="6"
/>
<output
for="snr"
>
SNR +6 dB
</output>
<p
id="snr-help"
>
음성이 배경보다 6 dB 커요.
 범위 −12~+12 dB · 1 dB 간격. 양수는 음성이 더 크게, 0은 같게, 음수는 더 작게 들려요. 변경은 다음 재생·재청취부터 적용돼요.
</p>
</div>
<div
class="volume-row"
>
<label
for="volume"
>
<span
aria-hidden="true"
>
◖))
</span>
 전체 음량
</label>
<input
id="volume"
max="100"
min="0"
type="range"
value="35"
/>
<output
for="volume"
>
35
%
</output>
</div>
<div
class="mini-stats"
>
<span>
점수 
<b>
0
</b>
</span>
<span>
연속 정답 
<b>
0
</b>
</span>
</div>
</aside>
<div
class="question-panel panel"
>
<div
class="question-meta"
>
<span>
바로잡은 말
</span>
<span>
객관식
</span>
</div>
<h2
tabindex="-1"
>
정정해서 알려준 단어는 무엇인가요?
</h2>
<p
class="muted"
>
가장 또렷하게 기억나는 단어를 골라 주세요.
</p>
<div
aria-label="답 선택"
class="answers"
role="group"
>
<button>
<span
aria-hidden="true"
>
1
</span>
시장
</button>
<button>
<span
aria-hidden="true"
>
2
</span>
지장
</button>
<button>
<span
aria-hidden="true"
>
3
</span>
사장
</button>
<button>
<span
aria-hidden="true"
>
4
</span>
신장
</button>
</div>
<div
class="play-controls"
>
<button
class="secondary"
>
↻ 다시 듣기
</button>
<small>
다시 들어도 점수는 줄어들지 않아요.
</small>
</div>
</div>
</div>
</section>
</main>
<footer
class="footer wrap"
>
<p>
Designed by OZ at Heat-T Medical Inc.
</p>
<p>
청각적 주의·식별 게임 · 의료적 진단이나 치료를 제공하지 않습니다.
</p>
<span>
개인정보 수집 없이, 오직 플레이만.
</span>
</footer>
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ getElementError node_modules/@testing-library/dom/dist/query-helpers.js:20:35
❯ getMultipleElementsFoundError node_modules/@testing-library/dom/dist/query-helpers.js:23:10
❯ node_modules/@testing-library/dom/dist/query-helpers.js:55:13
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:127:22
125| .toBeInTheDocument();
126| expect
127| .soft(screen.queryByText(description, { exact: false }))
| ^
128| .toBeInTheDocument();
129| await f.user.click(
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 17 passed (18)
Start at 07:59:02
Duration 4.01s (tests 79%, environment 14%, transform 2%, setup 2%, import 1%)
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 2 passed (2)
Tests 19 passed (19)
Start at 08:00:13
Duration 1.06s (environment 76%, setup 12%, transform 8%, tests 2%, import 1%, worker 1%)
@@ -0,0 +1,47 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/dsp.test.ts (4 tests | 2 failed) 13ms
❯ 상대 dB와 결정론적 배경 합성 (4)
× 여덟 환경은 고유 same-origin WAV이며 절차적 합성 매개변수를 갖지 않는다 6ms
× 피크가 큰 녹음도 공통 감쇠로 합성 peak 0.8을 넘지 않고 SNR 비율을 보존한다 1ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/dsp.test.ts > 상대 dB와 결정론적 배경 합성 > 여덟 환경은 고유 same-origin WAV이며 절차적 합성 매개변수를 갖지 않는다
AssertionError: expected 1 to be 8 // Object.is equality
- Expected
+ Received
- 8
+ 1
❯ tests/dsp.test.ts:23:61
21| });
22| it("여덟 환경은 고유 same-origin WAV이며 절차적 합성 매개변수를 갖지 않는다", () => {
23| expect(new Set(ENVIRONMENTS.map(e => e.audioUrl)).size).toBe(8);
| ^
24| for (const environment of ENVIRONMENTS) {
25| const url = new URL(environment.audioUrl, "http://localhost");
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯
FAIL tests/dsp.test.ts > 상대 dB와 결정론적 배경 합성 > 피크가 큰 녹음도 공통 감쇠로 합성 peak 0.8을 넘지 않고 SNR 비율을 보존한다
TypeError: mixingGains is not a function
❯ tests/dsp.test.ts:35:21
33| const background = normalizeRms(new Float32Array([0.2, -0.2]), 0.0…
34| for (const db of [-12, -6, 0, 6, 12]) {
35| const gains = mixingGains(voice, background, voice, db);
| ^
36| expect(20 * Math.log10(gains.voice / gains.background)).toBeClos…
37| expect(Math.max(...voice) * (gains.voice + gains.intro) + 0.03 *…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯
Test Files 1 failed (1)
Tests 2 failed | 2 passed (4)
Start at 07:59:47
Duration 849ms (environment 78%, setup 14%, transform 5%, tests 2%, worker 1%, import 1%)
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 1 passed (1)
Tests 11 passed (11)
Start at 08:01:57
Duration 1.05s (environment 60%, tests 21%, setup 10%, transform 6%, import 1%, worker 1%)
+272
View File
@@ -0,0 +1,272 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/audio.test.ts (11 tests | 9 failed) 139ms
❯ 오디오 재생 수명과 믹서 (8)
× intro와 목표 실제 fetch URL에 같은 명시적 cache-busting 버전을 붙인다 21ms
× intro 404 후 intro·목표를 동일한 versioned URL로 다시 요청한다 19ms
× target 404 후 intro·목표를 동일한 versioned URL로 다시 요청한다 20ms
× 생성 시 무음이며 gesture의 play에서만 장치를 연다 13ms
× 동시 시작을 거부하고 정지하면 모든 노드와 대기 Promise를 취소한다 14ms
× HTTP·디코드·장치 실패를 전달하고 다음 시도는 다시 로드한다 3ms
× 전체 음량은 두 신호의 비율을 보존하며 0부터 1 범위로 제한한다 9ms
× 선택한 녹음을 디코드·루프하고 목표 종료에서 모든 소스와 fetch를 정리한다 11ms
× 배경 404·디코드 실패는 한국어 복구 메시지이고 재시도로 성공한다 10ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 9 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > intro와 목표 실제 fetch URL에 같은 명시적 cache-busting 버전을 붙인다
TypeError: Cannot read properties of undefined (reading 'onended')
❯ tests/audio.test.ts:95:21
93| const done = f.engine.play(question, true, vi.fn());
94| await flush();
95| f.sources.at(-1)!.onended?.();
| ^
96| await done;
97| expect(f.fetcher.mock.calls.map(([url]) => url)).toEqual([
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/9]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > intro 404 후 intro·목표를 동일한 versioned URL로 다시 요청한다
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > target 404 후 intro·목표를 동일한 versioned URL로 다시 요청한다
TypeError: Cannot read properties of undefined (reading 'onended')
❯ tests/audio.test.ts:129:23
127| const done = f.engine.play(question, true, vi.fn());
128| await flush();
129| f.sources.at(-1)!.onended?.();
| ^
130| await done;
131| expect(f.fetcher.mock.calls.map(([url]) => url)).toEqual([
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/9]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > 생성 시 무음이며 gesture의 play에서만 장치를 연다
AssertionError: expected "vi.fn()" to be called once, but got 0 times
❯ tests/audio.test.ts:150:21
148| expect(f.context.resume).toHaveBeenCalledOnce();
149| await flush();
150| expect(started).toHaveBeenCalledOnce();
| ^
151| expect(f.sources).toHaveLength(3);
152| expect(
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/9]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > 동시 시작을 거부하고 정지하면 모든 노드와 대기 Promise를 취소한다
AssertionError: expected TypeError: (0 , __vite_ssr_import_1__.syn… to match object { name: 'AbortError' }
- Expected
+ Received
- {
- "name": "AbortError",
+ TypeError {
+ "message": "(0 , __vite_ssr_import_1__.synthesizeBackground) is not a function",
}
❯ tests/audio.test.ts:182:35
180| const f = fixture();
181| const done = f.engine.play(question, false, vi.fn());
182| const cancelled = expect(done).rejects.toMatchObject({
| ^
183| name: "AbortError",
184| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/9]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > HTTP·디코드·장치 실패를 전달하고 다음 시도는 다시 로드한다
AssertionError: expected [Function] to throw error including '해석' but got 'decode'
Expected: "해석"
Received: "decode"
❯ tests/audio.test.ts:226:58
224| );
225| f.context.decodeAudioData.mockRejectedValueOnce(new Error("decode"…
226| await expect(f.engine.play(question, false, vi.fn())).rejects.toTh…
| ^
227| "해석",
228| );
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/9]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > 전체 음량은 두 신호의 비율을 보존하며 0부터 1 범위로 제한한다
TypeError: Cannot read properties of undefined (reading 'onended')
❯ tests/audio.test.ts:248:21
246| f.engine.setVolume(-1);
247| expect(f.gains[0]!.gain.setTargetAtTime).toHaveBeenCalledWith(0, 2…
248| f.sources.at(-1)!.onended?.();
| ^
249| await done;
250| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/9]⎯
FAIL tests/audio.test.ts > 선택한 녹음을 디코드·루프하고 목표 종료에서 모든 소스와 fetch를 정리한다
AssertionError: expected "vi.fn()" to be called with arguments: [ Array(2) ]
Received:
1st vi.fn() call:
[
- "/ambience/street.wav?v=0.1.1",
- ObjectContaining {
- "signal": Any<AbortSignal>,
+ "/audio/correction-market.wav?v=0.1.1",
+ {
+ "signal": AbortSignal {
+ Symbol(kEvents): undefined,
+ Symbol(events.maxEventTargetListeners): 0,
+ Symbol(events.maxEventTargetListenersWarned): false,
+ Symbol(kHandlers): undefined,
+ Symbol(kAborted): true,
+ Symbol(kReason): AbortError {
+ "message": "This operation was aborted",
+ },
+ Symbol(kComposite): false,
+ },
},
]
Number of calls: 1
❯ tests/audio.test.ts:258:21
256| const done = f.engine.play(question, false, vi.fn());
257| await flush();
258| expect(f.fetcher).toHaveBeenCalledWith(question.environment.audioUrl…
| ^
259| expect(f.context.decodeAudioData).toHaveBeenCalledTimes(2);
260| expect(f.sources[0]!.loop).toBe(true);
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/9]⎯
FAIL tests/audio.test.ts > 배경 404·디코드 실패는 한국어 복구 메시지이고 재시도로 성공한다
AssertionError: expected [Function] to throw error matching /배경.*다시/ but got '(0 , __vite_ssr_import_1__.synthesize…'
- Expected:
/배경.*다시/
+ Received:
"(0 , __vite_ssr_import_1__.synthesizeBackground) is not a function"
❯ tests/audio.test.ts:272:56
270| f.fetcher.mockImplementationOnce(async () => ({ ok: true, arrayBuffe…
271| f.fetcher.mockResolvedValueOnce({ ok: false } as Response);
272| await expect(f.engine.play(question, false, vi.fn())).rejects.toThro…
| ^
273| f.context.decodeAudioData.mockRejectedValueOnce(new Error("EncodingE…
274| await expect(f.engine.play(question, false, vi.fn())).rejects.toThro…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[8/9]⎯
⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯
Vitest caught 6 unhandled errors during the test run.
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: synthesizeBackground is not a function
❯ src/audio/engine.ts:131:15
129| background.copyToChannel(
130| normalizeRms(
131| synthesizeBackground(
| ^
132| question.environment,
133| context.sampleRate,
❯ process.processTicksAndRejections node:internal/process/task_queues:104:5
This error originated in "tests/audio.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
The last test to run before this error was "로드 시간초과는 fetch를 취소하고 늦은 디코드가 소스를 만들지 않는다". This means either:
- the error was thrown while Vitest was running this test, or
- the error was thrown after the test completed, and this was the most recent test at that point.
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: synthesizeBackground is not a function
❯ src/audio/engine.ts:131:15
129| background.copyToChannel(
130| normalizeRms(
131| synthesizeBackground(
| ^
132| question.environment,
133| context.sampleRate,
❯ process.processTicksAndRejections node:internal/process/task_queues:104:5
This error originated in "tests/audio.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
The last test to run before this error was "로드 시간초과는 fetch를 취소하고 늦은 디코드가 소스를 만들지 않는다". This means either:
- the error was thrown while Vitest was running this test, or
- the error was thrown after the test completed, and this was the most recent test at that point.
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: synthesizeBackground is not a function
❯ src/audio/engine.ts:131:15
129| background.copyToChannel(
130| normalizeRms(
131| synthesizeBackground(
| ^
132| question.environment,
133| context.sampleRate,
❯ process.processTicksAndRejections node:internal/process/task_queues:104:5
This error originated in "tests/audio.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
The last test to run before this error was "로드 시간초과는 fetch를 취소하고 늦은 디코드가 소스를 만들지 않는다". This means either:
- the error was thrown while Vitest was running this test, or
- the error was thrown after the test completed, and this was the most recent test at that point.
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: synthesizeBackground is not a function
❯ src/audio/engine.ts:131:15
129| background.copyToChannel(
130| normalizeRms(
131| synthesizeBackground(
| ^
132| question.environment,
133| context.sampleRate,
❯ process.processTicksAndRejections node:internal/process/task_queues:104:5
This error originated in "tests/audio.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
The last test to run before this error was "로드 시간초과는 fetch를 취소하고 늦은 디코드가 소스를 만들지 않는다". This means either:
- the error was thrown while Vitest was running this test, or
- the error was thrown after the test completed, and this was the most recent test at that point.
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: synthesizeBackground is not a function
❯ src/audio/engine.ts:131:15
129| background.copyToChannel(
130| normalizeRms(
131| synthesizeBackground(
| ^
132| question.environment,
133| context.sampleRate,
❯ process.processTicksAndRejections node:internal/process/task_queues:104:5
This error originated in "tests/audio.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
The last test to run before this error was "로드 시간초과는 fetch를 취소하고 늦은 디코드가 소스를 만들지 않는다". This means either:
- the error was thrown while Vitest was running this test, or
- the error was thrown after the test completed, and this was the most recent test at that point.
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: synthesizeBackground is not a function
❯ src/audio/engine.ts:131:15
129| background.copyToChannel(
130| normalizeRms(
131| synthesizeBackground(
| ^
132| question.environment,
133| context.sampleRate,
❯ process.processTicksAndRejections node:internal/process/task_queues:104:5
This error originated in "tests/audio.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
The last test to run before this error was "로드 시간초과는 fetch를 취소하고 늦은 디코드가 소스를 만들지 않는다". This means either:
- the error was thrown while Vitest was running this test, or
- the error was thrown after the test completed, and this was the most recent test at that point.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Test Files 1 failed (1)
Tests 9 failed | 2 passed (11)
Errors 6 errors
Start at 08:00:38
Duration 979ms (environment 65%, tests 16%, setup 11%, transform 6%, import 1%, worker 1%)
@@ -0,0 +1,60 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/audio.test.ts (11 tests | 1 failed) 207ms
❯ 오디오 재생 수명과 믹서 (8)
× 생성 시 무음이며 gesture의 play에서만 장치를 연다 30ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > 생성 시 무음이며 gesture의 play에서만 장치를 연다
AssertionError: expected "vi.fn()" to be called with arguments: [ 1, Any<Number> ]
Received:
1st vi.fn() call:
[
- 1,
- Any<Number>,
+ 0.834156648304508,
+ 2.05,
]
2nd vi.fn() call:
[
- 1,
- Any<Number>,
+ 0,
+ 2.05,
]
3rd vi.fn() call:
[
- 1,
- Any<Number>,
+ 0.834156648304508,
+ 4.340000000000001,
]
Number of calls: 3
❯ tests/audio.test.ts:158:45
156| ).toEqual(["/audio/intro.wav", `/audio/${question.id}.wav`, new UR…
157| // master, 배경, 목표, 안내의 gain을 분리한다. 안내는 점차 소음에 묻힌다.
158| expect(f.gains[1]!.gain.setValueAtTime).toHaveBeenCalledWith(
| ^
159| 1,
160| expect.any(Number),
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 10 passed (11)
Start at 08:01:14
Duration 1.05s (environment 61%, tests 22%, setup 10%, transform 5%, import 1%, worker 1%)
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 1 passed (1)
Tests 1 passed | 1 skipped (2)
Start at 08:01:59
Duration 305ms (setup 52%, tests 39%, transform 5%, worker 2%, import 1%)
@@ -0,0 +1,53 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/generator.test.ts (2 tests | 1 failed | 1 skipped) 87ms
× Node type stripping 경로에서 기존 101문장 원문을 읽을 수 있다 86ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/generator.test.ts > Node type stripping 경로에서 기존 101문장 원문을 읽을 수 있다
AssertionError: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/hermes/worktrees/find-word-v0.2.3/src/audio/assets' imported from /home/hermes/worktrees/find-word-v0.2.3/src/content.ts
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:766:56)
at #resolve (node:internal/modules/esm/loader:690:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:610:35)
at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)
at ModuleJob.link (node:internal/modules/esm/module_job:389:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///home/hermes/worktrees/find-word-v0.2.3/src/audio/assets'
}
Node.js v26.7.0
: expected 1 to be +0 // Object.is equality
- Expected
+ Received
- 0
+ 1
❯ tests/generator.test.ts:20:40
18| const result = spawnSync("node", ["--experimental-strip-types", "--i…
19| "import {INTRO,PATTERNS,WORDS} from './src/content.ts'; console.lo…
20| expect(result.status, result.stderr).toBe(0);
| ^
21| expect(result.stdout.trim()).toBe("101");
22| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 1 skipped (2)
Start at 08:01:37
Duration 340ms (setup 45%, tests 39%, transform 12%, worker 2%, import 1%)
@@ -0,0 +1,4 @@
{
"files": 8,
"bytes": 4608352
}
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 08:08:06
Duration 482ms (tests 66%, setup 27%, transform 3%, import 2%, worker 1%)
+26
View File
@@ -0,0 +1,26 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/ambience.test.ts (1 test | 1 failed) 10ms
× 여덟 현장 녹음은 고유 PCM16 WAV·짧은 fade·완전한 provenance/notice를 가진다 8ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/ambience.test.ts > 여덟 현장 녹음은 고유 PCM16 WAV·짧은 fade·완전한 provenance/notice를 가진다
Error: ENOENT: no such file or directory, open 'public/ambience/manifest.json'
❯ tests/ambience.test.ts:9:31
7| // 생성기 자기 보고 대신 배포 바이트의 형식·파형·권리 추적 정보를 독립 측정한다.
8| it("여덟 현장 녹음은 고유 PCM16 WAV·짧은 fade·완전한 provenance/notice를 가진다", () => {
9| const manifest = JSON.parse(readFileSync("public/ambience/manifest.j…
| ^
10| expect(manifest.version).toBe("0.2.3");
11| expect(manifest.files.map((f: {environmentId: string}) => f.environm…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed (1)
Start at 08:02:57
Duration 378ms (setup 63%, transform 25%, import 5%, tests 4%, worker 2%)
@@ -0,0 +1,9 @@
test_convert_format_fade_normalization_and_reproducibility (test_ambience_audio.AmbienceConversion.test_convert_format_fade_normalization_and_reproducibility)
스테레오 48kHz 입력을 실제로 내려 섞고 길이·양끝·안전 피크를 확인한다. ... ok
test_silence_fails_before_publishing (test_ambience_audio.AmbienceConversion.test_silence_fails_before_publishing)
무음을 정상 현장 녹음으로 포장하지 않는다. ... ok
----------------------------------------------------------------------
Ran 2 tests in 1.134s
OK
@@ -0,0 +1,21 @@
test_ambience_audio (unittest.loader._FailedTest.test_ambience_audio) ... ERROR
======================================================================
ERROR: test_ambience_audio (unittest.loader._FailedTest.test_ambience_audio)
----------------------------------------------------------------------
ImportError: Failed to import test module: test_ambience_audio
Traceback (most recent call last):
File "/home/hermes/.local/share/uv/python/cpython-3.11.16-linux-x86_64-gnu/lib/python3.11/unittest/loader.py", line 419, in _find_test_path
module = self._get_module_from_name(name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/hermes/.local/share/uv/python/cpython-3.11.16-linux-x86_64-gnu/lib/python3.11/unittest/loader.py", line 362, in _get_module_from_name
__import__(name)
File "/home/hermes/worktrees/find-word-v0.2.3/tests/python/test_ambience_audio.py", line 11, in <module>
import ambience_audio as audio
ModuleNotFoundError: No module named 'ambience_audio'
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 2 passed (2)
Tests 34 passed (34)
Start at 08:04:49
Duration 4.34s (tests 66%, environment 26%, setup 4%, transform 2%, import 1%)
@@ -0,0 +1,33 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/game.test.ts (16 tests | 1 failed) 39ms
× 재생 중 변경된 SNR은 재청취 전 답변의 청취 이력을 덮어쓰지 않는다 7ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/game.test.ts > 재생 중 변경된 SNR은 재청취 전 답변의 청취 이력을 덮어쓰지 않는다
AssertionError: expected -10 to be 6 // Object.is equality
- Expected
+ Received
- 6
+ -10
❯ tests/game.test.ts:215:32
213| state = gameReducer(state, { type: "ENDED" });
214| state = gameReducer(state, { type: "ANSWER", input: state.questions[…
215| expect(state.answers[0]!.db).toBe(6);
| ^
216| expect(state.questions[1]!.db).toBe(-10);
217| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 15 passed (16)
Start at 08:04:23
Duration 904ms (environment 74%, setup 12%, transform 6%, tests 5%, import 1%, worker 1%)
@@ -0,0 +1,13 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 5 passed (5)
Tests 50 passed (50)
Start at 08:07:17
Duration 4.95s (tests 52%, environment 34%, setup 9%, transform 3%, import 1%)
Environment jsdom was created 5 times · 2.74s total, 34% of tracked time
create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares it across files)
learn more: https://vitest.dev/guide/improving-performance#test-environments
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 2 passed (2)
Tests 35 passed (35)
Start at 08:07:59
Duration 4.38s (tests 66%, environment 26%, setup 4%, transform 2%, import 1%)
@@ -0,0 +1,33 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/game.test.ts (17 tests | 1 failed) 43ms
× 순환 환경은 여러 라운드에서 여덟 장소 모두를 선택하고 라운드 내에는 중복되지 않는다 8ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/game.test.ts > 순환 환경은 여러 라운드에서 여덟 장소 모두를 선택하고 라운드 내에는 중복되지 않는다
AssertionError: expected 3 to be 8 // Object.is equality
- Expected
+ Received
- 8
+ 3
❯ tests/game.test.ts:227:21
225| questions.forEach(q => seen.add(q.environment.id));
226| }
227| expect(seen.size).toBe(8);
| ^
228| });
229|
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 16 passed (17)
Start at 08:07:35
Duration 902ms (environment 74%, setup 13%, transform 6%, tests 5%, import 1%, worker 1%)
@@ -0,0 +1,9 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
Test Files 2 passed (2)
Tests 26 passed (26)
Start at 08:08:44
Duration 4.72s (tests 85%, environment 9%, setup 3%, transform 2%, import 1%)
@@ -0,0 +1,596 @@
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.2.3
❯ tests/App.test.tsx (19 tests | 1 failed | 18 skipped) 485ms
× 환경음 저작자·라이선스 고지를 같은 출처 링크로 제공한다 483ms
❯ tests/build.test.ts (7 tests | 4 failed | 3 skipped) 1387ms
× 현장음 wav 손상은 빌드 실패로 처리한다 348ms
× 현장음 manifest 손상은 빌드 실패로 처리한다 342ms
× 현장음 notice 손상은 빌드 실패로 처리한다 371ms
× 현장음 extra 손상은 빌드 실패로 처리한다 324ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 5 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/App.test.tsx > 환경음 저작자·라이선스 고지를 같은 출처 링크로 제공한다
TestingLibraryElementError: Unable to find an accessible element with the role "link" and name "환경음 출처·라이선스"
Here are the accessible roles:
link:
Name "본문으로 건너뛰기":
<a
class="skip-link"
href="#main"
/>
Name "플레이 방법":
<a
href="#howto"
/>
--------------------------------------------------
banner:
Name "":
<header
class="header wrap"
/>
--------------------------------------------------
heading:
Name "단어를 맞춰라":
<h1 />
Name "소음 속 작은 단어,들리시나요?":
<h2
id="hero-title"
/>
Name "나의 소리 탐험 준비":
<h2
id="setup-title"
/>
Name "작은 단어를 발견하는 방법":
<h2
id="howto-title"
/>
Name "먼저, 귀 기울이기":
<h3 />
Name "기억한 단어 찾기":
<h3 />
Name "나의 발견 돌아보기":
<h3 />
--------------------------------------------------
main:
Name "":
<main
class="wrap"
id="main"
tabindex="-1"
/>
--------------------------------------------------
region:
Name "소음 속 작은 단어,들리시나요?":
<section
aria-labelledby="hero-title"
class="hero"
/>
Name "나의 소리 탐험 준비":
<section
aria-labelledby="setup-title"
class="setup-card"
/>
Name "작은 단어를 발견하는 방법":
<section
aria-labelledby="howto-title"
class="howto"
id="howto"
/>
--------------------------------------------------
paragraph:
Name "":
<p
class="eyebrow"
/>
Name "":
<p
class="lead"
/>
Name "":
<p />
Name "":
<p />
Name "":
<p
id="snr-help"
/>
Name "":
<p />
Name "":
<p />
Name "":
<p />
Name "":
<p />
Name "":
<p />
Name "":
<p />
--------------------------------------------------
emphasis:
Name "":
<em />
--------------------------------------------------
group:
Name "어떻게 답할까요?":
<fieldset
class="mode-field"
/>
--------------------------------------------------
radio:
Name "골고루 풀기":
<input
aria-label="골고루 풀기"
checked=""
name="mode"
type="radio"
value="mixed"
/>
Name "보기에서 선택":
<input
aria-label="보기에서 선택"
name="mode"
type="radio"
value="choice"
/>
Name "직접 입력":
<input
aria-label="직접 입력"
name="mode"
type="radio"
value="text"
/>
--------------------------------------------------
strong:
Name "":
<strong />
Name "":
<strong />
Name "":
<strong />
--------------------------------------------------
combobox:
Name "배경 환경":
<select
id="environment"
/>
Name "시작 난이도":
<select
id="starting-level"
/>
--------------------------------------------------
option:
Name "여덟 가지 일상 속으로":
<option
value="rotate"
/>
Name "길거리":
<option
value="street"
/>
Name "쇼핑몰":
<option
value="mall"
/>
Name "극장":
<option
value="theater"
/>
Name "백화점":
<option
value="department"
/>
Name "시장":
<option
value="market"
/>
Name "지하철":
<option
value="subway"
/>
Name "카페":
<option
value="cafe"
/>
Name "비 오는 거리":
<option
value="rain"
/>
Name "1단계 · 소리에 익숙해지기 · SNR +6 dB":
<option
value="1"
/>
Name "2단계 · 귀 기울이기 · SNR +3 dB":
<option
value="2"
/>
Name "3단계 · 소음 사이로 · SNR 0 dB":
<option
value="3"
/>
Name "4단계 · 작은 단서 찾기 · SNR -3 dB":
<option
value="4"
/>
Name "5단계 · 깊이 집중하기 · SNR -6 dB":
<option
value="5"
/>
--------------------------------------------------
slider:
Name "목표 음성 SNR":
<input
aria-describedby="snr-help"
id="snr"
max="12"
min="-12"
step="1"
type="range"
value="6"
/>
Name "전체 음량":
<input
id="volume"
max="100"
min="0"
type="range"
value="35"
/>
--------------------------------------------------
status:
Name "":
<output
for="snr"
/>
Name "":
<output
for="volume"
/>
--------------------------------------------------
button:
Name "게임 시작 · 소리 듣기":
<button
class="primary"
/>
--------------------------------------------------
article:
Name "":
<article />
Name "":
<article />
Name "":
<article />
--------------------------------------------------
contentinfo:
Name "":
<footer
class="footer wrap"
/>
--------------------------------------------------
Ignored nodes: comments, script, style
<body>
<div>
<a
class="skip-link"
href="#main"
>
본문으로 건너뛰기
</a>
<header
class="header wrap"
>
<div
class="brand"
>
<span
aria-hidden="true"
class="brand-mark"
>
<i />
<i />
<i />
<i />
</span>
<h1>
단어를 맞춰라
</h1>
<span
class="version"
>
v0.2.3
</span>
</div>
<div
class="header-actions"
>
<a
href="#howto"
>
플레이 방법 
<span
aria-hidden="true"
>
↓
</span>
</a>
<span
class="little-dot"
/>
소리에 집중하는 시간
</div>
</header>
<main
class="wrap"
id="main"
tabindex="-1"
>
<section
aria-labelledby="hero-title"
class="hero"
>
<div
class="hero-copy"
>
<p
class="eyebrow"
>
<span />
 귀를 기울이면, 발견하는 즐거움
</p>
<h2
id="hero-title"
>
소음 속 작은 단어,
<br />
<em>
들리시나요?
</em>
</h2>
<p
class="lead"
>
도시의 소리 사이에 에스가 숨겨 둔 단어.
<br />
조금 더 귀 기울여, 오늘의 행운을 찾아보세요.
</p>
<div
class="hero-meta"
>
<span>
한 라운드 
3
개의 질문
</span>
<span>
5단계 도전
</span>
<span>
나만의 속도로
</span>
</div>
</div>
<div
class="sound-room"
>
<div
class="room-top"
>
<span>
<i />
 에스의 사운드 룸
</span>
<span>
8
가지 환경
</span>
</div>
<div
class="orbit orbit-one"
/>
<div
class="orbit orbit-two"
/>
<span
aria-hidden="true"
class="floating-note note-one"
>
♪
</span>
<span
aria-hidden="true"
class="floating-note note-two"
>
✳
</span>
<div
class="speech-bubble"
>
행운의 단어를 찾아보거라!
</div>
<div
aria-hidden="true"
class="mascot "
>
<span
class="ear left"
/>
<span
class="ear right"
/>
<span
class="eye left"
/>
<span
class="eye right"
/>
<span
class="smile"
/>
<b>
S
</b>
</div>
<div
class="room-bottom"
>
<div
aria-hidden="true"
class="sound-wave "
>
<i
style="height: 10px; animation-delay: 0s;"
/>
<i
style="height: 33.799955450859244px; animation-delay: 0.05s;"
/>
<i
style="height: 16.132986448643948px; animation-delay: 0.1s;"
/>
<i
style="height: 32.219552375865575px; animation-delay: 0.15000000000000002s;"
/>
<i
style="height: 21.858720427326595px; animation-delay: 0.2s;"
/>
<i
style="height: 29.163690702963766px; animation-delay: 0.25s;"
/>
<i
style="height: 26.796992502245015px; animation-delay: 0.30000000000000004s;"
/>
<i
style="height: 24.835290693688798px; animation-delay: 0.35000000000000003s;"
/>
<i
style="height: 30.619883556555898px; animation-delay: 0.4s;"
/>
<i
style="height: 35.39139668035928px; animation-delay: 0.45s;"
/>
<i
style="height: 71.52943948029164px; animation-delay: 0.5s;"
/>
<i
style="height: 19.53593765210872px...
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ node_modules/@testing-library/dom/dist/query-helpers.js:76:38
❯ node_modules/@testing-library/dom/dist/query-helpers.js:52:17
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:420:17
418| it("환경음 저작자·라이선스 고지를 같은 출처 링크로 제공한다", () => {
419| fixture();
420| expect(screen.getByRole("link", { name: "환경음 출처·라이선스" })).toHaveAttr…
| ^
421| });
422|
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/5]⎯
FAIL tests/build.test.ts > 현장음 wav 손상은 빌드 실패로 처리한다
FAIL tests/build.test.ts > 현장음 manifest 손상은 빌드 실패로 처리한다
FAIL tests/build.test.ts > 현장음 notice 손상은 빌드 실패로 처리한다
FAIL tests/build.test.ts > 현장음 extra 손상은 빌드 실패로 처리한다
AssertionError: expected [Function verify] to throw an error
- Expected:
null
+ Received:
undefined
❯ tests/build.test.ts:76:20
74| if (damage === "notice") rmSync(join(temporary, "AMBIENCE_NOTICES.…
75| if (damage === "extra") writeFileSync(join(temporary, "ambience/or…
76| expect(verify).toThrow(/현장음/);
| ^
77| } finally { rmSync(temporary, {recursive: true, force: true}); }
78| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/5]⎯
Test Files 2 failed (2)
Tests 5 failed | 21 skipped (26)
Start at 08:08:41
Duration 1.71s (tests 64%, environment 23%, setup 8%, transform 3%, import 2%)
@@ -0,0 +1,15 @@
> find-word@0.2.3 build
> tsc --noEmit && vite build && node scripts/check-build.mjs
vite v8.3.4 building client environment for production...
transforming...
✓ 21 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-3VUCfIq1.js 247.59 kB │ gzip: 77.91 kB
✓ built in 178ms
빌드 음성 자산 101개 + 현장음 8개 및 런타임/현장음 notice 확인
@@ -0,0 +1,4 @@
> find-word@0.2.3 lint
> eslint . && tsc --noEmit
@@ -0,0 +1,13 @@
> find-word@0.2.3 lint
> eslint . && tsc --noEmit
src/App.tsx(249,54): error TS2367: This comparison appears to be unintentional because the types '"ready" | "loading" | "playing" | "answer" | "feedback"' and '"results"' have no overlap.
src/App.tsx(250,16): error TS2367: This comparison appears to be unintentional because the types '"ready" | "loading" | "playing" | "answer" | "feedback"' and '"results"' have no overlap.
tests/audio.test.ts(284,95): error TS2352: Conversion of type '(value: { duration: number; length: number; sampleRate: number; numberOfChannels: number; getChannelData: () => Float32Array<ArrayBuffer>; copyToChannel: Mock<(samples: Float32Array<...>) => void>; } | PromiseLike<...>) => void' to type '(buffer: AudioBuffer) => void' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Types of parameters 'value' and 'buffer' are incompatible.
Type 'AudioBuffer' is not comparable to type '{ duration: number; length: number; sampleRate: number; numberOfChannels: number; getChannelData: () => Float32Array<ArrayBuffer>; copyToChannel: Mock<(samples: Float32Array<ArrayBufferLike>) => void>; } | PromiseLike<...>'.
Type 'AudioBuffer' is not comparable to type '{ duration: number; length: number; sampleRate: number; numberOfChannels: number; getChannelData: () => Float32Array<ArrayBuffer>; copyToChannel: Mock<(samples: Float32Array) => void>; }'.
Types of property 'getChannelData' are incompatible.
Type '(channel: number) => Float32Array<ArrayBuffer>' is not comparable to type '() => Float32Array<ArrayBuffer>'.
Target signature provides too few arguments. Expected 1 or more, but got 0.
@@ -0,0 +1,23 @@
> find-word@0.2.3 test:browser
> playwright test tests/browser/release.spec.ts
[WebServer] (node:810785) 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 4 tests using 2 workers
(node:810805) 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:810806) 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)
✓ 2 [desktop] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다 (76ms)
✓ 1 [desktop] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (582ms)
(node:810893) 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:810963) 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)
✓ 3 [mobile] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (589ms)
✓ 4 [mobile] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다 (75ms)
4 passed (2.8s)
@@ -0,0 +1,79 @@
> find-word@0.2.3 test:browser
> playwright test tests/browser/release.spec.ts
[WebServer] (node:810330) 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 4 tests using 2 workers
(node:810350) 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:810351) 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)
✘ 2 [desktop] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다 (65ms)
✓ 1 [desktop] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (571ms)
(node:810452) 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:810501) 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)
✓ 3 [mobile] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (607ms)
✘ 4 [mobile] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다 (64ms)
1) [desktop] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다
Error: expect(received).toBe(expected) // Object.is equality
Expected: 404
Received: 200
25 | for (const directory of ["audio", "ambience"]) {
26 | const response = await request.get(`/${directory}/not-found.wav?v=0.2.3`, { headers: { Accept: "text/html" } });
> 27 | expect(response.status()).toBe(404);
| ^
28 | expect(await response.text()).not.toContain('<div id="root">');
29 | }
30 | const notice = await request.get("/AMBIENCE_NOTICES.md");
at /home/hermes/worktrees/find-word-v0.2.3/tests/browser/release.spec.ts:27:31
Error Context: test-results/release-누락-음성·환경음은-query와-HTML-accept에도-404이고-출처-고지는-번들된다-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/release-누락-음성·환경음은-query와-HTML-accept에도-404이고-출처-고지는-번들된다-desktop/trace.zip
Usage:
npx playwright show-trace test-results/release-누락-음성·환경음은-query와-HTML-accept에도-404이고-출처-고지는-번들된다-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2) [mobile] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다
Error: expect(received).toBe(expected) // Object.is equality
Expected: 404
Received: 200
25 | for (const directory of ["audio", "ambience"]) {
26 | const response = await request.get(`/${directory}/not-found.wav?v=0.2.3`, { headers: { Accept: "text/html" } });
> 27 | expect(response.status()).toBe(404);
| ^
28 | expect(await response.text()).not.toContain('<div id="root">');
29 | }
30 | const notice = await request.get("/AMBIENCE_NOTICES.md");
at /home/hermes/worktrees/find-word-v0.2.3/tests/browser/release.spec.ts:27:31
Error Context: test-results/release-누락-음성·환경음은-query와-HTML-accept에도-404이고-출처-고지는-번들된다-mobile/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/release-누락-음성·환경음은-query와-HTML-accept에도-404이고-출처-고지는-번들된다-mobile/trace.zip
Usage:
npx playwright show-trace test-results/release-누락-음성·환경음은-query와-HTML-accept에도-404이고-출처-고지는-번들된다-mobile/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2 failed
[desktop] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다
[mobile] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다
2 passed (2.9s)
@@ -0,0 +1,43 @@
> find-word@0.2.3 test:browser
> playwright test
[WebServer] (node:811225) 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 24 tests using 2 workers
(node:811278) 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:811277) 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)
✓ 1 [desktop] › tests/browser/audio.spec.ts:7:1 › 음성·현장음 109개 versioned WAV를 실제 Chrome에서 해시·디코드 검증한다 (18.5s)
✓ 3 [desktop] › tests/browser/game.spec.ts:130:3 › intro WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (12.6s)
✓ 2 [desktop] › tests/browser/game.spec.ts:8:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (40.4s)
✓ 5 [desktop] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (454ms)
✓ 6 [desktop] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다 (28ms)
✓ 4 [desktop] › tests/browser/game.spec.ts:130:3 › target WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (17.1s)
✓ 8 [desktop] › tests/browser/rounds.spec.ts:84:1 › 여덟 환경의 실제 WAV를 각각 디코드하고 루프 재생한 뒤 즉시 정지한다 (8.0s)
✓ 9 [desktop] › tests/browser/rounds.spec.ts:112:1 › 환경음 404는 한국어 오류와 재시도 후 정상 답변으로 복구한다 (16.2s)
✓ 10 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.6s)
✓ 11 [desktop] › tests/browser/ui.spec.ts:52:1 › 난이도 설명은 모바일에서도 보인다 (1.1s)
✓ 12 [desktop] › tests/browser/ui.spec.ts:68:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (17.5s)
(node:812211) 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)
✓ 7 [desktop] › tests/browser/rounds.spec.ts:29:1 › 시작 단계·SNR·3문항 완료→다음 단계·재생 중 종료·응답 후 종료·홈 초기화 (1.1m)
(node:812418) 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)
✓ 13 [mobile] › tests/browser/audio.spec.ts:7:1 › 음성·현장음 109개 versioned WAV를 실제 Chrome에서 해시·디코드 검증한다 (16.1s)
✓ 15 [mobile] › tests/browser/game.spec.ts:130:3 › intro WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (17.2s)
✓ 16 [mobile] › tests/browser/game.spec.ts:130:3 › target WAV query URL의 404를 표시하고 같은 URL로 재시도·정답 mapping한다 (12.6s)
✓ 17 [mobile] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (306ms)
✓ 18 [mobile] › tests/browser/release.spec.ts:24:1 › 누락 음성·환경음은 query와 HTML accept에도 404이고 출처 고지는 번들된다 (20ms)
✓ 14 [mobile] › tests/browser/game.spec.ts:8:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (37.7s)
✓ 20 [mobile] › tests/browser/rounds.spec.ts:84:1 › 여덟 환경의 실제 WAV를 각각 디코드하고 루프 재생한 뒤 즉시 정지한다 (6.1s)
✓ 21 [mobile] › tests/browser/rounds.spec.ts:112:1 › 환경음 404는 한국어 오류와 재시도 후 정상 답변으로 복구한다 (16.0s)
✓ 22 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (834ms)
✓ 23 [mobile] › tests/browser/ui.spec.ts:52:1 › 난이도 설명은 모바일에서도 보인다 (637ms)
✓ 24 [mobile] › tests/browser/ui.spec.ts:68:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (17.0s)
✓ 19 [mobile] › tests/browser/rounds.spec.ts:29:1 › 시작 단계·SNR·3문항 완료→다음 단계·재생 중 종료·응답 후 종료·홈 초기화 (57.4s)
24 passed (3.4m)

Some files were not shown because too many files have changed in this diff Show More