diff --git a/clippy.toml b/clippy.toml index 83d5ab8de..0bca55679 100644 --- a/clippy.toml +++ b/clippy.toml @@ -8,6 +8,16 @@ disallowed-methods = [ { path = "std::io::stdin", reason = "Returns a blocking handle; prefer tokio::io::stdin on Tokio paths. Document intentional sync stdin with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, { path = "std::io::stdout", reason = "Returns a blocking handle; prefer tokio::io::stdout on Tokio paths. Document intentional sync stdout with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, { path = "std::io::stderr", reason = "Returns a blocking handle; prefer tokio::io::stderr on Tokio paths. Document intentional sync stderr with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::read", reason = "Blocking disk read; prefer tokio::fs::read on Tokio paths. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::read_to_string", reason = "Blocking disk read; prefer tokio::fs::read_to_string on Tokio paths. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::write", reason = "Blocking disk write; prefer tokio::fs::write on Tokio paths. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::read_dir", reason = "Blocking directory enumeration; prefer tokio::fs::read_dir on Tokio paths. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::copy", reason = "Blocking disk copy; prefer tokio::fs::copy on Tokio paths. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::canonicalize", reason = "Blocking path resolution; prefer tokio::fs::canonicalize on Tokio paths. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::File::open", reason = "Blocking open; prefer tokio::fs::File::open on Tokio paths. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::File::create", reason = "Blocking open; prefer tokio::fs::File::create on Tokio paths. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::File::create_new", reason = "Blocking open; prefer tokio::fs::File::create_new on Tokio paths. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, + { path = "std::fs::OpenOptions::open", reason = "Blocking open; prefer tokio::fs::OpenOptions::open on Tokio paths. OS file-lock semantics may require spawn_blocking instead. Document intentional sync I/O with #[expect(clippy::disallowed_methods, reason = \"...\")]" }, { path = "reqwest::Client::new", reason = "Use fabro_http::http_client() or fabro_http::test_http_client()", allow-invalid = true }, { path = "reqwest::Client::builder", reason = "Use fabro_http::HttpClientBuilder::new()", allow-invalid = true }, { path = "reqwest::blocking::Client::new", reason = "Use fabro_http::blocking_http_client() or fabro_http::blocking_test_http_client()", allow-invalid = true }, diff --git a/lib/crates/fabro-agent/tests/it/parity_matrix.rs b/lib/crates/fabro-agent/tests/it/parity_matrix.rs index 51d3b7311..77bfe92fa 100644 --- a/lib/crates/fabro-agent/tests/it/parity_matrix.rs +++ b/lib/crates/fabro-agent/tests/it/parity_matrix.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "agent parity test harness: sync std::fs for staging fixture trees and reading captured outputs" +)] + use std::collections::HashMap; use std::fmt::Write as _; use std::path::Path; diff --git a/lib/crates/fabro-api/build.rs b/lib/crates/fabro-api/build.rs index c19164184..c2c9166f6 100644 --- a/lib/crates/fabro-api/build.rs +++ b/lib/crates/fabro-api/build.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "build script: runs at compile time outside any runtime" +)] + use std::path::{Path, PathBuf}; use std::{env, fs}; diff --git a/lib/crates/fabro-auth/src/resolve.rs b/lib/crates/fabro-auth/src/resolve.rs index 262a0dd9a..d9497c2e9 100644 --- a/lib/crates/fabro-auth/src/resolve.rs +++ b/lib/crates/fabro-auth/src/resolve.rs @@ -586,6 +586,11 @@ mod tests { #[cfg(unix)] #[tokio::test] + #[expect( + clippy::disallowed_methods, + reason = "integration-style test: writes and reads a fake codex script via sync std::fs to \ + verify the login_command string passes stdin correctly" + )] async fn openai_api_key_cli_login_command_executes_codex_from_local_bin() { let dir = tempfile::tempdir().unwrap(); let local_bin = dir.path().join(".local/bin"); diff --git a/lib/crates/fabro-checkpoint/src/git.rs b/lib/crates/fabro-checkpoint/src/git.rs index 30f4eca69..cf60498b8 100644 --- a/lib/crates/fabro-checkpoint/src/git.rs +++ b/lib/crates/fabro-checkpoint/src/git.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync git2 operations dominate this module; std::fs usage is part of the same blocking path and not on a Tokio hot path" +)] + use std::collections::BTreeMap; use std::path::Path; diff --git a/lib/crates/fabro-cli/src/commands/artifact/cp.rs b/lib/crates/fabro-cli/src/commands/artifact/cp.rs index b1e8e7304..9fa791919 100644 --- a/lib/crates/fabro-cli/src/commands/artifact/cp.rs +++ b/lib/crates/fabro-cli/src/commands/artifact/cp.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI `artifact cp` command: sync file I/O in command handler; not on a Tokio hot path" +)] + use std::path::{Path, PathBuf}; use anyhow::{Context, Result, bail}; diff --git a/lib/crates/fabro-cli/src/commands/doctor.rs b/lib/crates/fabro-cli/src/commands/doctor.rs index 894dc4da3..c2f461dc3 100644 --- a/lib/crates/fabro-cli/src/commands/doctor.rs +++ b/lib/crates/fabro-cli/src/commands/doctor.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI `doctor` command: sync directory scan in command handler" +)] + use std::path::{Path, PathBuf}; use anyhow::Result; diff --git a/lib/crates/fabro-cli/src/commands/install.rs b/lib/crates/fabro-cli/src/commands/install.rs index 5a6b44d19..02a114846 100644 --- a/lib/crates/fabro-cli/src/commands/install.rs +++ b/lib/crates/fabro-cli/src/commands/install.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI `install` command: sync file I/O in install command handler; not on a Tokio hot path" +)] + use std::future::Future; use std::net::SocketAddr; use std::path::Path; diff --git a/lib/crates/fabro-cli/src/commands/server/record.rs b/lib/crates/fabro-cli/src/commands/server/record.rs index 560f9d9f7..1a393fc52 100644 --- a/lib/crates/fabro-cli/src/commands/server/record.rs +++ b/lib/crates/fabro-cli/src/commands/server/record.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI server record helpers: sync read/write of local server record file" +)] + use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; diff --git a/lib/crates/fabro-cli/src/commands/server/start.rs b/lib/crates/fabro-cli/src/commands/server/start.rs index b270e1117..c9cee55ed 100644 --- a/lib/crates/fabro-cli/src/commands/server/start.rs +++ b/lib/crates/fabro-cli/src/commands/server/start.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI `server start` command: sync config/record file I/O in startup command; acquire_lock uses spawn_blocking" +)] + use std::path::{Path, PathBuf}; use std::time::Duration; @@ -14,6 +19,7 @@ use fabro_util::terminal::Styles; use fabro_util::{Home, dev_token, session_secret}; use tokio::net::{TcpStream, UnixStream}; use tokio::process::Command as TokioCommand; +use tokio::task::spawn_blocking; use tokio::time; use super::record; @@ -514,7 +520,12 @@ async fn acquire_lock(storage_dir: &Path) -> Result { .with_context(|| format!("creating server lock directory {}", parent.display()))?; } let lock_path_for_open = lock_path.clone(); - let lock_file = tokio::task::spawn_blocking(move || { + #[expect( + clippy::disallowed_methods, + reason = "OpenOptions::open inside spawn_blocking; file-lock semantics need a real \ + std::fs::File handle for fabro_proc::try_flock_exclusive" + )] + let lock_file = spawn_blocking(move || { std::fs::OpenOptions::new() .create(true) .write(true) diff --git a/lib/crates/fabro-cli/src/commands/store/dump.rs b/lib/crates/fabro-cli/src/commands/store/dump.rs index 6835d862f..401044530 100644 --- a/lib/crates/fabro-cli/src/commands/store/dump.rs +++ b/lib/crates/fabro-cli/src/commands/store/dump.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI `store dump` command: sync file I/O for dump outputs" +)] + use std::io::ErrorKind; use std::path::Path; diff --git a/lib/crates/fabro-cli/src/commands/uninstall.rs b/lib/crates/fabro-cli/src/commands/uninstall.rs index 8e2c0904e..34e0c4e79 100644 --- a/lib/crates/fabro-cli/src/commands/uninstall.rs +++ b/lib/crates/fabro-cli/src/commands/uninstall.rs @@ -2,6 +2,10 @@ clippy::disallowed_types, reason = "sync CLI `uninstall` command: blocking std::io::Write is the intended output mechanism" )] +#![expect( + clippy::disallowed_methods, + reason = "CLI `uninstall` command: sync file I/O in command handler" +)] use std::fs; use std::io::Write; diff --git a/lib/crates/fabro-cli/src/commands/workflow/create.rs b/lib/crates/fabro-cli/src/commands/workflow/create.rs index c09b4ae1d..845508a2a 100644 --- a/lib/crates/fabro-cli/src/commands/workflow/create.rs +++ b/lib/crates/fabro-cli/src/commands/workflow/create.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI `workflow create` command: sync file I/O creating workflow scaffolding" +)] + use std::path::Path; use anyhow::{Context, Result, bail}; diff --git a/lib/crates/fabro-cli/src/logging.rs b/lib/crates/fabro-cli/src/logging.rs index e787b6b13..ac7d01c07 100644 --- a/lib/crates/fabro-cli/src/logging.rs +++ b/lib/crates/fabro-cli/src/logging.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI logging setup: sync directory scan during startup" +)] + use std::path::Path; use anyhow::{Context, Result}; diff --git a/lib/crates/fabro-cli/src/manifest_builder.rs b/lib/crates/fabro-cli/src/manifest_builder.rs index 4aefd3007..946ec697a 100644 --- a/lib/crates/fabro-cli/src/manifest_builder.rs +++ b/lib/crates/fabro-cli/src/manifest_builder.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI manifest builder: sync file I/O building install manifests" +)] + use std::collections::{HashMap, HashSet}; use std::path::{Component, Path, PathBuf}; diff --git a/lib/crates/fabro-cli/src/server_client.rs b/lib/crates/fabro-cli/src/server_client.rs index da033e83e..62f74db51 100644 --- a/lib/crates/fabro-cli/src/server_client.rs +++ b/lib/crates/fabro-cli/src/server_client.rs @@ -1138,6 +1138,10 @@ fn non_zero_u64_from_usize(value: usize) -> Option { } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "server-client tests stage local dev-token fixtures with sync std::fs::write" +)] mod tests { use super::*; diff --git a/lib/crates/fabro-cli/src/user_config.rs b/lib/crates/fabro-cli/src/user_config.rs index a64cf5c46..a01b0091e 100644 --- a/lib/crates/fabro-cli/src/user_config.rs +++ b/lib/crates/fabro-cli/src/user_config.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "CLI user config: sync file I/O loading user config" +)] + use std::path::{Path, PathBuf}; use anyhow::{Context, Result, bail}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/config.rs b/lib/crates/fabro-cli/tests/it/cmd/config.rs index 105228a24..4c6f56e39 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/config.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/config.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use std::path::PathBuf; use fabro_config::parse_settings_layer; diff --git a/lib/crates/fabro-cli/tests/it/cmd/exec.rs b/lib/crates/fabro-cli/tests/it/cmd/exec.rs index 612ffb8b8..2c633d82c 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/exec.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/exec.rs @@ -1,4 +1,8 @@ #![allow(clippy::absolute_paths)] +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] use std::process::Output; diff --git a/lib/crates/fabro-cli/tests/it/cmd/install.rs b/lib/crates/fabro-cli/tests/it/cmd/install.rs index 9dd867551..8699fdf82 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/install.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/install.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_config::{Storage, envfile}; use fabro_test::{fabro_snapshot, test_context}; use fabro_vault::{SecretType, Vault}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/model.rs b/lib/crates/fabro-cli/tests/it/cmd/model.rs index de7c3feeb..f71cb466a 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/model.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/model.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_test::{fabro_snapshot, test_context}; use httpmock::MockServer; diff --git a/lib/crates/fabro-cli/tests/it/cmd/repo_init.rs b/lib/crates/fabro-cli/tests/it/cmd/repo_init.rs index 68c2ce931..1dddc9579 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/repo_init.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/repo_init.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_test::{fabro_snapshot, test_context}; use insta::assert_snapshot; diff --git a/lib/crates/fabro-cli/tests/it/cmd/resume.rs b/lib/crates/fabro-cli/tests/it/cmd/resume.rs index 5a1a940dc..bff92d58d 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/resume.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/resume.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_test::{fabro_snapshot, test_context}; use super::support::{git_stdout, output_stderr, setup_git_backed_changed_run}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/run.rs b/lib/crates/fabro-cli/tests/it/cmd/run.rs index 01b2220bf..6814b2604 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/run.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/run.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_auth::{AuthCredential, AuthDetails}; use fabro_config::Storage; use fabro_model::Provider; diff --git a/lib/crates/fabro-cli/tests/it/cmd/sandbox_cp.rs b/lib/crates/fabro-cli/tests/it/cmd/sandbox_cp.rs index 0b1f36d61..19f9e7926 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/sandbox_cp.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/sandbox_cp.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_test::{fabro_snapshot, test_context}; use super::support::{read_text, setup_created_dry_run, setup_local_sandbox_run, text_tree}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/server_start.rs b/lib/crates/fabro-cli/tests/it/cmd/server_start.rs index 83673a2e4..8c6a2fe32 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/server_start.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/server_start.rs @@ -3,6 +3,10 @@ reason = "integration test: occupies a fixed TCP port via sync std::net::TcpListener to \ verify the server-start fallback path when the default port is unavailable" )] +#![expect( + clippy::disallowed_methods, + reason = "integration test stages server-start fixtures with sync std::fs::write" +)] use std::process::Stdio; use std::sync::{Arc, Barrier}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/store_dump.rs b/lib/crates/fabro-cli/tests/it/cmd/store_dump.rs index e9df789a6..de9710755 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/store_dump.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/store_dump.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use std::fs; use std::time::Duration; diff --git a/lib/crates/fabro-cli/tests/it/cmd/system_df.rs b/lib/crates/fabro-cli/tests/it/cmd/system_df.rs index da931e5a0..f73032e3c 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/system_df.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/system_df.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_test::{fabro_snapshot, test_context}; use serde_json::Value; diff --git a/lib/crates/fabro-cli/tests/it/cmd/uninstall.rs b/lib/crates/fabro-cli/tests/it/cmd/uninstall.rs index 0817fa928..16bdc377f 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/uninstall.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/uninstall.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use std::fs; use fabro_test::{fabro_snapshot, test_context}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/upgrade.rs b/lib/crates/fabro-cli/tests/it/cmd/upgrade.rs index 64f44409e..fd25902d6 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/upgrade.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/upgrade.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use assert_cmd::Command; use fabro_test::{TestContext, fabro_snapshot, test_context}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs b/lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs index a4cf9abb6..0649c3387 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_test::{fabro_snapshot, test_context}; use insta::assert_snapshot; use serde_json::Value; diff --git a/lib/crates/fabro-cli/tests/it/scenario/exec.rs b/lib/crates/fabro-cli/tests/it/scenario/exec.rs index ba64536e3..0b5f77899 100644 --- a/lib/crates/fabro-cli/tests/it/scenario/exec.rs +++ b/lib/crates/fabro-cli/tests/it/scenario/exec.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use std::time::Duration; use fabro_test::test_context; diff --git a/lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs b/lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs index 895e23f92..8fb907a4a 100644 --- a/lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs +++ b/lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs @@ -1,4 +1,8 @@ #![allow(clippy::absolute_paths)] +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] use fabro_test::test_context; use serde_json::Value; diff --git a/lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs b/lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs index 1096914c1..287465b65 100644 --- a/lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs +++ b/lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_test::test_context; use super::{ diff --git a/lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs b/lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs index e4c339916..8050455d9 100644 --- a/lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs +++ b/lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_test::test_context; use super::{ diff --git a/lib/crates/fabro-cli/tests/it/workflow/full_stack.rs b/lib/crates/fabro-cli/tests/it/workflow/full_stack.rs index 8d9c2df31..f243cd5f0 100644 --- a/lib/crates/fabro-cli/tests/it/workflow/full_stack.rs +++ b/lib/crates/fabro-cli/tests/it/workflow/full_stack.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use fabro_test::test_context; use super::{ diff --git a/lib/crates/fabro-cli/tests/it/workflow/hooks.rs b/lib/crates/fabro-cli/tests/it/workflow/hooks.rs index 7d6448214..4d75f4fe6 100644 --- a/lib/crates/fabro-cli/tests/it/workflow/hooks.rs +++ b/lib/crates/fabro-cli/tests/it/workflow/hooks.rs @@ -3,6 +3,10 @@ clippy::needless_borrow, clippy::needless_borrows_for_generic_args )] +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] use std::process::Output; diff --git a/lib/crates/fabro-config/src/envfile.rs b/lib/crates/fabro-config/src/envfile.rs index 90f1671d9..e84848b70 100644 --- a/lib/crates/fabro-config/src/envfile.rs +++ b/lib/crates/fabro-config/src/envfile.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync env-file load/save used at CLI and server startup; not on a Tokio path" +)] + use std::collections::HashMap; use std::io; use std::path::{Path, PathBuf}; diff --git a/lib/crates/fabro-config/src/lib.rs b/lib/crates/fabro-config/src/lib.rs index 0929f62d8..fa6dc6f09 100644 --- a/lib/crates/fabro-config/src/lib.rs +++ b/lib/crates/fabro-config/src/lib.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync config loading utilities used at startup; not on a Tokio path" +)] + extern crate self as fabro_config; mod defaults; diff --git a/lib/crates/fabro-config/src/load.rs b/lib/crates/fabro-config/src/load.rs index 6ca272981..0943dcfb6 100644 --- a/lib/crates/fabro-config/src/load.rs +++ b/lib/crates/fabro-config/src/load.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync config file load used at startup; not on a Tokio path" +)] + use std::path::{Path, PathBuf}; use fabro_types::settings::run::RunGoalLayer; diff --git a/lib/crates/fabro-config/src/project.rs b/lib/crates/fabro-config/src/project.rs index 087fd619e..d62584669 100644 --- a/lib/crates/fabro-config/src/project.rs +++ b/lib/crates/fabro-config/src/project.rs @@ -4,6 +4,11 @@ //! tree in `fabro_types::settings::v2`. This module keeps the workflow //! discovery helpers and re-exports resolved project settings. +#![expect( + clippy::disallowed_methods, + reason = "sync project-level config discovery and workflow listing; not on a Tokio path" +)] + use std::fmt::Write; use std::path::{Component, Path, PathBuf}; diff --git a/lib/crates/fabro-config/src/run.rs b/lib/crates/fabro-config/src/run.rs index e4e536913..2cc603978 100644 --- a/lib/crates/fabro-config/src/run.rs +++ b/lib/crates/fabro-config/src/run.rs @@ -5,6 +5,11 @@ //! that used to be re-exported from here live under //! `fabro_types::settings::run` now. +#![expect( + clippy::disallowed_methods, + reason = "sync run-config loading helpers; not on a Tokio path" +)] + use std::path::{Path, PathBuf}; use fabro_types::settings::SettingsLayer; diff --git a/lib/crates/fabro-devcontainer/src/compose.rs b/lib/crates/fabro-devcontainer/src/compose.rs index 80450e848..59b05119c 100644 --- a/lib/crates/fabro-devcontainer/src/compose.rs +++ b/lib/crates/fabro-devcontainer/src/compose.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync parse of docker-compose.yml files during devcontainer resolution; not on a Tokio path" +)] + use std::collections::HashMap; use std::path::{Path, PathBuf}; diff --git a/lib/crates/fabro-devcontainer/src/features.rs b/lib/crates/fabro-devcontainer/src/features.rs index 8599e7cb6..2717bd448 100644 --- a/lib/crates/fabro-devcontainer/src/features.rs +++ b/lib/crates/fabro-devcontainer/src/features.rs @@ -731,6 +731,10 @@ mod tests { } #[tokio::test] + #[expect( + clippy::disallowed_methods, + reason = "test fixture setup uses sync std::fs::write to create a fake feature directory" + )] async fn fetch_feature_local_integration() { let tmp_src = tempfile::tempdir().unwrap(); let feature_dir = tmp_src.path().join("my-feature"); diff --git a/lib/crates/fabro-devcontainer/src/lib.rs b/lib/crates/fabro-devcontainer/src/lib.rs index 036eeea06..b1e4af4ba 100644 --- a/lib/crates/fabro-devcontainer/src/lib.rs +++ b/lib/crates/fabro-devcontainer/src/lib.rs @@ -161,6 +161,12 @@ pub struct DevcontainerResolver; impl DevcontainerResolver { /// path: repo root (or explicit .devcontainer/ path) + #[expect( + clippy::disallowed_methods, + reason = "FOLLOW-UP: DevcontainerResolver::resolve does sync std::fs::read_to_string / \ + read_dir across several devcontainer.json lookups. One-shot per workflow run \ + (not per-request); acceptable today but should migrate to tokio::fs." + )] pub async fn resolve(path: &Path) -> Result { let (json_path, devcontainer) = Self::find_and_parse(path)?; let repo_root = Self::repo_root_from_json_path(&json_path, path); @@ -409,6 +415,11 @@ impl DevcontainerResolver { }) } + #[expect( + clippy::disallowed_methods, + reason = "FOLLOW-UP: sync std::fs helpers for devcontainer.json lookup; called once at \ + workflow startup via resolve(). Should migrate to tokio::fs with resolve()." + )] fn find_and_parse(path: &Path) -> Result<(PathBuf, DevcontainerJson)> { // Check standard locations let candidates = [ diff --git a/lib/crates/fabro-install/src/lib.rs b/lib/crates/fabro-install/src/lib.rs index 5286f53d1..e13415561 100644 --- a/lib/crates/fabro-install/src/lib.rs +++ b/lib/crates/fabro-install/src/lib.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "fabro-install: sync CLI install/uninstall bookkeeping; not on a Tokio hot path" +)] + use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; diff --git a/lib/crates/fabro-interview/src/recording.rs b/lib/crates/fabro-interview/src/recording.rs index 9d803916a..ace98f179 100644 --- a/lib/crates/fabro-interview/src/recording.rs +++ b/lib/crates/fabro-interview/src/recording.rs @@ -51,6 +51,10 @@ impl RecordingInterviewer { /// /// # Errors /// Returns an error if serialization or file writing fails. + #[expect( + clippy::disallowed_methods, + reason = "sync helper for test-mode interview recording storage; not on a Tokio path" + )] pub fn save_to_file(&self, path: &Path) -> std::io::Result<()> { let json = self.to_json()?; std::fs::write(path, json).map_err(|err| { @@ -66,6 +70,10 @@ impl RecordingInterviewer { /// /// # Errors /// Returns an error if file reading or deserialization fails. + #[expect( + clippy::disallowed_methods, + reason = "sync helper for test-mode interview recording storage; not on a Tokio path" + )] pub fn load_from_file(path: &Path) -> std::io::Result> { let json = std::fs::read_to_string(path).map_err(|err| { std::io::Error::new( diff --git a/lib/crates/fabro-llm/src/providers/common.rs b/lib/crates/fabro-llm/src/providers/common.rs index 6a3cae449..893def7a5 100644 --- a/lib/crates/fabro-llm/src/providers/common.rs +++ b/lib/crates/fabro-llm/src/providers/common.rs @@ -92,6 +92,13 @@ pub fn mime_from_extension(path: &str) -> &str { /// /// # Errors /// Returns an error if the file cannot be read. +#[expect( + clippy::disallowed_methods, + reason = "FOLLOW-UP: sync std::fs::read for file:// attachments, invoked from sync \ + translators (translate_input/translate_messages) across all providers. Pre-existing; \ + 7 call sites in sync translators would need restructuring to wrap in spawn_blocking \ + at each async chokepoint. file:// URLs are rare in practice; revisit if usage grows." +)] pub fn load_file_as_base64(path: &str) -> Result<(String, String), std::io::Error> { let expanded = path.strip_prefix("~/").map_or_else( || path.to_string(), diff --git a/lib/crates/fabro-proc/src/flock.rs b/lib/crates/fabro-proc/src/flock.rs index ae3b70c83..552b143b7 100644 --- a/lib/crates/fabro-proc/src/flock.rs +++ b/lib/crates/fabro-proc/src/flock.rs @@ -32,6 +32,10 @@ pub fn flock_unlock(file: &File) -> io::Result<()> { } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "tests exercise sync flock semantics on real temp files; uses std::fs::File directly" +)] mod tests { use std::fs::File; diff --git a/lib/crates/fabro-sandbox/src/local.rs b/lib/crates/fabro-sandbox/src/local.rs index 15fbc17b5..e26635859 100644 --- a/lib/crates/fabro-sandbox/src/local.rs +++ b/lib/crates/fabro-sandbox/src/local.rs @@ -4,6 +4,7 @@ use std::time::Instant; use async_trait::async_trait; use tokio::io::AsyncReadExt; use tokio::process::{Child, Command}; +use tokio::task::spawn_blocking; use tokio::{fs, time}; use tokio_util::sync::CancellationToken; @@ -156,6 +157,10 @@ impl Sandbox for LocalSandbox { path: &str, depth: Option, ) -> Result, String> { + #[expect( + clippy::disallowed_methods, + reason = "sync recursive read_dir; caller wraps invocation in tokio::task::spawn_blocking" + )] fn list_recursive( base: &std::path::Path, prefix: &str, @@ -197,7 +202,7 @@ impl Sandbox for LocalSandbox { let full_path = self.resolve_path(path); let max_depth = depth.unwrap_or(1); - tokio::task::spawn_blocking(move || { + spawn_blocking(move || { let mut entries = Vec::new(); list_recursive(&full_path, "", 0, max_depth, &mut entries)?; Ok(entries) @@ -537,6 +542,10 @@ async fn sigterm_then_kill(child: &mut Child) { } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "sandbox tests stage fixtures with sync std::fs writes/reads" +)] mod tests { use std::path::PathBuf; diff --git a/lib/crates/fabro-sandbox/src/reconnect.rs b/lib/crates/fabro-sandbox/src/reconnect.rs index 2a7ae938a..804b7ff8b 100644 --- a/lib/crates/fabro-sandbox/src/reconnect.rs +++ b/lib/crates/fabro-sandbox/src/reconnect.rs @@ -14,6 +14,7 @@ use crate::sandbox_record::SandboxRecord; /// /// `daytona_api_key` is forwarded to the Daytona SDK when the provider is /// `"daytona"`. Pass `None` to fall back to the `DAYTONA_API_KEY` env var. +#[allow(clippy::unused_async, unused_variables)] pub async fn reconnect( record: &SandboxRecord, daytona_api_key: Option, diff --git a/lib/crates/fabro-sandbox/src/sandbox_spec.rs b/lib/crates/fabro-sandbox/src/sandbox_spec.rs index e3633bda1..305223116 100644 --- a/lib/crates/fabro-sandbox/src/sandbox_spec.rs +++ b/lib/crates/fabro-sandbox/src/sandbox_spec.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use anyhow::anyhow; #[cfg(feature = "daytona")] use fabro_github::GitHubCredentials; +#[allow(unused_imports)] use fabro_types::RunId; use crate::config::WorktreeMode; @@ -142,6 +143,7 @@ impl SandboxSpec { } } + #[allow(clippy::unused_async)] pub async fn build( &self, event_callback: Option, diff --git a/lib/crates/fabro-sandbox/src/worktree.rs b/lib/crates/fabro-sandbox/src/worktree.rs index da4d3c3e3..1d4a82d2b 100644 --- a/lib/crates/fabro-sandbox/src/worktree.rs +++ b/lib/crates/fabro-sandbox/src/worktree.rs @@ -358,6 +358,10 @@ impl Sandbox for WorktreeSandbox { // --------------------------------------------------------------------------- #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "worktree tests stage fixtures with sync std::fs writes in temp dirs" +)] mod tests { use std::sync::Mutex; diff --git a/lib/crates/fabro-server/src/install.rs b/lib/crates/fabro-server/src/install.rs index 38837784f..b2d9437aa 100644 --- a/lib/crates/fabro-server/src/install.rs +++ b/lib/crates/fabro-server/src/install.rs @@ -891,6 +891,11 @@ async fn post_install_finish( ("FABRO_DEV_TOKEN".to_string(), dev_token.clone()), ]); + #[expect( + clippy::disallowed_methods, + reason = "install-finish handler: reads current settings file once to produce a rollback \ + snapshot before writing the new settings; one-shot per install-finish request" + )] let previous_settings = std::fs::read_to_string(state.config_path.as_ref()).ok(); if let Err(err) = persist_install_outputs_direct( diff --git a/lib/crates/fabro-server/src/ip_allowlist.rs b/lib/crates/fabro-server/src/ip_allowlist.rs index 6ea4cd0c0..a2762bcfc 100644 --- a/lib/crates/fabro-server/src/ip_allowlist.rs +++ b/lib/crates/fabro-server/src/ip_allowlist.rs @@ -12,6 +12,7 @@ use fabro_types::settings::server::{ }; use ipnet::IpNet; use serde::{Deserialize, Serialize}; +use tokio::fs; use tracing::warn; use crate::ApiError; @@ -130,7 +131,7 @@ impl GitHubMetaResolver { } async fn load_cache(&self) -> Result> { - match tokio::fs::read(&self.cache_path).await { + match fs::read(&self.cache_path).await { Ok(contents) => match serde_json::from_slice(&contents) { Ok(cache) => Ok(Some(cache)), Err(error) => { @@ -151,13 +152,13 @@ impl GitHubMetaResolver { async fn store_cache(&self, cache: &GitHubMetaCache) -> Result<()> { if let Some(parent) = self.cache_path.parent() { - tokio::fs::create_dir_all(parent) + fs::create_dir_all(parent) .await .with_context(|| format!("creating {}", parent.display()))?; } let contents = serde_json::to_vec(cache).context("serializing GitHub meta cache")?; - tokio::fs::write(&self.cache_path, contents) + fs::write(&self.cache_path, contents) .await .with_context(|| format!("writing {}", self.cache_path.display()))?; Ok(()) @@ -322,6 +323,10 @@ pub fn github_meta_cache_path(cache_dir: &Path) -> PathBuf { } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "tests stage IP allowlist fixtures with sync std::fs::write" +)] mod tests { use std::net::Ipv4Addr; diff --git a/lib/crates/fabro-server/src/server.rs b/lib/crates/fabro-server/src/server.rs index b4c24e5dc..6054b5de6 100644 --- a/lib/crates/fabro-server/src/server.rs +++ b/lib/crates/fabro-server/src/server.rs @@ -1420,6 +1420,10 @@ struct PrunePlan { total_size_bytes: u64, } +#[expect( + clippy::disallowed_methods, + reason = "sync helper invoked from async handler via spawn_blocking (see callers at :1301 / :1341)" +)] fn build_disk_usage_response( summaries: &[fabro_store::RunSummary], storage_dir: &std::path::Path, @@ -2392,6 +2396,10 @@ pub fn create_app_state_with_env_lookup( } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "test helper writes a fixture server.env with sync std::fs::write" +)] pub(crate) fn create_test_app_state_with_session_key( settings: SettingsLayer, session_secret: Option<&str>, @@ -3535,6 +3543,11 @@ struct WorkerServerRecord { fn current_server_target(storage_dir: &std::path::Path) -> anyhow::Result { let record_path = Storage::new(storage_dir).server_state().record_path(); + #[expect( + clippy::disallowed_methods, + reason = "sync helper invoked from worker_command (sync) via spawn_blocking at the async \ + boundary in execute_run_subprocess; see commit 9d1c0d98c" + )] let content = std::fs::read_to_string(&record_path) .map_err(|err| anyhow::anyhow!("failed to read {}: {err}", record_path.display()))?; let record: WorkerServerRecord = serde_json::from_str(&content).map_err(|err| { @@ -4462,7 +4475,7 @@ async fn execute_run_subprocess(state: Arc, run_id: RunId) { let state_for_build = Arc::clone(&state); let run_dir_for_build = run_dir.clone(); - let build_cmd_result = tokio::task::spawn_blocking(move || { + let build_cmd_result = spawn_blocking(move || { worker_command( state_for_build.as_ref(), run_id, @@ -6894,6 +6907,10 @@ async fn get_graph( } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "server unit tests stage fixtures with sync std::fs writes" +)] mod tests { #[cfg(unix)] use std::os::unix::fs::PermissionsExt; diff --git a/lib/crates/fabro-server/src/static_files.rs b/lib/crates/fabro-server/src/static_files.rs index 9ea44e2f4..0090d4db1 100644 --- a/lib/crates/fabro-server/src/static_files.rs +++ b/lib/crates/fabro-server/src/static_files.rs @@ -4,6 +4,7 @@ use std::sync::OnceLock; use axum::body::Body; use axum::http::{HeaderMap, HeaderValue, StatusCode, header}; use axum::response::{IntoResponse, Response}; +use tokio::fs; const INSTALL_MODE_MARKER: &str = "__FABRO_MODE__ = \"install\""; @@ -143,7 +144,7 @@ async fn read_disk_asset(path: &str) -> Option> { async fn read_disk_asset_from_root(root: &Path, path: &str) -> Option> { let candidate = root.join(path); if candidate.is_file() { - tokio::fs::read(candidate).await.ok() + fs::read(candidate).await.ok() } else { None } @@ -197,6 +198,10 @@ fn is_source_map(path: &str) -> bool { } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "tests stage static asset fixtures with sync std::fs::write" +)] mod tests { use axum::http::{HeaderMap, HeaderValue, header}; diff --git a/lib/crates/fabro-server/tests/it/api/docs.rs b/lib/crates/fabro-server/tests/it/api/docs.rs index e3c3be45e..b694c8d72 100644 --- a/lib/crates/fabro-server/tests/it/api/docs.rs +++ b/lib/crates/fabro-server/tests/it/api/docs.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use std::path::PathBuf; fn read_doc(relative_path: &str) -> String { diff --git a/lib/crates/fabro-server/tests/it/api/install.rs b/lib/crates/fabro-server/tests/it/api/install.rs index 559916c5b..f7f73a5a4 100644 --- a/lib/crates/fabro-server/tests/it/api/install.rs +++ b/lib/crates/fabro-server/tests/it/api/install.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; diff --git a/lib/crates/fabro-server/tests/it/api/system.rs b/lib/crates/fabro-server/tests/it/api/system.rs index 5fc8008e2..e6bb27b98 100644 --- a/lib/crates/fabro-server/tests/it/api/system.rs +++ b/lib/crates/fabro-server/tests/it/api/system.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use std::path::PathBuf; use std::time::Duration; diff --git a/lib/crates/fabro-server/tests/it/api/tcp.rs b/lib/crates/fabro-server/tests/it/api/tcp.rs index ca2fb773b..0ecfef85b 100644 --- a/lib/crates/fabro-server/tests/it/api/tcp.rs +++ b/lib/crates/fabro-server/tests/it/api/tcp.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] + use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::sync::Arc; diff --git a/lib/crates/fabro-server/tests/it/openapi_conformance.rs b/lib/crates/fabro-server/tests/it/openapi_conformance.rs index 40cd93f4c..c15cbe8fc 100644 --- a/lib/crates/fabro-server/tests/it/openapi_conformance.rs +++ b/lib/crates/fabro-server/tests/it/openapi_conformance.rs @@ -6,6 +6,10 @@ clippy::manual_assert, clippy::manual_let_else )] +#![expect( + clippy::disallowed_methods, + reason = "integration tests stage fixtures with sync std::fs; test infrastructure, not Tokio-hot path" +)] use axum::body::Body; use axum::http::{Method, Request, StatusCode}; diff --git a/lib/crates/fabro-spa/src/lib.rs b/lib/crates/fabro-spa/src/lib.rs index a65b7d17c..35748003a 100644 --- a/lib/crates/fabro-spa/src/lib.rs +++ b/lib/crates/fabro-spa/src/lib.rs @@ -29,6 +29,10 @@ pub fn get(path: &str) -> Option { } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "test walks the assets/ directory with sync std::fs::read_dir to enforce a build invariant" +)] mod tests { use std::path::{Path, PathBuf}; diff --git a/lib/crates/fabro-telemetry/src/anonymous_id.rs b/lib/crates/fabro-telemetry/src/anonymous_id.rs index af9308aa3..672924cc6 100644 --- a/lib/crates/fabro-telemetry/src/anonymous_id.rs +++ b/lib/crates/fabro-telemetry/src/anonymous_id.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync read/write of the anonymous-id cache file; not on a Tokio path" +)] + use std::fs; use std::path::Path; diff --git a/lib/crates/fabro-telemetry/src/panic.rs b/lib/crates/fabro-telemetry/src/panic.rs index e2fa7ac08..3f5035c5c 100644 --- a/lib/crates/fabro-telemetry/src/panic.rs +++ b/lib/crates/fabro-telemetry/src/panic.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync read of the panic log at telemetry startup; not on a Tokio path" +)] + use std::panic::PanicHookInfo; use std::path::Path; diff --git a/lib/crates/fabro-telemetry/src/sender.rs b/lib/crates/fabro-telemetry/src/sender.rs index 0c2b531a5..26ce6384e 100644 --- a/lib/crates/fabro-telemetry/src/sender.rs +++ b/lib/crates/fabro-telemetry/src/sender.rs @@ -123,6 +123,11 @@ pub async fn upload(path: &Path) -> anyhow::Result<()> { let write_key = SEGMENT_WRITE_KEY .ok_or_else(|| anyhow::anyhow!("SEGMENT_WRITE_KEY not set at compile time"))?; + #[expect( + clippy::disallowed_methods, + reason = "telemetry uploader invoked via detached subprocess (__send_analytics); runs \ + outside the main Tokio runtime as a standalone process, so sync read is fine" + )] let content = std::fs::read_to_string(path) .with_context(|| format!("read telemetry batch {}", path.display()))?; let Some(payload) = build_segment_batch(&content) else { diff --git a/lib/crates/fabro-telemetry/src/spawn.rs b/lib/crates/fabro-telemetry/src/spawn.rs index 508e834b6..d72a6823e 100644 --- a/lib/crates/fabro-telemetry/src/spawn.rs +++ b/lib/crates/fabro-telemetry/src/spawn.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync pre-fork filesystem interaction; the whole module runs before fork/exec" +)] + /// Spawn a fully detached subprocess that survives parent exit and terminal /// close. /// diff --git a/lib/crates/fabro-test/src/lib.rs b/lib/crates/fabro-test/src/lib.rs index fa5af894b..70dce6b49 100644 --- a/lib/crates/fabro-test/src/lib.rs +++ b/lib/crates/fabro-test/src/lib.rs @@ -1,3 +1,10 @@ +#![expect( + clippy::disallowed_methods, + reason = "fabro-test: shared test infrastructure; sync std::fs throughout is intentional for \ + test fixtures, snapshots, and scratch directories. Tokio-path code under test sits \ + in other crates." +)] + use std::collections::HashMap; use std::fs::File; use std::path::{Path, PathBuf}; diff --git a/lib/crates/fabro-util/build.rs b/lib/crates/fabro-util/build.rs index 2c007b06a..aa5251751 100644 --- a/lib/crates/fabro-util/build.rs +++ b/lib/crates/fabro-util/build.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "build script: runs at compile time outside any runtime" +)] + use std::fmt::Write; use std::path::Path; use std::{env, fs}; diff --git a/lib/crates/fabro-util/src/dev_token.rs b/lib/crates/fabro-util/src/dev_token.rs index d192cd261..c7d9e2ee2 100644 --- a/lib/crates/fabro-util/src/dev_token.rs +++ b/lib/crates/fabro-util/src/dev_token.rs @@ -1,3 +1,9 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync atomic read/write of the local dev token file; not on a Tokio hot path. \ + OpenOptions::open is used for setting 0o600 mode on unix" +)] + use std::fs; #[expect( clippy::disallowed_types, diff --git a/lib/crates/fabro-util/src/run_log.rs b/lib/crates/fabro-util/src/run_log.rs index 51b8a5edd..8c1ac3063 100644 --- a/lib/crates/fabro-util/src/run_log.rs +++ b/lib/crates/fabro-util/src/run_log.rs @@ -3,6 +3,10 @@ reason = "file-backed tracing sink: sync BufWriter is intentional; writes happen on a \ dedicated per-event guard and are not in an async hot path" )] +#![expect( + clippy::disallowed_methods, + reason = "sync File::create and read_to_string for the on-disk run-log file; not on Tokio path" +)] use std::io::{self, BufWriter, Write}; use std::path::Path; diff --git a/lib/crates/fabro-util/tests/dev_token.rs b/lib/crates/fabro-util/tests/dev_token.rs index 6d177a7b9..99124b232 100644 --- a/lib/crates/fabro-util/tests/dev_token.rs +++ b/lib/crates/fabro-util/tests/dev_token.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "integration tests that exercise sync dev-token file operations" +)] + use std::fs; use fabro_util::Home; diff --git a/lib/crates/fabro-vault/src/lib.rs b/lib/crates/fabro-vault/src/lib.rs index 57e777581..121fa520d 100644 --- a/lib/crates/fabro-vault/src/lib.rs +++ b/lib/crates/fabro-vault/src/lib.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "fabro-vault: sync secret-file storage; not used on a Tokio hot path" +)] + use std::collections::HashMap; use std::path::{Component, Path, PathBuf}; use std::{fmt, io}; diff --git a/lib/crates/fabro-workflow/src/artifact.rs b/lib/crates/fabro-workflow/src/artifact.rs index 908a03e6d..eb1e370ed 100644 --- a/lib/crates/fabro-workflow/src/artifact.rs +++ b/lib/crates/fabro-workflow/src/artifact.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "FOLLOW-UP: mixed async/sync workflow artifact lifecycle. Sync std::fs::write remains at per-stage persistence points; the Tokio-path hot reads were migrated to tokio::fs in commit 9d1c0d98c. Remaining writes should follow." +)] + use std::collections::HashMap; use std::path::{Path, PathBuf}; @@ -9,6 +14,7 @@ use fabro_types::{ }; use futures::future::BoxFuture; use serde_json::Value; +use tokio::fs; use crate::context::{self, Context}; use crate::error::{Error, Result}; @@ -174,7 +180,7 @@ pub async fn sync_artifacts_to_env( } } - let content = tokio::fs::read_to_string(&local_path).await.map_err(|e| { + let content = fs::read_to_string(&local_path).await.map_err(|e| { Error::engine(format!("failed to read local artifact {local_path}: {e}")) })?; @@ -325,7 +331,7 @@ async fn resolve_explicit_file_ref(value: &str, env: &dyn Sandbox) -> Result String { /// Filenames allowed in per-node directories on the shadow branch. #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "tests write git state fixtures to disk" +)] mod tests { use std::fs; use std::sync::Arc; diff --git a/lib/crates/fabro-workflow/src/handler/agent.rs b/lib/crates/fabro-workflow/src/handler/agent.rs index 0c9008a7e..352149acb 100644 --- a/lib/crates/fabro-workflow/src/handler/agent.rs +++ b/lib/crates/fabro-workflow/src/handler/agent.rs @@ -395,6 +395,10 @@ impl Handler for AgentHandler { } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "tests persist per-iteration state fixtures" +)] mod tests { use std::sync::Arc; use std::time::Duration; diff --git a/lib/crates/fabro-workflow/src/handler/manager_loop.rs b/lib/crates/fabro-workflow/src/handler/manager_loop.rs index 7eeca7018..35b30629e 100644 --- a/lib/crates/fabro-workflow/src/handler/manager_loop.rs +++ b/lib/crates/fabro-workflow/src/handler/manager_loop.rs @@ -348,6 +348,10 @@ impl Handler for SubWorkflowHandler { } #[cfg(test)] +#[expect( + clippy::disallowed_methods, + reason = "tests persist manager-loop state fixtures" +)] mod tests { use std::collections::HashMap; use std::sync::Arc; diff --git a/lib/crates/fabro-workflow/src/operations/create.rs b/lib/crates/fabro-workflow/src/operations/create.rs index 925ff67f2..7b3e33b46 100644 --- a/lib/crates/fabro-workflow/src/operations/create.rs +++ b/lib/crates/fabro-workflow/src/operations/create.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "FOLLOW-UP: per-run `workflow create` operation; writes .fabro/ scaffolding to disk via sync std::fs" +)] + use std::collections::{BTreeMap, HashMap}; use std::path::{Path, PathBuf}; use std::sync::Arc; diff --git a/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs b/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs index 923d5cd60..0a8c9a222 100644 --- a/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs +++ b/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "FOLLOW-UP: rebuild metadata uses sync std::fs::canonicalize during async checkpoint rebuild; per-run, not per-request" +)] + use std::collections::HashMap; use std::fmt::Write; use std::path::PathBuf; diff --git a/lib/crates/fabro-workflow/src/operations/source.rs b/lib/crates/fabro-workflow/src/operations/source.rs index 40553e277..390109d1a 100644 --- a/lib/crates/fabro-workflow/src/operations/source.rs +++ b/lib/crates/fabro-workflow/src/operations/source.rs @@ -1,3 +1,8 @@ +#![expect( + clippy::disallowed_methods, + reason = "sync workflow operation loader; runs at workflow-load time" +)] + use std::path::{Path, PathBuf}; use std::sync::Arc; diff --git a/lib/crates/fabro-workflow/src/pipeline/persist.rs b/lib/crates/fabro-workflow/src/pipeline/persist.rs index c1e935b49..5b14f80f3 100644 --- a/lib/crates/fabro-workflow/src/pipeline/persist.rs +++ b/lib/crates/fabro-workflow/src/pipeline/persist.rs @@ -53,6 +53,7 @@ pub(crate) async fn load_from_store( } #[cfg(test)] +#[expect(clippy::disallowed_methods, reason = "tests stage pipeline fixtures")] mod tests { use std::collections::HashMap; use std::path::PathBuf; diff --git a/lib/crates/fabro-workflow/src/pipeline/transform.rs b/lib/crates/fabro-workflow/src/pipeline/transform.rs index ced3b2571..f2ff3c459 100644 --- a/lib/crates/fabro-workflow/src/pipeline/transform.rs +++ b/lib/crates/fabro-workflow/src/pipeline/transform.rs @@ -53,6 +53,7 @@ pub fn transform(parsed: Parsed, options: &TransformOptions) -> Result) -> Result { let path = path.as_ref(); + #[expect( + clippy::disallowed_methods, + reason = "twin test harness: sync fixture load from disk; not on a Tokio hot path" + )] let contents = fs::read_to_string(path) .map_err(|err| format!("failed to read fixture {}: {err}", path.display()))?; serde_json::from_str(&contents)