Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/commands/config/show.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1515,7 +1515,7 @@ fn fetch_latest_version() -> anyhow::Result<String> {
// --connect-timeout fails fast when offline, and a generous --max-time bounds a
// connected-but-stalled host without cutting off a slow-but-progressing fetch.
let output = {
let _watchdog = worktrunk::progress::Watchdog::start("the latest version", None);
let _watchdog = worktrunk::progress::Watchdog::start("the version check", None);
Cmd::new("curl")
.args([
"--silent",
Expand Down
2 changes: 1 addition & 1 deletion src/commands/worktree/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ fn fetch_ref_info(
repo: &Repository,
) -> anyhow::Result<RemoteRefInfo> {
let _watchdog = worktrunk::progress::Watchdog::start(
&format!("the {} info", provider.ref_type().name()),
&format!("the {} lookup", provider.ref_type().name()),
None,
);
provider.fetch_info(number, repo)
Expand Down
10 changes: 5 additions & 5 deletions src/llm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ pub(crate) fn render_llm_invocation(command: &str) -> anyhow::Result<String> {
/// ([`generate_summary_core`](crate::summary::generate_summary_core), up to 8
/// under a semaphore), where
/// per-call spinners would interleave.
fn watch_llm_command(command: &str, waiting_for: &str) -> worktrunk::progress::Watchdog {
fn watch_llm_command(command: &str) -> worktrunk::progress::Watchdog {
let invocation = render_llm_invocation(command).ok();
worktrunk::progress::Watchdog::start(waiting_for, invocation.as_deref())
worktrunk::progress::Watchdog::start("the commit generation command", invocation.as_deref())
}

/// Format a reproduction command, only wrapping with `sh -c` if needed.
Expand Down Expand Up @@ -667,7 +667,7 @@ pub(crate) fn generate_commit_message(
// A slow or hung command is otherwise silent (stdout is captured); the
// watchdog surfaces a "still waiting" status. Held until this function
// returns, clearing the block before the caller prints the message.
let _watchdog = watch_llm_command(command, "the commit message");
let _watchdog = watch_llm_command(command);
// Commit generation is explicitly configured - fail if it doesn't work
return try_generate_commit_message(
command,
Expand Down Expand Up @@ -841,7 +841,7 @@ pub(crate) fn generate_squash_message(

// See `generate_commit_message` — keep a slow squash-message generation
// from being silent.
let _watchdog = watch_llm_command(command, "the squash commit message");
let _watchdog = watch_llm_command(command);
return execute_llm_command(command, &prompt).map_err(|e| {
worktrunk::git::GitError::LlmCommandFailed {
command: command.clone(),
Expand Down Expand Up @@ -967,7 +967,7 @@ pub(crate) fn test_commit_generation(

// The connectivity test shells out the same way real generation does, so a
// slow command would be just as silent — surface the same waiting status.
let _watchdog = watch_llm_command(command, "the test commit message");
let _watchdog = watch_llm_command(command);
execute_llm_command(command, &prompt).map_err(|e| {
worktrunk::git::GitError::LlmCommandFailed {
command: command.clone(),
Expand Down
40 changes: 21 additions & 19 deletions src/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,13 @@ mod imp {
///
/// Unlike [`Progress`] (which counts work units), `Watchdog` just tracks
/// elapsed time. After a startup delay it renders a dim one-line status —
/// `↳ Waiting for the commit message (4s)` — redrawn in place each second;
/// the ticking counter is the "still alive" signal. After a longer delay it
/// escalates, revealing the exact command in a gutter beneath the status:
/// `↳ Waiting for the commit generation command (4s)` — redrawn in place
/// each second; the ticking counter is the "still alive" signal. After a
/// longer delay it escalates, revealing the exact command in a gutter
/// beneath the status:
///
/// ```text
/// ↳ Waiting for the commit message (12s)
/// ↳ Waiting for the commit generation command (12s)
/// sh -c 'claude -p --model=haiku'
/// ```
///
Expand All @@ -319,10 +320,11 @@ mod imp {
impl Watchdog {
/// Start a watchdog for a slow subprocess.
///
/// `waiting_for` names what is being awaited (e.g. `"the commit
/// message"`). `command`, if given, is the exact invocation — revealed
/// in a gutter beneath the status line once the command runs past the
/// escalation delay, so a slow or stuck command is debuggable.
/// `waiting_for` names the running operation (e.g. `"the commit
/// generation command"`). `command`, if given, is the exact
/// invocation — revealed in a gutter beneath the status line once the
/// command runs past the escalation delay, so a slow or stuck command
/// is debuggable.
///
/// Enabled only when stderr is a TTY and verbosity is 0 — under
/// `-v`/`-vv` the structured diagnostics take over, and a non-TTY (piped)
Expand Down Expand Up @@ -630,9 +632,9 @@ mod imp {
fn test_watchdog_block_status_only() {
// Before escalation (no command) the block is a single dim status
// row. A wide width leaves the status untruncated.
let block = watchdog_block("the commit message", 4, None, Some(200));
let block = watchdog_block("the commit generation command", 4, None, Some(200));
assert_eq!(block.len(), 1);
assert!(block[0].contains("Waiting for the commit message"));
assert!(block[0].contains("Waiting for the commit generation command"));
assert!(block[0].contains("(4s)"));
assert!(block[0].contains('↳'));
assert!(!block[0].contains('…'));
Expand All @@ -645,13 +647,13 @@ mod imp {
// command, since bash highlighting interleaves ANSI codes between
// tokens (a word itself is never split mid-token).
let block = watchdog_block(
"the commit message",
"the commit generation command",
12,
Some("claude --model=haiku"),
Some(200),
);
assert!(block.len() >= 2);
assert!(block[0].contains("Waiting for the commit message"));
assert!(block[0].contains("Waiting for the commit generation command"));
assert!(block.join("\n").contains("claude"));
}

Expand All @@ -662,7 +664,7 @@ mod imp {
// soft-wrapped second row desyncs the in-place cursor math.
use ansi_str::AnsiStr;
use unicode_width::UnicodeWidthStr;
let block = watchdog_block("the squash commit message", 1234, None, Some(20));
let block = watchdog_block("the commit generation command", 1234, None, Some(20));
assert_eq!(block.len(), 1);
let visible = block[0].ansi_strip();
assert!(UnicodeWidthStr::width(visible.as_ref()) <= 20);
Expand All @@ -672,15 +674,15 @@ mod imp {
#[test]
fn test_watchdog_start_with_non_tty_is_disabled() {
assert!(
Watchdog::start_with("the commit message", None, false)
Watchdog::start_with("the commit generation command", None, false)
.ticker
.is_none()
);
}

#[test]
fn test_watchdog_start_with_tty_is_enabled() {
let w = Watchdog::start_with("the commit message", None, true);
let w = Watchdog::start_with("the commit generation command", None, true);
assert!(w.ticker.is_some());
w.finish();
}
Expand All @@ -694,7 +696,7 @@ mod imp {
// far-off escalation delay keeps the gutter absent for the whole
// poll, so the `!escalated` assertion holds regardless of timing.
let w = Watchdog::enabled_with_delays(
"the commit message",
"the commit generation command",
Some("sh -c 'claude -p'"),
Duration::from_millis(10),
Duration::from_secs(3600),
Expand All @@ -720,7 +722,7 @@ mod imp {
// can't race the ticker into rendering under load; finish() wakes the
// parked thread immediately regardless.
let w = Watchdog::enabled_with_delays(
"the commit message",
"the commit generation command",
None,
Duration::from_secs(3600),
WATCHDOG_ESCALATE_DELAY,
Expand All @@ -739,7 +741,7 @@ mod imp {
// is what we're verifying. Generous timeout, fast poll — fast when
// healthy, reliable under load.
let w = Watchdog::enabled_with_delays(
"the commit message",
"the commit generation command",
Some("sh -c 'claude -p'"),
Duration::from_millis(10),
Duration::from_millis(30),
Expand Down Expand Up @@ -767,7 +769,7 @@ mod imp {
// load; escalation is gated on `command.is_some()`, so `!escalated`
// holds at any instant no matter how long the loop runs.
let w = Watchdog::enabled_with_delays(
"the commit message",
"the commit generation command",
None,
Duration::from_millis(10),
Duration::from_millis(30),
Expand Down
Loading