use std::path::Path;
use crate::rules::{Evidence, Examine, Finding, Rule, Stance, Trend};
use crate::shell::Parsed;
pub const RULE: Rule = Rule {
id: "code-read-not-lsp",
default_stance: Stance::Advise,
max_stance: Stance::Deny,
evidence: Evidence {
per_1000: 21.2,
measured: "2026-10-11",
trend: Trend::Flat(5),
},
examine: Examine::Legacy(examine),
confirm: None,
};
fn examine(_parsed: &Parsed) -> Option<Finding> {
None
}
const SMALL: u64 = 4096;
pub fn applies(path: &Path, window: &str) -> Option<u64> {
if window != "full" || !crate::lsp::is_code(&path.to_string_lossy()) {
return None;
}
if crate::rules::read_unbounded_large::exempt(path) {
return None;
}
let meta = std::fs::metadata(path).ok()?;
(meta.is_file() && meta.len() > SMALL).then_some(meta.len())
}
pub fn phrase(shown: &str, bytes: u64) -> (String, String) {
(
format!(
"`{shown}` is code, {} KB, Read whole: every line lands in the context, \
and every later turn carries it.",
bytes.div_ceil(1024)
),
"Ask LSP first: documentSymbol for the outline; goToDefinition, findReferences \
or hover for one symbol. Then Read with offset/limit around that line; to edit, \
Read the window you change. On an LSP error, apply its fix or say so."
.to_string(),
)
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
fn scratch(name: &str) -> PathBuf {
let dir =
std::env::temp_dir().join(format!("amont-agent-crnl-{}-{name}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).expect("scratch dir");
dir
}
#[test]
fn a_whole_read_of_code_over_4_kb_applies() {
let dir = scratch("applies");
let rs = dir.join("lib.rs");
std::fs::write(&rs, "x".repeat(5000)).expect("file");
assert_eq!(applies(&rs, "full"), Some(5000));
assert_eq!(applies(&rs, "0:40"), None, "a window");
std::fs::write(&rs, "x".repeat(4096)).expect("file");
assert_eq!(applies(&rs, "full"), None, "the floor is exclusive");
let md = dir.join("notes.md");
std::fs::write(&md, "x".repeat(9000)).expect("file");
assert_eq!(applies(&md, "full"), None, "not code");
let plan = dir.join("docs/plans/x.ts");
std::fs::create_dir_all(plan.parent().expect("parent")).expect("dirs");
std::fs::write(&plan, "x".repeat(9000)).expect("file");
assert_eq!(
applies(&plan, "full"),
None,
"exempt like read-unbounded-large"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn the_advice_stays_under_sixty_words() {
let (reason, remedy) = phrase("crates/relais/src/runner/mod.rs", 412_000);
let words = reason.split_whitespace().count() + remedy.split_whitespace().count();
assert!(words <= 60, "{words} words");
assert!(reason.contains("403 KB"), "{reason}");
}
}