WolfDavid commited on
Commit
da46df0
·
1 Parent(s): 64a7d12

docs: hand off Phase 01 at the deploy gate

Browse files

Record where the work stands, why 42 commits are unpushed on purpose, and the
traps that cost real time this session so none of them get re-derived: the two
Gradio 6.22.0 API defects, the ASR q8/WASM incompatibility, and the two spec
corrections that supersede the research doc.

HANDOFF.json is the machine-readable half that resume-work reads first. Its
absence after the earlier crash is what made recovering this plan expensive.

Files changed (2) hide show
  1. .planning/HANDOFF.json +44 -0
  2. RESUME.md +121 -0
.planning/HANDOFF.json ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "created": "2026-08-27",
3
+ "created_by": "session handoff — user stepping away from the machine",
4
+ "status": "blocked_on_user",
5
+ "phase": "01-voice-avatar-loop-skeleton",
6
+ "plan": "01-05",
7
+ "task": 1,
8
+ "total_tasks": 3,
9
+ "next_action": "Decide whether to run plan 01-05, the user-gated deploy. It is the only thing blocking the rest of Phase 01. Run `/gsd:execute-phase 01` to resume; the orchestrator will pick up at 01-05.",
10
+ "blockers": [
11
+ "01-05 Task 1 pushes to the `space` remote, which PUBLISHES the app publicly on the WolfDavid Hugging Face profile. This was deliberately not done autonomously.",
12
+ "Every remaining plan depends on it: 01-08 depends_on [01-05, 01-06, 01-07]; 01-09 depends_on [01-08]; 01-10 depends_on [01-09]. Nothing else in the phase can proceed.",
13
+ "The Space's README front-matter (which lives only on the HF side — there is NO README.md in this repo) was auto-generated wrong at Space creation: sdk_version 6.26.0 must become 6.22.0 to match requirements.txt, and python_version '3.12' must become 3.12.12. Plan 01-05 owns writing the corrected README. Pushing BEFORE 01-05 writes it would trigger a public build on the wrong Gradio version."
14
+ ],
15
+ "human_actions_pending": [
16
+ "Approve (or decline) the public deploy in plan 01-05.",
17
+ "Plan 01-05 Task 3 is a blocking human-verify gate: judge the spike verdict — confirm the inline gr.HTML transport, or take the AVATAR_TRANSPORT=iframe fallback.",
18
+ "Plan 01-10 re-raises the VRM sourcing gate: keep VRM1_Constraint_Twist_Sample as a permanent asset, or author a bespoke VRoid Studio character. It is currently a TEMPORARY dev asset."
19
+ ],
20
+ "completed_tasks": [
21
+ { "plan": "01-01", "status": "complete", "note": "hosting + VRM gates; both human checkpoints closed" },
22
+ { "plan": "01-02", "status": "complete", "note": "toolchain, LFS, package skeleton, test scaffolding" },
23
+ { "plan": "01-03", "status": "complete", "note": "avatar stage + transport seam; resumed from a crash at Task 3 this session" },
24
+ { "plan": "01-04", "status": "complete", "note": "VOICEVOX TTS, requirements.txt, ground-truth fixtures" },
25
+ { "plan": "01-06", "status": "complete", "note": "mora-to-viseme timeline builder" },
26
+ { "plan": "01-07", "status": "complete", "note": "push-to-talk mic gate + tiered browser ASR" },
27
+ { "plan": "01-05", "status": "not_started", "note": "BLOCKED — user-gated public deploy" },
28
+ { "plan": "01-08", "status": "not_started", "note": "blocked behind 01-05" },
29
+ { "plan": "01-09", "status": "not_started", "note": "blocked behind 01-08" },
30
+ { "plan": "01-10", "status": "not_started", "note": "blocked behind 01-09; user-gated" }
31
+ ],
32
+ "uncommitted_files": [],
33
+ "unpushed_commits": 42,
34
+ "push_state": "NOTHING PUSHED. The only remote is `space` (the public HF Space) and pushing it IS the gated deploy. Branch `master` has no upstream. There is no GitHub remote for this repo.",
35
+ "context_notes": [
36
+ "Working tree is clean. All work is committed locally. 42 commits are unpushed by design, not by omission.",
37
+ "Verified green at handoff: 82 quick-loop tests in ~12.8s, 15 e2e browser tests, ruff check + format --check clean.",
38
+ "RUN `pytest tests/e2e/` AS A WHOLE before trusting a green phase. Per-file runs hid two real defects this session — each plan's own verification passed while the full suite failed.",
39
+ "Gradio 6.22.0 traps found the hard way — copy from src/japanese_avatar/ui/avatar_component.py, NOT from 01-RESEARCH.md: (1) js_on_load cannot use top-level await, it compiles to a non-async Function; use an async IIFE. (2) Custom props are **kwargs, not props={...}.",
40
+ "ASR traps: dtype 'q8' cannot create an ONNX session on the WASM backend at all — q4 is the only quantisation working on both tiers. A failed WebGPU init poisons the ONNX backend registry for the page, so asr.js probes requestAdapter() first rather than relying on the prescribed fallback.",
41
+ "Plan frontmatter over-claims shared requirements. AVTR-01, AVTR-02, VOIC-02/03/04/05, DPLY-01 and DPLY-04 appear in plans that do not satisfy them. Never blind-run `requirements mark-complete` — verify the acceptance test actually ran.",
42
+ "01-VALIDATION.md and plan 01-06 assert the slow timeline is exactly 1/0.75x longer within 2 frames. It measures 1.3410852713178294x, which is 4 frames out. The plan's stated truth was written from a wrong assumption; the measured ratio is correct and corroborates a 01-04 finding. Update 01-VALIDATION.md when 01-10 touches it so the verifier does not read this as an unmet must-have."
43
+ ]
44
+ }
RESUME.md ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Resume here — Japanese Learning Avatar
2
+
3
+ **Written 2026-08-27, stepping away from the machine.**
4
+ Working tree is clean. Everything is committed. Nothing is pushed, deliberately — see below.
5
+
6
+ To pick back up, run `/gsd:resume-work` in this directory. It reads `.planning/HANDOFF.json`
7
+ first, which carries the same state in machine-readable form.
8
+
9
+ ---
10
+
11
+ ## One-line status
12
+
13
+ Phase 01 is **6 of 10 plans complete and hard-blocked on a decision only you can make**:
14
+ whether to publicly deploy the Hugging Face Space.
15
+
16
+ ---
17
+
18
+ ## Why nothing was pushed
19
+
20
+ This repo's **only** git remote is `space` — the public Hugging Face Space at
21
+ `WolfDavid/japanese-learning-avatar`. There is no GitHub remote. Branch `master` has no upstream.
22
+
23
+ So `git push` here does not mean "back up my work." It means **publish the app publicly and
24
+ trigger a Space rebuild** — which is precisely plan 01-05 Task 1, the gate that is waiting on you.
25
+
26
+ Pushing right now would also produce a **broken** public build. There is no `README.md` in this
27
+ repo; the Space's front-matter lives only on the HF side and was auto-generated wrong when the
28
+ Space was created:
29
+
30
+ | Front-matter key | Currently on HF | Must become | Why |
31
+ |---|---|---|---|
32
+ | `sdk_version` | `6.26.0` | `6.22.0` | must match `gradio==6.22.0` in `requirements.txt` |
33
+ | `python_version` | `'3.12'` | `3.12.12` | ZeroGPU provides only 3.10.13 and 3.12.12 |
34
+
35
+ Writing that corrected README is part of plan 01-05. Push after 01-05 runs, not before.
36
+
37
+ **42 commits are unpushed by design, not by omission.**
38
+
39
+ If what you actually want is an off-machine backup, that is a *different* action from deploying:
40
+ add a private GitHub remote and push there. That was not done unprompted because the standing
41
+ rule for this account is to ask before pushing nested repos.
42
+
43
+ ---
44
+
45
+ ## Where the work stands
46
+
47
+ | Plan | What it is | State |
48
+ |---|---|---|
49
+ | 01-01 | Hosting decision + Space creation + VRM sourcing | Complete |
50
+ | 01-02 | Toolchain, Git LFS, package skeleton, test scaffolding | Complete |
51
+ | 01-03 | Avatar stage, shared facade + turn loop, both transports | Complete (resumed from the crash) |
52
+ | 01-04 | VOICEVOX TTS, `requirements.txt`, ground-truth fixtures | Complete |
53
+ | 01-06 | Mora-to-viseme timeline builder | Complete |
54
+ | 01-07 | Push-to-talk mic gate + tiered browser ASR | Complete |
55
+ | **01-05** | **Space manifest, deploy the spike, verdict** | **BLOCKED — your call** |
56
+ | 01-08 | Turn-loop wiring — closes the round trip | Blocked behind 01-05 |
57
+ | 01-09 | Full deployed E2E suite + `LICENSES.md` | Blocked behind 01-08 |
58
+ | 01-10 | Vendor modules, latency harness (user-gated) | Blocked behind 01-09 |
59
+
60
+ The dependency chain is real, not a preference: `01-08 depends_on [01-05, 01-06, 01-07]`,
61
+ `01-09 depends_on [01-08]`, `01-10 depends_on [01-09]`. Nothing proceeds until 01-05 does.
62
+
63
+ ### Verified green at handoff
64
+
65
+ - 82 quick-loop tests in ~12.8 s
66
+ - 15 end-to-end browser tests
67
+ - `ruff check .` and `ruff format --check .` both clean
68
+
69
+ **Run `pytest tests/e2e/` as a whole before trusting a green phase.** Per-file runs hid two real
70
+ defects this session — each plan's own verification passed while the full suite failed.
71
+
72
+ ---
73
+
74
+ ## What deploying would actually test
75
+
76
+ The risky part is already retired. three.js + `@pixiv/three-vrm` render inside a real
77
+ Gradio 6.22.0 `gr.HTML` component locally, with `threeInstanceCount === 1` and
78
+ `mountCount === 1`, and the iframe fallback boots identically under `AVATAR_TRANSPORT=iframe`.
79
+
80
+ What remains genuinely deployment-specific: CDN reach from `*.hf.space`, `set_static_paths`
81
+ behind the Space proxy, cold-start behaviour, and mobile.
82
+
83
+ Also note: **1 of 2 free ZeroGPU slots is already consumed** by this Space. The remaining slot is
84
+ the last free Gradio Space this account can create without PRO — budget it against the other
85
+ HF-profile projects. `cpu-basic` is not a fallback; it returns HTTP 402 for Gradio Spaces.
86
+
87
+ ---
88
+
89
+ ## Traps found the hard way — do not re-derive these
90
+
91
+ `01-RESEARCH.md` is **wrong** on these points. Copy from
92
+ `src/japanese_avatar/ui/avatar_component.py` and `avatar/asr.js` instead.
93
+
94
+ 1. **`js_on_load` cannot use top-level `await`.** Gradio 6.22.0 compiles it into a plain
95
+ non-async `Function`, so the documented snippet throws `SyntaxError` and `window.Avatar`
96
+ never exists. Use an async IIFE with its own `.catch`.
97
+ 2. **Custom props are `**kwargs`, not `props={...}`.** The dict form creates one prop literally
98
+ named `props`, so `props.vrmUrl` comes back `undefined`.
99
+ 3. **ASR `dtype:'q8'` cannot create an ONNX session on the WASM backend at all** — for every
100
+ Whisper size tested. It works on WebGPU, so the prescribed default would ship green on a dev
101
+ machine and dead on exactly the browsers the WASM tier exists to serve. **`q4` is the only
102
+ quantisation working on both tiers.**
103
+ 4. **The prescribed WebGPU→WASM fallback does not fall back.** A failed WebGPU init poisons the
104
+ ONNX Runtime Web backend registry for the whole page. `avatar/asr.js` probes
105
+ `requestAdapter()` first so the doomed call is never issued.
106
+
107
+ ## Two spec corrections carried forward
108
+
109
+ - **Frame quantisation:** the correct form is `round(round(sec * 93.75) / speed)`, **not**
110
+ `round(sec / speed * 93.75)`. Measured 24/24 correct vs 8/24. Already shipped in `visemes.py`.
111
+ - **The slow/long duration ratio is 1.3410852713178294, not exactly 1/0.75** — 4 frames out from
112
+ what `01-VALIDATION.md` and plan 01-06 assert. The plan's stated truth was written from a wrong
113
+ assumption. **Update `01-VALIDATION.md` when 01-10 touches it**, so the phase verifier does not
114
+ read this as an unmet must-have.
115
+
116
+ ## Standing warning about requirements
117
+
118
+ Phase 1 plan frontmatter **over-claims shared requirements**. AVTR-01, AVTR-02, VOIC-02/03/04/05,
119
+ DPLY-01 and DPLY-04 all appear in the `requirements:` field of plans that do not actually satisfy
120
+ them. Never blind-run `requirements mark-complete` — verify the acceptance test has really run.
121
+ Three executors this session correctly declined to mark requirements complete for this reason.