Repository navigation
Give the command /dev/null when standard input is a terminal - #37
Merged
Merged
Conversation
Until now a terminal on the supervisor's standard input refused the launch with exit 64 before anything started, which is what every quick-start command did when typed into Terminal. The supervised command now receives /dev/null instead and the supervisor prints one stderr line saying so. A file, a pipe, or an explicit < /dev/null is passed on unchanged and silently. Shell job control stays unsupported. LaunchErrorKind::InteractiveTerminalUnsupported and validate_noninteractive_terminal are removed; resolve_stdin (pure) and OwnedProcess::stdin_disposition replace them. The run banner and the new line share one announce_launch helper, printed only after a successful launch. Tests: resolve_stdin unit cases in the existing openpty test; three real-binary cases (run and observe under a pseudo-terminal, bounded by the wall time so a regression ends in 75 instead of a hang, and a piped-stdin control); a Python pty.fork case that mirrors the failure recorded against the 0.2.0 wheel. The trial harness's T0 probe validator moves to the new contract.
The CLI, process-control, and support pages describe the /dev/null substitution and the one stderr line, and say why a command that reads the keyboard cannot run in its own process group. The README quick start, the Python paragraph, the exit-code table, the limits list, and the docs index drop the < /dev/null advice; so do EXAMPLES.md and PYTHON_API.md. The wrap-a-command transcripts keep the redirect they were recorded with, and the page says the commands work without it. CHANGELOG entry under Unreleased / Changed.
The line moves from after identity inspection into the two launch sites, so a command that exits at once (the early-exit window) is still told that it read /dev/null; the run banner stays where it was. A new real-binary case pins the line for /usr/bin/true under a pseudo-terminal. The bounded waits kill the supervisor before failing, the test directory cleanup no longer panics during unwinding, and the Python pseudo-terminal test gains a 20 s bound and moves above the unittest main block so a direct run collects it. Docs: the exit-64 row no longer says the report path was used (only a launch-time refusal does that); CLI.md says the supervisor prints the line; the CHANGELOG entry reads "through 0.2.0", attributes the 74 retry to the CLI, and drops crate-internal names; EXAMPLES.md notes the transcript was captured with input redirected; the wrap-a-command page says "alongside"; the 0.2.0 trial bundle README marks T0 as recorded before this change.
The fast-exit case's comment now says it is a pin of the line for a command that exits at once, red on the old placement only when the root is gone at the first inspect; the placement is pinned by the two call sites. The Python pseudo-terminal test reaps the child it kills on the timeout path instead of leaving a zombie.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Until now,
mlx-guardrefused to start when its standard input was a terminal: exit64,interactive terminal input is unsupported, nothing launched. That is what every README quick-start command and the Python example did when typed into Terminal, and the retry with the same--reportpath then exited74because the refused launch had already used it. The docs worked around it with< /dev/nullon every command.This change removes the refusal. When the supervisor's standard input is a terminal, the supervised command receives
/dev/nullinstead and the supervisor prints one line on stderr:Any other standard input (a file, a pipe, an explicit
< /dev/null) reaches the command unchanged and prints nothing. Shell job control stays unsupported, and the CLI and support pages now say why in plain terms: the command runs in its own process group, so a command that reads the keyboard would be stopped on its first read. There is no new flag for inheriting the terminal, since that is exactly the case that cannot work.In the core crate,
LaunchErrorKind::InteractiveTerminalUnsupportedandvalidate_noninteractive_terminalare gone;resolve_stdin(a pure function) andOwnedProcess::stdin_dispositionreplace them. The Python client is unchanged:start()still inherits the script's stdin, so a pipe fed to the script reaches the command, and a script started from a terminal gets the same substitution and line through the native binary.Tests: a unit test on
resolve_stdin(the existing pseudo-terminal test inowned_process_group.rs, rewritten), three real-binary tests incrates/mlx-guard-cli/tests/terminal_stdin.rs(runandobserveunder anopenptyslave, bounded by the wall time so a regression that passes the terminal on ends in75or a timed-out wait instead of a hang, plus a piped-stdin control that pins no substitution and no line), and a Pythonpty.forktest that mirrors the ticket's evidence. Each went red first: the pseudo-terminal tests against the refusal, the Python test against the published 0.2.0 wheel. Removing the substitution turned all of them red again before the revert. The in-house trial harness's T0 probe validator moved to the new contract.Docs:
docs/CLI.md,docs/PROCESS_CONTROL.md,docs/SUPPORT.md, the README (quick start, Python paragraph, exit-code table, limits, docs index),docs/EXAMPLES.md,docs/PYTHON_API.md, and the wrap-a-command page. The< /dev/nulladvice is gone from the quick starts; the wrap-a-command transcripts keep it, since that is how they were recorded. CHANGELOG under Unreleased / Changed.No evidence bundle is re-run: sampling, policy, identity, and intervention code are untouched. The recorded 0.2.0 trial bundle keeps its T0 row as a record of the old behaviour.