spec_driven_docs/gates/
agents_digest_size.rs1use crate::domain::finding::Finding;
9use crate::domain::rule_id::RuleId;
10use crate::gates::{GateCtx, GateResult, Violation, line_count, read_text, walk_files};
11
12pub const CITES: &[RuleId] = &[RuleId::AuthorInstructionsStayWithinBudget];
14
15pub fn run(ctx: &GateCtx, _files: &[String]) -> GateResult {
21 if !ctx.path("AGENTS.md").is_file() {
22 return Ok(vec![Violation::Layout("no root AGENTS.md".to_string())]);
23 }
24 let mut violations = Vec::new();
25 for file in walk_files(ctx) {
26 if file.file_name() != Some("AGENTS.md") {
27 continue;
28 }
29 let cap = if file == "./AGENTS.md" { 100 } else { 150 };
30 if line_count(&read_text(ctx, &file)?) > cap {
31 violations.push(Violation::Finding(Finding::on_file(
32 RuleId::AuthorInstructionsStayWithinBudget,
33 file,
34 "",
35 )));
36 }
37 }
38 Ok(violations)
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 fn write(dir: &tempfile::TempDir, path: &str, lines: usize) {
46 let path = dir.path().join(path);
47 std::fs::create_dir_all(path.parent().unwrap()).unwrap();
48 std::fs::write(path, "line\n".repeat(lines)).unwrap();
49 }
50
51 fn run_in(dir: &tempfile::TempDir) -> Vec<String> {
52 let ctx = GateCtx::new(dir.path().to_str().unwrap());
53 run(&ctx, &[])
54 .unwrap()
55 .iter()
56 .map(ToString::to_string)
57 .collect()
58 }
59
60 #[test]
61 fn accepts_digests_within_budget() {
62 let dir = tempfile::tempdir().unwrap();
63 write(&dir, "AGENTS.md", 100);
64 write(&dir, "method/AGENTS.md", 150);
65 assert!(run_in(&dir).is_empty());
66 }
67
68 #[test]
69 fn rejects_a_root_digest_over_its_budget() {
70 let dir = tempfile::tempdir().unwrap();
71 write(&dir, "AGENTS.md", 101);
72 let out = run_in(&dir);
73 assert_eq!(
74 out,
75 vec!["FAIL docs-format:author-instructions-stay-within-budget ./AGENTS.md".to_string()]
76 );
77 }
78
79 #[test]
80 fn ignores_vendored_digests() {
81 let dir = tempfile::tempdir().unwrap();
82 write(&dir, "AGENTS.md", 1);
83 write(&dir, "node_modules/pkg/AGENTS.md", 400);
84 assert!(run_in(&dir).is_empty());
85 }
86
87 #[test]
88 fn a_missing_root_digest_is_a_layout_failure() {
89 let dir = tempfile::tempdir().unwrap();
90 assert_eq!(run_in(&dir), vec!["FAIL no root AGENTS.md".to_string()]);
91 }
92}