SIGN IN SIGN UP

test(pty): Rust-native PTY conformance harness (M0.1) (#114)

* test(pty): add Rust-native PTY conformance harness

Shell hooks bind keyboard events (Enter, bracketed paste); those code
paths only run under a real terminal. The existing PTY tests shell out
to the `expect` Tcl tool, which is flaky on macOS and silently
sensitive to the bash version on PATH.

Add `tests/pty_support/mod.rs`: a Rust driver over `portable-pty` that
spawns a disposable shell through a real pseudo-terminal, sources a
tirith hook, sends bytes, reads terminal output, and asserts
invariants — no external `expect` dependency.

Harness features:
- `IsolatedEnv` — fresh HOME/XDG_STATE/DATA/CONFIG temp dirs per
  session so a test never touches the developer's real tirith state.
- `modern_bash()` / `fish_bin()` — locate a usable shell (bash >= 5,
  skipping macOS's ancient 3.2) and return None so callers skip
  cleanly when a shell is absent.
- A terminal-capability auto-responder (DA1, cursor position, OSC 11,
  kitty keyboard, XTGETTCAP) — fish 4.x blocks in startup forever
  without it; harmless for bash.
- `expect` / `wait_idle` / `count_occurrences` — drive the
  conversation and assert "executes exactly once" against filesystem
  side effects rather than noisy terminal echo.

Adds `portable-pty` as a dev-dependency of crates/tirith.

* test(pty): shell-hook conformance contract + tests

Define the hook conformance contract — the invariants every tirith
shell hook must satisfy — and enforce it with PTY-driven tests:

  (a) an allowed command executes exactly once
  (b) the command is not swallowed
  (c) not duplicated in shell history
  (d) a blocked command does not execute
  (e) a warned command executes once
  (f) degradation is visible, not silent
  (g) non-interactive sourcing is a complete no-op

`docs/shell-hook-conformance.md` states the contract; the harness
checks "exactly once" against marker-file side effects, not terminal
echo.

Coverage:
- bash preexec mode — a, b, c, e, g (passing); d via
  TIRITH_BASH_PREEXEC_ENFORCE=1 (passing).
- bash enter mode — f passing (a buggy enter mode must still fail
  loudly and persist the safe-mode flag).
- fish — a, b, c, d, e, g (passing).
- zsh / PowerShell / nushell — documented #[ignore]d follow-up stubs.

Known bug #111: bash enter mode `bind -x` on Enter runs the bound
function but never accepts the line, so PROMPT_COMMAND never fires and
the pending command is never delivered — a command is eaten, then a
degrade. The harness reproduces this precisely. The enter-mode
delivery tests (a, b, c, d) are written and #[ignore]d with a #111
reference: ready regression checks that pass once #111 is fixed and
keep `cargo test` green meanwhile.

All tests skip cleanly when their shell is unavailable, so
`cargo test --workspace` stays green without a modern bash or fish.

* test(pty): address Greptile review on the conformance harness

- TIRITH_SESSION_ID is now unique per IsolatedEnv. process::id() alone is
  identical for every test in one `cargo test` run, so concurrently-running
  tests shared a session identity; a per-call atomic counter makes it
  genuinely unique.
- Correct the history-test comment: clear_buffer() runs before `history`, so
  the probe string's "typed" occurrence is not in the captured output — it
  appears at most once, in the listing.
- Explain the BLOCKED / getvet.sh / "tirith run" assertion strings. getvet.sh
  is tirith's vet remediation pointer, not a stray artifact — kept.
S
Sheeki committed
f482a5a057afe19849ecb339e2dfbeebccb64bf4
Parent: e701277
Committed by GitHub <noreply@github.com> on 5/21/2026, 11:58:37 AM