a73x

14cf2f47

test: e2e — the healing leg pins the cut it restores, and names what it cannot

a73x   2026-08-28 19:53

Commit message
test: e2e — the healing leg pins the cut it restores, and names what it cannot

I5. The leg passed with the heal reverted, because everything it asserted
a default tree also satisfies. It now resizes in run 1 (so the saved tree
is not the default one) and pins the thing only a restore produces: at
80x24 the aspect rule cuts BESIDE — a vertical rail near column 40, which
run 1's capture has — and a restore that reported failure falls through to
`setRootOrient` and puts that rail back. Run 2 must have no vertical rail
at all. Run 1's own rail is asserted at >= 43 first, so run 2's silence is
a difference and not an empty capture.

Graded against the heal reverted (`restoreLayout` bailing whenever a saved
leaf is unmatched, which leaves the round-trip leg's exact match alone):
"run 2 has a vertical rail at column 42, so the aspect rule cut this wall
— the sidecar was not restored at all".

The bar grep moved BELOW the rail assertions, because the cut is what
makes a bar readable: a stacked stripe is the full 80 columns and a beside
one is 39, half a socket path.

...and the leg now NAMES what it does not claim, instead of framing a
withheld assertion as a design choice. Measured, sidecar read either side
of run 2:

    saved by run 1:   beside 0 / leaf 43 #0 / leaf 36 #b / focus 0
    after the heal:   stacked 0 / leaf 1  #0 / leaf 1  #c / focus 0

The survivor's weight and the container's orientation are both lost:
dropping the unmatched leaf collapses the two-child container to a bare
leaf, and the newcomer's `insert` re-wraps that leaf in a fresh STACKED
container with default weights. That and the focus landing on the newcomer
are defects, reported rather than fixed here.

test/e2e_12_panes.sh
Old New
@@ -755,6 +755,16 @@ XDG_STATE_HOME="$LPHSTATE" timeout 90 "$PTYCLIENT" --cols 80 --rows 24 \
755 "$MUX" > "$OUT.lphpc1" 2>&1 <<'EOF' 755 "$MUX" > "$OUT.lphpc1" 2>&1 <<'EOF'
756 expect lph-survivor-0 20000 756 expect lph-survivor-0 20000
757 settle 700 20000 757 settle 700 20000
758 send \x1cr
759 settle 300 5000
760 send l
761 settle 300 5000
762 send l
763 settle 300 5000
764 send l
765 settle 500 5000
766 send \x1b
767 settle 500 5000
758 send \x1cd 768 send \x1cd
759 waitexit 10000 769 waitexit 10000
760 EOF 770 EOF
@@ -802,20 +812,78 @@ set -e
802 echo "e2e FAIL: layout-heal: second ptyclient exited $RC:" 812 echo "e2e FAIL: layout-heal: second ptyclient exited $RC:"
803 cat "$OUT.lphpc2" "$OUT.lphcap2.err"; exit 1; } 813 cat "$OUT.lphpc2" "$OUT.lphcap2.err"; exit 1; }
804 # The wall must not have refused: the ptyclient exited 0 (checked above), 814 # The wall must not have refused: the ptyclient exited 0 (checked above),
805 # and stderr mentions no layout error. The survivor still reaches its 815 # and stderr mentions no layout error.
806 # session — tile 1 is the leaf the sidecar kept, and a marker typed there 816 #
807 # lands in the session that leaf names. 817 # The money assertion is that run 2's cut came from the SIDECAR and not
818 # from the aspect rule. At 80x24 the aspect rule cuts BESIDE — a vertical
819 # rail, a reverse-video column somewhere near 40, which run 1's capture
820 # has and this one must not. A restore that reported failure falls through
821 # to `setRootOrient(rootOrient(size))` and puts that rail back, so its
822 # absence is the heal having run AND won.
823 #
824 # What this leg does NOT claim, because the product does not do it —
825 # measured, with the sidecar read either side of the second run:
826 #
827 # saved by run 1: beside 0 / leaf 43 #0 / leaf 36 #b / focus 0
828 # after the heal: stacked 0 / leaf 1 #0 / leaf 1 #c / focus 0
808 # 829 #
809 # The focus is CHOSEN rather than inherited, and deliberately: where a 830 # * the survivor's WEIGHT does not survive (43 -> 1) and neither does the
810 # heal leaves the focus is the restore leg's claim, and that leg makes it 831 # container's ORIENTATION (beside -> stacked). Dropping the unmatched
811 # with no drift in the way. Here the drift is the subject, so the leg 832 # leaf collapses the two-child container to a bare leaf, and the
812 # names the tile it means. 833 # newcomer's `insert` then re-wraps that leaf in a fresh STACKED
834 # container with default weights (`layout.insert`'s root-is-a-leaf
835 # arm). A heal keeps WHICH tiles, never HOW they were cut.
836 # * the focus lands on the newcomer rather than the leaf the record
837 # names, which is why the marker below is typed at a tile this leg
838 # names with `\x1c1` instead of inheriting one.
839 #
840 # Both are defects, not design: a sidecar exists to remember a cut. They
841 # are reported, not fixed here.
813 grep -qi "layout" "$OUT.lphcap2.err" && { 842 grep -qi "layout" "$OUT.lphcap2.err" && {
814 echo "e2e FAIL: layout-heal: stderr mentions layout (heal was not silent):" 843 echo "e2e FAIL: layout-heal: stderr mentions layout (heal was not silent):"
815 cat "$OUT.lphcap2.err"; exit 1; } 844 cat "$OUT.lphcap2.err"; exit 1; }
816 # Two tiles: the survivor and the newcomer. The daemon has 0 and c, and 845 # Two tiles: the survivor and the newcomer. The daemon has 0 and c, and
817 # the tile the sidecar's surviving leaf carries is the one the marker 846 # the tile the sidecar's surviving leaf carries is the one the marker
818 # landed in. 847 # landed in.
848 # A rail is a CUP followed by reverse video; a BESIDE cut puts one in a
849 # column of its own, a stacked cut only ever at column 1 (the label bars).
850 # Spelled here rather than leaning on the earlier legs' copy: a variable
851 # an unrelated block happens to have left set is not an input.
852 _lph_re=$'\x1b\\[[0-9][0-9]*;[0-9][0-9]*H\x1b\\[7m'
853 _lph_cols() {
854 grep -ao "$_lph_re" "$1" | \
855 sed 's/.*\x1b\[[0-9]*;\([0-9]*\)H\x1b\[7m/\1/' | sort -n | uniq
856 }
857 # Run 1 is the anchor: it really was cut beside and `lll` really moved the
858 # rail, so run 2's silence below is a difference and not an empty capture.
859 _lph_r1=$(_lph_cols "$OUT.lphcap1" | awk '$1 > 1' | tail -1)
860 [ -n "$_lph_r1" ] && [ "$_lph_r1" -ge 43 ] || {
861 echo "e2e FAIL: layout-heal: run 1's rail ended at column ${_lph_r1:-none}, want >= 43"
862 echo " — the resize this leg saves a tree for did not happen"
863 cat "$OUT.lphpc1"; exit 1; }
864 # What run 2 may NOT be is the aspect rule's own cut. Two shapes pass, and
865 # both of them are the sidecar's:
866 #
867 # * no vertical rail — today's heal, which drops the unmatched leaf,
868 # collapses the container and re-wraps STACKED (measured above).
869 # * a rail at the column run 1 dragged the boundary to (>= 43) — a heal
870 # that also kept the saved orientation and weights, which is the fix
871 # the note above says is owed.
872 #
873 # The aspect rule at 80x24 cuts beside down the middle, around column 40,
874 # and that is the one answer neither of those can produce. Asserting "no
875 # rail at all" instead would fail the improvement and blame the restore for
876 # not having run, which is the opposite of what would have happened.
877 _lph_r2=$(_lph_cols "$OUT.lphcap2" | awk '$1 > 1' | tail -1)
878 [ -z "$_lph_r2" ] || [ "$_lph_r2" -ge 43 ] || {
879 echo "e2e FAIL: layout-heal: run 2's vertical rail is at column $_lph_r2 — neither"
880 echo " the sidecar's own cut (>= 43, where run 1 left it) nor the bare"
881 echo " leaf a heal collapses to. The aspect rule cut this wall, so the"
882 echo " saved tree never reached it"
883 cat "$OUT.lphpc2"; exit 1; }
884 # ...and only now the bars, which the cut above is what makes readable: a
885 # stacked stripe is the full 80 columns, and a beside one is 39 — half a
886 # socket path, which `labelText` truncates before it reaches the `#c`.
819 grep -q -- "--sock $SOCK60#c \[up\]" "$OUT.lphcap2" || { 887 grep -q -- "--sock $SOCK60#c \[up\]" "$OUT.lphcap2" || {
820 echo "e2e FAIL: layout-heal: the newcomer never became a tile:" 888 echo "e2e FAIL: layout-heal: the newcomer never became a tile:"
821 cat "$OUT.lphpc2"; exit 1; } 889 cat "$OUT.lphpc2"; exit 1; }