mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 16:02:24 +00:00
fix(cli): the advertised wait idiom fails in one of its own end states
`tty7 wait`'s headline example is
tty7 wait %3 && tty7 capture %3 --plain
and `exit` is one of the three states `--until` waits for by default. Run
it that way and the pane is gone by the time capture runs: wait reports
`exit` and succeeds, `&&` proceeds, and capture answers that nothing is
running. An agent following the documented pattern gets no output for a
command that produced some.
The example is right for an agent — `waiting` and `done` leave the pane
alive. It is wrong for a plain command, which finishes by the shell
exiting. Spell both out in `wait`'s long help, and say plainly that
`exit` is the end state with nothing left to read.
Verified against a live server: `wait --until free` blocks for the
command (5s for a 4s sleep), and the capture after it finds the marker.
Also: a server running one pane had `doctor` say "1 panes" on its first
line. It is the first thing a new user runs. Every other count in the
tree is already plural-aware — the GUI routes these through `t_plural`
with "one"/"other" branches, and the CLI's other count (`closed N panes`)
handles the single case separately — so this was the last one.
This commit is contained in:
@@ -95,7 +95,18 @@ pub enum Command {
|
|||||||
|
|
||||||
#[command(
|
#[command(
|
||||||
about = "Block until a pane's agent needs input, finishes its turn, or the pane \
|
about = "Block until a pane's agent needs input, finishes its turn, or the pane \
|
||||||
exits — the orchestration primitive: `tty7 wait %3 && tty7 capture %3 --plain`"
|
exits — the orchestration primitive: `tty7 wait %3 && tty7 capture %3 \
|
||||||
|
--plain`",
|
||||||
|
long_about = "Block until a pane's agent needs input, finishes its turn, or the \
|
||||||
|
pane exits — the orchestration primitive:\n\n \
|
||||||
|
tty7 wait %3 && tty7 capture %3 --plain\n\n\
|
||||||
|
That pairs with an agent, whose pane is still alive in `waiting` and \
|
||||||
|
`done`. For a plain command, wait for `free` instead:\n\n \
|
||||||
|
tty7 wait %3 --until free && tty7 capture %3 --plain\n\n\
|
||||||
|
`exit` is the one end state with nothing left to read: the pane's \
|
||||||
|
output goes with the pane, and the workspace keeps only a leaf the \
|
||||||
|
GUI can revive into a fresh shell. Capture before the shell exits, \
|
||||||
|
or run the command under `tty7 run`, which streams it."
|
||||||
)]
|
)]
|
||||||
Wait(WaitArgs),
|
Wait(WaitArgs),
|
||||||
|
|
||||||
|
|||||||
@@ -1517,8 +1517,11 @@ fn doctor(ctx: &Context, backend: &mut dyn Backend) -> Result<Outcome> {
|
|||||||
rows.push(vec![
|
rows.push(vec![
|
||||||
"status".to_string(),
|
"status".to_string(),
|
||||||
format!(
|
format!(
|
||||||
"pid {}, up {}s, {} panes",
|
"pid {}, up {}s, {} pane{}",
|
||||||
status.pid, status.uptime_secs, status.panes
|
status.pid,
|
||||||
|
status.uptime_secs,
|
||||||
|
status.panes,
|
||||||
|
if status.panes == 1 { "" } else { "s" }
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
let routes = match backend.control(ControlRequest::Routes)? {
|
let routes = match backend.control(ControlRequest::Routes)? {
|
||||||
@@ -3853,6 +3856,45 @@ mod tests {
|
|||||||
assert!(out.contains("set (/cfg/tty7)"), "{out}");
|
assert!(out.contains("set (/cfg/tty7)"), "{out}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A server running one pane said "1 panes".
|
||||||
|
///
|
||||||
|
/// The count comes straight off the status reply, so every fresh server
|
||||||
|
/// says it — `doctor` is the first thing a new user runs, and the first
|
||||||
|
/// line it shows them was ungrammatical.
|
||||||
|
#[test]
|
||||||
|
fn doctor_counts_one_pane_in_the_singular() {
|
||||||
|
use tty7_core::daemon::control::ServerStatus;
|
||||||
|
|
||||||
|
let status = |panes: u64| ServerStatus {
|
||||||
|
pid: 4242,
|
||||||
|
uptime_secs: 61,
|
||||||
|
panes,
|
||||||
|
control_version: CONTROL_VERSION,
|
||||||
|
protocol_version: PROTOCOL_VERSION,
|
||||||
|
build: "26.7.5".into(),
|
||||||
|
socket: "127.0.0.1:5555".into(),
|
||||||
|
};
|
||||||
|
let line = |panes: u64| {
|
||||||
|
let mut backend = mock();
|
||||||
|
backend.replies.push_back(ReplyOk::Status(status(panes)));
|
||||||
|
backend.replies.push_back(ReplyOk::Routes(Vec::new()));
|
||||||
|
human(run_cli(
|
||||||
|
&["tty7", "doctor"],
|
||||||
|
&Context::default(),
|
||||||
|
&mut backend,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
line(1).contains("1 pane\n") || line(1).contains("1 pane "),
|
||||||
|
"{}",
|
||||||
|
line(1)
|
||||||
|
);
|
||||||
|
assert!(!line(1).contains("1 panes"), "{}", line(1));
|
||||||
|
assert!(line(0).contains("0 panes"), "{}", line(0));
|
||||||
|
assert!(line(2).contains("2 panes"), "{}", line(2));
|
||||||
|
}
|
||||||
|
|
||||||
/// Missing hooks are the reason a perfectly healthy-looking agent never
|
/// Missing hooks are the reason a perfectly healthy-looking agent never
|
||||||
/// reports and `tty7 wait` sits there until it times out. `doctor` is the
|
/// reports and `tty7 wait` sits there until it times out. `doctor` is the
|
||||||
/// verb people run when something is not working, so it is where that has
|
/// verb people run when something is not working, so it is where that has
|
||||||
|
|||||||
Reference in New Issue
Block a user