Commit graph pcbjam/tests/apps
Author SHA1 Message Date
Viktor Vaczi
db819850ee jspi: fix the dead-tools ownership bug, emscripten-6 fallout, and green the full suite on Playwright 1.62
Live-app fix (Place Footprints / routing dead in Chrome): submodule
bumps carry the coroutine ownership fix (kicad 012d95ecb4) and the
handler-exception survival fix (wxwidgets 1b5f0e31f4).

Emscripten-6 fallout:
- occ/ngspice worker wrappers: mainScriptUrlOrBlob was removed
  upstream; pthread children re-run the wrapper blob, so an em-pthread
  realm now importScripts the glue and gets out of the way (before:
  recursive service boots, pool never fills, silent 180s boot hangs —
  every occ spec and ngspice bg_run).
- Makefile.wasm: -sASYNCIFY frankenlinks on the no-wx coroutine repro
  targets ported to -sJSPI (the JSPI-only libcontext crashed at first
  yield under them); mainloop/gl repro pages drive their tick through a
  promising export (emscripten_set_main_loop callbacks cannot suspend);
  retired inject-dyncall-shims lines removed (targets were unbuildable
  since Phase 8); $stringToNewUTF8 force-included (the EM_ASM value
  bridge aborted the runtime on the first decoded exception).
- fiber-park levers: neither embind shape can drive suspending levers
  (plain throws on strict-JSPI Firefox; emscripten::async() re-executes
  its invoker on settle) — kept sync for manual Chromium probing, spec
  coverage moved to the jspi-coroutine harness (18 cases).

Suite work:
- Playwright 1.61.1 -> 1.62.1 (Firefox 153: JSPI on by default).
- fiber-resume-park.spec retired -> coroutine-lifecycle.spec: census
  gate over boot / board load / chooser open / cancel (deterministically
  red on the pre-fix build).
- Blind asyncify-era pins re-keyed: quasimodal-strand + wait-beacons
  beacon regexes, footprint-chooser-close liveness -> wx parking-timer
  heartbeat (scheduler counters idle flat on Firefox).
- occ/ngspice test providers: 60s boot timeout + worker error
  surfacing (a worker death used to be a silent 180s timeout).
- Harness pages: stale 9.99 config dir -> 10.0 (library_manager wxCHECK
  noise, chooser had no libraries).
- gal-webgl harness: missing artifacts rebuilt (boost/glm extracted to
  the host sysroot), PgmOrNull stub added for the rebased GAL.
- jspi-scheduler: clean-shutdown console line restored (app-quit
  contract), quarantine never yanks SP from a live window.

Gates: test:e2e 699 passed / 0 failed (wx-chromium, kicad-firefox,
kicad-chromium, jspi-firefox, coroutine-firefox); web ff/cr/mobile 71
passed; lint:ci-coverage 166, lint:determinism 163, screenshots
manifest 492 current, corpus 7/7, tools:contract green. Offline
screenshot baselines show expected mass drift from the engine bump —
re-baseline (screenshots:noise -> promote) is a follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016X9eh1s5sTx1o9Em9KBuwR
2026-08-13 17:41:28 +02:00
Viktor Vaczi
e14faeca8b jspi: retire the asyncify pipeline — knob, post-link tail, binaryen hooks
Phase 8 in the parent repo. Deleted: asyncify-scheduler.js, apply-asyncify.sh,
apply-finalize.sh, inject-dyncall-shims.sh, asyncify-imports/removelist.txt,
the wasm-opt/finalize stub pair, scripts/binaryen-hoist-pass/ (the fork stays
a dormant submodule; removal is a follow-up), bench/wasm-opt-bench.sh (README
marked historical), wasm/shims/context_sleep.cpp, and the sched-context
harness app + Makefile targets.

PCBJAM_ASYNC_BACKEND is gone: build-wx-wasm.sh hardcodes the jspi stamp
(still force-cleans pre-migration trees), build-kicad-target.sh gives editors
the JSPI link surface and the CLIs nothing (they pin ASYNCIFY=0), the stub
dance is replaced by an unconditional .real-restore, build-wasm-test.sh lost
its whole post-link loop, docker/build.sh's postprocess is the ENV shim only,
and Makefile.wasm links every app JSPI with the scheduler shim as a tracked
prerequisite. pcbjam_async_policy.h keys on __EMSCRIPTEN__.

jspi-scheduler.js: wxWasmMainLoopPump dropped from the wrap census (the
export died with the D5 detach); inert [TRACE] instrumentation removed.

CI: wasm-build.yml rewritten for the single-cache pipeline (one output cache
keyed on compile inputs; post-processed bytes cached after the shim);
opt_level input removed from both callers. wasm-cache-hash.mjs inputs now
cover patch-env-shim.mjs + jspi-scheduler.js + jspi-exports.txt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 08:39:12 +02:00
Viktor Vaczi
3f09a46ff5 jspi: migration phases 0-7 — build knob, scheduler shim, test successor suite
Toolchain: emsdk 6.0.6 (versions.sh; cache-hash keys on it). Build knob
PCBJAM_ASYNC_BACKEND=jspi|asyncify: build-kicad-target.sh links editors with
-sJSPI + -sJSPI_EXPORTS=@scripts/common/jspi-exports.txt + --pre-js
jspi-scheduler.js (no DYNCALLS, no post-link asyncify pipeline); wx build
stamps the backend and forces clean on flip or unknown provenance;
docker/build.sh passes the knob, seeds the emscripten ports cache from the
volume every launch, jspi postprocess = patch-env-shim only.

