@@ -368,6 +368,44 @@ Implemented and E2E-validated (playwright headless):
368368 version/cache-busting story before defaulting on. VueComponent (component_url) is the
369369 symmetric next step (httpVueLoader URL mode already exists in the JS).
370370
371+ ## ipywidgets caches as a monkeypatch in exploration repo (2026-06-15)
372+
373+ Goal: ship the ipywidgets get_state caches without waiting on upstream. Confirmed our
374+ perf-widget-construction branch is a SUPERSET of Maarten's stalled PR #3602 (it has #3602 's
375+ keys-cache + trait_to_json-cache, PLUS a static-default fast path, and deliberately OMITS the
376+ breaking comm-trait removal that blocked #3602 — so a monkeypatch is safe).
377+
378+ Built in a git worktree (exploration-wt-ipyw, branch perf/ipywidgets-cache-monkeypatch, commit
379+ 9bf4b44b8, UNSIGNED — gpg couldn't prompt; local only, no PR):
380+ - leasing/src/shared/utils/ipywidgets_cache.py — default-on monkeypatch of Widget.get_state +
381+ per-class caches, with a per-class verify-on-first-use guard (falls back to stock per class on
382+ any byte mismatch).
383+ - leasing/tests/shared/test_ipywidgets_cache.py — 35-case offline byte-identical test (passes vs
384+ stock 8.1.8). Invariant: patched output == pre-install output, so meaningful in any venv.
385+ - wired install() into solara_app.py before install_perf_hooks (before any widget exists).
386+
387+ TWO REAL BUGS found only because reconfirmation was insisted on (first 'PASSED' was bogus —
388+ stock-vs-stock for disabled classes):
389+ 1 . Reentrancy: _ verify_class constructs a probe -> __ init__ -> open() -> get_state() re-entered
390+ verification for the same class -> class wrongly disabled (+ the RecursionError spew). Fixed
391+ with a _ verifying sentinel.
392+ 2 . Two-instance compare: probes embedding layout/style sub-widgets have per-instance model ids
393+ that never match -> all core widgets disabled. Fixed by verifying on a single instance.
394+
395+ Delta reconfirmed (stock vs patched, identical-deps venv): widget create 1.33-1.45x; 200 v.Btn
396+ construction 24.5->18.7ms (1.31x); byte-identical across 14 widget types + add_traits; guard
397+ verifies all (none disabled).
398+
399+ IN-APP caveat (couldn't run real leasing app — needs secrets/DB): measured proxies only.
400+ - synthetic 1024-button solara render cycle: only ~ 3% faster (952->930ms min) because that bench
401+ is reacton-reconciler/comm-dominated; widget construction is a small fraction of it.
402+ - the leasing home page is get_state-DOMINATED (other agent profiled get_state at 41% of the
403+ cycle, cut 0.383->0.171s, render cycle -28% with the equivalent editable branch). The monkeypatch
404+ is byte-identical to that branch with the same microbench speedup, so the -28% should transfer.
405+ - DEFINITIVE in-app number needs the leasing agent: force-reinstall stock ipywidgets==8.1.8 in the
406+ leasing venv (it still has the editable branch, now redundant — and the monkeypatch would
407+ otherwise wrap the branch), run from the worktree, confirm with PROFILE=1.
408+
371409## Stash (possible bugs / questionable behavior found along the way)
372410
373411- pandas 3.0 re-render on equal DataFrame (see above) — env pin papering over it for now.
0 commit comments