The layout oracle checks the saved profile's round trip at every settled step and the live layout's model rules on every published frame, which covers what this 1 s settle logger did.
* test(e2e): read the live runtime layout in the layout oracle
The oracle now reads the runtime's layout from a layout.subscribe consumer
(the window's renderer, or a headless host's pairing offer) instead of
session:get or the saved profile. Model rules run on every published frame,
replacing the 300 ms settle read and the 100 ms rule sampler; disk-format
rules and the round trip run on the saved profile at each settled step.
Views are compared to the live layout exactly for every group and tab kind.
The known-on-main list is refilled from runs of this oracle on main.
* test(e2e): known-on-main differences do not hold the oracle's settle wait
A difference main is known to keep never converges, so a step settles once
only known-on-main differences remain. The orcad title-after-restart entry
is narrowed to the scenario a run on main reproduced.
* test(e2e): oracle settle counts frames across sleeps, pairs renamed groups, flushes saves
The settle wait carries the frame count across reads, so a frame during the
sleep restarts the quiet window. The client check pairs a group listed under
another id with the runtime group holding the same tabs in the same order,
so only that rename is known on main and a dropped group still fails. The
window flushes main's debounced save before the saved-profile read.
* test(e2e): refill known-on-main from Linux CI and post-flush runs on main
Removes the two-writers saved-profile entry (gone once the window flushes
main's save). Re-adds client-reorder tab order and the cold-relaunch client
list, and adds the cold- and live-daemon relaunch tab-bar entries and the
Electron rename-after-restart entry, each reproduced by Linux CI run
38170663248 or a local run on main's code.
* test(e2e): scope known-on-main entries to the OS that reproduced them
Entries reproduced on one OS only carry a platform and match only there.
The cold-relaunch restart entry now matches only a single group whose tabs
and order are unchanged, so a reorder still fails.
* List the cold-relaunch tab-order swap as known on main
The cold-daemon-restart oracle scenario intermittently finds the tab rows in swapped order after
the relaunch. The relaunch's exits for the killed daemon's terminals retire both tab rows, the
respawns re-add them in the order they bind, and the window's next save keeps main's row order.
Co-Authored-By: Claude <noreply@anthropic.com>
* Wrap the known-on-main comment to the line limit
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(layout): read-only layout.subscribe stream and the round-trip self-check (inert)
- layout.subscribe streams each workspace's layout as an explicit published projection
(tabs, groups, panes and their terminals), never the model itself: no runtime records,
closed-tab records, spawn inputs or legacy persistence values. It is re-projected from the
Store's sessions after each write, only while someone subscribes, and only from the
partition that owns each workspace. Not advertised as a capability and not on the mobile
allowlist, so nothing calls it yet.
- Replaces the unused terminal-topology publisher (#25682) it generalizes.
- The round-trip self-check: a partition must load and save to a fixed point, changing only
the kinds of value the Loader's rules change. It runs on Store writes in dev and e2e (logged,
never thrown) and as the oracle's new roundTrip check.
* fix(layout): know the spawn handoff main's minimal row mint stores
* fix(layout): name-based invented ids, no partition on the wire, isolated listeners
- Every id the Loader invents is derived from what it stands in for (a workspace's group from
its key, a pane from its row or tab and position, a repeated tab id from its terminal), so
removing an earlier workspace or tab no longer re-ids later ones.
- The published layout no longer names its partition: the workspace key is the address and
routing stays with the host; each tab still names the host that runs it.
- One throwing stream listener no longer costs the others the event.
- The self-check says plainly it is an idempotence check, and logs each new kind of first-load
change with its count.
* test(terminal): check the runtime's workspace layout against structural rules
A pure rules check over the runtime's session partitions: a terminal in two
panes, a pane in two tabs, a tab outside exactly one group, tab rows and tab
bar disagreeing, two tab orders, and ids changing for the same pane, tab or
group across an update. Wired into the seeded layout model test after every
step, plus late-spawn cases (one known on main, kept as an expected failure).
* test(e2e): layout oracle scenarios with window, headless serve and SSH
Drives layout journeys through window gestures, CLI and paired-client runtime
commands, and after every step checks the runtime's layout against the
structural rules, the window's DOM, the paired-client and CLI views, the
expected end layout, restarts (relaunch, cold daemon, in-app daemon restart)
and per-pane terminal markers. Findings main still has are listed with their
cause; anything else fails. Includes headless orcad/Electron serve scenarios
for the serve issues and a Docker SSH scenario.
* test(terminal): check layout rules on every write and race client commands with the window
The layout model now checks the structural rules on every in-memory
workspace-session write, not only at each step's end, naming the writer
of any breach. Three documented two-writer transients (main mints a tab
row before the window writes its tab bar) are allow-listed by rule, op
and writer; they must still heal by the step's end.
New model ops race a stale window save against a paired client's tab
close, a host-created split, and main's PTY-exit retirement. A new file
runs main's updateMobileSessionPaneLayout (old phone pane trees) against
the real Store; the case with no topology revision is an it.fails: main
drops a live pane and its binding there.
* test(e2e): oracle expected outcomes, transient sampling, remount counts, more scenarios, latency
Every step checks the expected panes, which panes went away and that the rest
kept their terminals; a 100 ms sampler runs the rules between steps; xterm
creations are counted per pane and an unexpected recreation fails. Adds host
navigation, window reload (graph republication), quit right after a change
(quit-time save), an old client's stale pane tree, and a latency measurement
(command to drawn update, idle and under an output flood; no pass/fail).
* docs(e2e): describe the oracle's added checks and latency measurement
* test(e2e): #12723 SSH folder scenario, CI lane for the SSH oracle spec, short socket dir
Adds an SSH folder-workspace (no repo) scenario that checks the layout lives in
one partition across relaunch; registers the SSH oracle spec in the Docker SSH
lane's selection lists; headless hosts put long-TMPDIR profiles under ~/.orca-lo
instead of /tmp.
* ci(e2e): run the headless layout oracle in the job that builds orcad; Linux relaunch findings
The headless oracle spec needs out/orcad, which only the orca serve mode-switch
job builds, so it runs there (and fails rather than skips in CI if orcad is
missing). Records what the first Linux CI run found on main: after a relaunch
main can hold tab rows with no tab bar past the settle wait, and Electron serve
on Linux also lists a renamed terminal's title as null after a restart.
* test(e2e): join partition lists in the SSH folder scenario's log lines
* test(e2e): retry the latency spec's reads through a brief terminal-ownership gap
Linux CI saw terminal.list answer terminal_surface_ownership_unavailable while
the window re-published its terminals; reads retry for up to 10 s, commands do not.
* test(e2e): add terminal layout parity check for topology refactor PRs
Runs fixed terminal-layout journeys in the real app on two builds, captures
the renderer topology and the saved workspace session, normalizes volatile
values and fails on any difference not declared for a named bug.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): record quit exit status and report paths main does not reproduce
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): invoke pnpm correctly under corepack and silence the typeless-module warning
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): accept pnpm's forwarded -- in the layout parity runner
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): close parity panes through the user's chord and treat absent maps as empty
Driving PaneManager.closePane directly left main to learn of the close from the
PTY exit, which raced quit; the keyboard path commits the close in main first.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): build parity checkouts before running, forward -g, and add a drag-out scenario
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>