chore: satisfy workspace clippy gate

This commit is contained in:
albertoelopez 2026-06-10 21:15:59 -07:00
parent 3588bbcf94
commit ecad06f6d6
4 changed files with 47 additions and 71 deletions

View File

@ -731,7 +731,7 @@ mod tests {
} }
#[test] #[test]
#[allow(clippy::duration_suboptimal_units, clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn executable_decision_table_emits_retry_rebase_merge_escalate_cleanup_and_approval_events() { fn executable_decision_table_emits_retry_rebase_merge_escalate_cleanup_and_approval_events() {
let engine = PolicyEngine::new(vec![ let engine = PolicyEngine::new(vec![
PolicyRule::new( PolicyRule::new(

View File

@ -29,7 +29,7 @@ impl Default for TridentConfig {
} }
/// Statistics from a Trident compaction run. /// Statistics from a Trident compaction run.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TridentStats { pub struct TridentStats {
pub superseded_count: usize, pub superseded_count: usize,
pub collapsed_chains: usize, pub collapsed_chains: usize,
@ -41,21 +41,6 @@ pub struct TridentStats {
pub final_message_count: usize, pub final_message_count: usize,
} }
impl Default for TridentStats {
fn default() -> Self {
Self {
superseded_count: 0,
collapsed_chains: 0,
messages_collapsed: 0,
clusters_found: 0,
messages_clustered: 0,
tokens_saved_estimate: 0,
original_message_count: 0,
final_message_count: 0,
}
}
}
impl TridentStats { impl TridentStats {
pub fn format_report(&self) -> String { pub fn format_report(&self) -> String {
let compression = if self.final_message_count > 0 { let compression = if self.final_message_count > 0 {
@ -192,7 +177,7 @@ fn stage1_supersede(messages: &[ConversationMessage]) -> (Vec<ConversationMessag
let mut obsolete_indices: BTreeSet<usize> = BTreeSet::new(); let mut obsolete_indices: BTreeSet<usize> = BTreeSet::new();
for (_path, ops) in &file_ops { for ops in file_ops.values() {
if ops.len() < 2 { if ops.len() < 2 {
continue; continue;
} }
@ -205,11 +190,7 @@ fn stage1_supersede(messages: &[ConversationMessage]) -> (Vec<ConversationMessag
if let Some(last_write) = last_write_idx { if let Some(last_write) = last_write_idx {
for op in ops { for op in ops {
if op.op_type == FileOp::Read && op.index < last_write { if op.index < last_write {
obsolete_indices.insert(op.index);
} else if (op.op_type == FileOp::Write || op.op_type == FileOp::Edit)
&& op.index < last_write
{
obsolete_indices.insert(op.index); obsolete_indices.insert(op.index);
} }
} }
@ -325,7 +306,7 @@ fn stage2_collapse(
usage: None, usage: None,
}); });
} else { } else {
result.extend(buffer.drain(..)); result.append(&mut buffer);
} }
buffer.clear(); buffer.clear();
result.push(msg.clone()); result.push(msg.clone());
@ -476,7 +457,7 @@ fn stage3_cluster(
} }
let total_clustered: usize = cluster_assignments.len(); let total_clustered: usize = cluster_assignments.len();
let clusters_found = cluster_id as usize; let clusters_found = cluster_id;
let mut result: Vec<ConversationMessage> = Vec::new(); let mut result: Vec<ConversationMessage> = Vec::new();
let mut cluster_buffers: BTreeMap<usize, Vec<usize>> = BTreeMap::new(); let mut cluster_buffers: BTreeMap<usize, Vec<usize>> = BTreeMap::new();
@ -677,7 +658,7 @@ fn truncate_text(text: &str, max_chars: usize) -> String {
mod tests { mod tests {
use super::*; use super::*;
use crate::compact::CompactionConfig; use crate::compact::CompactionConfig;
use crate::session::{ContentBlock, ConversationMessage, MessageRole, Session}; use crate::session::{ContentBlock, ConversationMessage, Session};
#[test] #[test]
fn stage1_removes_obsolete_file_reads() { fn stage1_removes_obsolete_file_reads() {
@ -736,7 +717,7 @@ mod tests {
fn stage2_collapses_chatty_messages() { fn stage2_collapses_chatty_messages() {
let mut messages = vec![]; let mut messages = vec![];
for i in 0..6 { for i in 0..6 {
messages.push(ConversationMessage::user_text(&format!("ok {i}"))); messages.push(ConversationMessage::user_text(format!("ok {i}")));
messages.push(ConversationMessage::assistant(vec![ContentBlock::Text { messages.push(ConversationMessage::assistant(vec![ContentBlock::Text {
text: format!("got {i}"), text: format!("got {i}"),
}])); }]));
@ -767,9 +748,9 @@ mod tests {
}, },
])); ]));
messages.push(ConversationMessage::tool_result( messages.push(ConversationMessage::tool_result(
&format!("read_{i}"), format!("read_{i}"),
"read_file", "read_file",
&format!(r#"{{"path":"src/{i}.rs","content":"data {i}"}}"#), format!(r#"{{"path":"src/{i}.rs","content":"data {i}"}}"#),
false, false,
)); ));
} }

View File

@ -3452,11 +3452,7 @@ impl DiagnosticCheck {
fn json_value(&self) -> Value { fn json_value(&self) -> Value {
// Derive a stable snake_case id from the check name for machine-readable keying (#704). // Derive a stable snake_case id from the check name for machine-readable keying (#704).
let id = self let id = self.name.to_ascii_lowercase().replace([' ', '-'], "_");
.name
.to_ascii_lowercase()
.replace(' ', "_")
.replace('-', "_");
let mut value = Map::from_iter([ let mut value = Map::from_iter([
("id".to_string(), Value::String(id.clone())), ("id".to_string(), Value::String(id.clone())),
( (
@ -6730,16 +6726,15 @@ fn run_resume_command(
} }
SlashCommand::Plugins { action, target } => { SlashCommand::Plugins { action, target } => {
// Only list is supported in resume mode (no runtime to reload) // Only list is supported in resume mode (no runtime to reload)
match action.as_deref() { if let Some(action @ ("install" | "uninstall" | "enable" | "disable" | "update")) =
Some(action @ ("install" | "uninstall" | "enable" | "disable" | "update")) => { action.as_deref()
// #777: use interactive_only: prefix + \n hint so #776's classify/split {
// emits error_kind:interactive_only + non-null hint instead of unknown+null. // #777: use interactive_only: prefix + \n hint so #776's classify/split
// Orchestrators can now detect this and switch to a live REPL instead of retrying. // emits error_kind:interactive_only + non-null hint instead of unknown+null.
return Err(format!( // Orchestrators can now detect this and switch to a live REPL instead of retrying.
"interactive_only: /plugins {action} requires a live session to reload the plugin runtime.\nStart `claw` and run `/plugins {action}` inside the REPL, or use `claw plugins {action}` as a direct CLI command." return Err(format!(
).into()); "interactive_only: /plugins {action} requires a live session to reload the plugin runtime.\nStart `claw` and run `/plugins {action}` inside the REPL, or use `claw plugins {action}` as a direct CLI command."
} ).into());
_ => {}
} }
let cwd = env::current_dir()?; let cwd = env::current_dir()?;
let payload = plugins_command_payload_for( let payload = plugins_command_payload_for(
@ -7852,8 +7847,12 @@ impl LiveCli {
let max_compact_rounds = 4; let max_compact_rounds = 4;
let preserve_schedule = [4, 2, 1, 0]; let preserve_schedule = [4, 2, 1, 0];
for round in 0..max_compact_rounds { for (round, preserve) in preserve_schedule
let preserve = preserve_schedule[round]; .iter()
.copied()
.enumerate()
.take(max_compact_rounds)
{
println!( println!(
" Auto-compacting session (round {}/{}, preserving {} recent messages)...", " Auto-compacting session (round {}/{}, preserving {} recent messages)...",
round + 1, round + 1,
@ -8611,8 +8610,8 @@ impl LiveCli {
let cwd = env::current_dir()?; let cwd = env::current_dir()?;
// #803: reject flag-shaped tokens in list filter for BOTH text and JSON modes. // #803: reject flag-shaped tokens in list filter for BOTH text and JSON modes.
// Previously the guard was JSON-only (#793); text mode silently returned empty success. // Previously the guard was JSON-only (#793); text mode silently returned empty success.
if action.as_deref() == Some("list") { if action == Some("list") {
if let Some(filter) = target.as_deref() { if let Some(filter) = target {
if filter.starts_with('-') { if filter.starts_with('-') {
if matches!(output_format, CliOutputFormat::Json) { if matches!(output_format, CliOutputFormat::Json) {
// ROADMAP #817: this is a handled local inventory parse error. // ROADMAP #817: this is a handled local inventory parse error.
@ -10073,9 +10072,7 @@ fn sandbox_json_value(status: &runtime::SandboxStatus) -> serde_json::Value {
// (#731: "not supported on macOS" is a degraded state, not a hard error; // (#731: "not supported on macOS" is a degraded state, not a hard error;
// filesystem_active:true means partial containment is working) // filesystem_active:true means partial containment is working)
// error = enabled but unsupported AND no filesystem sandbox either (nothing active) // error = enabled but unsupported AND no filesystem sandbox either (nothing active)
let top_status = if !status.enabled { let top_status = if !status.enabled || status.active {
"ok"
} else if status.active {
"ok" "ok"
} else if status.supported { } else if status.supported {
"warn" "warn"
@ -14008,39 +14005,37 @@ fn convert_messages(messages: &[ConversationMessage]) -> Vec<InputMessage> {
let content = message let content = message
.blocks .blocks
.iter() .iter()
.filter_map(|block| match block { .map(|block| match block {
ContentBlock::Text { text } => { ContentBlock::Text { text } => InputContentBlock::Text { text: text.clone() },
Some(InputContentBlock::Text { text: text.clone() })
}
ContentBlock::Thinking { ContentBlock::Thinking {
thinking, thinking,
signature, signature,
} => { } => {
// 保留 Thinking 块:OpenAI 兼容协议会把它转成 reasoning_content 字段 // 保留 Thinking 块:OpenAI 兼容协议会把它转成 reasoning_content 字段
// 回传给 DeepSeek V4(避免 400 "reasoning_content must be passed back" 错误) // 回传给 DeepSeek V4(避免 400 "reasoning_content must be passed back" 错误)
Some(InputContentBlock::Thinking { InputContentBlock::Thinking {
thinking: thinking.clone(), thinking: thinking.clone(),
signature: signature.clone(), signature: signature.clone(),
}) }
} }
ContentBlock::ToolUse { id, name, input } => Some(InputContentBlock::ToolUse { ContentBlock::ToolUse { id, name, input } => InputContentBlock::ToolUse {
id: id.clone(), id: id.clone(),
name: name.clone(), name: name.clone(),
input: serde_json::from_str(input) input: serde_json::from_str(input)
.unwrap_or_else(|_| serde_json::json!({ "raw": input })), .unwrap_or_else(|_| serde_json::json!({ "raw": input })),
}), },
ContentBlock::ToolResult { ContentBlock::ToolResult {
tool_use_id, tool_use_id,
output, output,
is_error, is_error,
.. ..
} => Some(InputContentBlock::ToolResult { } => InputContentBlock::ToolResult {
tool_use_id: tool_use_id.clone(), tool_use_id: tool_use_id.clone(),
content: vec![ToolResultContentBlock::Text { content: vec![ToolResultContentBlock::Text {
text: output.clone(), text: output.clone(),
}], }],
is_error: *is_error, is_error: *is_error,
}), },
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
(!content.is_empty()).then(|| InputMessage { (!content.is_empty()).then(|| InputMessage {

View File

@ -1007,13 +1007,13 @@ fn inventory_commands_emit_structured_json_when_requested() {
assert!( assert!(
!plugins !plugins
.as_object() .as_object()
.map_or(false, |o| o.contains_key("reload_runtime")), .is_some_and(|o| o.contains_key("reload_runtime")),
"plugins list should not include reload_runtime" "plugins list should not include reload_runtime"
); );
assert!( assert!(
!plugins !plugins
.as_object() .as_object()
.map_or(false, |o| o.contains_key("target")), .is_some_and(|o| o.contains_key("target")),
"plugins list should not include target" "plugins list should not include target"
); );
// #703: structured summary replaces prose message // #703: structured summary replaces prose message
@ -1706,13 +1706,13 @@ fn resumed_inventory_commands_emit_structured_json_when_requested() {
assert!( assert!(
!plugins !plugins
.as_object() .as_object()
.map_or(false, |o| o.contains_key("reload_runtime")), .is_some_and(|o| o.contains_key("reload_runtime")),
"plugins list should not include reload_runtime" "plugins list should not include reload_runtime"
); );
assert!( assert!(
!plugins !plugins
.as_object() .as_object()
.map_or(false, |o| o.contains_key("target")), .is_some_and(|o| o.contains_key("target")),
"plugins list should not include target" "plugins list should not include target"
); );
assert!( assert!(
@ -2983,9 +2983,9 @@ fn flag_value_errors_have_error_kind_and_hint_756() {
"invalid --reasoning-effort must be invalid_flag_value (#756): {parsed}" "invalid --reasoning-effort must be invalid_flag_value (#756): {parsed}"
); );
assert!( assert!(
parsed["hint"].as_str().map_or(false, |h| h.contains("low") parsed["hint"]
|| h.contains("medium") .as_str()
|| h.contains("high")), .is_some_and(|h| h.contains("low") || h.contains("medium") || h.contains("high")),
"hint must mention valid values (#756): {parsed}" "hint must mention valid values (#756): {parsed}"
); );
@ -3011,7 +3011,7 @@ fn flag_value_errors_have_error_kind_and_hint_756() {
"missing --model value must be missing_flag_value (#756): {parsed2}" "missing --model value must be missing_flag_value (#756): {parsed2}"
); );
assert!( assert!(
parsed2["hint"].as_str().map_or(false, |h| !h.is_empty()), parsed2["hint"].as_str().is_some_and(|h| !h.is_empty()),
"missing --model hint must be non-empty (#756): {parsed2}" "missing --model hint must be non-empty (#756): {parsed2}"
); );
} }
@ -3255,7 +3255,7 @@ fn short_p_flag_swallows_no_flags_755() {
"flag-like token after -p must be rejected as missing_prompt (#755): {parsed2}" "flag-like token after -p must be rejected as missing_prompt (#755): {parsed2}"
); );
assert!( assert!(
parsed2["hint"].as_str().map_or(false, |h| !h.is_empty()), parsed2["hint"].as_str().is_some_and(|h| !h.is_empty()),
"missing_prompt hint must be non-empty (#755)" "missing_prompt hint must be non-empty (#755)"
); );
} }
@ -3397,7 +3397,7 @@ fn config_unsupported_section_json_hint_741() {
assert!( assert!(
parsed["supported_sections"] parsed["supported_sections"]
.as_array() .as_array()
.map_or(false, |a| !a.is_empty()), .is_some_and(|a| !a.is_empty()),
"config {section} JSON must include supported_sections (#741)" "config {section} JSON must include supported_sections (#741)"
); );
} }