mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-11-06 00:15:06 +00:00
fix(board): a run just started is Running, not Review
Between Start and the agent showing up in its tab — the shell starting, the command being typed — the run's tab has no agent in it, and a run with no agent read as finished: the card jumped to Review and back. A run whose tab is open and which started under two minutes ago now reads as starting (task::STARTING, an agent yet to take its first turn), which is Running. Past the grace, a run that never started is over after all. The same for `tty7 task ls`.
This commit is contained in:
@@ -1208,12 +1208,17 @@ fn run_live(
|
||||
AgentStatus::Done => 1,
|
||||
AgentStatus::Idle => 0,
|
||||
};
|
||||
tab.root
|
||||
let live = tab
|
||||
.root
|
||||
.pane_ids()
|
||||
.iter()
|
||||
.filter_map(|p| states.get(p))
|
||||
.max_by_key(|s| urgency(s.status))
|
||||
.map(tty7_core::core::task::Live::from)
|
||||
.map(tty7_core::core::task::Live::from);
|
||||
// Its tab is open and nothing has reported yet: still starting.
|
||||
let fresh = tty7_core::core::machine::unix_now().saturating_sub(run.started)
|
||||
< tty7_core::core::task::START_GRACE_SECS;
|
||||
live.or(fresh.then_some(tty7_core::core::task::STARTING))
|
||||
}
|
||||
|
||||
fn column_name(column: tty7_core::core::task::Column) -> &'static str {
|
||||
@@ -2652,6 +2657,29 @@ mod tests {
|
||||
assert_eq!(columns, vec!["queued", "needs-input"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_run_whose_agent_has_not_shown_up_yet_is_running_not_over() {
|
||||
use tty7_core::core::task::{Run, Task};
|
||||
let mut backend = mock();
|
||||
let ws = &mut backend.machine.workspaces[0];
|
||||
let tab = ws.tabs[0].id;
|
||||
let mut fresh = Task::new("just started");
|
||||
fresh.push_run(Run {
|
||||
agent: CLIAgent::Claude,
|
||||
tab: Some(tab),
|
||||
session_id: None,
|
||||
started: tty7_core::core::machine::unix_now(),
|
||||
worktree: None,
|
||||
});
|
||||
ws.tasks = vec![fresh];
|
||||
let out = json_of(run_cli(
|
||||
&["tty7", "task", "ls", "api"],
|
||||
&Context::default(),
|
||||
&mut backend,
|
||||
));
|
||||
assert_eq!(out["tasks"][0]["column"], "running");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_done_finds_the_task_by_the_start_of_its_id() {
|
||||
use tty7_core::core::task::Task;
|
||||
|
||||
@@ -36,6 +36,19 @@ pub const MAX_PROMPT_BYTES: usize = 16 * 1024;
|
||||
/// resumed and nobody resumes the tenth attempt at the same thing.
|
||||
pub const MAX_RUNS: usize = 8;
|
||||
|
||||
/// How long a run's tab may take to show its agent before the run counts as
|
||||
/// never having started: a shell's startup, then the agent's own. Until then
|
||||
/// a run with its tab open and no agent in it is starting, not over — see
|
||||
/// [`STARTING`].
|
||||
pub const START_GRACE_SECS: u64 = 120;
|
||||
|
||||
/// What a run that is still starting reads as: an agent that has not taken
|
||||
/// its first turn, which [`column`] puts in Running.
|
||||
pub const STARTING: Live = Live {
|
||||
status: AgentStatus::Idle,
|
||||
turns: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct TaskId(uuid::Uuid);
|
||||
|
||||
+20
-2
@@ -366,7 +366,25 @@ impl Tty7App {
|
||||
let local = self.can_spawn_locally(cx);
|
||||
let mut cards = Vec::new();
|
||||
for task in &self.board.tasks {
|
||||
let column = task::column(task, |run| find(run.tab).map(|f| f.live));
|
||||
// A run whose tab is open but whose agent has not shown up yet —
|
||||
// the shell is still starting, the command still being typed —
|
||||
// is starting, not over. Read as an agent that has not taken its
|
||||
// first turn, which `column` counts as Running. Past the grace a
|
||||
// run that never started is over after all.
|
||||
let now = unix_now();
|
||||
let starting = |run: &Run| {
|
||||
run.tab.is_some_and(|id| {
|
||||
find(Some(id)).is_none()
|
||||
&& self.tabs.iter().any(|t| t.tree_id.get() == id)
|
||||
&& now.saturating_sub(run.started) < task::START_GRACE_SECS
|
||||
})
|
||||
};
|
||||
let column = task::column(task, |run| {
|
||||
find(run.tab)
|
||||
.map(|f| f.live)
|
||||
.or_else(|| starting(run).then_some(task::STARTING))
|
||||
});
|
||||
let starting_tab = task.runs.last().filter(|r| starting(r)).and_then(|r| r.tab);
|
||||
// The run this card reports: the newest one still open, else the
|
||||
// newest one at all.
|
||||
let open = task.runs.iter().rev().find_map(|r| find(r.tab));
|
||||
@@ -397,7 +415,7 @@ impl Tty7App {
|
||||
Column::Queued => task.paused.or(Some(task.created)),
|
||||
_ => last.map(|r| r.started),
|
||||
},
|
||||
tab: open.map(|f| f.id),
|
||||
tab: open.map(|f| f.id).or(starting_tab),
|
||||
resumable: open.is_none() && last.is_some_and(|r| r.session_id.is_some()),
|
||||
paused: task.paused.is_some(),
|
||||
worktree_gone: local
|
||||
|
||||
Reference in New Issue
Block a user