feat: release Find-Word v0.2.3

This commit is contained in:
oz
2026-10-14 10:01:46 +00:00
parent c92660b514
commit 812540c743
531 changed files with 103693 additions and 325 deletions
+33 -16
View File
@@ -1,8 +1,8 @@
# Find-Word v0.1.1 설계
# Find-Word v0.2.3 설계
## 데이터와 출제
`src/content.ts`는 패턴, 질문, 단어, 오답 규칙, 배경 프리셋, 상대 dB를 서로 다른
`src/content.ts`는 패턴, 질문, 단어, 오답 규칙, 현장음 URL, 상대 dB를 서로 다른
배열로 보관한다. `renderSentence`가 각 패턴의 `{word}`를 정답으로,
`{distractor1}`·`{distractor2}`를 해당 보기의 오답으로 채운다. 같은 함수를
음성 생성기도 사용하며 런타임 문장과 WAV의 대응은 manifest 테스트가 검증한다.
@@ -29,8 +29,9 @@
목표 반복 횟수, 취소·조건·방향 단서와 질문 의도를 검증한다.
`src/game.ts`의 `createQuestions`는 seed 기반 PRNG와 Fisher–Yates로
유형·단어·정답 위치를 섞는다. 한 세션에서 유형과 정답 단어는 각각 한 번씩
나온다. 100개 문장 조합에 응답 방식·환경·난이도를 독립적으로 결합한다.
유형·단어·정답 위치를 섞는다. 한 라운드는 3문항이며 유형과 정답 단어가 중복되지 않는다. 시작 단계는 1~5로
정규화하며 세 문항에 같은 level/db를 적용한다. 혼합 모드는 선택/입력/선택이다.
환경 순환은 seed로 여덟 곳을 섞어 세 곳을 선택한다. 100개 문장 조합에 응답 방식·환경·난이도를 독립적으로 결합한다.
오답의 `rule`은 `RULE_DIFFICULTY`에 연결된다. 음절 교체 1, 초성 교체 2,
모음 교체 3, 받침 교체 4는 편집상 예상 혼동 순서이며 검증된 청각 척도가
@@ -49,9 +50,23 @@ stateDiagram-v2
answer --> loading: 다시 듣기
answer --> feedback: ANSWER
feedback --> ready: NEXT (다음 문제)
feedback --> results: NEXT (마지막 문제)
feedback --> results: NEXT (세 번째 문제)
ready --> results: EXIT
loading --> results: EXIT
playing --> results: EXIT
answer --> results: EXIT
feedback --> results: EXIT
```
정상 결과는 다음 단계(최대 5)의 새 라운드로 연결되며 중도 종료는 응답 수·정답 수를
표시한 뒤 홈으로 연결된다. 무응답 정답률은 `— / 응답 없음`, 응답하지 않은 단계는 `미진행`으로 표시한다.
중도 종료 제목과 현재 단계 완료 제목을 구분한다. 진행 막대는 답변 수가 아닌 현재 문항의 1/3, 2/3, 3/3이다.
결과 단계는 모든 지연 이벤트를 무시한다.
SNR은 −12~+12 dB 정수로 제한하고 NaN/Infinity는 단계 기본값으로 복구한다.
수동 SNR은 재생 도중 현재 gain을 바꾸지 않고 현재 재청취/남은 문항/다음 라운드에
적용한다. `heardDb`는 실제 재생 시작값을 보존해 결과를 정확히 기록한다. 홈은
수동 SNR·답·입력·IME·토큰을 초기화하고 시작 단계·모드·환경·전체 음량은 유지한다.
허용되지 않은 이벤트는 동일 상태를 반환한다. 재생 전/중에는 채점할 수 없고,
피드백 이후의 중복 답변·다음 클릭도 한 번만 반영된다. UI에서도 버튼 비활성화와
동기 ref 잠금을 함께 사용한다. 세션 토큰으로 처음 화면 복귀·unmount 뒤 완료된
@@ -63,7 +78,7 @@ Promise를 무시한다. 질문과 보기는 재생 완료 후 DOM에 생성되
입력을 HTML로 해석하지 않는다.
점수는 정답 시 `100 × 단계 + 10 × min(연속 정답 - 1, 4)`이다. 오답은 0점,
현재 연속 정답을 초기화한다. 최고 연속 정답과 난이도별 정답 수는 유지한다.
현재 연속 정답을 초기화한다. 기존 공식의 cap은 40점이며 세 문항 라운드에서는 문항별 최대 20점, 라운드 합계 최대 30점(0 + 10 + 20)이다. 1단계 세 정답은 330점이다. 최고 연속 정답과 난이도별 정답 수는 유지한다.
## 오디오
@@ -73,26 +88,28 @@ Promise를 무시한다. 질문과 보기는 재생 완료 후 DOM에 생성되
[AUDIO_PROVENANCE](AUDIO_PROVENANCE.md)에 있으며 앱은 provider에 연결하지 않는다.
`src/audio/dsp.ts`는 순수 수치 함수이며 장치가 없어도 테스트할 수 있다.
정수 seed 잡음에 저역 통과 필터, 사인파, 진폭 변조를 조합하고 양 끝을 페이드한다.
8개 프리셋은 실제 현장 녹음이 아니라 환경을 추상적으로 표현한 합성 신호다.
8개 현장 녹음 WAV를 디코드해 재생한다. 각 12초, 24kHz mono PCM16이며 양끝
50ms fade로 루프 접합 클릭을 줄인다. 절차적 잡음 합성 코드는 제거했다.
`src/audio/engine.ts`는 클릭 이벤트에서 AudioContext 생성·resume을 호출한 뒤
같은 출처의 WAV를 로드한다. mono PCM을 decode하고 음성과 배경의 전체 버퍼
RMS를 0.03으로 정규화한다. `gain = 10^(dB/20)`으로 목표 gain을 지정한다.
RMS를 0.03으로 정규화한다. `gain = 10^(dB/20)`으로 목표/배경 비율을 지정한다. 정규화한 세 신호의 최대
피크 합을 계산해 모든 경로를 공통 감쇠하므로 합계 peak ≤ 0.8이고 SNR이 유지된다.
시간별·주파수별 청감이나 물리적 dB SPL을 보장하는 수치는 아니다.
`src/audio/assets.ts`는 음성 자산 버전과 URL 생성을 한 곳에서 관리한다.
intro와 목표 100개 모두 `?v=0.1.1`을 붙여 동일 파일명에 남은 이전 캐시를
intro·목표 100개·현장음 8개 모두 `?v=0.2.3`을 붙여 동일 파일명에 남은 이전 캐시를
피한다. 파일을 다시 생성하면 제품 버전 유지 여부와 관계없이 자산 버전을
갱신한다. 버전 확인을 위한 외부 요청이나 런타임 manifest 요청은 없다.
```text
배경 BufferSource → 배경 Gain (1, 시작/끝 fade) ─┐
목표 BufferSource → 음성 Gain (10^(dB/20)) ─────┼→ master Gain → destination
안내 BufferSource → 안내 Gain (1.8 → 0.12) ─────┘
배경 BufferSource → 배경 Gain (공통 감쇠, 시작/끝 fade) ─┐
목표 BufferSource → 음성 Gain (공통 감쇠 × 10^(dB/20)) ─────┼→ master Gain → destination
안내 BufferSource → 안내 Gain (공통 감쇠 × 1.8 → 0.12) ─────┘
```
첫 문제에서는 웃음과 안내 음성을 낮추어 배경에 묻히게 한 후 목표 문장을
배경 소스는 `loop=true`이며 목표 ended에서 즉시 stop/disconnect한다.
각 라운드의 첫 문제에서는 웃음과 안내 음성을 낮추어 배경에 묻히게 한 후 목표 문장을
재생한다. 이후에는 목표만 재생한다. 안내 도중 실패했으면 재시도 시 안내도
다시 재생한다. 기본 master는 35%이며 음량 조절은 두 신호의 비율을 유지한다.
@@ -116,7 +133,7 @@ reduced motion에서는 파형이 움직이지 않는다. 오류는 role=alert
## 검증 경계
Vitest는 실제 RED/GREEN으로 데이터·채점·상태·오디오 그래프·화면을 검증한다.
Playwright는 production build와 실제 Chrome AudioContext로 두 크기의 10문제
완주·재청취·오류 복구·axe 검사를 수행한다. 테스트 자동화는 한국어 발음의
Playwright는 production build와 실제 Chrome AudioContext로 두 크기의 3문항
완주·다음 단계·SNR·중도 종료·재청취·오류 복구·axe 검사를 수행한다. 테스트 자동화는 한국어 발음의
자연스러움, 실제 사용자의 청각 난이도, 모바일 Safari의 장치별 정책까지
증명하지 않으므로 인수 시 직접 청취를 추가한다.
+133 -6
View File
@@ -1,4 +1,4 @@
# Find-Word v0.1.1 오디오 출처와 재현
# Find-Word v0.2.3 오디오 출처와 재현
## 포함 자산과 생성 설정
@@ -6,7 +6,7 @@
| --- | --- | --- | --- |
| `public/audio/intro.wav` | Microsoft Edge online TTS neural 음성 | `src/content.ts`의 INTRO | 24kHz mono PCM16 WAV |
| 목표 음성 100개 | 같은 provider·voice | 10 패턴 × 10 단어, `renderSentence` | 같은 WAV |
| 8개 배경 환경 | 직접 작성한 결정론적 DSP | `ENVIRONMENTS`, 고정 seed | 브라우저 메모리에서 합성 |
| 8개 배경 환경 | 공개 라이선스 실제 현장 녹음 | `public/ambience/manifest.json` | 12초·24kHz mono PCM16 WAV |
| 화면 파형·S 캐릭터 | 직접 작성한 CSS | `App.tsx`, `styles.css` | CSS |
voice는 **`ko-KR-HyunsuMultilingualNeural`**, rate는 **`-6%`**, pitch는
@@ -69,8 +69,8 @@ MP3를 24kHz mono signed 16-bit little-endian PCM으로 디코드한다. 파일
생성기와 독립 자산 테스트는 101개 파일의 원문·집합·SHA-256·bytes·duration,
24kHz mono PCM16, 1–20초 길이, RMS > 100/32768, full-scale 미도달,
총 48MiB 미만을 검사한다. 런타임 RMS 0.03과 최대 +6dB에서도 peak < 0.8을
요구한다. 기존 앱은 Web Audio에서 배경·음성을 RMS 정규화한 뒤 상대 SNR을 적용한다.
총 48MiB 미만을 검사한다. 기존 음성 단독 검사는 RMS 0.03과 +6dB에서 peak < 0.8을
요구한다. 0.2.3의 사용자 최대 +12dB 혼합은 세 경로 공통 감쇠로 별도 검증한다. 앱은 Web Audio에서 배경·음성을 RMS 정규화한 뒤 상대 SNR을 적용한다.
물리적 음압이나 주파수별 청감을 보정한 값은 아니다.
## 도구 라이선스와 서비스 조건
@@ -99,5 +99,132 @@ MP3를 24kHz mono signed 16-bit little-endian PCM으로 디코드한다. 파일
자동 검증은 PCM 신호·무결성·재생 흐름을 확인한다. 한국어 발음, 유사 자모의
구분, 웃음의 자연스러움과 실제 사용자의 난이도는 청취 평가가 필요하다.
대표 샘플과 게임 내 소음 혼합 상태를 직접 들어 인수 판단한다. 배경은 장소를
추상화한 합성 신호다. 이 게임의 수치는 임상 검증·치료·청력 판정용이 아니다.
대표 샘플과 게임 내 소음 혼합 상태를 직접 들어 인수 판단한다. 배경은 실제 현장 녹음의 짧은 구간이다. 이 게임의 수치는 임상 검증·치료·청력 판정용이 아니다.
## v0.2.3 현장 녹음 파생본
원본 선정 계약은 [.agent/AMBIENCE-SOURCES-v0.2.3.json](../.agent/AMBIENCE-SOURCES-v0.2.3.json),
배포 바이트의 출처·직접 URL·저작자·라이선스·변환·SHA-256·bytes·duration·RMS·peak는
[manifest](../public/ambience/manifest.json), 정확한 attribution과 변경 고지는
[AMBIENCE_NOTICES](../public/ambience/AMBIENCE_NOTICES.md)에 있다. 이 고지는 dist에도 포함되고 UI에서 링크한다.
여덟 원본 모두 서로 다른 현장 녹음이며 NC/ND 자산을 사용하지 않는다. 원본은 저장소/빌드에 포함하지 않는다.
| 환경 | 저작자 | 라이선스 | 원본 구간(초) |
| --- | --- | --- | --- |
| 길거리 | cori (Cori Samuel) | PD-author | 35–47 |
| 쇼핑몰 | thore (Thore) | PD-author | 36–48 |
| 극장 | ElevatorFan2020 | CC0 1.0 | 45–57 |
| 백화점 | icmusic | CC0 1.0 | 90–102 |
| 시장 | Marie Flutre | CC BY-SA 4.0 | 24–36 |
| 지하철 | PascalHD | CC BY 4.0 | 65–77 |
| 카페 | Marble Toast | CC0 1.0 | 0–12 |
| 비 오는 거리 | Extemporalist | CC0 1.0 | 180–192 |
Commons primary 페이지/API와 Freesound primary 페이지의 라이선스 근거를 evidence에 보존한다.
Freesound 두 원본은 지정된 preview MP3이다. 프로젝트의 비공개 라이선스를 제3자 녹음에
적용하지 않으며, 시장 파생 WAV는 **CC BY-SA 4.0**으로 계속 공개·수정·재배포할 수 있다.
지하철은 **CC BY 4.0**의 PascalHD credit·변경 고지·라이선스 링크를 유지한다.
재현: 검증한 원본 8개를 `/tmp/findword-ambience-sources`에 놓고
`python3 scripts/ambience_audio.py`를 실행한다. 계획의 파일별 `selectedSegmentStartSeconds`에서 12초를 선택하고,
FFmpeg로 mono 24kHz float PCM 디코드 후 양끝 50ms 선형 fade, RMS 목표 .08,
최대 증폭 4배/peak .85 중 가장 작은 선형 gain을 적용한다. 마지막은 표준 44-byte
WAV 헤더의 PCM16이다. 시간 변형·절차적 합성·원본 음성 대체는 없다.
원본 SHA-256과 실측 FFmpeg 버전을 함께 기록하므로 같은 원본/도구로 재현 가능하다.
런타임은 음성/배경 각각 RMS .03 후 상대 gain `10^(dB/20)`을 적용한다. −12~+12 dB
범위에서도 세 경로의 최악 피크 합을 공통 감쇠하여 최대 .8로 제한한다. 양끝 fade가
있는 배경을 loop하고 목표 끝에서 정리한다. 전체 버퍼 RMS 기준이며 짧은 시간창별
SNR이나 실제 음압을 보증하지 않는다. 원본 녹음의 우연한 사람 말소리는 환경의 일부다.
자동 검사로 음악·개인 대화의 완전한 부재를 인증할 수 없으므로 사람의 최종 청취 검토가 필요하다.
기존 neural WAV 101개의 원문·해시는 v0.1.1 그대로다. 음성 manifest의 version은
배포 버전 0.2.3, `generatedForVersion`은 0.1.1이며 원래 생성 시각·도구 정보는 보존한다.
### 리뷰 후 출처 고정과 원자 발행
승인 계획의 `expectedSourceSha256`와 `sourceBytes`는 불변 입력이다. 생성기는 로컬 경로나
현재 파일에서 이 승인값을 새로 기록하지 않으며, 여덟 원본의 해시·크기를 **첫 변환 전**에 모두 검사한다.
한 원본이라도 다르거나 누락되면 실패한다. `sourceVerification`은 검증 상태·일자·방법·보존된 증거를 가리킨다.
임시 경로를 담던 `verifiedLocalSource` 필드는 제거했다.
`directMediaDurationSeconds`는 **directUrl에서 받은 바로 그 바이트**의 ffprobe `format.duration`이다.
Freesound 항목은 preview MP3의 길이이며 primary 페이지 원본 길이 또는 반올림한 표시 길이가 아니다.
배포 manifest의 `duration`과 `conversion.durationSeconds`는 파생 WAV의 길이(12초),
`selectedSegmentStartSeconds`는 파일별 불변 승인 시작점이며 생성기는 별도 hardcoded map 없이 이 값을 읽는다.
`conversion.startSeconds`는 같은 값이어야 한다. 시작점은 유한한 0 이상 수이고, 시작점+12초가
`directMediaDurationSeconds` 이하여야 한다. 누락·음수·NaN·Infinity·문자열·bool·범위 초과는 첫 변환 전에 거부한다. 원본 전체 디코드와 실제 수치는
[보존된 source-integrity.json](../.agent/evidence-v0.2.3/source-integrity.json)에 있다.
생성기는 출력 폴더와 같은 부모의 `.ambience-stage-*/ready`에서 8 WAV·manifest·`AMBIENCE_NOTICES.md`를
완성한다. 정확한 파일 집합, 고유 해시, 원본 계획, PCM 형식·길이·fade·RMS·peak,
항목별 저작자/primary URL/direct URL/라이선스 링크/outputLicense/변경 설명을 다시 검사한다.
존재하는 symlink 및 dangling symlink 출력을 모두 거부한다. Linux `renameat2(RENAME_EXCHANGE)` 한 번이 교체의
commit 지점이며, 최초 생성은 같은 파일시스템의 rename 한 번이다. 지원하지 않는 환경에서는 실패한다.
파일별 overlay를 하지 않으므로 오래된 파일이 남지 않고 고지도 음원과 같은 디렉터리로 교체된다.
commit 전 변환·쓰기·검증·교환 실패 또는 중단은 이전 디렉터리를 보존한다. syscall 이후에는 완성된 새 디렉터리가
공개된 상태다. SIGKILL 등 강제 종료는 숨은 임시 디렉터리를 남길 수 있지만 부분 배포는 만들지 않는다.
전원 손실에 대한 디스크 내구성이나 여러 HTTP 요청에 걸친 snapshot 격리를 주장하지 않는다.
앞선 독립 리뷰에서는 기존 109 WAV를 재생성하지 않았다. 이후 아래 privacy refinement에서
전체 현장음 bundle을 원자 재생성했으며 mall/market/cafe 3개만 바이트가 바뀌었다.
**사람의 현장음 청취 인수는 미실행**이다.
### 최종 privacy excerpt refinement
현재 선택은 **mall 36–48초, market 24–36초, cafe 0–12초**다. Neo가 동일한
`scripts/ambience_audio.convert` 경로와 아래 ASR 설정으로 해시 고정 원본을 재검사해 제시한
market/cafe 후보를 사용했다. 제공받은 재검사 설명은
[selection-context.json](../.agent/evidence-v0.2.3/privacy-screening/retry-market24-cafe0/selection-context.json)에
별도로 기록했으며, 원본 후보 결과를 최종 WAV 검사 결과로 대신하지 않았다.
승인 원본 8개의 SHA-256·bytes·ffprobe 길이를 먼저 대조하고 하드닝된 생성기로
8 WAV·manifest·notice 전체를 staging/검증/원자 교환했다.
[생성 실측 기록](../.agent/evidence-v0.2.3/privacy-screening/retry-market24-cafe0/regeneration.json)에
현재 해시·RMS·peak·변환이 있다. 이번 재시도는 market/cafe 두 WAV만 변경했다.
**101개 voice, 다섯 unaffected ambience, 기존 mall 36초 WAV는 모두 바이트 동일**하다.
일부 Ogg 구간은 FFmpeg timestamp/seek 때문에 12초보다 몇 프레임 짧게 출력된다.
짧은 결과일 때만 처음부터 디코드한 24kHz PCM의 `round(start × 24000)`부터 정확한 288,000개
sample을 선택하고 출력 PTS를 sample 수로 재설정한다. silent padding이나 time stretching은 없다.
정말 원본이 짧으면 여전히 실패하며 `conversion.seekMethod`가 실제 경로를 기록한다.
[final-asr.json](../.agent/evidence-v0.2.3/privacy-screening/final-asr.json)은 정확한 현재 8 WAV를
로컬 **faster-whisper 1.2.1, tiny multilingual, CPU int8, beam_size=5, best_of=5,
기본 Silero VAD 활성화, condition_on_previous_text=true**로 검사한 원문이다.
모델 revision `d90ca5fe260221311c53c58e660288d3deb8d356`, 모델/VAD 파일 해시,
라이브러리 버전·전체 설정·각 입력 해시·반환 segment/info를 저장했다.
이전 실패 실행과 모든 decoder/VAD 인자가 같은지 스크립트가 대조한다.
**mall/market/cafe 각각 segment 0개, duration_after_vad=0.0, requiredEmptyPassed=true**다.
다른 다섯 WAV도 segment 0개다. threshold 조절이나 반환 segment 삭제는 하지 않았다.
테스트는 지정 시작점·true gate·VAD 설정·실제 WAV/manifest 해시와 아래 false 인수값을 함께 강제한다.
**humanListeningAcceptance=false, legalPrivacyClearance=false**를 유지한다.
Tiny-model ASR은 screening signal일 뿐이며 빈 결과는 말소리·음악·개인 대화가 없음을 증명하지 않는다.
출력 text/language도 환각일 수 있고 충실한 전사나 화자 식별로 주장하지 않는다.
독립적인 사람 청취 인증은 수행하지 않았다. 다만 제품 소유자 Oz는 2026-10-14 제공된
최종 WAV 8개 청취 검수 묶음과 명시된 자동 검사 한계를 기준으로 현재 자산 그대로 릴리스를
진행하도록 승인했다. 이 승인은 제3자의 법적/privacy clearance를 대신하지 않는다.
이전 market 60–72초/cafe 180–192초 실행은 market 3개/cafe 1개 segment를 반환하여
**requiredEmptyPassed=false**였으며 release acceptance 실패였다.
그 실행의 모든 screening 파일은 읽기 전용
[prior-attempt-market60-cafe180](../.agent/evidence-v0.2.3/privacy-screening/prior-attempt-market60-cafe180/)
아래 바이트 그대로 보존했다. [당시 final-asr.json](../.agent/evidence-v0.2.3/privacy-screening/prior-attempt-market60-cafe180/final-asr.json)을
성공으로 덮어쓰지 않았고 회귀 테스트가 이전 실패 상태와 모든 보존 파일 해시를 검사한다.
[기존 no-VAD 증거](../.agent/evidence-v0.2.3/privacy-screening/no-vad-asr.json)도 바이트 그대로 남겼다.
이 결과는 mall 36초/market 60초/cafe 180초에서 환각일 수 있는 candidate text를 냈으며,
새 market/cafe WAV의 검사 결과가 아니다. **no-VAD 결과는 지정된 VAD gate를 대체하거나 뒤집지 않는다.**
두 방법 모두 말소리/음악의 부재를 증명하지 않으며 사람의 청취 인수나 법적/privacy clearance를 제공하지 않는다.
Neo의 [이전 후보 scan](../.agent/evidence-v0.2.3/privacy-screening/candidate-scan-original.json),
[이전 파생본 ASR](../.agent/evidence-v0.2.3/privacy-screening/prior-derivatives-asr-original.json),
[당시 provenance](../.agent/evidence-v0.2.3/privacy-screening/earlier-asr-provenance.json)도 변경하지 않았다.
raw JSON에 없던 설정이나 입력 연결을 사후에 있었던 것으로 주장하지 않는다.
새 RED → GREEN, 전체 검증과 보존 기록은
[retry-market24-cafe0](../.agent/evidence-v0.2.3/privacy-screening/retry-market24-cafe0/),
현재 검증 요약은 [RESULT](../.agent/RESULT-v0.2.3.md)의 마지막 절에 있다.
+35 -5
View File
@@ -1,4 +1,4 @@
# Find-Word v0.1.1 정적 배포 인계
# Find-Word v0.2.3 정적 배포 인계
이 문서는 **Neo가 검토 후 수행할 절차**다. Codex는 서버, DNS, 인증정보에
접근하지 않았고 배포·서비스 생성·Git commit/push도 하지 않았다.
@@ -7,8 +7,9 @@
Node.js 최소 **22.22.2** (`>=22.22.2`)의 지원 LTS 환경에서 `npm ci`, `npm run lint`, `npm test`,
`npm run build`를 실행한다. `dist/` 전체를 하나의 릴리스로 전달한다.
`dist/audio/`의 24kHz neural WAV 101개와 version 0.1.1 manifest가 반드시 포함되어야 한다. 런타임은
Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·API 키는 필요 없다.
`dist/audio/`의 24kHz neural WAV 101개와 version 0.2.3 manifest가 반드시 포함되어야 한다.
`dist/ambience/`의 고유 현장 WAV 8개·manifest와 `dist/ambience/AMBIENCE_NOTICES.md`도 필수다.
런타임은 Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·API 키는 필요 없다.
잠긴 jsdom 계열 의존성 때문에 24.x는 24.15.0 이상, 26.x는 26.0.0 이상이어야 한다.
브라우저 검증의 Chromium sandbox는 기본 활성화다. sandbox를 지원하지 않는
격리된 개발 환경에서만 `PLAYWRIGHT_NO_SANDBOX=1`로 명시적으로 끈다.
@@ -55,6 +56,12 @@ server {
expires 7d;
}
# 현장음도 누락 시 SPA HTML로 대체하지 않는다.
location /ambience/ {
try_files $uri =404;
expires 7d;
}
# SPA의 직접 진입·새로고침을 index.html로 받되 HTML은 재검증한다.
location / {
try_files $uri $uri/ /index.html;
@@ -67,9 +74,9 @@ server {
1. 서버에 적용하기 전 `nginx -t`, 기존 서비스 충돌, 파일 읽기 권한을 확인한다.
2. TLS/HTTPS와 DNS를 운영자가 확인한다. 실제 도메인에서 시작 후 음성이 들리는지 확인한다.
3. `audio/intro.wav?v=0.1.1`와 목표 WAV의 같은 버전 query가 정상 응답하고
3. `audio/intro.wav?v=0.2.3`와 목표 WAV의 같은 버전 query가 정상 응답하고
존재하지 않는 오디오는 query가 있어도 404인지 확인한다.
4. 모바일 Safari/Chrome, 데스크톱에서 음량·중단·다시 듣기·10문제 완주를 직접 확인한다.
4. 모바일 Safari/Chrome, 데스크톱에서 음량·중단·다시 듣기·3문항 완주·다음 난이도·시작 단계·SNR 변경·중도 종료를 직접 확인한다.
5. 원자적으로 릴리스 디렉터리를 전환하고 이전 디렉터리를 보관해 되돌릴 수 있게 한다.
오프라인 캐시를 제공하는 PWA는 아니다. 정적 자산의 최초 로드에는 해당 서버
@@ -77,3 +84,26 @@ server {
같은 파일명으로 WAV를 교체할 때 `src/audio/assets.ts`의 `AUDIO_ASSET_VERSION`도
갱신한다. 제품 버전을 유지하는 음성 수정에서도 새 자산 버전이 필요하다.
이 query는 로컬 정적 파일 요청에만 사용하며 런타임 외부 요청을 추가하지 않는다.
## 0.2.3 독립 인수와 릴리스 (Neo 수행, 이 작업에서는 미실행)
1. `.agent/RESULT-v0.2.3.md`와 RED/GREEN 원문, 원본/배포 해시, 라이선스 고지를 검토한다.
2. sandboxed system Chrome desktop/mobile suite와 `npm audit`를 다시 실행한다.
3. 검토 완료 후 feature branch push, main fast-forward, annotated `v0.2.3` tag,
fresh-clone readback으로 commit과 자산 해시를 확인한다.
4. commit-addressed release 디렉터리로 dist를 복사하고 current 심볼릭 링크를 원자 전환한다.
이전 release는 rollback 대상으로 보존한다.
5. 공개 HTTPS에서 title/UI 0.2.3, WAV MIME·SHA-256·cache-busting query, 누락 자산 404,
시작 단계·3문항 완료→다음 단계·SNR·중도 종료를 실제 Chrome으로 재검증한다.
이 문서의 Nginx 설정은 인계 예시이며 실행 증거가 아니다. 이번 작업은 localhost의
production preview만 사용하며 Git commit/push/merge/tag, 외부 배포, secrets 사용은 하지 않는다.
v0.2.3 리뷰 검증은 `.agent/evidence-v0.2.3/review-fixes/`에 별도로 보존한다.
`check-build`는 앱 콘텐츠로 구성한 **정확히 101개**의 음성 파일명과 dist 파일 집합을 독립 대조하므로,
manifest와 디렉터리를 함께 축약하거나 추가 WAV를 넣어도 실패한다. 현장음은 승인 계획의 출처 수치와
각 notice 항목의 저작자·primary URL·direct URL·라이선스 링크·outputLicense·변경 고지까지 검사한다.
현장음 notice의 배포 URL은 `/ambience/AMBIENCE_NOTICES.md`다. 고지는 음원/manifest와 함께
원자 교체하는 같은 디렉터리 안에 있으며, 기존 루트의 notice 사본은 제거했다.
+3 -1
View File
@@ -1,4 +1,4 @@
# 단어와 오답 변형
# Find-Word v0.2.3 단어와 오답 변형
임의의 한글 조합을 자동 생성하지 않고 사전에 있는 표제어만 고른다. 다음의
짧은 의미 구분은 앱 작성자가 작성한 설명이며 사전의 정의·예문·음성을
@@ -38,3 +38,5 @@
기록한다. 각 묶음의 세 오답은 서로 다른 규칙과 순위를 가진다. 이 순서는
출제 메타데이터이며 청감·발음·청력에 대한 실측 난이도나 임상 척도가 아니다.
새 단어를 넣을 때는 표제어·의미·변형 규칙을 검토하고 이 목록을 갱신한다.
0.2.3은 기존 10가지 mechanic·100문장을 보존하며 한 라운드에서 서로 다른 세 문항을 고른다.