c3ecb3b9
test(e2e): the session's OSC 52 reaches the host terminal
a73x 2026-08-15 16:27
Commit message
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -770,6 +770,10 @@ cleanup() { | |||
| 770 | "$OUT.as" "$OUT.as.err" "$OUT.as2" "$OUT.as2.err" \ | 770 | "$OUT.as" "$OUT.as.err" "$OUT.as2" "$OUT.as2.err" \ |
| 771 | "$OUT.stop" "$OUT.stop2" \ | 771 | "$OUT.stop" "$OUT.stop2" \ |
| 772 | "$OUT.pa" "$OUT.pa.err" "$OUT.pa.log" | 772 | "$OUT.pa" "$OUT.pa.err" "$OUT.pa.log" |
| 773 | # The side-channel leg: its capture, the fixture's log, and the script | ||
| 774 | # the session runs. Spelled from $OUT rather than from a variable of its | ||
| 775 | # own, for the reason the M12 controls above are. | ||
| 776 | rm -f "$OUT.clip" "$OUT.clip.err" "$OUT.clip.log" "$OUT.clip.sh" | ||
| 773 | # M14 handoff. The shim, both runtime dirs (each holding its daemon's | 777 | # M14 handoff. The shim, both runtime dirs (each holding its daemon's |
| 774 | # socket), the second key, the unusable config home, and the five | 778 | # socket), the second key, the unusable config home, and the five |
| 775 | # captures. | 779 | # captures. |
| @@ -2673,6 +2677,61 @@ PAPID="" | |||
| 2673 | rm_swept "$OUT.pa" "$OUT.pa.err" "$OUT.pa.log" "$OUT.stop" | 2677 | rm_swept "$OUT.pa" "$OUT.pa.err" "$OUT.pa.log" "$OUT.stop" |
| 2674 | ok "local mux auto-start under the pty fixture" | 2678 | ok "local mux auto-start under the pty fixture" |
| 2675 | 2679 | ||
| 2680 | # --- side channel: an OSC 52 written by the session reaches the HOST tty -- | ||
| 2681 | # | ||
| 2682 | # Measured ABSENT on 2026-08-15 (decisions.md, that date): a session emitted | ||
| 2683 | # this exact escape, and the bytes the client wrote to its host terminal | ||
| 2684 | # carried one \x1b[?1049h, one \x1b[?2026h, one marker — and zero of the | ||
| 2685 | # clipboard escape. The daemon's engine had parsed all of it; a client that | ||
| 2686 | # paints a grid forwards nothing that is not grid state. This is the same | ||
| 2687 | # instrument (the fixture's capture is what a host terminal would have | ||
| 2688 | # received) taking the same reading with the path built, and it is the first | ||
| 2689 | # end-to-end evidence that any of it does anything. | ||
| 2690 | # | ||
| 2691 | # The payload is emitted by a FILE the session runs, never typed. The shell | ||
| 2692 | # echoes what is typed, so a needle that could arrive as an echo would pass | ||
| 2693 | # on a client that forwards nothing at all — the M12 rule (tp2), and here it | ||
| 2694 | # is the difference between an assertion and a decoration. The typed line | ||
| 2695 | # names the script and cannot spell the escape. | ||
| 2696 | cat > "$OUT.clip.sh" <<'CLIPSH' | ||
| 2697 | printf '\033]52;c;aGVsbG8gZnJvbSB0aGUgc2Vzc2lvbg==\007' | ||
| 2698 | printf 'CLIPDONE\n' | ||
| 2699 | CLIPSH | ||
| 2700 | # Unquoted heredoc, unlike every other pty script in this file: the send | ||
| 2701 | # verb has to carry $OUT's per-run path. Nothing else in it needs expanding | ||
| 2702 | # — `\n` and `\x1c` are not escapes the shell touches. | ||
| 2703 | set +e | ||
| 2704 | timeout 40 "$PTYCLIENT" --cols 80 --rows 24 --out "$OUT.clip" --err "$OUT.clip.err" \ | ||
| 2705 | -- "$MUX" --sock "$SOCK" > "$OUT.clip.log" 2>&1 <<EOF | ||
| 2706 | expect \x1b[?1049h 15000 | ||
| 2707 | settle 400 15000 | ||
| 2708 | send sh $OUT.clip.sh\n | ||
| 2709 | expect CLIPDONE 15000 | ||
| 2710 | settle 400 15000 | ||
| 2711 | send \x1c | ||
| 2712 | waitexit 10000 | ||
| 2713 | EOF | ||
| 2714 | RC=$? | ||
| 2715 | set -e | ||
| 2716 | [ "$RC" -eq 0 ] || { | ||
| 2717 | echo "e2e FAIL: clipboard scenario did not run: ptyclient exited $RC" | ||
| 2718 | cat "$OUT.clip.log"; cat -v "$OUT.clip.err" 2>/dev/null; exit 1; } | ||
| 2719 | # The positive control, and it is not redundant with the check below: the | ||
| 2720 | # marker travels the ordinary grid path, so a capture holding it is a | ||
| 2721 | # capture of a session that RAN. Without it, "no escape" and "no session" | ||
| 2722 | # are the same failure line. | ||
| 2723 | grep -qa 'CLIPDONE' "$OUT.clip" || { | ||
| 2724 | echo "e2e FAIL: the session never ran (no marker on the host)"; exit 1; } | ||
| 2725 | # Terminator included: the client builds the escape whole or writes nothing | ||
| 2726 | # at all (client.zig, appendTermEvent), and a needle stopping at the payload | ||
| 2727 | # could not tell a complete escape from half of one left painting on the | ||
| 2728 | # user's tty. -F because the needle is bytes, not a pattern. | ||
| 2729 | CLIPESC=$(printf '\033]52;c;aGVsbG8gZnJvbSB0aGUgc2Vzc2lvbg==\007') | ||
| 2730 | grep -qaF "$CLIPESC" "$OUT.clip" || { | ||
| 2731 | echo "e2e FAIL: OSC 52 never reached the host tty"; exit 1; } | ||
| 2732 | rm_swept "$OUT.clip" "$OUT.clip.err" "$OUT.clip.log" "$OUT.clip.sh" | ||
| 2733 | ok "the session's OSC 52 reaches the host terminal" | ||
| 2734 | |||
| 2676 | # --- M14: the ssh→QUIC handoff ----------------------------------------- | 2735 | # --- M14: the ssh→QUIC handoff ----------------------------------------- |
| 2677 | # | 2736 | # |
| 2678 | # `mux HOST` fetches QUIC coordinates over ssh once, caches them, and | 2737 | # `mux HOST` fetches QUIC coordinates over ssh once, caches them, and |
| @@ -3678,16 +3737,17 @@ DPID="" | |||
| 3678 | 3737 | ||
| 3679 | # The pins. Literals, not variables set from counting something else — | 3738 | # The pins. Literals, not variables set from counting something else — |
| 3680 | # "assert the literal, never the constant the code under test reads" | 3739 | # "assert the literal, never the constant the code under test reads" |
| 3681 | # (decisions.md, M10). 26 scenario checkpoints; 35 convergence points. | 3740 | # (decisions.md, M10). 27 scenario checkpoints; 35 convergence points. |
| 3682 | # Anyone adding a scenario updates these by hand, on purpose. | 3741 | # Anyone adding a scenario updates these by hand, on purpose. |
| 3683 | # | 3742 | # |
| 3684 | # M18 added three checkpoints and no convergence points: its wall block | 3743 | # M18 added three checkpoints and no convergence points: its wall block |
| 3685 | # asserts through assert_ws_converged, which is deliberately outside | 3744 | # asserts through assert_ws_converged, which is deliberately outside |
| 3686 | # CONV_COUNT (see its comment) — that pin counts assert_converged call | 3745 | # CONV_COUNT (see its comment) — that pin counts assert_converged call |
| 3687 | # sites, and folding the two together would make either number stop | 3746 | # sites, and folding the two together would make either number stop |
| 3688 | # meaning anything. | 3747 | # meaning anything. The side-channel leg added the 27th and no convergence |
| 3689 | [ "$OK_COUNT" = "26" ] || { | 3748 | # point: its capture is asserted on for a byte the grid does not carry. |
| 3690 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 26 —" | 3749 | [ "$OK_COUNT" = "27" ] || { |
| 3750 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 27 —" | ||
| 3691 | echo " a scenario was added (update the pin) or silently lost" | 3751 | echo " a scenario was added (update the pin) or silently lost" |
| 3692 | exit 1 | 3752 | exit 1 |
| 3693 | } | 3753 | } |
| @@ -3695,4 +3755,4 @@ DPID="" | |||
| 3695 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" | 3755 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" |
| 3696 | exit 1 | 3756 | exit 1 |
| 3697 | } | 3757 | } |
| 3698 | echo "e2e OK (26 scenarios, 35 convergence points)" | 3758 | echo "e2e OK (27 scenarios, 35 convergence points)" |