WolfDavid commited on
Commit
6bacf18
Β·
1 Parent(s): eeb4e4d

chore(01-08): surface the synthesiser warm-up time in the app log and record the SC-4 guard

Browse files

app.py configures INFO logging so the warm-up line blocks.warm_synthesizer emits on the
first page load is visible in the Space log (measured 2.12 s in a fresh process). Marks
the SC-4 static-guard row green in 01-VALIDATION.md and logs the deferred items found
during the plan.

.planning/phases/01-voice-avatar-loop-skeleton/01-VALIDATION.md CHANGED
@@ -66,7 +66,7 @@ the planner MUST bind each row to a task and the executor MUST fill in Status.
66
  | VOIC-05 | p50/p95 over β‰₯30 warm turns against the deployed Space β†’ `docs/LATENCY.md` | harness (deployed) | `pytest tests/e2e/test_latency_harness.py --space-url=$SPACE_URL` | ❌ W0 | ⬜ pending |
67
  | VOIC-05 | Cold-start turn measured after Space pause/restart | **manual (deployed)** | record in `docs/LATENCY.md` with Space revision SHA | ❌ W0 | ⬜ pending |
68
  | SC-4 | Full loop completes with `DISABLE_GPU=1` on the Space | E2E (deployed) | `pytest tests/e2e/test_avatar_loop.py -x` against GPU-disabled Space | ❌ W0 | ⬜ pending |
69
- | SC-4 | Static guard: no `@spaces.GPU` anywhere on the turn path | unit | `pytest tests/test_no_gpu_on_turn_path.py -x` (AST scan) | ❌ W0 | ⬜ pending |
70
  | DPLY-01 | Public Space returns HTTP 200 and the app shell paints | E2E (deployed) | `pytest tests/e2e/test_avatar_loop.py::test_space_reachable -x` | ❌ W0 | ⬜ pending |
71
  | DPLY-04 | Credit string `VOICEVOX:γšγ‚“γ γ‚‚γ‚“` + flow-down terms notice present and visible | E2E | `pytest tests/e2e/test_avatar_loop.py::test_credits_visible -x` | ❌ W0 | ⬜ pending |
72
  | DPLY-04 | `vrm.meta` embedded license matches the `LICENSES.md` entry | E2E | `pytest tests/e2e/test_avatar_loop.py::test_vrm_meta_matches_licenses -x` | ❌ W0 | ⬜ pending |
@@ -81,7 +81,7 @@ the planner MUST bind each row to a task and the executor MUST fill in Status.
81
  - [ ] `tests/conftest.py` β€” canned `AudioQuery` JSON, golden timeline, silence/noise/speech WAV fixtures, `--space-url` option
82
  - [ ] `tests/test_visemes.py` β€” AVTR-02, VOIC-03 (densest test file in the phase)
83
  - [ ] `tests/test_tts_contract.py` β€” VOIC-01
84
- - [ ] `tests/test_no_gpu_on_turn_path.py` β€” SC-4 static AST scan
85
  - [ ] `tests/e2e/test_avatar_loop.py` β€” AVTR-01, VOIC-02/03/04/05, DPLY-01/04
86
  - [ ] `tests/e2e/test_latency_harness.py` β€” VOIC-05, emits `docs/LATENCY.md`
87
  - [ ] Fixture audio β€” Japanese speech WAV, 30s silence, 30s cafΓ© noise
 
66
  | VOIC-05 | p50/p95 over β‰₯30 warm turns against the deployed Space β†’ `docs/LATENCY.md` | harness (deployed) | `pytest tests/e2e/test_latency_harness.py --space-url=$SPACE_URL` | ❌ W0 | ⬜ pending |
67
  | VOIC-05 | Cold-start turn measured after Space pause/restart | **manual (deployed)** | record in `docs/LATENCY.md` with Space revision SHA | ❌ W0 | ⬜ pending |
68
  | SC-4 | Full loop completes with `DISABLE_GPU=1` on the Space | E2E (deployed) | `pytest tests/e2e/test_avatar_loop.py -x` against GPU-disabled Space | ❌ W0 | ⬜ pending |
69
+ | SC-4 | Static guard: no `@spaces.GPU` anywhere on the turn path | unit | `pytest tests/test_no_gpu_on_turn_path.py -x` (AST scan) | βœ… 01-08 | βœ… green (3 passed, 2026-09-06; the ZeroGPU probe in app.py is carved out by name and proven unreachable from `blocks.turn`) |
70
  | DPLY-01 | Public Space returns HTTP 200 and the app shell paints | E2E (deployed) | `pytest tests/e2e/test_avatar_loop.py::test_space_reachable -x` | ❌ W0 | ⬜ pending |
71
  | DPLY-04 | Credit string `VOICEVOX:γšγ‚“γ γ‚‚γ‚“` + flow-down terms notice present and visible | E2E | `pytest tests/e2e/test_avatar_loop.py::test_credits_visible -x` | ❌ W0 | ⬜ pending |
72
  | DPLY-04 | `vrm.meta` embedded license matches the `LICENSES.md` entry | E2E | `pytest tests/e2e/test_avatar_loop.py::test_vrm_meta_matches_licenses -x` | ❌ W0 | ⬜ pending |
 