scripts/common/shims/jspi-scheduler.js: the JSPI successor scheduler —
token-wait registry, resume turnstile (one armed resume between engine
re-entries, SP swaps only at microtask boundaries), green-region spill
stacks (16-aligned tops), S1 embind mutator FIFO lane + parker wraps, S6
shutdown, libctx integration hooks (suspend/end/quarantine + g_current
arm/clear), SuspendError attributor, lost-wake + stuck-window watchdogs,
__wxWaitDump observability.

Embind: PARKER registrations get emscripten::async() under PCBJAM_JSPI
(wasm/bindings/pcbjam_async_policy.h). nanosleep yields route via the shim.

Tests: tests/asyncify -> tests/jspi successor suite (jspi-stack red/green
shadow-stack battery, jspi-coroutine MiniCoro harness, suspend-races
semantic scenarios + __wxWaitDump books coherence); projects jspi-firefox/
jspi-chrome (asyncify-webkit retired — no JSPI in WebKit); unconditional
Firefox JSPI pref; guard-beacons -> wait-beacons (+wxScheduler/libctxJspi
families); Makefile.wasm links test apps against JSPI with the shim as a
tracked link prerequisite.

Web: WasmTool setRo await + __wxWaitDump forensics, open-flow contained
promise, scheduler-shim.test.ts retargeted (8 green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 07:06:24 +02:00
Gergő Törcsvári
7dfd4f1834
docs 22 D-on probe + app-quit teardown spec; bump wx (alignment) + kicad (root identity)
The D-on probe (wxWASM_STAR_DISPATCH=1 on top of D5) is measured and
recorded in doc 22 §10: the week's 'environmental cliff' was adopted-fiber-
stack alignment (trap 1, fixed in wx - retires the D5 entry's findings 2-3
with a mechanism); with it fixed D5+D reached 388 passed; the remaining
reds decompose into three named Phase B gaps (wake ordering on a running
context, finished-coroutine transfer livelock, invocation-aware root
routing), each with a deterministic repro suite. D parks again behind the
switch until Phase B owns coroutine lifetimes.

app-quit.spec.ts + a wx_test_quit hook in minimal_test drive a real
File->Quit-shaped exit through D5's detached teardown (loop exits on its
context, S6 latch 'clean', OnExit + wxUninitialize there) - green in the
D-on probe, closing the D5 teardown-gate open item.

