fix(TASK-046): make inspection flags look-don't-touch and pin the contracts
- Skip the builtins bootstrap, function-binary build, and tool-scope rebuild for inspection-only runs (--info/--list-*/--mcp-list/ --list-secrets without --agent/--role): a pristine COYOTE_CONFIG_DIR now stays byte-empty (was 269 files written before dispatch) - Tolerate a model-less default config on the inspection surface via AppConfig::from_config_lenient + Model::default() in bootstrap; strict 'No available model' behavior unchanged for the REPL path and all mutating mcp/vault flags - --list-secrets degrades to an rc=0 informational message in sandbox mode (mutating vault flags still fail there), pinned by test - Add the 041.7-4 regression test: a malformed COYOTE_<NAME>_GLOBAL_TOOLS env override logs the debug message and leaves global_tools untouched - Adopt scripts/usage-probe-inspection.sh (10 flags: rc=0 + zero writes, output required only for --info) and add binary-level tests/inspection_flags.rs, incl. the agent+--info still-bootstraps pin
This commit is contained in:
9 files changed
+389
-31
No files matched your search
+24
-18
@@ -9,25 +9,30 @@ struct TestLogCollector;
|
||||
static WARN_MESSAGES: OnceLock<Mutex<Vec<String>>> = OnceLock::new();
|
||||
static DEBUG_MESSAGES: OnceLock<Mutex<Vec<String>>> = OnceLock::new();
|
||||
|
||||
/// Debug capture is restricted to the hooks module so the buffer is not
|
||||
/// flooded by debug output from the rest of the crate. Matching is
|
||||
/// module-boundary-aware: only this exact module or its `::` submodules
|
||||
/// Debug capture is restricted to the modules whose tests assert on debug
|
||||
/// output so the buffer is not flooded by the rest of the crate. Matching is
|
||||
/// module-boundary-aware: only these exact modules or their `::` submodules
|
||||
/// qualify, so a sibling like `hooks_registry` would not.
|
||||
const DEBUG_TARGET_PREFIX: &str = concat!(env!("CARGO_CRATE_NAME"), "::hooks");
|
||||
const DEBUG_TARGET_PREFIXES: [&str; 2] = [
|
||||
concat!(env!("CARGO_CRATE_NAME"), "::hooks"),
|
||||
concat!(env!("CARGO_CRATE_NAME"), "::config::agent"),
|
||||
];
|
||||
|
||||
fn captures_warn(metadata: &Metadata) -> bool {
|
||||
metadata.level() <= Level::Warn
|
||||
}
|
||||
|
||||
fn captures_hooks_debug(metadata: &Metadata) -> bool {
|
||||
fn captures_module_debug(metadata: &Metadata) -> bool {
|
||||
if metadata.level() != Level::Debug {
|
||||
return false;
|
||||
}
|
||||
let target = metadata.target();
|
||||
target == DEBUG_TARGET_PREFIX
|
||||
|| target
|
||||
.strip_prefix(DEBUG_TARGET_PREFIX)
|
||||
.is_some_and(|rest| rest.starts_with("::"))
|
||||
DEBUG_TARGET_PREFIXES.iter().any(|prefix| {
|
||||
target == *prefix
|
||||
|| target
|
||||
.strip_prefix(prefix)
|
||||
.is_some_and(|rest| rest.starts_with("::"))
|
||||
})
|
||||
}
|
||||
|
||||
/// Every warn- or error-level message captured since the collector was
|
||||
@@ -38,9 +43,9 @@ pub(crate) fn warn_messages() -> &'static Mutex<Vec<String>> {
|
||||
WARN_MESSAGES.get_or_init(Mutex::default)
|
||||
}
|
||||
|
||||
/// Every debug-level message from the hooks module captured since the
|
||||
/// collector was installed. Same marker-filtering discipline as
|
||||
/// [`warn_messages`].
|
||||
/// Every debug-level message from the [`DEBUG_TARGET_PREFIXES`] modules
|
||||
/// captured since the collector was installed. Same marker-filtering
|
||||
/// discipline as [`warn_messages`].
|
||||
pub(crate) fn debug_messages() -> &'static Mutex<Vec<String>> {
|
||||
DEBUG_MESSAGES.get_or_init(Mutex::default)
|
||||
}
|
||||
@@ -66,7 +71,7 @@ fn snapshot_of(buffer: &Mutex<Vec<String>>) -> Vec<String> {
|
||||
|
||||
impl Log for TestLogCollector {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
captures_warn(metadata) || captures_hooks_debug(metadata)
|
||||
captures_warn(metadata) || captures_module_debug(metadata)
|
||||
}
|
||||
|
||||
// A test that panics while holding a buffer poisons its mutex; the logger
|
||||
@@ -77,7 +82,7 @@ impl Log for TestLogCollector {
|
||||
.lock()
|
||||
.unwrap_or_else(|poisoned| poisoned.into_inner())
|
||||
.push(record.args().to_string());
|
||||
} else if captures_hooks_debug(record.metadata()) {
|
||||
} else if captures_module_debug(record.metadata()) {
|
||||
debug_messages()
|
||||
.lock()
|
||||
.unwrap_or_else(|poisoned| poisoned.into_inner())
|
||||
@@ -89,10 +94,11 @@ impl Log for TestLogCollector {
|
||||
}
|
||||
|
||||
/// Installs the process-wide log collector capturing warn-level messages and
|
||||
/// hooks-module debug messages. `log::set_logger` accepts one logger per
|
||||
/// process, so every test that captures either stream must install through
|
||||
/// this shared entry point. The max level is Debug so the hooks debug capture
|
||||
/// sees its records; warn capture is unaffected.
|
||||
/// debug messages from the [`DEBUG_TARGET_PREFIXES`] modules.
|
||||
/// `log::set_logger` accepts one logger per process, so every test that
|
||||
/// captures either stream must install through this shared entry point. The
|
||||
/// max level is Debug so the debug capture sees its records; warn capture is
|
||||
/// unaffected.
|
||||
pub(crate) fn install_log_collector() {
|
||||
static INSTALL: Once = Once::new();
|
||||
INSTALL.call_once(|| {
|
||||
|
||||
Reference in new issue
Block a user