Note
🤖 Claude Opus 5.5 responding on behalf of Sam
Reproduced on current main. The root cause is a missing feature, not a regression. Acceptance criterion 2 needs a change before work starts, so this issue gets needs-info.
Reproduction
- Build: the
mainengine pina20f56e. The tested bundle was built from471737fb. Between471737fbandmainatffefedfa, onlyPROGRESS.mdanddocs/limitations.mdchanged. - Display 3 is the main display, 2560 x 1440 points, so 50% is 1280 points.
verify-mirirun20261106-192150openedfixture-1andfixture-2(380-point content) while Miri ran.- Both fixtures tiled as 388-point columns, at x = 1015 and x = 1403. Neither column was 1280 points.
- The run ended with
matched=10, missing=0atbeginand atend, and the layout check passed.
Root cause
- Miri has no default width for new windows. The engine's only way to set a window's first width is a
[windows]rule withwidth. apply_window_defaults(vendor/paneru/src/ecs/triggers.rs:1383) applies that rule to every window entity that the engine adds. It runs at startup too. The comment attriggers.rs:1430says "Safe during init". The schedule atvendor/paneru/src/ecs.rs:287has noInitializingcondition.- So a blanket
width = 0.5rule would also resize windows that exist at startup. That is why Miri writes no width rule (decisions/configuration.md:54). The engine testtest_step_grow_then_shrink_keeps_a_fitting_strip_at_viewport_edges(vendor/paneru/src/tests/tiling.rs:595) shows this. Its two windows exist at startup, and a 0.5 rule makes each one 512 points wide on a 1024-point display. - Per-window
widthrules arrived in panerudbf6ead. No commit ever gave new windows their own width, so nothing regressed.
Where a fix can hook in
- The engine already has the facts a fix needs, and
apply_window_defaultsis the hook. The fix applies 50% only when no rule setswidth, and only after the floating check returns. LaunchWindows(vendor/paneru/src/ecs.rs:546) records every window that existed when the engine started, even one that a slow scan finds late. An engine restart is a new process, so it records them again.- The
Initializingresource (vendor/paneru/src/ecs.rs:960) exists untilfinish_setupremoves it (vendor/paneru/src/ecs/systems.rs:407). - Session restore keeps matching windows for a startup grace period (
restore_startup_grace, 2 s by default,vendor/paneru/src/ecs/restore.rs:453). A window that appears after startup but inside that grace period is not inLaunchWindows, and restore can still place it.matches_startup_restore_state(vendor/paneru/src/ecs/restore.rs:394) tells whether restore will place a window. The engine test atvendor/paneru/src/tests/session_restore.rs:857covers such a late window. - So a new window gets 50% only when all three hold: its id is not in
LaunchWindows,Initializingis absent, and restore does not match it. Without the third condition, a late restored window would first get 50% and then its saved width. I read this in the code and did not observe it live. - The engine computes the ratio from the active display (
active_display.bounds(),vendor/paneru/src/ecs/triggers.rs:1424), not from the display that holds the window. The issue says "its display". The two differ only when a new window opens on a display that is not active. - Fixed-size windows float, and
apply_window_defaultsreturns for them before any width code (triggers.rs:1405-1418), so a hook placed after that check cannot resize them.
Acceptance criteria gaps
- Criterion 2 ("open a fixture window 900 points wide, then quit and relaunch Miri") cannot run as written, and passing it would not prove the behavior.
verify-miri fixturehas no width option. AXFixture always opens 380 points wide (Sources/AXFixture/main.swift:40).- With the new behavior, a fixture opened while Miri runs gets 1280 points, not 900.
- After a quit and relaunch, session restore sets the saved width. So the check passes through restore even if the startup path resizes found windows.
- Proposed replacement for criterion 2, after AXFixture and
verify-miri fixtureget a--widthoption:verify-miri: with Miri quit, open a 900-point fixture, then launch Miri. The fixture, which is not in the saved session, keeps a 900-point width. This checks the startup path.verify-miri: with that fixture still open and now saved in the session, quit and relaunch Miri. The fixture keeps its 900-point width. This checks the restore path.- Engine test: a window that appears inside the restore grace period and matches the saved session gets its saved width and never gets 50%.
Criteria 1, 3, and 4 can be checked as written.