diff --git a/experiments/shell/run.py b/experiments/shell/run.py index a4878c4d..dc054577 100644 --- a/experiments/shell/run.py +++ b/experiments/shell/run.py @@ -40,6 +40,21 @@ } +def require_fixed_vim(mode): + """candidate-tui types a Vim command into the real shell, so Vim must be on PATH. + + Without this check a missing Vim surfaces as a 33-second scenario timeout whose + only trace is the shell echoing "not recognized as an internal or external + command"; the requirement itself is documented in + docs/research/joint-conpty-baseline.md and is a failure, not a skippable + optional dependency (docs/research/more-capture-ech.md). + """ + if mode == "candidate-tui" and shutil.which("vim") is None: + raise RuntimeError( + "candidate-tui drives Vim inside the shell but no fixed Vim is on PATH; " + "see docs/research/joint-conpty-baseline.md") + + def validate(row, mode): if mode in WORKSPACE_DATA_MODES: closed = 3 if mode in ("ui-workspace-save", "ui-workspace-restore") else 0 if mode == "ui-workspace-empty" else 1 @@ -430,6 +445,7 @@ def close(self, record=None): def execute(exe, mode, out, timeout=40, settings_source=None, *, resources=False): if mode in ("ui-paste-system", "ui-clipboard-system", "ui-copy-system", "ui-copy-bytes-system", "ui-copy-context-system", "ui-actions-system"): require_hosted_clipboard(os.environ) + require_fixed_vim(mode) if resources and mode != "ui-workflow-sessions": raise ValueError("resource observation is limited to the bounded sessions workflow") out.mkdir(parents=True, exist_ok=False) diff --git a/experiments/shell/shell.cpp b/experiments/shell/shell.cpp index 94d14d7d..29aa6e1f 100644 --- a/experiments/shell/shell.cpp +++ b/experiments/shell/shell.cpp @@ -821,8 +821,13 @@ class ShellWindow { case 2: terminal_title_ = utf16(event.bytes); break; case 3: terminal_title_.clear(); break; case 4: MessageBeep(MB_OK); break; - case 5: case 7: case 12: break; + case 5: case 7: case 10: case 11: case 12: break; case 6: begin_close(); break; + // The candidate adapter also reports clipboard traffic (8, 9) and the + // child's exit status (13). This harness compares text, modes and + // lifecycle; it owns no clipboard and reads the exit code from the + // session, so these are drained instead of failing the scenario. + case 8: case 9: case 13: break; default: throw std::runtime_error("unsupported terminal event kind"); } }