Skip to content
Merged
Show file tree
Hide file tree
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
7 changes: 5 additions & 2 deletions crates/mu-ai/src/providers/anthropic_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -997,8 +997,11 @@ fn map_stop_reason_known_and_unknown() {
assert_eq!(map_stop_reason(Some(&A::EndTurn)), StopReason::EndTurn);
assert_eq!(map_stop_reason(Some(&A::ToolUse)), StopReason::ToolUse);
assert_eq!(map_stop_reason(Some(&A::MaxTokens)), StopReason::MaxTokens);
// unmapped wire reasons (stop_sequence / refusal / pause_turn / unknown)
// all fold to EndTurn.
// Gen-5 terminal states map to their own core reasons
// (mu-provider-drift-2026q3-y43la).
assert_eq!(map_stop_reason(Some(&A::Refusal)), StopReason::Refusal);
assert_eq!(map_stop_reason(Some(&A::PauseTurn)), StopReason::PauseTurn);
// stop_sequence and unknown/absent reasons fold to EndTurn.
assert_eq!(map_stop_reason(Some(&A::StopSequence)), StopReason::EndTurn);
assert_eq!(map_stop_reason(Some(&A::Other)), StopReason::EndTurn);
assert_eq!(map_stop_reason(None), StopReason::EndTurn);
Expand Down
19 changes: 19 additions & 0 deletions crates/mu-coding/src/ask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,25 @@ pub async fn run(opts: AskOptions) -> Result<()> {
closed without a terminal stop event (connection drop or upstream \
truncation). Output above may be a fragment."
),
// mu-provider-drift-2026q3-y43la: the gen-5 terminal states must not
// exit 0 — a refused ask has no answer, and a server-paused ask ended
// early. Same rationale as max_tokens: the nonzero exit keeps
// headless scoring pipelines and spawn callers from reading a
// non-answer as a clean success. Edge (panel-raised): if a refusal
// ever cut a turn that ALSO completed a final_answer call, the
// answer still prints above but the exit is nonzero — deliberate:
// an answer the safety classifier cut mid-delivery should force
// caller scrutiny, not score as clean.
Some("refusal") => bail!(
"ask refused (stop_reason=refusal): the provider's safety \
classifier declined the request or cut generation. There is no \
answer; consider a different seat/model."
),
Some("pause_turn") => bail!(
"response paused (stop_reason=pause_turn): the server paused a \
long-running turn and mu does not implement continuation. \
Output above may be partial."
),
_ => Ok(()),
}
}
Expand Down
15 changes: 15 additions & 0 deletions crates/mu-coding/src/resume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@ pub async fn run(opts: ResumeOptions) -> Result<()> {
Some("degraded_eof") => {
bail!("response degraded (stop_reason=degraded_eof). Output above may be a fragment.")
}
// Same guard as ask.rs (mu-provider-drift-2026q3-y43la): a refused or
// server-paused resumed ask must not exit 0 as if it answered.
Some("refusal") => {
bail!(
"ask refused (stop_reason=refusal): the provider's safety classifier \
declined the request or cut generation. There is no answer."
)
}
Some("pause_turn") => {
bail!(
"response paused (stop_reason=pause_turn): the server paused a \
long-running turn and mu does not implement continuation. Output \
above may be partial."
)
}
_ => Ok(()),
}
}
Expand Down