Fix pre-release defects from the v1.0.30 regression audit - #747
Open
brentyi wants to merge 7 commits into
Open
Conversation
Python:
- Initialize _share_tunnel before the atexit registration so a failed
__init__ can't leave the atexit stop() recursing through the
backwards-compat __getattr__ shim at interpreter exit.
- request_share_url now closes and replaces a dead ("failed"/"closed")
tunnel instead of returning None forever; ViserTunnel.close()
tolerates the failed connection's already-closed loop.
- GuiTabGroupHandle.remove() tombstones under _panel_lifecycle_lock
(mirroring PanelHandle.remove) so a racing add_tab can't register an
orphan container for a dead group.
- min/max_orbit_distance setters validate finiteness, positivity, and
min<=max before the no-op short-circuit.
- _colors_to_int_tuple materializes its argument once so generator
inputs can't be half-consumed by the warning pass.
- Async callback dispatch reachable from user threads (on_client_connect
replay, value-setter update callbacks, form submit, pointer-done
cleanups) now uses run_coroutine_threadsafe instead of the
non-thread-safe loop.create_task.
Client:
- App.tsx reads the mobile media query synchronously on first render so
mobile-width loads no longer mount the canvas inside the dock surface
and immediately remount it (throwaway WebGL context + GPU re-upload).
- The mobile bottom-sheet panel list subscribes with value-level
equality, so visible/order/tab-metadata updates propagate.
- InstancedMesh2.dispose() nulls instanceIndex and drops the geometry
attribute so a frame drawn in the dispose->swap window re-initializes
instead of binding a deleted GL buffer.
- The splat sort worker tracks buffer dirtiness so an updateBuffer that
lands during a running sort is re-sorted even with a stationary
camera.
Dock (spec-traced per dock-ux-spec.md):
- All resize surfaces (region resizer, docked dividers, stack divider,
window grips) share the one-gesture slot move-drags already used, so
a second pointer can't start a concurrent gesture (spec 4).
- Hit-testing: vertical tab insertion gets the same proximity bound as
the horizontal bar path so rail merge is reachable (spec 5.3); the
region side band yields across its overhang of a collapsed cell so
one seam yields one drop with no hint hop (D55); sub-8px bottom
split/snap bands are removed rather than shrunk (P11).
- The stack divider's cancel/motionless-press path commits the stored
pinned height instead of the capped rendered height (P2/D56).
- Escape during a region resize restores float positions pushed by
pushFloatsAheadOfSeam, not just widths (spec 4, P2).
Every behavioral fix is pinned by a regression test verified to fail on
the pre-fix code: 5 Python (tunnel recovery, orbit validation, color
coercion, atexit exit, tab-group race) and 11 client assertions across
hit-testing, divider cancel, float restore, splat staleness, and
dispose. Gates: ruff check/format clean, 689 Python tests pass; tsc,
eslint, prettier clean, 630 client tests pass.
The new setter validation rejected non-finite values, which removed the only opt-out from the 1e4 default: infinity is the pre-1.1.0 unbounded behavior and the camera-controls default, and large-scale scenes need it. Accept +inf for the max bound (NaN and <= 0 still raise); the min bound stays strictly finite.
brentyi
force-pushed
the
claude/regressions-v1-0-30-5wehcp
branch
from
July 26, 2026 15:19
37b58d6 to
e922562
Compare
A local npm 10 re-resolve during the gate runs dropped the @emnapi/core and @emnapi/runtime entries that @napi-rs/wasm-runtime references, which npm 11's stricter 'npm ci' sync check rejects. No dependency changes were intended; this restores main's lockfile byte-for-byte.
Two coverage holes that hid this cycle's worst bugs: - test_mobile_breakpoint.py: a mobile-width load must not mount the canvas desktop-first and remount it (counts WebGL context creations and canvas removals against a desktop self-baseline), and the mobile bottom sheet must reflect server-side panel visibility updates. - test_touch_gesture_mutex.py: while a divider resize is in progress via one touch, a second concurrent touch on a floating window's grip must be ignored (spec 4, one active gesture at a time), driven as real multi-touch through CDP Input.dispatchTouchEvent. Both verified to fail against a client built from the pre-fix sources and pass against the fixed build.
…uite Twice on CI (py3.9 shard 2: runs 30298026392 attempt 1 and 30418320431) the per-worker Vite dev server wedged permanently partway through the run: page.goto kept succeeding but the module graph never completed, so the dock never mounted and all remaining playground tests on that worker burned the full 60s timeout twice each -- 17 consecutive failures over ~30 minutes, with orphaned esbuild children left behind (the dependency optimizer hanging is the prime suspect). The failure onset shifted between the two runs, so it is not tied to a specific test, and unlike the documented 2026-07-18 starved-runner incident it never recovers on its own -- which means the existing generous-timeout hardening cannot help. Waiting cannot rescue a wedged server; a restart can. - tests/e2e/vite_manager.py: extract the dev-server process management from the conftest fixture into a restartable ViteServerManager. The server now runs in its own process group and teardown signals + waits on the WHOLE group (escalating to SIGKILL), fixing the esbuild orphan leak: `proc.terminate()` only killed the npx wrapper, and waiting on the direct child returned as soon as npx died while node/esbuild lived on. stdout/stderr now go to a log file instead of DEVNULL, and the tail is printed on restart so a future wedge is diagnosable. - dock_helpers.open_playground: after the existing two page-boot attempts fail, restart the worker's Vite server and make one final attempt against the fresh process -- turning the 30-minute cascade into a ~2 minute self-heal with zero test failures. The third attempt only exists when a managed server was actually restarted. - conftest: the vite_server fixture now just owns a manager's lifetime. Validated end-to-end outside CI: normal boot, SIGSTOP-simulated wedge (two failed attempts -> restart with log tail -> healed boot on the same port), old process group fully reaped, and teardown leaving no processes. ruff check/format clean; playground tests pass through the new fixture path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EXMGHKyd8kaEDcVZ85qzHf
An advocate/critic subagent panel reviewed the PR's effective diff vs main. The advocate independently re-ran the falsification protocol (all new Python and client regression tests fail on pre-fix sources, pass on the branch); the critics surfaced the defects fixed here, each verified against the code before fixing. Tunnel (should-fix): the dead-tunnel replacement path leaked one SyncManager child process + watcher thread per request_share_url retry while the share backend was unreachable. A tunnel that ends "failed" never sets _connect_event, so on_connect's wait_job blocked forever, pinning the mp.Manager proxies; pre-fix the leak was bounded at one because the failed tunnel poisoned the slot, but the replacement branch creates a fresh tunnel per retry. ViserTunnel.close() now releases the watcher (wait_job gains a status guard so the release cannot fire the connect callback; _connect_job stores "connected" before setting the event, making the guard race-free). Pinned by test_close_of_failed_tunnel_releases_watcher_thread, verified to fail pre-fix. Also: the failed tunnel's on_disconnect no longer broadcasts ShareUrlUpdated(None) when a newer tunnel owns the slot -- the message persists in the broadcast buffer, so the stale None also hit late joiners. InstancedMesh2 (should-fix, vendored -- LOCAL_PATCHES.md updated): a frame drawn in the dispose->unmount window crashed the shadow pass: shadow maps render BEFORE the main pass, onBeforeShadow only guarded its culling call, and the unconditional instanceIndex.update() dereferenced the nulled attribute (any shadow-casting batched mesh, the Python API default). onBeforeShadow now has the same null gate as onBeforeRender. LOD levels are separate meshes with per-level geometries, so dispose() now drops each level geometry's retained instanceIndex attribute too, instead of leaving them binding the freed GL buffer. Both pinned by new dispose tests, verified to fail pre-fix. Mobile sheet test gap (should-fix): panelUpdates.test.ts pinned the equality utilities but not the component wiring -- reverting PanelsFallback to key-set equality left the suite green. The comparator choice now lives in panelsEquality.ts (imported by the component), and the test pins both the module identity and, via a ?raw source assertion, the selector call site. Harness nits: vite_manager falls back to direct-child terminate on Windows (os.killpg/getpgid are POSIX-only; the old fixture worked everywhere), and a failed mid-suite vite restart no longer masks the page-boot diagnostic (open_playground re-raises the page error with the restart failure chained). Gates: ruff check/format clean; targeted Python suites 49/49 (new leak test fails on pre-fix code); tsc, eslint, prettier clean; client suite 637/637 with the four new pins failing on pre-fix sources. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EXMGHKyd8kaEDcVZ85qzHf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.