81
  - [ ] `tests/conftest.py` β€” canned `AudioQuery` JSON, golden timeline, silence/noise/speech WAV fixtures, `--space-url` option
82
  - [ ] `tests/test_visemes.py` β€” AVTR-02, VOIC-03 (densest test file in the phase)
83
  - [ ] `tests/test_tts_contract.py` β€” VOIC-01
84
+ - [x] `tests/test_no_gpu_on_turn_path.py` β€” SC-4 static AST scan (plan 01-08)
85
  - [ ] `tests/e2e/test_avatar_loop.py` β€” AVTR-01, VOIC-02/03/04/05, DPLY-01/04
86
  - [ ] `tests/e2e/test_latency_harness.py` β€” VOIC-05, emits `docs/LATENCY.md`
87
  - [ ] Fixture audio β€” Japanese speech WAV, 30s silence, 30s cafΓ© noise
.planning/phases/01-voice-avatar-loop-skeleton/deferred-items.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deferred items β€” Phase 01
2
+
3
+ Out-of-scope discoveries logged during execution, not fixed in the plan that found them.
4
+
5
+ ## Logged by 01-08 (2026-09-06)
6
+
7
+ - **"Better accuracy" ASR toggle** (`MODELS.accurate` = whisper-large-v3-turbo q4f16, WebGPU only).
8
+ 01-07 handed the control to 01-08; 01-08's task list does not include it and no VOIC-02 acceptance
9
+ row needs it. Building it means re-creating the ASR instance in `turn-loop.js` (the model is fixed
10
+ at `createAsr`), so it is a small turn-loop feature, not a host.js button. Candidate for 01-10 polish.
11
+ - **`tests/e2e/test_asr_standalone.py` under full-fleet load** β€” the pre-existing flake (fake-mic
12
+ `%noloop` WAV finishing before `getUserMedia` attaches). Passed 7/7 in both whole-suite runs today;
13
+ still watched.
14
+ - **Synthesis speed on this machine** β€” `synthesis_ms` for こんにけは measured 1.6–1.7 s in a headed
15
+ browser session and 3.3–4.0 s in an idle bare process today, versus the 1.215 s recorded in
16
+ `docs/VOICEVOX-SETUP.md` on 2026-08-27. Not a code change; noted so 01-10's latency harness treats
17
+ the deployed Space's number as the only one that counts. Under headless SwiftShader rendering the
18
+ same call takes 14–29 s (CPU starvation), which is why the parity suite's `lastTurnMs` is not a
19
+ latency measurement.
20
+ - **`THREE.Clock` β†’ `THREE.Timer` and `removeUnnecessaryJoints`** deprecations: unchanged, still
21
+ 01-10's while vendoring.
app.py CHANGED
@@ -1,16 +1,17 @@
1
  """Space entry point: assembly only. The layout and the turn live in japanese_avatar.ui.blocks."""
2
 
 
3
  import os
4
  import sys
5
  from pathlib import Path
6
 
7
  import spaces
8
 
9
- # The Space runs `python app.py` against requirements.txt alone and never pip-installs this
10
- # repository, so the src/ layout is not on sys.path there (RUNTIME_ERROR on the first deploy).
11
  _SRC = Path(__file__).resolve().parent / "src"
12
  if str(_SRC) not in sys.path:
13
  sys.path.insert(0, str(_SRC))
 
14
 
15
  from japanese_avatar.ui.blocks import build_blocks # noqa: E402
16
 
@@ -20,9 +21,8 @@ def gpu_disabled() -> bool:
20
  return os.environ.get("DISABLE_GPU", "0").strip().lower() in {"1", "true", "yes"}
21
 
22
 
23
- # ZeroGPU refuses to run a Space with no @spaces.GPU function ("No @spaces.GPU function
24
- # detected during startup", docs/HOSTING.md). This probe exists only to satisfy that scan. It is
25
- # not on the turn path, nothing calls it, and tests/test_no_gpu_on_turn_path.py proves both.
26
  @spaces.GPU(duration=1)
27
  def zerogpu_probe() -> str:
28
  """The GPU entry point ZeroGPU requires to exist. Deliberately unreachable in Phase 1."""
 
1
  """Space entry point: assembly only. The layout and the turn live in japanese_avatar.ui.blocks."""
2
 
3
+ import logging
4
  import os
5
  import sys
6
  from pathlib import Path
7
 
8
  import spaces
9
 
10
+ # The Space never pip-installs this repo, so the src/ layout is not on sys.path there.
 
11
  _SRC = Path(__file__).resolve().parent / "src"
12
  if str(_SRC) not in sys.path:
13
  sys.path.insert(0, str(_SRC))
14
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
15
 
16
  from japanese_avatar.ui.blocks import build_blocks # noqa: E402
17
 
 
21
  return os.environ.get("DISABLE_GPU", "0").strip().lower() in {"1", "true", "yes"}
22
 
23
 
24
+ # ZeroGPU refuses to run a Space with no @spaces.GPU function (docs/HOSTING.md). This probe only
25
+ # satisfies that scan: nothing calls it, and tests/test_no_gpu_on_turn_path.py proves both.
 
26
  @spaces.GPU(duration=1)
27
  def zerogpu_probe() -> str:
28
  """The GPU entry point ZeroGPU requires to exist. Deliberately unreachable in Phase 1."""