fix(clippy): fix all clippy warnings in hive-ag3nt, hive-forge, hive-matrix-mcp, hive-sh4re

Fixes all clippy -D warnings errors in the crates iris owns:

hive-sh4re:
- doc_lazy_continuation: add blank /// separator in priv_proto.rs
- doc_markdown: backtick PRIVATE_NETWORK=0 / PRIVATE_NETWORK=1

hive-matrix-mcp:
- map_unwrap_or: map().unwrap_or_else() -> map_or_else() in paths.rs
- collapsible_if: if-let chains in wake.rs
- doc_markdown: backtick M_UNKNOWN_TOKEN in main.rs
- cast_possible_truncation: usize/u64 -> u32::try_from in handlers.rs
- map_unwrap_or: map_or_else() in handlers.rs
- manual_let_else: match Ok(r) => r, Err => return -> let Ok in handlers.rs
- unused_async: remove async from list_invites; update socket.rs call site

hive-forge:
- doc_markdown: backtick REQUEST_CHANGES / APPROVED / COMMENT in pr_reviews.rs
- unnecessary_wraps: list_reviews_text returns () not Result<()>
- doc_markdown: backtick start_page / last_page in comments.rs
- cast_possible_truncation: PAGE_SIZE u64 -> usize; remove as usize casts

hive-ag3nt:
- collapsible_if: if-let chains in events.rs and mcp.rs
- single_match_else: match -> if let in events.rs and mcp.rs
- items_after_statements: hoist STATUS_MAX_CHARS const in mcp.rs
- map_unwrap_or: map_or_else() in mcp.rs and mcp_loose_ends.rs
- cast_possible_truncation: usize -> u32::try_from in mcp.rs
- doc_markdown: backtick snake_case in mcp.rs, needs_update/deployed_sha
  in web_ui.rs, HISTORY_CAPACITY in web_ui.rs
- identical_match_arms: combine manage_root_agent | query_agent_state
- redundant_closure: |s| s.to_string() -> ToString::to_string in web_ui.rs
- duration_suboptimal_units: from_secs(3600) -> from_hours(1) in turn.rs

Remaining failures in hive-c0re (39), hive-priv (8), hive-bash-mcp (11)
are owned by damocles.
This commit is contained in:
iris 2026-06-05 14:28:05 +02:00 committed by mara
commit 92b32d06fb
13 changed files with 128 additions and 133 deletions

View file

