chore: satisfy workspace clippy gate
This commit is contained in:
parent
3588bbcf94
commit
ecad06f6d6
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue