Skip to content
Merged
Changes from all commits
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
69 changes: 49 additions & 20 deletions crates/broker/src/pty_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,24 @@ fn evaluate_startup_gate(
}
}

/// The startup gate's verdict.
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
///
/// Three states, not two booleans: `Ready && Blocked` is not representable,
/// and the difference between "we could not recognise the prompt" and "the
/// harness is deliberately refusing work" decides whether the deadline may
/// release queued work at all.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum StartupGate {
/// A real input prompt was proven.
Ready,
/// No prompt recognised yet. Our heuristic may simply be blind, so the
/// deadline is allowed to release queued work.
Unrecognised,
/// A known blocking dialog is on screen — the harness is deliberately not
/// accepting input. Never released by any deadline.
Blocked,
}

/// Whether a KNOWN blocking dialog is on screen, as opposed to a prompt we
/// simply failed to recognise.
///
Expand Down Expand Up @@ -460,8 +478,7 @@ async fn try_emit_worker_ready(
init_request_id: &mut Option<RequestId>,
init_received_at: Option<Instant>,
readiness: &mut StartupReadinessState,
startup_ready: bool,
startup_blocked: bool,
gate: StartupGate,
) {
// init_received_at is Some only after init_worker has been received.
// We use it (not init_request_id) as the gate because the broker sends
Expand All @@ -470,10 +487,11 @@ async fn try_emit_worker_ready(
return;
}

let startup_ready = gate == StartupGate::Ready;
// A deliberate veto is not a blind spot: never time out past a known
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
// blocking dialog, or the brief is typed into a trust prompt and answers a
// security question nobody asked us to answer.
let timed_out = !startup_blocked
let timed_out = gate != StartupGate::Blocked
&& init_received_at.is_some_and(|started| started.elapsed() >= STARTUP_READY_TIMEOUT);

if !startup_ready && !timed_out {
Expand Down Expand Up @@ -842,16 +860,21 @@ pub(crate) async fn run_pty_worker(cmd: PtyCommand) -> Result<()> {
&post_boot_output,
&pty,
);
let startup_blocked = startup_gate_blocked(&pty);
let gate = if startup_ready {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
StartupGate::Ready
} else if startup_gate_blocked(&pty) {
StartupGate::Blocked
} else {
StartupGate::Unrecognised
};
try_emit_worker_ready(
&out_tx,
&worker_name,
pty.child_pid(),
&mut init_request_id,
init_received_at,
&mut startup_readiness,
startup_ready,
startup_blocked,
gate,
)
.await;
}
Expand Down Expand Up @@ -1236,16 +1259,21 @@ pub(crate) async fn run_pty_worker(cmd: PtyCommand) -> Result<()> {
&post_boot_output,
&pty,
);
let startup_blocked = startup_gate_blocked(&pty);
let gate = if startup_ready {
StartupGate::Ready
} else if startup_gate_blocked(&pty) {
StartupGate::Blocked
} else {
StartupGate::Unrecognised
};
try_emit_worker_ready(
&out_tx,
&worker_name,
pty.child_pid(),
&mut init_request_id,
init_received_at,
&mut startup_readiness,
startup_ready,
startup_blocked,
gate,
)
.await;

Expand Down Expand Up @@ -1808,16 +1836,21 @@ pub(crate) async fn run_pty_worker(cmd: PtyCommand) -> Result<()> {
&post_boot_output,
&pty,
);
let startup_blocked = startup_gate_blocked(&pty);
let gate = if startup_ready {
StartupGate::Ready
} else if startup_gate_blocked(&pty) {
StartupGate::Blocked
} else {
StartupGate::Unrecognised
};
try_emit_worker_ready(
&out_tx,
&worker_name,
pty.child_pid(),
&mut init_request_id,
init_received_at,
&mut startup_readiness,
startup_ready,
startup_blocked,
gate,
)
.await;

Expand Down Expand Up @@ -2231,8 +2264,7 @@ mod tests {
&mut request_id,
Some(started),
&mut readiness,
false, // prompt never recognised
false, // and no blocking dialog on screen
StartupGate::Unrecognised,
)
.await;

Expand Down Expand Up @@ -2265,8 +2297,7 @@ mod tests {
&mut request_id,
Some(started),
&mut readiness,
false, // prompt not ready
true, // ...because a known blocking dialog is on screen
StartupGate::Blocked,
)
.await;

Expand Down Expand Up @@ -2294,8 +2325,7 @@ mod tests {
&mut request_id,
Some(started),
&mut readiness,
false,
false,
StartupGate::Unrecognised,
)
.await;

Expand All @@ -2316,8 +2346,7 @@ mod tests {
&mut request_id,
Some(started),
&mut readiness,
true,
false,
StartupGate::Ready,
)
.await;

Expand Down
Loading