design-b D0: record the audit + red pin in docs 19/20/21

Doc 19: status DIAGNOSED -> DIAGNOSED + PINNED RED; the repro section
leads with the automated deterministic spec (the manual Leonardo flow
stays as the field repro).
Doc 20: D0 work-log entry; status D-1 + D0 done; next = D1.
Doc 21: red-spec section updated to "landed", with the staging test's
anti-vacuity role.

Carried finding: the strand reproduces on a 2-OBJECT fixture schematic,
so warm-load byte volume (the 68/1 dice-loader) is not an ingredient —
two concurrent parks suffice. That is why D3 closes this class and D5 is
not required for it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
This commit is contained in:
Gergő Törcsvári 2026-08-05 21:16:18 +02:00
commit 832cbcdf91
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
3 changed files with 58 additions and 12 deletions

View file

@ -1,14 +1,27 @@
# 19 — Symbol Properties dialog hangs: a stranded tool fiber (live investigation)
> **Status: DIAGNOSED, NOT FIXED (2026-08-05).** Reproduced end-to-end in a real browser
> against the dev platform on the scheduler build. Regression-vs-pre-existing is **not yet
> **Status: DIAGNOSED + PINNED RED, NOT FIXED (2026-08-05).** Reproduced end-to-end in a
> real browser against the dev platform on the scheduler build, and since D0 also
> **deterministically in e2e**: `tests/kicad/quasimodal-strand.spec.ts` (6/6 identical —
> `closed=false dialogs=1 refused-resumes=1`). The fix lands at doc 20 **D3**, where that
> spec's `test.fail()` marker comes off. Regression-vs-pre-existing is **not yet
> determined** — see §5. Related: [`17`](17-mailbox-scheduler-plan.md) S4 (waits),
> [`16`](16-fiber-resume-guard.md) (the quarantine guard), the 8/4 three-UI-bugs triage
> (which blamed the interlock drain — that is the *symptom layer*, not the proximate cause).
## 1. Repro (100%, ~40 s)
## 1. Repro
Dev platform (`npm run dev`), editor at `:3048`, Arduino Leonardo schematic:
**Automated (D0, ~30 s, deterministic):**
`npx playwright test --project=kicad-firefox kicad/quasimodal-strand.spec.ts`. The
"staging" test proves the window is real (dialog open + timer fired + concurrent-park
beacons); the "doc-19 red" test clicks OK and records the hang. The overlap is
structural, not a race: the parking timer (`wasm/bindings/timer_park.h`) is armed
*after* the dialog is confirmed open, so it necessarily parks on top of the opener's
open-ended fiber park. Note the strand reproduces on a **2-object fixture schematic**
Leonardo's byte volume is not an ingredient, only concurrent parks are.
**Manual (100%, ~40 s):** dev platform (`npm run dev`), editor at `:3048`, Arduino
Leonardo schematic:
1. Open the project URL, wait for load (~30 s).
2. Double-click the USB receptacle (J1) → **Symbol Properties** opens.

View file

@ -1,6 +1,6 @@
# 20 — Design B core: parkable activities as scheduler contexts
> **Status: IN PROGRESS — D-1 DONE (2026-08-05, work log §10).** The remaining core of
> **Status: IN PROGRESS — D-1 + D0 DONE (2026-08-05, work log §10).** The remaining core of
> Design B ([`06`](06-design-b-fiber-first-runtime.md) B1+B2), scoped against what S0S6
> actually built ([`17`](17-mailbox-scheduler-plan.md)) and motivated by the
> stranded-fiber hang ([`19`](19-quasimodal-fiber-strand.md)). Supersedes doc 12's
@ -218,4 +218,29 @@ occ-probe `glb` case (known-unrelated, predates D-1). Vestigial `startModal` scr
from `ASYNCIFY_IMPORTS` (Makefile.wasm) and `asyncify-imports.txt` post-gate.
Consequence: every later phase is single-path — one code path per park site, one glue
per build, one battery per gate. Next: **D0** (park-site audit + the doc-19 red spec).
per build, one battery per gate.
### D0 — park-site audit + red spec (2026-08-05) ✅
- **Audit** → [`21`](21-park-site-audit.md) (pcbjam `d4b25a0`): 14 production park sites
(8 wx, 9 KiCad/bridge) + 3 deliberate test levers, each classified fiber /
entry-stack / main-loop with its routing phase. W1 `wxWasmYieldUntilJs` and W3 popup →
D3; the clipboard/font/lib/3D/occ/ngspice/nanosleep bridges → D4; W2's per-frame yield
stays safe-by-construction unless D5 is taken. Doc 20 risk 4 (pthreads) settled: all
Asyncify parks are main-thread-only; the lib bridge's worker path is a blocking
proxy, not a park, so contexts stay a main-thread concept.
- **Red spec**`tests/kicad/quasimodal-strand.spec.ts` (pcbjam `6ab0843`): a GREEN
"staging" test (the window is real) plus the `test.fail()` doc-19 pin (OK must close
the dialog). 6/6 identical: `closed=false dialogs=1 refused-resumes=1`. The overlap is
structural — the parking timer is armed *after* the dialog opens, on top of the
opener's open-ended fiber park.
- **Finding worth carrying into D1:** the strand reproduces on a **2-object fixture
schematic**. Byte volume (the doc-17/gal-refresh dice-loader) is NOT an ingredient
here; two concurrent parks suffice. That makes the doc-19 class strictly easier to
hit than the 68/1 warm-load family, and it is why D3 — not D5 — is the phase that
closes it.
- Retired `tests/kicad/dialog-deadlock-probe.spec.ts` (the 8/4 throwaway probe, and the
tree's only determinism-lint violations).
Next: **D1** — context primitives on libcontext, exercised by a dedicated test app, no
production path switched, with the context-count/peak-RSS gate doc 20 §7 risk 1 asks for.

View file

@ -87,9 +87,17 @@ belongs in the scheduler shim (it already owns `Fibers` bookkeeping and the
## 4. Red spec (the other D0 deliverable)
The doc-19 strand lands as `tests/kicad/quasimodal-strand.spec.ts`: open a schematic,
double-click a symbol → Symbol Properties (quasi-modal on a tool fiber = W1's fiber
lane), click OK → the dialog must close and dispatch must stay live. RED today by the
doc-19 mechanism; goes green at D3. Marked `test.fail()` so the battery stays runnable
while red — when D3 lands, Playwright flags "expected to fail but passed", forcing the
flip to a plain green pin.
**Landed** as `tests/kicad/quasimodal-strand.spec.ts` (pcbjam `6ab0843`): open a
schematic, double-click a symbol → Symbol Properties (quasi-modal on a tool fiber =
W1's fiber lane), arm the parking timer *while the dialog is up* (so its park lands on
top of the opener's open-ended park — structural overlap, not a race), click OK → the
dialog must close and the wait books must balance. RED today by the doc-19 mechanism,
6/6 identical (`closed=false dialogs=1 refused-resumes=1`); goes green at D3. Marked
`test.fail()` so the battery stays runnable while red — when D3 lands, Playwright flags
"expected to fail but passed", forcing the flip to a plain green pin. A separate GREEN
"staging" test asserts the window is real, so the pin cannot rot into vacuity.
**Audit consequence recorded there:** the strand reproduces on a 2-object fixture — the
warm-load byte volume that dice-loads the 68/1 family is not an ingredient. Two
concurrent parks suffice, which is why D3 (waits on contexts) closes this class and D5
(main as a context) is not required for it.