style: Cleaned up unnecessary path prefixes, imports, stray scripts, and some documentation

This commit is contained in:
Dark-Alex-17 committed 2026-09-17 12:00:16 -06:00
1 parent 5bd1174ddd
commit fcdcb303a3
26 files changed
+132 -1286

No files matched your search

+64 -116
View File
@@ -14,7 +14,7 @@ use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use std::process::Stdio;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use std::time::{Duration, SystemTime};
use tokio::io::AsyncWriteExt;
use tokio::process::Command;
use tokio::sync::Notify;
@@ -60,16 +60,10 @@ pub fn install_builtin_hooks(mode: InstallMode, sticky: &mut StickyMode) -> Resu
}
}
// Only direct children of the hooks dir reconcile through the builtin
// manifest — the only shape it tracks — so a hook a release stops
// shipping is removed while user scripts in the same directory are
// never touched.
let shipped: BTreeSet<String> = HookAssets::iter()
.map(|file| file.as_ref().to_string())
.filter(|name| !name.contains('/'))
.collect();
// Reconciliation is best-effort housekeeping: a failure here must not
// abort startup (install_builtins), matching the role/agent-side policy.
if let Err(err) =
builtin_manifest::reconcile_builtin_dir(&paths::hooks_dir(), &shipped, &written)
{
@@ -79,8 +73,6 @@ pub fn install_builtin_hooks(mode: InstallMode, sticky: &mut StickyMode) -> Resu
);
}
// Both sides canonicalize so an override that merely spells the default
// path differently (symlinks, `..`, trailing components) does not warn.
let hooks_dir = canonical_or_original(&paths::hooks_dir());
let default_hooks_dir = canonical_or_original(&paths::config_dir().join("hooks"));
if wrote_any && hooks_dir != default_hooks_dir {
@@ -174,9 +166,6 @@ impl HookEvent {
}
}
/// Every event, in declaration order. Dotted names stay single-sourced
/// through `as_str`; this exists so whitelist-glob validation can tell a
/// real `<event>.*` from a typo over a nonexistent event.
const ALL: [HookEvent; 26] = [
HookEvent::TurnStarted,
HookEvent::TurnCompleted,
@@ -260,12 +249,6 @@ fn resolve_hooks(
let event_name = event.as_str();
let mut resolved = Vec::new();
// Escalations are a root/user-level concern: the user wires these hooks
// to hear about children asking for help, so requiring every agent to
// whitelist them through `global_hooks` would silence exactly the events
// they were set up for. The per-agent gate never applies here (this also
// skips the unknown-entry diagnostics: whitelisting an escalation hook is
// harmless and needs no warning).
let agent_gate = match event {
HookEvent::EscalationRaised | HookEvent::EscalationAnswered => None,
_ => agent_gate,
@@ -296,12 +279,6 @@ fn resolve_hooks(
{
let prefix = format!("{event_name}.");
for entry in gate {
// Entries containing `*` are grammatical only in the wildcard
// forms `*`, `*.*`, `*.<name>`, and `<event>.*` over a real
// event. Anything else (`tool.st*`, `ag*.completed.mark`) is a
// typo that admits nothing on any event and never extends a full
// event name, so the prefix check below would stay silent about
// it — flag it here instead.
if entry.contains('*') {
if !entry_is_valid_wildcard(entry) {
debug!(
@@ -341,12 +318,6 @@ fn resolve_hooks(
resolved
}
/// Whitelist grammar for a `global_hooks` entry: `<event>.<name>` exact,
/// `<event>.*` (every hook of that event), `*.<name>` (that hook name on
/// every event), and `*` / `*.*` (everything). Event names contain dots
/// while hook names never do, so `*.<name>` compares everything after the
/// leading `*.` against the hook name alone. Anything else containing `*`
/// (a partial glob like `tool.st*`) is not a wildcard and admits nothing.
fn gate_entry_admits(entry: &str, event_name: &str, hook_name: &str, full_name: &str) -> bool {
if entry == full_name || entry == "*" || entry == "*.*" {
return true;
@@ -357,12 +328,6 @@ fn gate_entry_admits(entry: &str, event_name: &str, hook_name: &str, full_name:
entry.strip_suffix(".*") == Some(event_name)
}
/// True when a `*`-containing gate entry is one of the wildcard forms
/// `gate_entry_admits` understands. `*.<name>` takes a literal hook name
/// (globbing inside the name is not supported), and `<event>.*` requires a
/// real event: event names contain dots, so e.g. `tool.*` names the
/// nonexistent event `tool` rather than a prefix over `tool.started` and
/// `tool.completed`.
fn entry_is_valid_wildcard(entry: &str) -> bool {
if entry == "*" || entry == "*.*" {
return true;
@@ -520,11 +485,6 @@ fn base_envs(event: HookEvent, ctx: &RequestContext) -> Vec<(String, String)> {
)
}
/// The single construction site for the base env set every hook receives.
/// [`base_envs`] feeds it from a live context; detached call sites that
/// dispatch pre-resolved snapshots after their context is gone pass the
/// names they captured with the snapshot. The timestamp is taken here, at
/// fire time, so it reflects when the event actually happened.
pub(crate) fn base_envs_parts(
event: HookEvent,
session_name: Option<&str>,
@@ -596,12 +556,6 @@ fn payload_dir() -> PathBuf {
const STALE_PAYLOAD_MAX_AGE: Duration = Duration::from_secs(24 * 60 * 60);
/// Removes payload files left in the shared payload directory by crashed
/// processes — `run_hook` deletes its own file after the hook exits, so
/// anything older than [`STALE_PAYLOAD_MAX_AGE`] is an orphan. Runs once per
/// startup from `install_builtins`. Only exact `coyote-hook-*.json` names are
/// candidates; the directory is shared, so nothing else may ever be touched.
/// Best-effort: failures log at debug and never abort startup.
pub fn sweep_stale_payload_files() {
sweep_payload_files_older_than(STALE_PAYLOAD_MAX_AGE);
}
@@ -618,7 +572,7 @@ fn sweep_payload_files_older_than(max_age: Duration) {
return;
}
};
let now = std::time::SystemTime::now();
let now = SystemTime::now();
for entry in entries.flatten() {
let name = entry.file_name();
let Some(name) = name.to_str() else {
@@ -643,6 +597,7 @@ fn sweep_payload_files_older_than(max_age: Duration) {
if !stale {
continue;
}
match std::fs::remove_file(entry.path()) {
Ok(()) => debug!("Removed stale hook payload file {}", entry.path().display()),
Err(err) => debug!(
@@ -840,9 +795,11 @@ mod tests {
use super::*;
use crate::config::conflict::prompt_script;
use crate::config::{AppConfig, AppState, Role, WorkingMode};
use crate::{testing, utils};
use serial_test::serial;
use std::env;
use std::sync::Arc;
use std::time::UNIX_EPOCH;
fn hooks_map(event: &str, defs: &[(&str, &str)]) -> HooksMap {
let defs = defs
@@ -871,22 +828,19 @@ mod tests {
/// Points the hooks dir at a fresh temp directory for the guard's
/// lifetime and removes it on drop. Tests using it must serialize.
struct HooksDirGuard {
_env: crate::testing::EnvVarGuard,
_env: testing::EnvVarGuard,
root: PathBuf,
}
impl HooksDirGuard {
fn new(label: &str) -> Self {
let unique = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
let unique = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let root = env::temp_dir().join(format!("coyote-{label}-{unique}"));
Self {
_env: crate::testing::EnvVarGuard::set(
crate::utils::get_env_name("hooks_dir"),
&root,
),
_env: testing::EnvVarGuard::set(utils::get_env_name("hooks_dir"), &root),
root,
}
}
@@ -938,8 +892,8 @@ mod tests {
}
fn fresh_payload_dir(label: &str) -> PathBuf {
let unique = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
let unique = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let dir = env::temp_dir().join(format!("coyote-{label}-{unique}"));
@@ -1026,13 +980,13 @@ mod tests {
#[test]
#[serial]
fn install_builtin_hooks_installs_executable_scripts_and_honors_force() {
let env_name = crate::utils::get_env_name("hooks_dir");
let unique = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
let env_name = utils::get_env_name("hooks_dir");
let unique = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let root = env::temp_dir().join(format!("coyote-hooks-install-{unique}"));
let env_guard = crate::testing::EnvVarGuard::set(&env_name, &root);
let env_guard = testing::EnvVarGuard::set(&env_name, &root);
// Capture every outcome first and assert only after cleanup: the
// guard restores the env var even on panic, but the temp dir removal
@@ -1222,16 +1176,16 @@ mod tests {
#[test]
#[serial]
fn install_builtin_hooks_respelled_default_hooks_dir_does_not_warn() {
crate::testing::install_log_collector();
let guard = crate::testing::TestConfigDirGuard::new("hooks-warn-respelled");
testing::install_log_collector();
let guard = testing::TestConfigDirGuard::new("hooks-warn-respelled");
let guard_path_display = guard.path.display().to_string();
let env_name = crate::utils::get_env_name("hooks_dir");
let env_name = utils::get_env_name("hooks_dir");
// An override that reaches the default `config_dir()/hooks` through a
// `..` hop is the default path in disguise: the canonicalized compare
// must stay quiet even though the strings differ.
let alias = guard.path.join("hooks").join("..").join("hooks");
let env_guard = crate::testing::EnvVarGuard::set(&env_name, &alias);
let env_guard = testing::EnvVarGuard::set(&env_name, &alias);
install_builtin_hooks(InstallMode::Skip, &mut StickyMode::None).unwrap();
drop(env_guard);
@@ -1249,7 +1203,7 @@ mod tests {
let link = guard.path.join("hooks-link");
std::os::unix::fs::symlink(guard.path.join("hooks"), &link).unwrap();
std::fs::remove_file(&default_notify).unwrap();
let _env_guard = crate::testing::EnvVarGuard::set(&env_name, &link);
let _env_guard = testing::EnvVarGuard::set(&env_name, &link);
install_builtin_hooks(InstallMode::Skip, &mut StickyMode::None).unwrap();
assert!(
default_notify.is_file(),
@@ -1259,7 +1213,7 @@ mod tests {
// The warn buffer is process-global, so scope the check to messages
// naming this test's unique directory.
let warns = crate::testing::warn_snapshot();
let warns = testing::warn_snapshot();
assert!(
warns.iter().all(|message| {
!(message.contains("overrides the hooks dir")
@@ -1272,13 +1226,10 @@ mod tests {
#[test]
#[serial]
fn install_builtin_hooks_genuine_override_warns_when_default_dir_is_missing() {
crate::testing::install_log_collector();
let guard = crate::testing::TestConfigDirGuard::new("hooks-warn-genuine");
testing::install_log_collector();
let guard = testing::TestConfigDirGuard::new("hooks-warn-genuine");
let override_dir = guard.path.join("elsewhere-hooks");
let _env_guard = crate::testing::EnvVarGuard::set(
crate::utils::get_env_name("hooks_dir"),
&override_dir,
);
let _env_guard = testing::EnvVarGuard::set(utils::get_env_name("hooks_dir"), &override_dir);
install_builtin_hooks(InstallMode::Skip, &mut StickyMode::None).unwrap();
@@ -1294,7 +1245,7 @@ mod tests {
"scripts must land in the override dir, so wrote_any was true"
);
let override_display = override_dir.display().to_string();
let warns = crate::testing::warn_snapshot();
let warns = testing::warn_snapshot();
assert!(
warns.iter().any(|message| {
message.contains("overrides the hooks dir") && message.contains(&override_display)
@@ -1683,7 +1634,7 @@ mod tests {
#[test]
fn wildcard_whitelist_entries_skip_unknown_entry_diagnostics() {
crate::testing::install_log_collector();
testing::install_log_collector();
let global = hooks_map("turn.started", &[("a", "cmd-a")]);
let gate = vec![
"turn.started.*".to_string(),
@@ -1701,7 +1652,7 @@ mod tests {
);
assert_eq!(names(&resolved), ["a"]);
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(
debugs
.iter()
@@ -1712,7 +1663,7 @@ mod tests {
#[test]
fn partial_glob_whitelist_entries_stay_flagged_as_unknown() {
crate::testing::install_log_collector();
testing::install_log_collector();
let global = hooks_map("turn.started", &[("a", "cmd-a")]);
let gate = vec!["turn.started.st*-marker-k4w".to_string()];
@@ -1725,7 +1676,7 @@ mod tests {
);
assert!(resolved.is_empty(), "an invalid glob must admit nothing");
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(debugs.iter().any(|message| {
message.contains("turn.started.st*-marker-k4w")
&& message.contains("for agent 'glob-flag-agent-k4w'")
@@ -1734,7 +1685,7 @@ mod tests {
#[test]
fn partial_glob_over_event_prefix_logs_invalid_glob_diagnostic() {
crate::testing::install_log_collector();
testing::install_log_collector();
let global = hooks_map("tool.started", &[("st", "cmd-st")]);
// `tool.st*` extends no full event name (`tool` is not an event), so
// the event-prefix diagnostic can never see it; the glob check must
@@ -1750,7 +1701,7 @@ mod tests {
);
assert!(resolved.is_empty(), "a partial glob admits nothing");
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(debugs.iter().any(|message| {
message.contains("tool.st*") && message.contains("for agent 'prefix-glob-agent-t7c'")
}));
@@ -1758,7 +1709,7 @@ mod tests {
#[test]
fn inner_glob_entries_log_invalid_glob_diagnostic() {
crate::testing::install_log_collector();
testing::install_log_collector();
let global = hooks_map("agent.completed", &[("mark", "cmd-mark")]);
let gate = vec!["ag*.completed.mark".to_string()];
@@ -1771,7 +1722,7 @@ mod tests {
);
assert!(resolved.is_empty(), "an inner glob admits nothing");
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(debugs.iter().any(|message| {
message.contains("ag*.completed.mark")
&& message.contains("for agent 'inner-glob-agent-m2r'")
@@ -1780,7 +1731,7 @@ mod tests {
#[test]
fn unknown_whitelist_entries_log_debug_and_stay_inert() {
crate::testing::install_log_collector();
testing::install_log_collector();
let global = hooks_map("turn.started", &[("a", "cmd-a")]);
let gate = vec![
"turn.started.a".to_string(),
@@ -1796,7 +1747,7 @@ mod tests {
);
assert_eq!(names(&resolved), ["a"]);
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(debugs.iter().any(|message| {
message.contains("turn.started.nonexistent-marker-xyz")
&& message.contains("for agent 'gate-agent-xyz'")
@@ -1805,7 +1756,7 @@ mod tests {
#[test]
fn empty_global_map_short_circuits_whitelist_diagnostics() {
crate::testing::install_log_collector();
testing::install_log_collector();
let global = HooksMap::default();
let role = hooks_map("turn.started", &[("role-only", "role-cmd")]);
let gate = vec!["turn.started.x-shortcircuit-marker-p3q".to_string()];
@@ -1819,7 +1770,7 @@ mod tests {
);
assert_eq!(names(&resolved), ["role-only"]);
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(
debugs
.iter()
@@ -2084,12 +2035,12 @@ mod tests {
#[tokio::test(start_paused = true)]
#[serial]
async fn drain_pending_logs_when_the_timeout_expires() {
crate::testing::install_log_collector();
testing::install_log_collector();
let pending = SpawnAckGuard::register();
drain_pending(Duration::from_millis(50)).await;
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(
debugs
.iter()
@@ -2116,7 +2067,7 @@ mod tests {
#[test]
#[serial]
fn fire_resolved_without_runtime_skips_instead_of_panicking() {
crate::testing::install_log_collector();
testing::install_log_collector();
let hook = ResolvedHook {
name: "no-runtime-marker-a7c".to_string(),
full_name: "turn.failed.no-runtime-marker-a7c".to_string(),
@@ -2126,7 +2077,7 @@ mod tests {
fire_resolved(HookEvent::TurnFailed, vec![hook], Vec::new(), &[], None);
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(debugs.iter().any(|message| {
message.contains("no-runtime-marker-a7c")
&& message.contains("no Tokio runtime on this thread")
@@ -2136,7 +2087,7 @@ mod tests {
#[test]
#[serial]
fn fire_without_runtime_skips_instead_of_panicking() {
crate::testing::install_log_collector();
testing::install_log_collector();
let ctx = ctx_with_global_hooks(hooks_map(
"turn.failed",
&[("fire-no-runtime-marker-b8d", "true")],
@@ -2144,7 +2095,7 @@ mod tests {
fire(HookEvent::TurnFailed, &ctx, &[], None);
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(debugs.iter().any(|message| {
message.contains("fire-no-runtime-marker-b8d")
&& message.contains("no Tokio runtime on this thread")
@@ -2153,7 +2104,7 @@ mod tests {
#[test]
fn debug_capture_excludes_non_hooks_targets() {
crate::testing::install_log_collector();
testing::install_log_collector();
let hook = ResolvedHook {
name: "included-marker-k2v".to_string(),
full_name: "turn.failed.included-marker-k2v".to_string(),
@@ -2171,7 +2122,7 @@ mod tests {
);
fire_resolved(HookEvent::TurnFailed, vec![hook], Vec::new(), &[], None);
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(
debugs
.iter()
@@ -2192,9 +2143,9 @@ mod tests {
#[test]
#[serial]
fn debug_capture_recovers_after_buffer_poisoning() {
crate::testing::install_log_collector();
testing::install_log_collector();
std::thread::spawn(|| {
let _held = crate::testing::debug_messages().lock().unwrap();
let _held = testing::debug_messages().lock().unwrap();
panic!("poison the debug buffer");
})
.join()
@@ -2208,7 +2159,7 @@ mod tests {
};
fire_resolved(HookEvent::TurnFailed, vec![hook], Vec::new(), &[], None);
let mut debugs = crate::testing::debug_messages()
let mut debugs = testing::debug_messages()
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
assert!(debugs.iter().any(|message| {
@@ -2255,7 +2206,7 @@ mod tests {
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn spawn_failure_removes_the_payload_file() {
crate::testing::install_log_collector();
testing::install_log_collector();
let guard = TestConfigDirGuard::new("hooks-dispatch");
let payload_dir = guard.path.join("payloads");
create_dir_all(&payload_dir).unwrap();
@@ -2281,12 +2232,12 @@ mod tests {
// does the empty directory below demonstrate the orphan unlink
// rather than a payload file that never existed.
wait_for("spawn failure log", || {
crate::testing::debug_snapshot().iter().any(|message| {
testing::debug_snapshot().iter().any(|message| {
message.contains("Failed to spawn hook 'tool.started.spawnfail-marker-c9d'")
})
})
.await;
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(debugs.iter().all(|message| {
!(message.contains("spawnfail-marker-c9d")
&& message.contains("Failed to write payload file"))
@@ -2305,10 +2256,10 @@ mod tests {
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn error_paths_log_debug_only() {
crate::testing::install_log_collector();
testing::install_log_collector();
let empty_cwd = env::temp_dir();
let unique = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
let unique = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let bad_cwd = env::temp_dir().join(format!("coyote-badcwd-marker-f5b-{unique}"));
@@ -2330,7 +2281,7 @@ mod tests {
fire_resolved(HookEvent::TurnFailed, hooks, Vec::new(), &[], None);
drain_pending(Duration::from_secs(10)).await;
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
let empty_cwd_display = empty_cwd.display().to_string();
assert!(debugs.iter().any(|message| {
message.contains("empty-marker-f5b")
@@ -2342,7 +2293,7 @@ mod tests {
message.contains("Failed to spawn hook 'turn.failed.badcwd-marker-f5b'")
&& message.contains(bad_cwd_display.as_str())
}));
let warns = crate::testing::warn_snapshot();
let warns = testing::warn_snapshot();
assert!(warns.iter().all(|message| !message.contains("marker-f5b")));
}
}
@@ -2488,7 +2439,7 @@ mod tests {
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn failing_hook_never_disturbs_the_engine() {
crate::testing::install_log_collector();
testing::install_log_collector();
let guard = TestConfigDirGuard::new("hooks-dispatch");
let first = guard.path.join("first-out");
let second = guard.path.join("second-out");
@@ -2514,7 +2465,7 @@ mod tests {
);
wait_for("second hook output", || second.exists()).await;
let warns = crate::testing::warn_snapshot();
let warns = testing::warn_snapshot();
assert!(
warns
.iter()
@@ -2558,7 +2509,7 @@ mod tests {
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn payload_write_failure_still_runs_hook_without_payload_env() {
crate::testing::install_log_collector();
testing::install_log_collector();
let guard = TestConfigDirGuard::new("hooks-dispatch");
let out = guard.path.join("env-out");
let _payload_dir = PayloadDirOverrideGuard::new(guard.path.join("missing-payload-dir"));
@@ -2582,7 +2533,7 @@ mod tests {
.lines()
.all(|line| !line.starts_with("COYOTE_HOOK_PAYLOAD_FILE="))
);
let debugs = crate::testing::debug_snapshot();
let debugs = testing::debug_snapshot();
assert!(debugs.iter().any(|message| message.contains(
"Failed to write payload file for hook 'tool.started.payload-fallback-marker'"
)));
@@ -2674,7 +2625,7 @@ mod tests {
)
.unwrap();
ctx.use_agent(&app, agent_name, None, crate::utils::create_abort_signal())
ctx.use_agent(&app, agent_name, None, utils::create_abort_signal())
.await
.unwrap();
@@ -2818,18 +2769,15 @@ mod tests {
#[test]
#[serial]
fn install_builtin_hooks_lands_under_a_backslashed_override() {
let unique = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
let unique = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let hooks_dir = PathBuf::from(format!(
"{}\\coyote-win-bs-{unique}\\hooks",
env::temp_dir().display()
));
let _env = crate::testing::EnvVarGuard::set(
crate::utils::get_env_name("hooks_dir"),
&hooks_dir,
);
let _env = testing::EnvVarGuard::set(utils::get_env_name("hooks_dir"), &hooks_dir);
let result = install_builtin_hooks(InstallMode::Skip, &mut StickyMode::None);