@ -80,18 +80,18 @@ fn harness_json_path() -> PathBuf {
fn read_harness_state() -> (bool, bool) {
// Try the new consolidated file first.
if let Ok(raw) = std::fs::read_to_string(harness_json_path()) {
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&raw) {
let rate_limited = v
.get("rate_limited")
.and_then(|x| x.as_bool())
.unwrap_or(false);
let needs_login = v
.get("needs_login")
.and_then(|x| x.as_bool())
.unwrap_or(false);
return (rate_limited, needs_login);
}
if let Ok(raw) = std::fs::read_to_string(harness_json_path())
&& let Ok(v) = serde_json::from_str::<serde_json::Value>(&raw)
{
let rate_limited = v
.get("rate_limited")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
let needs_login = v
.get("needs_login")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
return (rate_limited, needs_login);
}
// Fall back to legacy sentinel files written by older harness builds.
let state_dir = crate::paths::state_dir();
@ -265,38 +265,35 @@ impl EventStore {
let conn = self.conn.lock().unwrap();
// Fetch one extra row so we can tell whether more exist.
let fetch = limit_i.saturating_add(1);
let rows: Vec<(i64, LiveEvent)> = match before_id {
Some(bid) => {
let mut stmt = conn.prepare(
"SELECT id, payload_json FROM events
WHERE id < ?1
ORDER BY id DESC
LIMIT ?2",
)?;
stmt.query_map(params![bid, fetch], |row| {
let id: i64 = row.get(0)?;
let s: String = row.get(1)?;
Ok(serde_json::from_str::<LiveEvent>(&s).ok().map(|e| (id, e)))
})?
.flatten()
.flatten()
.collect()
}
None => {
let mut stmt = conn.prepare(
"SELECT id, payload_json FROM events
ORDER BY id DESC
LIMIT ?1",
)?;
stmt.query_map(params![fetch], |row| {
let id: i64 = row.get(0)?;
let s: String = row.get(1)?;
Ok(serde_json::from_str::<LiveEvent>(&s).ok().map(|e| (id, e)))
})?
.flatten()
.flatten()
.collect()
}
let rows: Vec<(i64, LiveEvent)> = if let Some(bid) = before_id {
let mut stmt = conn.prepare(
"SELECT id, payload_json FROM events
WHERE id < ?1
ORDER BY id DESC
LIMIT ?2",
)?;
stmt.query_map(params![bid, fetch], |row| {
let id: i64 = row.get(0)?;
let s: String = row.get(1)?;
Ok(serde_json::from_str::<LiveEvent>(&s).ok().map(|e| (id, e)))
})?
.flatten()
.flatten()
.collect()
} else {
let mut stmt = conn.prepare(
"SELECT id, payload_json FROM events
ORDER BY id DESC
LIMIT ?1",
)?;
stmt.query_map(params![fetch], |row| {
let id: i64 = row.get(0)?;
let s: String = row.get(1)?;
Ok(serde_json::from_str::<LiveEvent>(&s).ok().map(|e| (id, e)))
})?
.flatten()
.flatten()
.collect()
};
let has_more = rows.len() > limit;
let mut rows: Vec<(i64, LiveEvent)> = rows.into_iter().take(limit).collect();

View file

@ -111,6 +111,9 @@ impl From<hive_sh4re::Response> for SocketReply {
/// the file is never written with text the server would later reject (which
/// would leave a stale invalid entry on disk).
fn write_status_file(text: &str) -> Result<(), String> {
// 200 chars mirrors STATUS_MAX_CHARS in hive-c0re/src/limits.rs.
// Keep in sync if that constant changes.
const STATUS_MAX_CHARS: usize = 200;
let trimmed = text.trim();
if !trimmed.is_empty() {
if trimmed.contains('\n') || trimmed.contains('\r') {
@ -120,9 +123,6 @@ fn write_status_file(text: &str) -> Result<(), String> {
.to_owned(),
);
}
// 200 chars mirrors STATUS_MAX_CHARS in hive-c0re/src/limits.rs.
// Keep in sync if that constant changes.
const STATUS_MAX_CHARS: usize = 200;
let len = trimmed.chars().count();
if len > STATUS_MAX_CHARS {
return Err(format!(
@ -310,9 +310,10 @@ struct MatrixRoomUnread {
async fn matrix_unread_summary() -> Option<Vec<MatrixRoomUnread>> {
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::UnixStream;
let socket = std::env::var_os("HIVE_MATRIX_SOCKET")
.map(std::path::PathBuf::from)
.unwrap_or_else(|| std::path::PathBuf::from("/run/hive-matrix/socket"));
let socket = std::env::var_os("HIVE_MATRIX_SOCKET").map_or_else(
|| std::path::PathBuf::from("/run/hive-matrix/socket"),
std::path::PathBuf::from,
);
if !socket.exists() {
return None;
}
@ -339,11 +340,11 @@ fn format_matrix_summary(rooms: &[MatrixRoomUnread]) -> String {
}
let mut out = String::new();
for r in rooms {
if r.count == 1 {
if let (Some(body), Some(sender)) = (&r.last_body, &r.last_sender) {
let _ = writeln!(out, "- {}: {sender}: {body}", r.label);
continue;
}
if r.count == 1
&& let (Some(body), Some(sender)) = (&r.last_body, &r.last_sender)
{
let _ = writeln!(out, "- {}: {sender}: {body}", r.label);
continue;
}
let _ = writeln!(out, "- {}: {} unread", r.label, r.count);
}
@ -775,19 +776,19 @@ impl AgentServer {
};
// Prepend matrix unread entry for self-queries only (can't
// reach another agent's matrix daemon from here).
if is_self_query {
if let Some(unread_rooms) = matrix_unread_summary().await {
let total = unread_rooms.len() as u32;
if total > 0 {
let summary = format_matrix_summary(&unread_rooms);
loose_ends.insert(
0,
hive_sh4re::LooseEnd::UnreadMatrix {
rooms: total,
summary,
},
);
}
if is_self_query
&& let Some(unread_rooms) = matrix_unread_summary().await
{
let total = u32::try_from(unread_rooms.len()).unwrap_or(u32::MAX);
if total > 0 {
let summary = format_matrix_summary(&unread_rooms);
loose_ends.insert(
0,
hive_sh4re::LooseEnd::UnreadMatrix {
rooms: total,
summary,
},
);
}
}
let mut out = annotate_retries(render_loose_ends(&loose_ends), retries);
@ -1754,14 +1755,14 @@ pub const SERVER_NAME: &str = "hyperhive";
pub const ALLOWED_BUILTIN_TOOLS: &[&str] = &["Edit", "Glob", "Grep", "Read", "Write"];
/// Env var written by the meta renderer with a comma-separated list of
/// `hive_sh4re::ToolGroup` snake_case names (e.g. `"messaging,inbox,meta"`).
/// `hive_sh4re::ToolGroup` `snake_case` names (e.g. `"messaging,inbox,meta"`).
/// When present, the harness expands the groups into per-tool allow entries
/// instead of using the hardcoded flavor default. See `docs/conventions.md::Tool groups`.
const TOOL_GROUPS_ENV: &str = "HIVE_TOOL_GROUPS";
/// `HIVE_CAPABILITIES` env var injected by `meta::render_flake` when the
/// operator grants capabilities to this agent. Comma-separated
/// `hive_sh4re::Capability` snake_case names. Absent = no extra capabilities.
/// `hive_sh4re::Capability` `snake_case` names. Absent = no extra capabilities.
const CAPABILITIES_ENV: &str = "HIVE_CAPABILITIES";
/// Returns the MCP tool names (without `mcp__hyperhive__` prefix) that are
@ -1778,14 +1779,12 @@ fn allowed_capability_tools() -> Vec<String> {
let t = token.trim().to_ascii_lowercase();
match t.as_str() {
"read_host_journal" => tools.push("get_host_journal".to_owned()),
// manage_root_agent doesn't expose new MCP tools (it gates
// existing lifecycle tools via the topology enforcement).
"manage_root_agent" => {}
// query_agent_state doesn't expose new MCP tools; it unlocks
// the `agent` field in get_loose_ends / count_pending_reminders
// / reminder_rollup on the agent socket (c0re enforces the cap
// server-side; the harness honours it by passing the field).
"query_agent_state" => {}
// manage_root_agent / query_agent_state don't expose new MCP
// tools: manage_root_agent gates existing lifecycle tools via
// topology enforcement; query_agent_state unlocks the `agent`
// field in get_loose_ends / count_pending_reminders /
// reminder_rollup (c0re enforces the cap server-side).
"manage_root_agent" | "query_agent_state" => {}
unknown => {
tracing::warn!(capability = %unknown, "unrecognised capability in HIVE_CAPABILITIES — skipped");
}
@ -1810,13 +1809,12 @@ fn effective_tool_groups() -> Vec<hive_sh4re::ToolGroup> {
for token in raw.split(',') {
let t = token.trim().to_ascii_lowercase();
// Parse via serde_json (the canonical deserialization path).
match serde_json::from_value::<hive_sh4re::ToolGroup>(serde_json::Value::String(t.clone()))
if let Ok(g) =
serde_json::from_value::<hive_sh4re::ToolGroup>(serde_json::Value::String(t.clone()))
{
Ok(g) => groups.push(g),
Err(_) => tracing::warn!(
token = %t,
"{TOOL_GROUPS_ENV}: unknown tool group, skipping"
),
groups.push(g);
} else {
tracing::warn!(token = %t, "{TOOL_GROUPS_ENV}: unknown tool group, skipping");
}
}
if groups.is_empty() {

View file

@ -21,8 +21,7 @@ fn loose_ends_dir() -> PathBuf {
let state_path = PathBuf::from(&state);
state_path
.parent()
.map(|p| p.join("harness"))
.unwrap_or_else(|| PathBuf::from(state))
.map_or_else(|| PathBuf::from(state), |p| p.join("harness"))
};
base.join("mcp-loose-ends")
}

View file

@ -905,7 +905,7 @@ mod tests {
fs::write(dir.path().join("credentials.json"), b"{}").unwrap();
let snapshot = DirSnapshot {
file_count: 1,
newest_mtime: Some(SystemTime::now() + Duration::from_secs(3600)),
newest_mtime: Some(SystemTime::now() + Duration::from_hours(1)),
};
assert!(!session_refreshed(snapshot, snapshot_dir(dir.path())));
}

View file

@ -579,7 +579,7 @@ async fn api_state(State(state): State<AppState>) -> axum::Json<StateSnapshot> {
/// fetches this once per running agent to get fresh, agent-authoritative
/// values instead of relying on hive-c0re's periodic file-reads.
///
/// Structural fields (running, needs_update, deployed_sha, parent, …)
/// Structural fields (running, `needs_update`, `deployed_sha`, parent, …)
/// continue to come from hive-c0re's `/api/state`; this endpoint covers
/// only the fields the agent itself is the source of truth for.
#[derive(serde::Serialize)]
@ -808,7 +808,7 @@ struct HistoryParams {
/// Cursor: only return events with sqlite row id < `before`.
/// Omit for the initial (most-recent) page.
before: Option<i64>,
/// Page size (default 100, capped at HISTORY_CAPACITY).
/// Page size (default 100, capped at `HISTORY_CAPACITY`).
limit: Option<usize>,
}
@ -1094,7 +1094,7 @@ fn available_models() -> Vec<String> {
const DEFAULT: &[&str] = &["haiku", "sonnet", "opus"];
let raw = match std::env::var("HIVE_AVAILABLE_MODELS") {
Ok(v) if !v.trim().is_empty() => v,
_ => return DEFAULT.iter().map(|s| s.to_string()).collect(),
_ => return DEFAULT.iter().map(ToString::to_string).collect(),
};
let models: Vec<String> = raw
.split(',')
@ -1102,7 +1102,7 @@ fn available_models() -> Vec<String> {
.filter(|s| !s.is_empty())
.collect();
if models.is_empty() {
DEFAULT.iter().map(|s| s.to_string()).collect()
DEFAULT.iter().map(ToString::to_string).collect()
} else {
models
}