113a7e2c
test: the restore leg asserts the focus came back
a73x 2026-08-25 09:10
Commit message
docs/superpowers/specs/2026-08-24-layout-persistence-design.md
| Old | New | ||
|---|---|---|---|
| @@ -49,6 +49,7 @@ beside 0 | |||
| 49 | stacked 39 | 49 | stacked 39 |
| 50 | leaf 12 --sock /tmp/x#b | 50 | leaf 12 --sock /tmp/x#b |
| 51 | leaf 11 --sock /tmp/x#c | 51 | leaf 11 --sock /tmp/x#c |
| 52 | focus 2 | ||
| 52 | ``` | 53 | ``` |
| 53 | 54 | ||
| 54 | - First line: `mux-layout 1`. Any other first line means "not mine": | 55 | - First line: `mux-layout 1`. Any other first line means "not mine": |
| @@ -66,12 +67,20 @@ beside 0 | |||
| 66 | - Leaf line: `leaf CELLS SPELLING`. `flatten` treats weights | 67 | - Leaf line: `leaf CELLS SPELLING`. `flatten` treats weights |
| 67 | proportionally, so a different terminal restores the same ratios at | 68 | proportionally, so a different terminal restores the same ratios at |
| 68 | its own size; absolute cells are a snapshot, not a promise. | 69 | its own size; absolute cells are a snapshot, not a promise. |
| 70 | - Focus line: `focus K`, optional, after all node lines. K is the | ||
| 71 | focused leaf's encounter index (0..n-1 in depth-first order). A | ||
| 72 | present but malformed focus record (K out of range, a second focus | ||
| 73 | line, any node line after it) makes the whole parse return null. | ||
| 69 | - The root line's `beside`/`stacked` IS the persisted root orientation. | 74 | - The root line's `beside`/`stacked` IS the persisted root orientation. |
| 70 | When a restore applies, `rootOrient` (the aspect rule) is not | 75 | When a restore applies, `rootOrient` (the aspect rule) is not |
| 71 | consulted. | 76 | consulted. |
| 72 | - Fullscreen is NOT persisted. It is a transient view op; reattach is | 77 | - Fullscreen is NOT persisted. It is a transient view op; reattach is |
| 73 | always un-fullscreened. | 78 | always un-fullscreened. |
| 74 | - Focus is NOT persisted. Focus starts at tile 0, as today. | 79 | - Focus IS persisted (amended 2026-08-25 after user hands-on). The |
| 80 | sidecar's optional `focus K` line names the focused leaf by encounter | ||
| 81 | index in the depth-first walk; restore maps it through the spelling | ||
| 82 | match and falls back to tile 0 when the leaf did not survive. The | ||
| 83 | Ctrl-\ w fold never moves focus — the user is typing when they fold. | ||
| 75 | - Size cap mirrors the wall file's load cap (1 MiB); a bigger file is | 84 | - Size cap mirrors the wall file's load cap (1 MiB); a bigger file is |
| 76 | malformed. | 85 | malformed. |
| 77 | 86 | ||
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -1447,7 +1447,7 @@ cleanup() { | |||
| 1447 | "$OUT.lpr.d" "$OUT.lpra" "$OUT.lpra.err" "$OUT.lprb" "$OUT.lprb.err" \ | 1447 | "$OUT.lpr.d" "$OUT.lpra" "$OUT.lpra.err" "$OUT.lprb" "$OUT.lprb.err" \ |
| 1448 | "$OUT.lprcap1" "$OUT.lprcap1.err" "$OUT.lprpc1" \ | 1448 | "$OUT.lprcap1" "$OUT.lprcap1.err" "$OUT.lprpc1" \ |
| 1449 | "$OUT.lprcap2" "$OUT.lprcap2.err" "$OUT.lprpc2" \ | 1449 | "$OUT.lprcap2" "$OUT.lprcap2.err" "$OUT.lprpc2" \ |
| 1450 | "$OUT.lprfa" "$OUT.lprstop" "$SOCK59" \ | 1450 | "$OUT.lprfa" "$OUT.lprfb" "$OUT.lprstop" "$SOCK59" \ |
| 1451 | "$OUT.lph.d" "$OUT.lpha" "$OUT.lpha.err" "$OUT.lphb" "$OUT.lphb.err" \ | 1451 | "$OUT.lph.d" "$OUT.lpha" "$OUT.lpha.err" "$OUT.lphb" "$OUT.lphb.err" \ |
| 1452 | "$OUT.lphc" "$OUT.lphc.err" "$OUT.lphcap1" "$OUT.lphcap1.err" \ | 1452 | "$OUT.lphc" "$OUT.lphc.err" "$OUT.lphcap1" "$OUT.lphcap1.err" \ |
| 1453 | "$OUT.lphpc1" "$OUT.lphcap2" "$OUT.lphcap2.err" "$OUT.lphpc2" \ | 1453 | "$OUT.lphpc1" "$OUT.lphcap2" "$OUT.lphcap2.err" "$OUT.lphpc2" \ |
| @@ -8071,6 +8071,8 @@ send l | |||
| 8071 | settle 500 5000 | 8071 | settle 500 5000 |
| 8072 | send \x1b | 8072 | send \x1b |
| 8073 | settle 500 5000 | 8073 | settle 500 5000 |
| 8074 | send \x1cl | ||
| 8075 | settle 500 5000 | ||
| 8074 | send \x1cd | 8076 | send \x1cd |
| 8075 | waitexit 10000 | 8077 | waitexit 10000 |
| 8076 | EOF | 8078 | EOF |
| @@ -8095,6 +8097,10 @@ case "$_head2" in | |||
| 8095 | *) echo "e2e FAIL: layout-restore: sidecar root is '$_head2', want 'beside 0'" | 8097 | *) echo "e2e FAIL: layout-restore: sidecar root is '$_head2', want 'beside 0'" |
| 8096 | cat "$LPLAYOUT"; exit 1 ;; | 8098 | cat "$LPLAYOUT"; exit 1 ;; |
| 8097 | esac | 8099 | esac |
| 8100 | # The focus record: focus-right moved focus to pane b (leaf 1). | ||
| 8101 | grep -q '^focus 1$' "$LPLAYOUT" || { | ||
| 8102 | echo "e2e FAIL: layout-restore: sidecar carries no focus record" | ||
| 8103 | cat "$LPLAYOUT"; exit 1; } | ||
| 8098 | 8104 | ||
| 8099 | set +e | 8105 | set +e |
| 8100 | # Second ptyclient: reattach. The sidecar restores the resized tree — | 8106 | # Second ptyclient: reattach. The sidecar restores the resized tree — |
| @@ -8127,11 +8133,15 @@ _max_rail=$(grep -ao "$_rail_re" "$OUT.lprcap2" | \ | |||
| 8127 | [ "$_max_rail" -ge 43 ] || { | 8133 | [ "$_max_rail" -ge 43 ] || { |
| 8128 | echo "e2e FAIL: layout-restore: rail column $_max_rail < 43 (resize did not survive)" | 8134 | echo "e2e FAIL: layout-restore: rail column $_max_rail < 43 (resize did not survive)" |
| 8129 | cat "$OUT.lprcap2"; exit 1; } | 8135 | cat "$OUT.lprcap2"; exit 1; } |
| 8130 | # The marker typed into the restored wall landed in session a (tile 0, | 8136 | # The marker typed into the restored wall landed in session b — the |
| 8131 | # the default focus). | 8137 | # focus was saved on pane b and restore put it back there, not tile 0. |
| 8138 | timeout 20 "$MUXA" capture --sock "$SOCK59" --session b > "$OUT.lprfb" 2>&1 | ||
| 8139 | grep -q "lpr-rt-marker" "$OUT.lprfb" || { | ||
| 8140 | echo "e2e FAIL: layout-restore: marker not in session b (focus did not survive):" | ||
| 8141 | cat "$OUT.lprfb"; exit 1; } | ||
| 8132 | timeout 20 "$MUXA" capture --sock "$SOCK59" --session a > "$OUT.lprfa" 2>&1 | 8142 | timeout 20 "$MUXA" capture --sock "$SOCK59" --session a > "$OUT.lprfa" 2>&1 |
| 8133 | grep -q "lpr-rt-marker" "$OUT.lprfa" || { | 8143 | ! grep -q "lpr-rt-marker" "$OUT.lprfa" || { |
| 8134 | echo "e2e FAIL: layout-restore: marker not in session a after round trip:" | 8144 | echo "e2e FAIL: layout-restore: marker leaked into session a (focus was tile 0, not b):" |
| 8135 | cat "$OUT.lprfa"; exit 1; } | 8145 | cat "$OUT.lprfa"; exit 1; } |
| 8136 | assert_stopped "$SOCK59" "$D60PID" "layout-restore" "$OUT.lprstop" | 8146 | assert_stopped "$SOCK59" "$D60PID" "layout-restore" "$OUT.lprstop" |
| 8137 | D60PID="" | 8147 | D60PID="" |