clippy: apply auto-fixable warnings across workspace (closes #265 partial)

This commit is contained in:
damocles 2026-05-22 17:50:11 +02:00 committed by Mara
parent 56d0b02c2f
commit 30d82148e0
18 changed files with 83 additions and 102 deletions

1
Cargo.lock generated
View file

@ -589,6 +589,7 @@ dependencies = [
"hive-fr0nt", "hive-fr0nt",
"hive-sh4re", "hive-sh4re",
"libc", "libc",
"reqwest",
"rusqlite", "rusqlite",
"serde", "serde",
"serde_json", "serde_json",

View file

@ -312,24 +312,19 @@ pub fn context_window_tokens(model: &str) -> u64 {
let m = model.to_ascii_lowercase(); let m = model.to_ascii_lowercase();
// Per-model env vars set by `hyperhive.contextWindowTokens` in Nix. // Per-model env vars set by `hyperhive.contextWindowTokens` in Nix.
for (key, val) in std::env::vars() { for (key, val) in std::env::vars() {
if let Some(suffix) = key.strip_prefix("HIVE_CONTEXT_WINDOW_TOKENS_") { if let Some(suffix) = key.strip_prefix("HIVE_CONTEXT_WINDOW_TOKENS_")
if !suffix.is_empty() && m.contains(&suffix.to_ascii_lowercase()) { && !suffix.is_empty() && m.contains(&suffix.to_ascii_lowercase())
if let Ok(v) = val.trim().parse::<u64>() { && let Ok(v) = val.trim().parse::<u64>()
if v > 0 { && v > 0 {
return v; return v;
} }
} }
}
}
}
// Global override (single value, any model). // Global override (single value, any model).
if let Ok(s) = std::env::var("HIVE_CONTEXT_WINDOW_TOKENS") { if let Ok(s) = std::env::var("HIVE_CONTEXT_WINDOW_TOKENS")
if let Ok(v) = s.trim().parse::<u64>() { && let Ok(v) = s.trim().parse::<u64>()
if v > 0 { && v > 0 {
return v; return v;
} }
}
}
// Hard fallback for dev/test outside NixOS where env vars aren't set. // Hard fallback for dev/test outside NixOS where env vars aren't set.
200_000 200_000
} }

View file

@ -93,7 +93,7 @@ pub async fn run(socket: PathBuf, is_manager: bool) {
let url = format!("{forge_url}/api/v1/user"); let url = format!("{forge_url}/api/v1/user");
fetch_json(&client, &url, &token) fetch_json(&client, &url, &token)
.await .await
.and_then(|v| v["login"].as_str().map(|s| s.to_owned())) .and_then(|v| v["login"].as_str().map(std::borrow::ToOwned::to_owned))
.unwrap_or_default() .unwrap_or_default()
}; };
if own_login.is_empty() { if own_login.is_empty() {
@ -184,7 +184,7 @@ fn truncate(s: &str, max: usize) -> String {
/// Map a Forgejo review state to a readable action label. /// Map a Forgejo review state to a readable action label.
/// Returns `None` for non-review states (regular comments have no `state` field; /// Returns `None` for non-review states (regular comments have no `state` field;
/// `PENDING` means the review was saved but not submitted yet). /// `PENDING` means the review was saved but not submitted yet).
/// Forgejo review states: "APPROVED", "REQUEST_CHANGES", "COMMENT", "PENDING". /// Forgejo review states: "APPROVED", "`REQUEST_CHANGES`", "COMMENT", "PENDING".
fn review_state_label(state: &str) -> Option<&str> { fn review_state_label(state: &str) -> Option<&str> {
match state { match state {
"APPROVED" => Some("approved"), "APPROVED" => Some("approved"),
@ -204,7 +204,7 @@ fn review_state_label(state: &str) -> Option<&str> {
/// - New item: `[new issue #N repo] title\nurl: ...\nassignee: user` /// - New item: `[new issue #N repo] title\nurl: ...\nassignee: user`
/// - State: `[PR merged #N repo] title\nurl: ...\nassignee: user` /// - State: `[PR merged #N repo] title\nurl: ...\nassignee: user`
/// ///
/// Assignees (and, for PRs, requested_reviewers) are appended unconditionally /// Assignees (and, for PRs, `requested_reviewers`) are appended unconditionally
/// on all issue/PR notifications (closes #256). /// on all issue/PR notifications (closes #256).
/// ///
/// Number is extracted from `html_url` last path segment before any `#`. /// Number is extracted from `html_url` last path segment before any `#`.
@ -246,10 +246,10 @@ async fn format_notification(
// Always fetch subject detail for assignee/reviewer metadata (#256). // Always fetch subject detail for assignee/reviewer metadata (#256).
// Keeps agents informed of current ownership without a follow-up fetch. // Keeps agents informed of current ownership without a follow-up fetch.
let subject = if !subject_api_url.is_empty() { let subject = if subject_api_url.is_empty() {
fetch_json(client, subject_api_url, token).await
} else {
None None
} else {
fetch_json(client, subject_api_url, token).await
}; };
let is_pr = matches!(notif_type, "Pull Request" | "Pull"); let is_pr = matches!(notif_type, "Pull Request" | "Pull");
@ -328,10 +328,10 @@ async fn format_notification(
// Review submission on a PR. // Review submission on a PR.
let kind = format!("PR {review_label}{num}{repo}"); let kind = format!("PR {review_label}{num}{repo}");
let mut out = format!("[{kind}] {title}\nurl: {url}"); let mut out = format!("[{kind}] {title}\nurl: {url}");
if !body_text.is_empty() { if body_text.is_empty() {
out.push_str(&format!("\n\n{author}: {}", truncate(body_text, BODY_TRUNCATE)));
} else {
out.push_str(&format!("\n\nreviewer: {author}")); out.push_str(&format!("\n\nreviewer: {author}"));
} else {
out.push_str(&format!("\n\n{author}: {}", truncate(body_text, BODY_TRUNCATE)));
} }
out.push_str(&meta_suffix); out.push_str(&meta_suffix);
Some(out) Some(out)
@ -457,12 +457,9 @@ async fn poll_once(
let body_opt = format_notification(client, token, notif, own_login).await; let body_opt = format_notification(client, token, notif, own_login).await;
// None means self-echo — mark read silently, no delivery. // None means self-echo — mark read silently, no delivery.
let body = match body_opt { let body = if let Some(b) = body_opt { b } else {
Some(b) => b,
None => {
mark_read(client, forge_url, token, id).await; mark_read(client, forge_url, token, id).await;
continue; continue;
}
}; };
let delivered = if is_manager { let delivered = if is_manager {
@ -501,9 +498,9 @@ async fn poll_once(
// when HIVE_FORGE_KEEP_SUBSCRIPTIONS=1 — triage and other firehose // when HIVE_FORGE_KEEP_SUBSCRIPTIONS=1 — triage and other firehose
// consumers set this to retain broad repo visibility. // consumers set this to retain broad repo visibility.
let reason = notif["reason"].as_str().unwrap_or(""); let reason = notif["reason"].as_str().unwrap_or("");
if !keep_subscriptions && reason == "subscribed" { if !keep_subscriptions && reason == "subscribed"
if let Some(repo) = notif["repository"]["full_name"].as_str() { && let Some(repo) = notif["repository"]["full_name"].as_str()
if !unsubbed_repos.contains(repo) { && !unsubbed_repos.contains(repo) {
let unsub_url = format!("{forge_url}/api/v1/repos/{repo}/subscription"); let unsub_url = format!("{forge_url}/api/v1/repos/{repo}/subscription");
match client match client
.delete(&unsub_url) .delete(&unsub_url)
@ -524,8 +521,6 @@ async fn poll_once(
} }
} }
} }
}
}
} }
/// Mark a notification thread as read. Best-effort — logs on failure but /// Mark a notification thread as read. Best-effort — logs on failure but

View file

@ -681,7 +681,7 @@ pub async fn serve_manager_stdio(socket: PathBuf) -> Result<()> {
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)] #[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct RequestInitConfigArgs { pub struct RequestInitConfigArgs {
/// New sub-agent name (≤9 chars). Queues an InitConfig approval; on /// New sub-agent name (≤9 chars). Queues an `InitConfig` approval; on
/// approval hive-c0re seeds the proposed config repo at /// approval hive-c0re seeds the proposed config repo at
/// `/agents/<name>/config/agent.nix` with the default template. /// `/agents/<name>/config/agent.nix` with the default template.
/// After the approval the manager can edit and commit the config before /// After the approval the manager can edit and commit the config before

View file

@ -272,11 +272,10 @@ pub async fn drive_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutco
// turn overflows into the reactive path. Best-effort — never changes // turn overflows into the reactive path. Best-effort — never changes
// the outcome of the turn that already succeeded, but records it as // the outcome of the turn that already succeeded, but records it as
// `Compacted` so turn stats can distinguish it from a plain `Ok`. // `Compacted` so turn stats can distinguish it from a plain `Ok`.
if matches!(outcome, TurnOutcome::Ok) { if matches!(outcome, TurnOutcome::Ok)
if maybe_checkpoint_and_compact(files, bus).await { && maybe_checkpoint_and_compact(files, bus).await {
return TurnOutcome::Compacted; return TurnOutcome::Compacted;
} }
}
outcome outcome
} }
@ -528,8 +527,7 @@ async fn run_claude(prompt: &str, files: &TurnFiles, bus: &Bus) -> Result<(bool,
if line.contains(PROMPT_TOO_LONG_MARKER) { if line.contains(PROMPT_TOO_LONG_MARKER) {
flag_out.store(true, Ordering::Relaxed); flag_out.store(true, Ordering::Relaxed);
} }
match serde_json::from_str::<serde_json::Value>(&line) { if let Ok(v) = serde_json::from_str::<serde_json::Value>(&line) {
Ok(v) => {
// Rate-limit detection: only fire on JSON `error` events, // Rate-limit detection: only fire on JSON `error` events,
// not on arbitrary text content. An agent discussing a past // not on arbitrary text content. An agent discussing a past
// rate limit in its response would otherwise trigger a false // rate limit in its response would otherwise trigger a false
@ -553,8 +551,7 @@ async fn run_claude(prompt: &str, files: &TurnFiles, bus: &Bus) -> Result<(bool,
} }
bus_out.observe_stream(&v); bus_out.observe_stream(&v);
bus_out.emit(LiveEvent::Stream(v)); bus_out.emit(LiveEvent::Stream(v));
} } else {
Err(_) => {
// Non-JSON stdout: raw text check is fine here since these // Non-JSON stdout: raw text check is fine here since these
// are claude CLI messages, not conversation content. // are claude CLI messages, not conversation content.
if RATE_LIMIT_MARKERS.iter().any(|m| line.contains(m)) { if RATE_LIMIT_MARKERS.iter().any(|m| line.contains(m)) {
@ -565,7 +562,6 @@ async fn run_claude(prompt: &str, files: &TurnFiles, bus: &Bus) -> Result<(bool,
}); });
} }
} }
}
}); });
let stderr_tail: Arc<Mutex<VecDeque<String>>> = let stderr_tail: Arc<Mutex<VecDeque<String>>> =
Arc::new(Mutex::new(VecDeque::with_capacity(STDERR_TAIL_LINES))); Arc::new(Mutex::new(VecDeque::with_capacity(STDERR_TAIL_LINES)));

View file

@ -265,7 +265,7 @@ async fn screen_ws(
} }
/// Pure byte pump: forwards raw bytes between the WebSocket client and /// Pure byte pump: forwards raw bytes between the WebSocket client and
/// the VNC TCP stream. Transparent to any RFB variant (plain, VeNCrypt). /// the VNC TCP stream. Transparent to any RFB variant (plain, `VeNCrypt`).
async fn relay_ws_vnc(socket: axum::extract::ws::WebSocket, vnc_port: u16) { async fn relay_ws_vnc(socket: axum::extract::ws::WebSocket, vnc_port: u16) {
// Import futures traits locally so they don't conflict with // Import futures traits locally so they don't conflict with
// tokio_stream::StreamExt used at module scope. // tokio_stream::StreamExt used at module scope.

View file

@ -65,11 +65,10 @@ pub async fn approve(coord: Arc<Coordinator>, id: i64) -> Result<()> {
if let Err(e) = crate::forge::ensure_config_repo(&approval.agent).await { if let Err(e) = crate::forge::ensure_config_repo(&approval.agent).await {
tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_config_repo after first spawn failed"); tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_config_repo after first spawn failed");
} }
if let Some(core_token) = crate::forge::core_token() { if let Some(core_token) = crate::forge::core_token()
if let Err(e) = crate::forge::meta_read_access(&approval.agent, &core_token).await { && let Err(e) = crate::forge::meta_read_access(&approval.agent, &core_token).await {
tracing::warn!(agent = %approval.agent, error = ?e, "forge: meta_read_access after first spawn failed"); tracing::warn!(agent = %approval.agent, error = ?e, "forge: meta_read_access after first spawn failed");
} }
}
if let Err(e) = crate::forge::ensure_meta_remote(&approval.agent).await { if let Err(e) = crate::forge::ensure_meta_remote(&approval.agent).await {
tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_meta_remote after first spawn failed"); tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_meta_remote after first spawn failed");
} }
@ -91,11 +90,10 @@ pub async fn approve(coord: Arc<Coordinator>, id: i64) -> Result<()> {
} }
.await; .await;
// Wire the meta remote now that the proposed repo exists. // Wire the meta remote now that the proposed repo exists.
if result.is_ok() { if result.is_ok()
if let Err(e) = crate::forge::ensure_meta_remote(&approval.agent).await { && let Err(e) = crate::forge::ensure_meta_remote(&approval.agent).await {
tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_meta_remote after init_config failed"); tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_meta_remote after init_config failed");
} }
}
finish_approval(&coord, &approval, result, None, false) finish_approval(&coord, &approval, result, None, false)
} }
ApprovalKind::UpdateMetaInputs => { ApprovalKind::UpdateMetaInputs => {
@ -145,11 +143,10 @@ pub async fn approve(coord: Arc<Coordinator>, id: i64) -> Result<()> {
if let Err(e) = crate::forge::push_config(&agent_bg).await { if let Err(e) = crate::forge::push_config(&agent_bg).await {
tracing::warn!(agent = %agent_bg, error = ?e, "forge: push_config after spawn failed"); tracing::warn!(agent = %agent_bg, error = ?e, "forge: push_config after spawn failed");
} }
if let Some(core_token) = crate::forge::core_token() { if let Some(core_token) = crate::forge::core_token()
if let Err(e) = crate::forge::meta_read_access(&agent_bg, &core_token).await { && let Err(e) = crate::forge::meta_read_access(&agent_bg, &core_token).await {
tracing::warn!(agent = %agent_bg, error = ?e, "forge: meta_read_access after spawn failed"); tracing::warn!(agent = %agent_bg, error = ?e, "forge: meta_read_access after spawn failed");
} }
}
if let Err(e) = crate::forge::ensure_meta_remote(&agent_bg).await { if let Err(e) = crate::forge::ensure_meta_remote(&agent_bg).await {
tracing::warn!(agent = %agent_bg, error = ?e, "forge: ensure_meta_remote after spawn failed"); tracing::warn!(agent = %agent_bg, error = ?e, "forge: ensure_meta_remote after spawn failed");
} }

View file

@ -613,8 +613,7 @@ impl Broker {
let now = std::time::SystemTime::now() let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH) .duration_since(std::time::UNIX_EPOCH)
.ok() .ok()
.map(|d| d.as_secs() as i64) .map_or(0, |d| d.as_secs() as i64);
.unwrap_or(0);
now - since_secs as i64 now - since_secs as i64
} else { } else {
i64::MIN i64::MIN

View file

@ -49,7 +49,7 @@ pub struct ContainerView {
#[serde(default)] #[serde(default)]
pub pending_reminders: u64, pub pending_reminders: u64,
/// Context-window size (prompt tokens) from the agent's most recent /// Context-window size (prompt tokens) from the agent's most recent
/// completed turn, read directly from the turn-stats SQLite. /// completed turn, read directly from the turn-stats `SQLite`.
/// `None` when the file is absent or the agent has no turns yet. /// `None` when the file is absent or the agent has no turns yet.
/// Stale by up to one crash-watch cycle (~10s); good enough for /// Stale by up to one crash-watch cycle (~10s); good enough for
/// the "which agent is close to the window?" dashboard glance. /// the "which agent is close to the window?" dashboard glance.
@ -84,7 +84,7 @@ pub async fn build_all(coord: &Coordinator) -> Vec<ContainerView> {
} else { } else {
continue; continue;
}; };
let deployed_full = locked.get(&format!("agent-{logical}")).map(|s| s.as_str()); let deployed_full = locked.get(&format!("agent-{logical}")).map(std::string::String::as_str);
let needs_update = crate::auto_update::agent_config_pending(&logical, deployed_full); let needs_update = crate::auto_update::agent_config_pending(&logical, deployed_full);
let needs_login = let needs_login =
!is_manager && !claude_has_session(&Coordinator::agent_claude_dir(&logical)); !is_manager && !claude_has_session(&Coordinator::agent_claude_dir(&logical));
@ -159,7 +159,7 @@ fn is_rate_limited(name: &str) -> bool {
} }
/// Read the most recent completed turn's context-window size (prompt /// Read the most recent completed turn's context-window size (prompt
/// tokens) from the agent's turn-stats SQLite. Returns `None` when /// tokens) from the agent's turn-stats `SQLite`. Returns `None` when
/// the file is absent or has no rows. Best-effort — any DB error /// the file is absent or has no rows. Best-effort — any DB error
/// silently yields `None` so a missing/corrupt file never blocks /// silently yields `None` so a missing/corrupt file never blocks
/// `build_all`. /// `build_all`.

View file

@ -80,7 +80,7 @@ pub fn spawn(coord: Arc<Coordinator>) {
seeded = true; seeded = true;
tokio::select! { tokio::select! {
_ = tokio::time::sleep(POLL_INTERVAL) => {} () = tokio::time::sleep(POLL_INTERVAL) => {}
_ = shutdown.changed() => { _ = shutdown.changed() => {
tracing::info!("crash watcher: shutdown signal received"); tracing::info!("crash watcher: shutdown signal received");
break; break;

View file

@ -29,7 +29,7 @@ pub fn spawn(coord: Arc<Coordinator>) {
loop { loop {
sweep_once(); sweep_once();
tokio::select! { tokio::select! {
_ = tokio::time::sleep(VACUUM_INTERVAL) => {} () = tokio::time::sleep(VACUUM_INTERVAL) => {}
_ = shutdown.changed() => { _ = shutdown.changed() => {
tracing::info!("events vacuum: shutdown signal received"); tracing::info!("events vacuum: shutdown signal received");
break; break;

View file

@ -54,9 +54,9 @@ const SEEDED_ORGS: &[&str] = &[CONFIG_ORG];
/// a forge namespace). /// a forge namespace).
/// - `read:user` — token-owner endpoint clients call to introspect. /// - `read:user` — token-owner endpoint clients call to introspect.
/// - `write:misc` — hooks, attachments, the rest of the long tail. /// - `write:misc` — hooks, attachments, the rest of the long tail.
/// - `read:notification` — required by forge_notify to poll /// - `read:notification` — required by `forge_notify` to poll
/// `GET /notifications` for unread PR/review events. /// `GET /notifications` for unread PR/review events.
/// - `write:notification` — required by forge_notify to mark /// - `write:notification` — required by `forge_notify` to mark
/// notifications as read via `PATCH /notifications/threads/{id}`. /// notifications as read via `PATCH /notifications/threads/{id}`.
const TOKEN_SCOPES: &str = const TOKEN_SCOPES: &str =
"read:user,write:user,read:notification,write:notification,write:repository,write:issue,write:organization,write:misc"; "read:user,write:user,read:notification,write:notification,write:repository,write:issue,write:organization,write:misc";
@ -583,11 +583,10 @@ pub async fn ensure_all() {
} }
// Grant read-only access to core/meta and wire the `meta` remote // Grant read-only access to core/meta and wire the `meta` remote
// into the proposed repo so agents can fetch their deployment context. // into the proposed repo so agents can fetch their deployment context.
if let Some(token) = core_token.as_deref() { if let Some(token) = core_token.as_deref()
if let Err(e) = meta_read_access(&name, token).await { && let Err(e) = meta_read_access(&name, token).await {
tracing::warn!(%name, error = ?e, "forge: ensure_meta_read_access failed"); tracing::warn!(%name, error = ?e, "forge: ensure_meta_read_access failed");
} }
}
if let Err(e) = ensure_meta_remote(&name).await { if let Err(e) = ensure_meta_remote(&name).await {
tracing::warn!(%name, error = ?e, "forge: ensure_meta_remote failed"); tracing::warn!(%name, error = ?e, "forge: ensure_meta_remote failed");
} }

View file

@ -176,7 +176,7 @@ async fn main() -> Result<()> {
Err(e) => tracing::warn!(error = ?e, "broker vacuum failed"), Err(e) => tracing::warn!(error = ?e, "broker vacuum failed"),
} }
tokio::select! { tokio::select! {
_ = tokio::time::sleep(interval) => {} () = tokio::time::sleep(interval) => {}
_ = vacuum_shutdown.changed() => { _ = vacuum_shutdown.changed() => {
tracing::info!("broker vacuum: shutdown signal received"); tracing::info!("broker vacuum: shutdown signal received");
break; break;
@ -219,7 +219,7 @@ async fn main() -> Result<()> {
tracing::info!("SIGINT received — requesting shutdown"); tracing::info!("SIGINT received — requesting shutdown");
coord_sig.request_shutdown(); coord_sig.request_shutdown();
} }
_ = async { () = async {
let mut sig = tokio::signal::unix::signal( let mut sig = tokio::signal::unix::signal(
tokio::signal::unix::SignalKind::terminate() tokio::signal::unix::SignalKind::terminate()
).expect("failed to install SIGTERM handler"); ).expect("failed to install SIGTERM handler");

View file

@ -299,12 +299,11 @@ fn render_flake(
// Forge URL — injected when hive-c0re itself has HIVE_FORGE_URL set // Forge URL — injected when hive-c0re itself has HIVE_FORGE_URL set
// (the NixOS module derives it from hyperhive.forge.{domain,httpPort}). // (the NixOS module derives it from hyperhive.forge.{domain,httpPort}).
// Agents use it in forge_notify to poll Forgejo for PR/review events. // Agents use it in forge_notify to poll Forgejo for PR/review events.
if let Ok(forge_url) = std::env::var("HIVE_FORGE_URL") { if let Ok(forge_url) = std::env::var("HIVE_FORGE_URL")
if !forge_url.is_empty() { && !forge_url.is_empty() {
let escaped = forge_url.replace('\\', "\\\\").replace('"', "\\\""); let escaped = forge_url.replace('\\', "\\\\").replace('"', "\\\"");
let _ = writeln!(out, " HIVE_FORGE_URL = \"{escaped}\";"); let _ = writeln!(out, " HIVE_FORGE_URL = \"{escaped}\";");
} }
}
out.push_str( out.push_str(
r#" HYPERHIVE_STATE_DIR = "/agents/${name}/state"; r#" HYPERHIVE_STATE_DIR = "/agents/${name}/state";
}; };

View file

@ -57,7 +57,7 @@ pub fn spawn(coord: Arc<Coordinator>) {
loop { loop {
tick(&coord); tick(&coord);
tokio::select! { tokio::select! {
_ = tokio::time::sleep(POLL_INTERVAL) => {} () = tokio::time::sleep(POLL_INTERVAL) => {}
_ = shutdown.changed() => { _ = shutdown.changed() => {
tracing::info!("reminder scheduler: shutdown signal received"); tracing::info!("reminder scheduler: shutdown signal received");
break; break;

View file

@ -27,7 +27,7 @@ pub fn spawn(coord: Arc<Coordinator>) {
loop { loop {
sweep_once(); sweep_once();
tokio::select! { tokio::select! {
_ = tokio::time::sleep(VACUUM_INTERVAL) => {} () = tokio::time::sleep(VACUUM_INTERVAL) => {}
_ = shutdown.changed() => { _ = shutdown.changed() => {
tracing::info!("stats vacuum: shutdown signal received"); tracing::info!("stats vacuum: shutdown signal received");
break; break;

View file

@ -41,7 +41,7 @@ pub const TERMINAL_JS: &str = include_str!("../assets/terminal.js");
/// without internet egress (the container itself never fetches it). /// without internet egress (the container itself never fetches it).
/// ///
/// NB: must be the **UMD** build, not `marked.min.js` / `lib/marked.cjs` — /// NB: must be the **UMD** build, not `marked.min.js` / `lib/marked.cjs` —
/// those are CommonJS (`exports.parse = …`, no wrapper) and throw /// those are `CommonJS` (`exports.parse = …`, no wrapper) and throw
/// `exports is not defined` in a `<script>` tag, leaving `window.marked` /// `exports is not defined` in a `<script>` tag, leaving `window.marked`
/// undefined and markdown silently falling back to raw text (issue #244). /// undefined and markdown silently falling back to raw text (issue #244).
pub const MARKED_JS: &str = include_str!("../assets/marked.umd.js"); pub const MARKED_JS: &str = include_str!("../assets/marked.umd.js");

View file

@ -124,11 +124,11 @@ pub enum ApprovalStatus {
/// Reminder activity statistics for an agent over a time window. /// Reminder activity statistics for an agent over a time window.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReminderStats { pub struct ReminderStats {
/// Total reminders scheduled in the window (created_at >= cutoff). /// Total reminders scheduled in the window (`created_at` >= cutoff).
pub scheduled: u64, pub scheduled: u64,
/// Reminders that have been delivered in the window (sent_at IS NOT NULL). /// Reminders that have been delivered in the window (`sent_at` IS NOT NULL).
pub delivered: u64, pub delivered: u64,
/// Reminders still pending in the window (sent_at IS NULL). /// Reminders still pending in the window (`sent_at` IS NULL).
pub pending: u64, pub pending: u64,
} }