Traced, not inferred: TOOL_MANAGER::RunSynchronousAction spins
`while(synchronousControl == STS_RUNNING) { wxYield(); wxMilliSleep(1); }`
(tool_manager.cpp:370-371), and on wasm wxMilliSleep -> nanosleep ->
__wasm_main_thread_yield_ms is an ASYNCIFY PARK OF THE STACK IT STANDS ON —
in a loop, inside a tool body, with a nested wxYield() dispatch running on
top of the parked stack.
The case closes on its callers: they are exactly the tools whose specs died
at D-on — edit_tool_move_fct (move-with-m, presence-locks move),
sch_drawing_tools (draw wires), the drawing/edit tools behind draw-lines.
The pcbnew spec comments already described the symptom from outside ("the
asyncified pointer-move handler") without naming the park; this is it.
So doc 21's K7 "anywhere" class has ONE caller that blocks Phase D, and it
moves first: the wait must yield the owning context instead of sleeping in
place, with the atomic's transition marking it ready. Recorded with the fix
shape, the upstream-divergence question it raises, and the gate that matters
(the KiCad suite's four canvas-tool specs — the harness has no
RunSynchronousAction and stays green either way).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBjomQfKyRa3jBdeAKpmTw
Doc 20: D1 entry (star topology as the load-bearing decision, the
9-scenario gate, measured memory) and the §3 asset table flipped —
registry/park/resume/drain are no longer "stubs that throw".
Doc 21: what D1 built, plus the sizing instruction for D4 — take each
bridge's deep-park high-water from its own beacon; do NOT carry the
harness's ~34 B/frame figure (synthetic frames, three locals each = a
floor) and do not inherit libcontext's 512 K by default either.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
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
Doc 20 D0 first deliverable: every Asyncify park site post-D-1,
classified by whose stack it suspends (tool fiber / entry stack / main
loop) with a routing decision per site — 14 production sites (8 wx, 9
KiCad/bridge counting quartets/pairs) + 3 deliberate test levers.
W1 (wxWasmYieldUntilJs) and W3 (popup) route to D3 context yields;
the clipboard/font/lib/3D/occ/ngspice/nanosleep bridges route to D4;
W2 (per-frame yield) stays safe-by-construction unless D5 is taken.
Also settles doc 20 risk 4 (pthreads): all parks are main-thread only;
the lib bridge's pthread path is blocking-proxy, not Asyncify, and is
out of migration scope. Defines what D4's "no handleSleep on a fiber
stack" assertion must cover.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz