cmux-tui: an oversized launch snapshot keeps the window state and says what it left out

Merged
LA
7d ago
+396-26

Summary

When the daemon's launch snapshot is over its 8 MiB bound, the writer wrote it again with no frontend projections and no sidebar document, and nothing said so. The app draws its first frame from that file before it connects, so it lost the last window layout although the window state itself was small. The registry kept the rows; the layout came back only after the connect.

Now the writer drops the largest projections first, only as many as it must, and logs and reports (report_internal_diagnostic) the names it left out when they change (R9 R-L5). If the tree alone is still over the bound, it falls back to the smaller form as before.

The original red test (a 1.4 MB window state) is withdrawn: a realistic window state is about 30 KB (reviewer estimate), and the derived bound is the old 1 MiB floor. The PR now fixes the silent drop, the triple copy in the ledger and the old-document size query.

  • The projection bound is derived (R-L1): an even share of what the snapshot leaves after a named tree reserve, (MAX_LAUNCH_SNAPSHOT_BYTES - LAUNCH_SNAPSHOT_TREE_RESERVE_BYTES) / LAUNCH_SNAPSHOT_PROJECTION_SLOTS, with the old 1 MiB as the floor. The reserve is 4 MiB ("estimate: 4,096 tabs x ~1 KiB; not measured"); the slots are the five native projections the app keeps. Today that is 819 KiB, so the floor applies: 1 MiB. Resource-API rows keep 1 MiB, which older daemons check at start.
  • The projection idempotency ledger stored each save's whole body twice more (fingerprint and result). It now stores a sha256 digest and a result without the body; a replay returns the request's body, which the matching digest proves is the same. Rows written before still compare and replay.
  • A log line fires once when a projection crosses half of its bound. It reads the old size with octet_length(payload), which does not load the payload (the base put path read only the revision; an intermediate commit of this PR read the whole payload, now replaced).
  • A refusal names the projection and its size.

Not in this PR: per-window delta records (not needed at about 30 KB per window state). Follow-ups from review 2 (low): the slot count covers the app's five rows only (cmux-tui dock rows are small and fall in the tree reserve); the floor's comment should say that a lower bound would refuse rows the last release accepted; the ledger value could be built without cloning the body; tests for an old-format ledger replay and for "report only on change".

Land as one squash commit with this body: the intermediate commits 1a81c21326c, 588d36e3fbd and 6193e5c535f describe the withdrawn claim.

Owner of the area: PL-daemon (session cmux-b6), plans/cmux-next/OWNERS.md.

Testing

Red: an_oversized_launch_snapshot_keeps_the_window_state (launch_snapshot_size_tests.rs, commit 00b042ee2e9) at the red-proof ref aab5ed3268c (fix/pa-b7-r15-red-proof, never merge): the head with launch_snapshot.rs set back to origin/feat-cmux-next (only its bound made pub(crate) and the test module added). git diff 00b042ee2e9 aab5ed3268c -- cmux-tui/crates/cmux-tui-core/src/server/launch_snapshot_size_tests.rs is empty. The test commit came after the fix commits on this branch; the red proof is the ref with the fix reverted.

whatrefjobresult
test an_oversized_launch_snapshot redaab5ed3268ceaf23e09f1e73fec9994ce9d1 run, 1 failed: "the window state is left out of an oversized snapshot: []"
test projection00b042ee2e944aa233a5c5c2797639e2d5d98 run, 98 passed
test launch_snapshot00b042ee2e94e8efa46aabcdcb8d65d09458 run, 8 passed
clippy00b042ee2e909131a35af3acb8a66279a6epassed

Earlier, for the withdrawn claim: red b84b814d7bde9d703a8ddbc5 at 1a81c21326c (the 1 MiB refusal) and green a9381627f0a60939e2e3d364 at 6193e5c535f.

Observed / Not run

  • Observed: prepush-gate.py passes; the test file is identical on the head and the red-proof ref.
  • Not run: no real launch snapshot was measured; the tree reserve is an estimate. Austin's-day numbers: pending harness; the scenario has no restore step.

Changelog

Fixed: A large session no longer opens with an empty layout before it connects; the launch snapshot keeps the window state and drops only its largest saved pages.

Proof

No capture: daemon-only change, no UI.