Landing state (D off): coroutine + coroutine-pthread + app-quit green;
nested red at case 3 in the pre-existing doc-19 wake-window family
(cases 1-2, yesterday's blockers, now pass); races wakeup_during_transition
stays layout-sensitive. Both are the class the C+B+E flip removes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBjomQfKyRa3jBdeAKpmTw
2026-08-10 10:14:18 +02:00
Gergő Törcsvári
258cb37487
star-flip: harness scenarios for star-mode transfers (doc 22 Phase B)
star_transfer_call_is_synchronous stages exactly the TOOL_MANAGER shape -
Call() then read Running() on the next line - and pins that a parked caller
resumed by the scheduler is indistinguishable from a synchronous return.
star_transfer_chain pins a three-deep nested call (root -> A -> B and back) in
strict order, with a parked star context proved undisturbed throughout.

A rule learned the hard way here: a fiber entry must NEVER return (emscripten
ends the program if it does), so these bodies park forever once their script
is done, like libcontext's trampoline loops. Getting it wrong wedges the
battery with no output at all.

The fiberRunning assertion flips to the stronger Phase B invariant: once the
pump is quiescent NO fiber is on the CPU, the scheduler is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EarUW9DS1c1sSW4ZNrkGQS
2026-08-10 10:14:17 +02:00
Gergő Törcsvári
586fbadfe9
design-b Phase A: absorb libcontext's wasm backend (doc 22)
Bumps wxwidgets (fiber lane in sched_context.h) and kicad (libcontext's wasm
backend becomes an adapter over it), so there is now ONE party performing
every emscripten_fiber_swap and recording who is on the CPU. Behaviour is
preserved throughout - libcontext still decides, the registry observes and
beacons any disagreement - which is the de-risking step doc 20's D2 never had.

Harness + spec gain three fiber-lane scenarios: a fresh fiber enters at its
entry and a swap suspends the swapper (fiber_roundtrip), releasing a suspended
fiber is legal while a stale id refuses instead of use-after-free
(fiber_release_suspended), and symmetric swaps leave a parked star context
undisturbed (fiber_and_star_coexist). The spec asserts the lane's counters,
including the tripwire that must stay zero: fiberNonEnterableSwaps.

Doc 22 gains the Phase A work log: the gate (kicad suite 139/1, the 1 being the
pre-existing occ-probe glb matrix; wx 346/1/3; batteries 48/48; zero tripwires
anywhere), the four bugs the tripwires caught and the single rule behind three
of them, the grace ring and the 33-coroutine measurement Phase B needs from it,
and the process traps this run paid for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EarUW9DS1c1sSW4ZNrkGQS
2026-08-10 10:14:17 +02:00
Gergő Törcsvári
48da418f34
design-b D3: fix the doc-19 hang — quasi-modals off the coroutine stack
THE BUG IS FIXED. tests/kicad/quasimodal-strand.spec.ts flips from a
test.fail() pin to a plain green regression test: 3/3 runs
closed=true dialogs=0 refused-resumes=0 (was closed=false dialogs=1
refused-resumes=1 on every run).

Mechanism: a quasi-modal's nested event loop parked on the TOOL
COROUTINE's stack, which suspends the fiber's body where the fiber layer
cannot see it — so the stale-fiber guard quarantined the fiber and then
refused its own resume, the dispatch guard was never released, and every
click after that was deferred forever. Bouncing the nested loop onto the
main stack leaves the coroutine suspended the legitimate way (a recorded
fiber swap), so nothing is quarantined and nothing is refused.

Layering, so this is not a pile of WASM ifdefs in KiCad:
- wx (3d37db3bf1) owns the POLICY and the hook; it must not know what a
  coroutine is.
- wasm/bindings/main_stack_runner.h is the only place that may know both
  sides: it finds the frame's TOOL_MANAGER and bounces via RunMainStack.
  Header-only and self-installing, so no build-script change; included by
  every editor's binding TU.
- KiCad gets ONE ifdef-free method (2c777efede), needed only because
  TOOL_STATE is opaque outside TOOL_MANAGER. libcontext and dialog_shim
  are untouched — an earlier draft edited both and was reverted.

This also reframes the remaining plan: the doc-19 class is closed WITHOUT
migrating tool coroutines onto scheduler contexts. Note it does not make
the wait a context yield — waits still park in place, just never on a
coroutine stack.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
2026-08-10 10:14:17 +02:00
Gergő Törcsvári
6e80827cec
design-b D3 prep: pin fiber-on-context nesting and the foreign-stack refusal
Two scenarios, deliberately separate because they answer different
questions that D3 depends on:

- fiber_nests_in_context: can a libcontext-style fiber run nested inside
  a scheduler context at all? YES. Worth pinning explicitly — an earlier
  version of this scenario trapped `unreachable` and briefly looked like
  proof that nesting was impossible, which would have been a serious
  wrong turn for the plan. The real cause was the test's own bug: the
  host-side emscripten_fiber_t was zero-initialised, so the swap unwound
  into a null asyncify buffer. Nesting works; the scenario now proves it.
- foreign_stack_refused: a yield from that nested fiber is REFUSED, and
  the host context and scheduler both survive it.

The spec asserts foreignStackRefusals == 1, so the refusal path cannot
silently stop being exercised.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
2026-08-10 10:14:16 +02:00
Gergő Törcsvári
7dbc9fb36e
design-b D1b/c: context harness + the bounded-and-measured memory gate
Doc 20 §6 D1, second slice: the dedicated test app D1 calls for, plus
the doc 20 §7 risk-1 gate.

tests/apps/standalone/sched-context/ drives the primitives with no wx
linked (a failure can only be the contexts layer) and assertions ON —
unlike the races harness, an emscripten "cannot start an async
operation" state here IS the bug, not a tolerated state. Nine
scenarios, each pinning one invariant; the load-bearing ones:
- parked_does_not_block: a parked context does not stop others running.
  This is doc 19's freeze made unrepresentable — there a parked activity
  held a global interlock and the UI died; here "parked" simply means
  "not runnable", and three workers run past it before it resumes.
- one_transition_in_flight: a context calling drain() gets a no-op, with
  a second ready context queued so a buggy nested drain would actually
  run something and be caught.
- async_wake: a real macrotask hop (setTimeout → mark_ready → drain),
  the shape every production bridge has.
- deep_park_sizing: parks 64 live frames deep to measure what a park
  actually costs.

The memory gate asserts the ceiling (peak live contexts, peak bytes),
that nothing leaked (live=0, bytes=0, created==finished), that nothing
was left mid-transition, and that refusals occurred (zero would mean the
illegal-operation scenarios stopped provoking).

Measured here: ~34 B/frame, 2200 B for a 64-frame park, 1 MB peak for 4
concurrent contexts. Recorded with the caveat that the harness's frames
carry three locals each, so this is a FLOOR — real bridges save far more
per frame (libcontext runs 512 K after a 64 K buffer silently
overflowed). The apparatus and its units are validated; the sizing
DECISION needs deep-park numbers from real bridges at D3/D4.

playwright.config: asyncify-firefox now matches every spec in ./asyncify
instead of only asyncify-races* — a new harness there is covered by
construction rather than by remembering to widen the pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
2026-08-10 10:14:16 +02:00
Gergő Törcsvári
d519ac89c0
design-b D-1 gate: battery green scheduler-only; startModal import scrub + work log
Gate results (single battery, scheduler-only): wx app battery +
asyncify + coroutine 363 passed / 3 skipped / 0 failed; full kicad
suite 138 passed / 30 skipped / 1 failed — the one failure is the
pre-existing local occ-probe glb case (predates D-1, unrelated).

Post-gate scrubs: vestigial 'startModal' removed from ASYNCIFY_IMPORTS
(tests/apps/Makefile.wasm) and env.startModal from
scripts/common/asyncify-imports.txt (the import no longer exists in any
wasm; both lists are boundary supersets so behavior is identical).
Stale comment pointers to the deleted legacy modal machinery updated.
Doc 20: D-1 work log added, status flipped to IN PROGRESS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
2026-08-10 10:14:15 +02:00
Gergő Törcsvári
e7b8c2725b
design-b D-1a: retire the legacy-shim ablation builds + redundancy pins
Doc 20 D-1 (legacy runtime deletion), first slice: races_test_noheal /
races_test_nosleepfix pinned behavior of the legacy handlesleep shim,
which is being deleted — the pins now assert properties of a runtime
that no longer exists. Drops the two Makefile.wasm link+inject variants
(SHIM_DISABLE_TRAMPOLINE_HEAL / SHIM_DISABLE_HANDLESLEEP), the
shim-redundancy pin specs in asyncify-races.spec.ts, and resolves the
tests/README.md open task. The green battery still runs every scenario
against the scheduler glue.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
2026-08-10 10:14:14 +02:00
Gergő Törcsvári
ab4e8090fe
test: uipolish regression app + spec for the wasm-ui-polish fixes
Self-asserting wx app (tests/apps/standalone/uipolish) with 8 checks:
clip-clear / clip-empty / clip-box (DC clip box reaches the canvas — the
collapsed wire-properties-panel bug), blit-origin (wxBufferedDC device
origin), mask-alpha (ConvertToImage carries wxMask — infobar close button),
scaled-dims (physical size for scaled bitmaps), checkbox-floor (selection-
filter density) and statbmp-best (bundle logical size — layer-panel icons).
The spec runs a default-DPR pass plus a deviceScaleFactor:2 pass that also
asserts the statbmp <img> ships the 32px asset at 16 CSS px.

Bumps wxwidgets for the CloneGDIRefData SOURCE_NONE fix the @2x pass
surfaced (empty statbmp data URL on hi-DPI).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mh188aysvgeaTRYh6syztQ
2026-07-28 18:00:35 +02:00
Viktor Vaczi
d538b73557 fix(wx): thread-safe wxString for the AsyncLoad fan-out — CvPcb-open trap solved
The ~1/9 wasm trap on CvPcb open ("index out of bounds" / "indirect call to
null" in a footprint AsyncLoad pool worker, then eeschema aborting on the
broken future — and the eeschema-fp-selector "CI-only" trap family, which was
never llvmpipe-specific) was wxString's UTF-8 build mutating SHARED strings on
read-only access from concurrent pool workers: every iterator ctor/dtor
spliced an intrusive list inside the string object, and torn splices wrote
through dead node pointers into other threads' stack frames. Second defect:
the UTF-8 position cache returned stale offsets when another thread's string
died and its address was reused.

Fixed in the wxwidgets fork (per-thread iterator registry + position cache
disabled under Emscripten) — kicad is untouched and AsyncLoad keeps its full
multi-worker fan-out. Falsified along the way (all perturbation masks, not
fixes): serializing the items, mimalloc vs dlmalloc, pthread stack size,
ASYNCIFY_STACK_SIZE, private-copy EnumFromStr, hot-path logging.

New red-first standalone app tests/apps/standalone/wxstring-mt (+ spec
coroutine-wxstring-mt.spec.ts, wx-chromium + coroutine-firefox): shared-string
compares alternating with wide-literal conversions reproduce the exact editor
trap signatures on the unfixed wx and run 4.7M rounds clean on the fixed one;
the pos-cache address-reuse dance corrupts on the first reuse before and
survives 673 after; two guard modes keep the iterator fix-up feature honest
(incl. an anti-elision liveness check — balanced register/unregister pairs in
tight loops can legally be optimized away, so a naive red test tests nothing).

Verification: wx+coroutine suites 382 passed; in-app AsyncLoad hammer 3x1000
rounds clean (baseline died <10); eeschema-assign-footprints spec 20/20
firefox + 20/20 chromium on the final build; lint:determinism clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb9jsqtHsC3tHTaJ45244j
2026-07-20 11:56:20 +02:00
Viktor Vaczi
3dfa093477 test(wx): drop wall-clock stamp from demo-app Event Log — deterministic screenshots
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 17:34:07 +02:00
Viktor Vaczi
4c3a4cacd4 test(determinism): deterministic waits + stableShot screenshots; drop blind sleeps/ifs/retries
Make the Playwright e2e + kicad suites deterministic so screenshot flake stops
tracing to timing races.

- Blind page.waitForTimeout -> condition waits (expect.poll, web-first
  assertions, waitUntil) + readiness helpers (waitForWxApp, waitForCanvasApp).
  Remaining sleeps are documented interaction dwells (annotated).
- Defensive "if element exists" branches -> loud asserts; label-fallback chains
  -> normalized clickMenuItemByText. First-run wizard for/if loops removed by
  seeding calculator/gerbview/pcbnew HTMLs.
- Screenshots: new stableShot(page, name) settles the render in-page (canvas
  hash over rAF) then writes a raw PNG to test-results/ for the existing offline
  gate (tools/screenshots vs baseline-screenshots). Replaces toHaveScreenshot,
  which did inline compare + its own baselines and had decoupled the specs from
  the real gate. scale:'css' pinned.
- retries: 0 in both configs.
- Guard: tests/tools/lint-determinism.ts (npm run lint:determinism) bans blind
  sleeps / toHaveScreenshot / inline retries / swallowed catches in specs;
  documented exceptions carry a marker. Rules in tests/TESTING.md.

Assertions, coverage, and renders unchanged (semantic-equivalence reviewed;
captures pixel-identical modulo inherent timer/timestamp/3d-raytrace variance).
Both suites green at retries:0 (e2e 340, kicad 92); ~35-61% faster.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVX1pHMvRPYHdp6ZfEawrk
2026-07-07 10:50:24 +02:00
Istvan Matejcsok
d9264549bf test: 💍 regression test for the notebook layer-list collapse 2026-07-06 09:17:10 +02:00
Istvan Matejcsok
ce1473c9ab test(3d): screenshot-baseline TDD suite for the 3D viewer OpenGL->WebGL port — 47 native goldens + red-state WebGL harness
tests/3d-regression mirrors the gal-regression pattern at renderer scale:
shared C++ scenarios call real KiCad 3D-viewer code (opengl_utils, display
lists + DrawCulled stencil subtraction, MODEL_3D VBOs, private generators via
a rob-template accessor, and full reload()+Redraw() composites over a
synthetic BOARD_ADAPTER). A native macOS harness renders them on real OpenGL
into 47 committed goldens (bit-deterministic, FBO capture); the wasm harness
compiles the same TUs against wasm/stubs/gl_ffp_stub.c no-ops so every
scenario renders blank — the TDD red state (parity meter: 47/47 changed).
Comparisons use the CI pixelmatch engine via the new generic compare-dirs.ts
(floors.json levels; manifest.json cmp-guards registry drift).

Documents an upstream bug: appendPostMachiningGeometry's countersink path
adds middle quads without normals, silently erasing the walls of any
display list it is batched into (3d-post-machining.png keeps the lists
separate to record it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:45:58 +02:00
Istvan Matejcsok
bbeef6d20e feat(wasm): kicad_editor — merge the pcbnew+eeschema kifaces into ONE bundle (Part 2)
All four editors (PCB / Footprint / Schematic / Symbol) are now runtime --frame
choices of a single kicad_editor.wasm (178 MB at -O1 vs 147+82 separate; shared
wx/common/boost linked once). One editor per page load, as before; frames pcb /
fpedit / sch / symedit.

- wasm/editor/: the merged executable target (single_top + both kiface library sets,
  whole-archive pcbcommon) + the safety-net focus-walk Kiface() dispatch TU. Gated by
  KICAD_WASM_MERGED_EDITOR (kicad submodule bump carries the fork side: per-engine
  Kiface/getter binding + ODR renames + dual-kiface launcher).
- wasm/bindings/: per-editor collab entries renamed pcbCollab*/schCollab* (JS names
  unchanged); duplicate kicadOpenFile/kicadCollabOnSave + shared-name registrations
  guarded behind KICAD_MERGED_EMBIND; new kicad_editor_embind.cpp registers each
  shared JS name once, dispatching on the live frame.
- Build: kicad_editor app (build wrapper, target case arms, 3-object embind compile
  with the ABI-critical flags, STUB_APP=pcbnew); docker/build.sh "all" =
  kicad_editor calculator pl_editor gerbview (pcbnew/eeschema stay as explicit debug
  apps); scripts/kicad/audit-merged-symbols.sh = repeatable ODR-collision audit (run
  on kicad bumps).
- Frontend: Bundle type (bundle ≠ tool); TOOL_BUNDLE maps all four editors to
  kicad_editor; explicit --frame tokens for pcbnew (pcb) and eeschema (sch); publish
  list = the 4 real bundles.
- Tests/CI: five harnesses load kicad_editor.js with explicit frame tokens;
  PCBNEW_FAMILY_SPECS renamed BIG_MODULE_SPECS + the 8 eeschema-family specs (they
  now boot the merged module — SpiderMonkey x86 CI OOM routing); frame-runtime spec
  covers all four frames from the one bundle.

Validated so far: frame-runtime 4/4 (each frame boots with the right title, no
aborts, no duplicate embind registration); 24-spec merged-module regression green;
3D raytracer renders. Known pre-existing failure: 3d-viewer title-bar drag deadlock,
fixed on main by 7630c7e (2N+8 pthread pre-warm) — picked up by the follow-up rebase.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 17:56:02 +02:00
Istvan Matejcsok
c6716e6b60 feat(wasm): unify editor builds — footprint/symbol editors via runtime --frame
The footprint and symbol editors are no longer separate WASM bundles: the frontend loads the parent pcbnew/eeschema bundle and passes --frame=fpedit / --frame=symedit (TOOL_BUNDLE + TOOL_FRAME -> Module.arguments in boot). Drops the two duplicate build+deploy targets and their wrapper scripts + vestigial embind; adds low-level harnesses (footprint_editor.html, symbol_editor.html) and a runtime-frame spec. Bumps the kicad submodule to the runtime --frame launcher.

The frame-runtime spec is listed in PCBNEW_FAMILY_SPECS so CI routes it to the chromium-ci (V8) project — its footprint case boots the pcbnew module, which OOMs SpiderMonkey on x86 CI. Includes the editor-unification dossier (research docs 01-04 + the as-built implementation record 05).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 17:56:02 +02:00
Istvan Matejcsok
3628a090dd feat: 🎸 wxWidgets dialog/frame header/drag/resize 2026-06-30 15:46:52 +02:00
Istvan Matejcsok
cf1fdf5c3d feat: 🎸 wxWidgets dialog/frame DOM header 2026-06-30 15:46:49 +02:00
Viktor Vaczi
c1ef489cfa feat(wasm-eh): migrate the WASM build to native wasm exceptions (+ 3D viewer default-on)
Replace the legacy Emscripten JS-exceptions model with native wasm-EH (legacy
encoding) across the whole build, keeping Asyncify coroutines working via a
from-source Binaryen --hoist-cpp-catches pre-pass. Net result: native-EH is the
only build mode, the 3D viewer is on by default, and pcbnew shrinks substantially.

Highlights:
- Binaryen submodule everywhere + --hoist-cpp-catches integration in apply-asyncify;
  post-link Asyncify covers every app wasm (not just standalone test wasm).
- Build deps (incl. OpenCASCADE without OCC_CONVERT_SIGNALS) and all KiCad apps
  with -fwasm-exceptions; emscripten_sleep added to the post-link asyncify-imports.
- libcontext fiber entry wired under native exceptions; while-loop main loop +
  currData shim injected into all wx apps.
- Native-EH collab apply fixed: DEBUG-define the embind TU + match all out-of-CMake
  C++ TUs' ABI flags to the core, fixing the vtable-layout skew / mis-dispatch.
- 3D viewer enabled by default (real raytracer linked, not the stub).
- Retire the EH-spike scaffolding; flip the asyncify-races ablation pins to
  shim-redundancy pins (native-EH stays clean with the legacy shims ablated).
- Fix the asyncify-races quiescence check to not require Asyncify.currData==0:
  under the native-EH per-frame-yield top loop the main stack is asyncify-suspended
  every frame, so currData legitimately churns (a freed-but-not-yet-nulled buffer,
  not a leak). Refresh the pcbnew toolbar screenshot baseline for the new kicad.
- CI: drop the obsolete binaryen_version input/env (the build uses the binaryen
  submodule fork's wasm-opt, not a version download); key the wasm-output cache on
  the binaryen submodule SHA instead.

Bumps the wxwidgets + binaryen submodules to their squashed feature commits.

Validated green: all 7 apps native-EH (real 3D in pcbnew); KiCad e2e 63/63
Firefox + Chromium (3D viewer renders); wx 336; coroutine 34/34 both engines;
asyncify 7/7 both engines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:40:26 +02:00
Istvan Matejcsok
13cac06778 fix(wasm): radio groups no longer merge into one
Bump wxwidgets to the %p->%zu radio-group name fix and add a standalone
wxWidgets harness plus an e2e regression that asserts three radio groups
in one window select independently. Build artifacts are gitignored; only
radiogroups_test.cpp is tracked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 12:54:35 +02:00
Istvan Matejcsok
a4dd0eaf3b fix: 🐛 wxWdigets popup position 2026-06-23 14:32:38 +02:00
Istvan Matejcsok
6493f8fe00 fix: 🐛 remove css duplication 2026-06-23 13:00:08 +02:00
Istvan Matejcsok
6bbd211c4a fix: 🐛 modal border + background after dnd
 Closes: #22
2026-06-22 13:41:01 +02:00
Istvan Matejcsok
4c199dff9f fix: 🐛 tooltip + test cases
 Closes: #18, #16
2026-06-19 14:32:09 +02:00
Istvan Matejcsok
5ff9f1c7b8 test: 💍 fix raytracer test cases 2026-06-19 10:43:35 +02:00
Istvan Matejcsok
5c6e426b00 fix: 🐛 hide print preview 2026-06-19 10:25:14 +02:00
Istvan Matejcsok
5d18bd0c45 test: 💍 deadlock test + readme update 2026-06-19 09:22:58 +02:00
Istvan Matejcsok
1b1e7884ee test: 💍 wxWidgets secondary canvas 2026-06-19 09:22:58 +02:00
Istvan Matejcsok
77a04821ef test: 💍 multithread nested asyncify 2026-06-19 09:22:56 +02:00
Istvan Matejcsok
5f02608a91 fix: 🐛 wxWidgets text ellipsis 2026-06-17 14:26:03 +02:00
Istvan Matejcsok
aa9d613f50 fix: 🐛 press m & move w/ arrows 2026-06-16 15:46:43 +02:00
Istvan Matejcsok
f4af42514a test: 💍 add tab switch fix test 2026-06-16 11:22:18 +02:00
Istvan Matejcsok
28615aba02 test: 💍 select height wxWidget 2026-06-15 18:41:18 +02:00
Viktor Vaczi
68430fa87c feat(wasm-dom): tests for context menus + scrollbars; fix clean wx build
- Standalone wx test apps (contextmenu, scrollbar) + e2e specs proving the
  wxScrollBar control, the wxScrolledWindow gutter, and DoPopupMenu.
- KiCad in-app specs: pl_editor + pcbnew canvas right-click context menus and
  scrolled-panel scrollbar gutters, with baseline screenshots.
- build-wx-wasm.sh: serial -j1 fallback when the parallel build trips the
  intermittent clean-build race (a generated header read mid-rewrite by
  concurrent compiles; never happens at -j1). Recovery resumes from built
  objects; a genuine error still fails the serial pass.
- Bump wxwidgets submodule (context menus + draggable scrollbars).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 23:15:49 +02:00
Viktor Vaczi
4186ea490f test(wasm-dom): repros + fixes for the text-ctrl reentry and tooltip UAF bugs
Bump wxwidgets (8814ddb) for the two DOM-port fixes and add their reproductions:

- tests/apps/standalone/{textctrl-reentry,tooltip-lifetime}: standalone wx repro
  apps + Makefile.wasm targets (textctrl links -fexceptions to throw from a
  wxEVT_TEXT handler), driven by tests/e2e/dom-port-bugs.spec.ts. Each app is
  deterministic and self-contained (no UB, ASAN, or timing dependence).
- docs/features/wx-dom-port/branch-review.md: branch review with findings #2/#3
  marked fixed and a "Bug reproductions and fixes" section, including the
  asyncify + legacy-EH gotcha (catch/destructor landing pads are unreliable
  while unwinding through asyncify frames).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 17:39:34 +02:00
Viktor Vaczi
7dc1bf27e0 refactor: collapse dual-mode plumbing — the DOM port is the only WASM build
The canvas (wxUniversal) mode is gone (wxwidgets submodule); remove every
piece of side-by-side plumbing so there is exactly one build and one test
flow:

- scripts/build-wxuniversal-wasm.sh -> scripts/build-wx-wasm.sh; no
  --dom/--enable-universal; builds into build-wasm/wxwidgets
- build-wasm-test.sh: no DOM_BUILD / apps-dom rsync mirror / PORT=dom;
  apps build straight into tests/apps (Makefile.wasm PORT conditionals
  collapsed; wx.js + wx-dom.js always pre-js)
- docker/build.sh, build-kicad-target.sh, env.sh: WX_PORT / -dom /
  -universal suffixes removed; kicad builds to kicad-<app>, outputs to
  output/; wx.js/wx-dom.js copied from the real source path
  (/workspace/wxwidgets/build/wasm — the old build-wasm path never
  existed and silently failed)
- setup-kicad-wasm.sh: single target dir; the perl wx-dom.js injection is
  gone — the 7 checked-in kicad pages now reference wx-dom.js directly
- playwright configs serve apps/; fixtures drop the test-results/dom and
  logs/wxwidgets/dom namespacing; boot.spec asserts wxDomPort
  unconditionally; pcbnew.spec uses one reference image;
  appearance.spec assertions unconditional
- compare/update-baseline-screenshots.sh: --port removed
- tests/gal-regression/wasm/Makefile: links build-wasm/wxwidgets and
  carries wx-dom.js as a second pre-js — the gal-webgl suite (30 specs)
  now actually builds and runs here (it needed host-side boost+glm via
  scripts/deps; the bundle had been missing, timing the whole spec out)
- tests: clickCanvas() dispatches via page.mouse (DOM widgets
  legitimately cover the canvas; locator actionability refused the
  click); the comprehensive spec drives wxChoice through its native
  <select> (browser-owned popup cannot be coordinate-clicked)
- docs: README/CLAUDE.md/build.md script names and dirs;
  features/wx-dom-port README reframed (DOM is THE port), visual-notes
  bugs 26-28; FindwxWidgets.cmake config label drops 'wasmuniv'
- wxwidgets submodule -> 9dbacc9448 (DOM-only port, fork diff shrunk)

Gate: full wx e2e suite 292 passed / 1 skipped / 0 failed — first run
ever with the gal-webgl specs green (28 scenarios + load + sequential).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:19:48 +02:00
Viktor Vaczi
a6ce7fbc11 feat(wasm-dom): Phase 2 — real DOM controls; seed DOM-port screenshot baselines
tests/apps/Makefile.wasm: pre-js shims are link dependencies (editing
wx.js/wx-dom.js now relinks apps); $^ link rules filter to %.o/%.a so
the new deps stay out of wasm-ld inputs. Seed baseline-screenshots-dom
(251 screenshots) from the green WX_PORT=dom full-suite run (251/0).
Cross-port visual comparison notes in docs/features/wx-dom-port/
(10 bugs found by the protocol, all fixed in wxwidgets).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:17:09 +02:00
Viktor Vaczi
249010a582 feat(wasm-dom): dual-mode build scaffold — native DOM port builds, links and boots alongside canvas
Build/test plumbing for the second wxWidgets mode: --dom on
build-wxuniversal-wasm.sh (build-wasm/wxwidgets-dom, no universal,
--disable-tooltips, libwx_wasmu_* stubs) and build-wasm-test.sh
(mirrors sources into tests/apps-dom, PORT=dom). Build script now
reconfigures when Makefile.in or autoconf_inc.m4 regenerate (bakefile
chain). Harness: WX_PORT=dom serves apps-dom, namespaces screenshots
into test-results/dom and logs into logs/wxwidgets/dom; compare/update
screenshot scripts take --port dom with baseline-screenshots-dom.
New port-agnostic boot.spec.ts (green on both ports).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:17:09 +02:00
Viktor Vaczi
14ca16cbd3 test(asyncify): red-green race harness + ablation flags, unwind-catch shim, spec tightening, decisions docs
The asyncify single-slot work, executed red-green (full ledger:
docs/features/asyncify-arbiter/redgreen.md; decisions record:
docs/features/async/07-decisions-and-outcome.md):

- tests/apps/standalone/asyncify-races/ + tests/asyncify/ + dedicated
  playwright config: 8 scenarios reproducing the KiCad asyncify failure
  family with the kicad-faithful startup topology (pre-park fiber swap →
  park throw through the live trampoline). Built in 3 variants; the
  SHIM_DISABLE_TRAMPOLINE_HEAL / SHIM_DISABLE_HANDLESLEEP ablation builds
  keep the historical hang and index-out-of-bounds crash reproducible
  forever (mutation-style pins for the existing shims).
- scripts/common/shims/handlesleep.js: catch the "unwind" park sentinel
  in the wakeUp path — when main's last pre-park suspension was a sleep,
  the main-loop park throw escaped through that sleep's promise reaction
  as an uncaught rejection (the calculator/gerbview console errors).
- scripts/common/inject-dyncall-shims.sh: SHIM_DISABLE_* ablation knobs.
- Spec tightening (the acceptance bar): 'uncaught exception: unwind'
  tolerance DELETED from pcbnew/eeschema specs; load-pcb gained a hard
  clean-console gate over 5 asyncify corruption signatures.
- wxwidgets pointer bump: modal LIFO resolvers, pump resolve-on-error,
  sync clipboard IsSupported (014f67e6c1).

Final state: asyncify suite 7/7, wx e2e 291/292 (1 skip), KiCad e2e 40
passed / 2 skipped with ZERO corruption signatures in any log across all
six apps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 17:02:59 +02:00
Viktor Vaczi
7629ab978d test(e2e): fix wx suite — header deps, port pinning, drop legacy-GL tests, wx+pthread worker fix
Suite went 64 failed → 291 passed / 0 failed:
- Makefile.wasm: compile with -MMD -MP and include .d files — stale objects
  relinked against a newer wx lib crashed apps at startup with "function
  signature mismatch" after any header/vtable change
- playwright(.coroutine).config.ts: pin the server port for the whole run
  (resolvePort from playwright-kicad.config.ts); the 60s freshness window made
  workers restarted after a failure rotate to a dead port (ERR_CONNECTION_REFUSED
  cascade across ~50 tests)
- drop legacy-GL testing: KiCad renders via WebGL GAL (gal-webgl.spec.ts);
  remove minimal_test's OpenGL tab/GLTestCanvas, opengl.spec.ts, GL z-order
  describes, -sLEGACY_GL_EMULATION + gl_immediate_shim.js from the test build,
  and the orphaned GL baselines
- coroutine-pthread repro apps join the `all` target (make clean used to delete
  them while all never rebuilt them); clean no longer eats checked-in JS like
  worker_dom_stub.js
- bump wxwidgets: guard module-eval document access so wx+pthread apps
  (threadpool tests) survive Web Worker eval

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:40:00 +02:00
Gergő Törcsvári
2dc55c6138
feat(pcbnew): Yjs collab bridge — snapshot/diff emit + BOARD_COMMIT apply (move/delete/track-add), verified two-tab in real app
pcbnew's half of the unified Yjs collab bridge (4th tool; yjs-bridge commit 4),
a near-verbatim port of the eeschema design. Root-repo only — kicad/wxwidgets
submodules untouched.

- wasm/bindings/pcbnew_embind.cpp: BOARD_LISTENER trigger + post-settle snapshot
  diff emit; BOARD_COMMIT apply inside a CallAfter + COROUTINE fiber (so a new
  item's GAL view->Add dispatches correctly). Move/delete sync for any top-level
  item by uuid; native PCB_TRACK add. Footprint/via/zone add deferred.
- WasmTool.tsx: add pcbnew to COLLAB_TOOLS.
- tests/apps/kicad/pcbnew-collab.html: seeded (wizard-free) harness, leaving
  pcbnew.html untouched for its wizard test.
- tests/kicad/pcbnew-collab.spec.ts: snapshot + apply(move/remove/add) — 2 pass,
  two-tab skipped headless.

Verified two-tab in the real web app: footprint move applies + syncs A->B.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 06:03:59 +02:00
Balint Ipkovich
4467bcd2c4 feat: gerbview WASM port 2026-06-03 14:23:57 +02:00
Gergő Törcsvári
3ef461a0f6
test(e2e): web-app tool open-path suite + pl_editor open/wizard-skip
Add an e2e suite that drives the real React web app (not the standalone
harness): tests/playwright-web.config.ts + tests/web/tools-open.spec.ts navigate
/p/demo/<tool>/<file> for all five tools and assert each boots, opens its demo
file (title drops "untitled"), shows no first-run wizard, and emits no WASM abort
or URL-regex modal. global-setup-web.ts re-seeds the demo project through the API
if missing, so the suite is self-sufficient against a running dev stack. Wired as
`npm run test:web`.

Also at the harness level:
- pl_editor-load.spec.ts: prove the pl_editor kicadOpenFile embind hook opens a
  .kicad_wks (mirrors eeschema-load.spec.ts).
- seed KiCad config in pl_editor.html / symbol_editor.html (matching eeschema.html
  and the web app's boot.ts) so the harness boots wizard-free; repurpose
  pl_editor.spec.ts's stale "wizard completes" test into a wizard-skip regression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 08:52:34 +02:00
Gergő Törcsvári
735e5aa8e9
feat(web): checkpoint web app init
- frontend (Vite/React) + server (Hono/Drizzle) scaffold under web/
- eeschema WASM embind kicadOpenFile hook + programmatic open-flow
- skip KiCad first-run setup wizard by seeding default config in preRun
- dev: auto-sync output/ WASM artifacts into tests/apps/kicad via link-wasm

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 20:32:19 +02:00
Balint Ipkovich
e28f473040 feat: symbol_editor WASM port 2026-06-02 13:37:26 +02:00
Balint Ipkovich
d735779e23 feat: pl_editor WASM port + browser file dialog fixes
Brings up KiCad's pagelayout_editor (drawing-sheet editor) in the
browser, to roughly the same "boots, canvas visible, partially usable
in-session" level as the existing pcbnew/eeschema/calculator ports.

Build:
- docker/build.sh: add pl_editor to the unified app dispatch (case,
  subdir map, all-loop).
- scripts/kicad/build-kicad-target.sh: add pl_editor to the case;
  upstream target name pl_editor under source subdir pagelayout_editor.
- scripts/kicad/build-pl_editor.sh: 7-line thin wrapper matching the
  pcbnew/eeschema/calculator pattern.
- tests/scripts/setup-kicad-wasm.sh: copy_app pl_editor.

App glue:
- wasm/stubs/nl_pl_editor_plugin_stub.cpp: no-op SpaceMouse plugin so
  pl_editor_frame.cpp's NL_PL_EDITOR_PLUGIN symbols resolve. Mirrors
  nl_pcbnew_plugin_stub.cpp.
- tests/apps/kicad/pl_editor.html: browser shell. preRun creates
  /home/kicad and FS.chdir there so file dialogs land somewhere
  friendly instead of MEMFS root (/dev/, /proc/, etc.).

E2E coverage:
- tests/kicad/pl_editor.spec.ts: 5 tests — smoke (canvas, no abort),
  wizard, File menu has Open/Save As, file-dialog folder-navigation
  regression, canvas + toolbar metrics.
- tests/e2e/filedialog-folder-nav.spec.ts: wxWidgets-level twin of
  the regression test (exercises the underlying widget directly via
  the standalone filedialog_test app).

Submodule bumps:
- kicad → feature/pl-editor (WASM gating in pagelayout_editor's
  CMakeLists + navlib stub).
- wxwidgets → feature/pl-editor (wxGenericFileDialog::OnOk navigates
  into selected directories; wasm/mouse.cpp emits wxEVT_LEFT_DCLICK
  via timestamp-based double-click detection — the latter benefits
  every wxWidgets-WASM app).

See features/pl-editor/ for the design doc + per-repo diff patches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:30:56 +02:00
Gergő Törcsvári
730147d690
feat(schematic): eeschema WASM build + e2e harness 2026-05-29 16:01:17 +02:00