1pub(crate) mod agents_conformance;
2pub(crate) mod bench;
3pub(crate) mod canon;
4pub(crate) mod check;
5pub(crate) mod codemod;
6pub(crate) mod config_cmd;
7pub(crate) mod conformance_helper;
8pub(crate) mod connect;
9pub(crate) mod connector;
10pub(crate) mod connector_schema_codegen;
11pub(crate) mod contracts;
12pub(crate) mod counterfactual;
13pub(crate) mod crystallize;
14pub mod demo;
15pub(crate) mod dev;
16pub(crate) mod diagnostics_catalog;
17pub(crate) mod dispatch_explain;
18pub(crate) mod doc;
19pub(crate) mod doctor;
20pub(crate) mod dump_highlight_keywords;
21pub(crate) mod dump_protocol_artifacts;
22pub(crate) mod dump_trigger_quickref;
23pub(crate) mod embedded_report;
24pub mod eval_coding_agent;
25pub(crate) mod eval_coding_agent_preset;
26pub mod eval_context;
27pub(crate) mod eval_model_selector;
28pub mod eval_prompt;
29pub(crate) mod eval_prompt_context;
30pub(crate) mod eval_scope_triage;
31pub mod eval_skill_gate;
32pub(crate) mod eval_tool_calls;
33pub(crate) mod explain;
34pub(crate) mod fix;
35pub mod flow;
36pub(crate) mod graph;
37pub(crate) mod guard;
38pub(crate) mod hardware;
39pub(crate) mod host;
40pub(crate) mod init;
41pub(crate) mod json_schemas;
42pub(crate) mod local;
43pub(crate) mod local_readiness;
44pub(crate) mod mcp;
45pub(crate) mod merge_captain;
46pub(crate) mod merge_captain_mock;
47pub(crate) mod models;
48pub mod orchestrator;
49pub mod pack;
50pub(crate) mod package_scaffold;
51pub(crate) mod parse_tokens;
52pub mod persona;
53pub mod persona_activation;
54pub mod persona_apply;
55pub mod persona_dispatch;
56pub mod persona_doctor;
57pub mod persona_prompt;
58pub mod persona_scaffold;
59pub mod persona_supervision;
60#[cfg(test)]
61pub(crate) mod persona_test_support;
62pub(crate) mod pg_codegen;
63pub mod playground;
64pub(crate) mod portal;
65pub mod precompile;
66pub(crate) mod protocol_conformance;
67pub(crate) mod provider;
68pub(crate) mod provider_capabilities;
69pub(crate) mod provider_limits;
70pub(crate) mod provider_report;
71pub(crate) mod provider_support;
72pub(crate) mod provider_tool_calibrate;
73pub(crate) mod providers;
74pub(crate) use provider_tool_calibrate::run as run_tool_calibrate;
75pub(crate) mod quickstart;
76pub(crate) mod repl;
77pub(crate) mod replay;
78pub(crate) mod routes;
79pub(crate) mod rule;
80pub(crate) mod rules_cli;
81pub mod run;
82pub(crate) mod runs_export_training;
83pub(crate) mod scaffold_common;
84pub(crate) mod scan;
85pub(crate) mod serve;
86pub(crate) mod session;
87pub(crate) mod skill;
88pub(crate) mod skills;
89pub(crate) mod supervisor;
90pub(crate) mod test;
91pub mod test_bench;
92pub(crate) mod test_worker;
93pub mod time;
94pub(crate) mod tool;
95pub(crate) mod tool_mode_parity;
96pub(crate) mod trace;
97pub mod trigger;
98pub(crate) mod trust;
99pub(crate) mod try_cmd;
100pub(crate) mod upgrade;
101pub(crate) mod usage;
102pub(crate) mod viz;
103pub(crate) mod workflow;
104
105use std::path::{Path, PathBuf};
106
107use ignore::WalkBuilder;
108
109pub(crate) fn nearest_rank_percentile(sorted: &[u64], quantile: f64) -> Option<u64> {
113 if sorted.is_empty() {
114 return None;
115 }
116 let rank = ((sorted.len() as f64 * quantile.clamp(0.0, 1.0)).ceil() as usize)
117 .saturating_sub(1)
118 .min(sorted.len() - 1);
119 sorted.get(rank).copied()
120}
121
122const GENERATED_SOURCE_WALK_DIRS: &[&str] = &[
123 ".burin",
124 ".build",
125 ".claude",
126 ".codex",
127 ".git",
128 ".harn",
129 ".harn-runs",
130 ".next",
131 ".svelte-kit",
132 ".turbo",
133 ".venv",
134 "build",
135 "coverage",
136 "dist",
137 "node_modules",
138 "target",
139];
140
141pub(crate) fn should_skip_recursive_source_dir(dir: &Path) -> bool {
142 let Some(name) = dir.file_name().and_then(|name| name.to_str()) else {
143 return false;
144 };
145 GENERATED_SOURCE_WALK_DIRS.contains(&name) || name.starts_with(".harn-")
146}
147
148pub(crate) fn should_skip_recursive_source_file(file: &Path) -> bool {
149 let Some(name) = file.file_name().and_then(|name| name.to_str()) else {
150 return false;
151 };
152 name.starts_with(".harn-")
153}
154
155#[derive(Default)]
156pub(crate) struct SourceTargets {
157 pub(crate) harn: Vec<PathBuf>,
158 pub(crate) prompts: Vec<PathBuf>,
159}
160
161impl SourceTargets {
162 fn sort_and_dedup(&mut self) {
163 self.harn.sort();
164 self.harn.dedup();
165 self.prompts.sort();
166 self.prompts.dedup();
167 }
168}
169
170pub(crate) fn collect_source_targets(
171 targets: &[&str],
172 include_harn: bool,
173 include_prompts: bool,
174) -> SourceTargets {
175 let mut files = SourceTargets::default();
176 for target in targets {
177 let path = Path::new(target);
178 if path.is_dir() {
179 collect_source_targets_dir(path, include_harn, include_prompts, &mut files);
180 } else {
181 push_matching_source_target(path, include_harn, include_prompts, false, &mut files);
182 }
183 }
184 files.sort_and_dedup();
185 files
186}
187
188fn collect_source_targets_dir(
189 dir: &Path,
190 include_harn: bool,
191 include_prompts: bool,
192 files: &mut SourceTargets,
193) {
194 let root = dir.to_path_buf();
195 let mut walker = WalkBuilder::new(dir);
196 walker
197 .hidden(false)
198 .ignore(true)
199 .git_ignore(true)
200 .git_global(true)
201 .git_exclude(true)
202 .require_git(false)
203 .parents(true)
204 .follow_links(false)
205 .filter_entry(move |entry| {
206 let path = entry.path();
207 if path == root {
208 return true;
209 }
210 if path.is_dir() {
211 !should_skip_recursive_source_dir(path)
212 } else {
213 !should_skip_recursive_source_file(path)
214 }
215 });
216
217 for entry in walker.build().filter_map(Result::ok) {
218 let path = entry.path();
219 if entry
220 .file_type()
221 .is_some_and(|file_type| file_type.is_file())
222 {
223 push_matching_source_target(path, include_harn, include_prompts, true, files);
224 }
225 }
226}
227
228fn push_matching_source_target(
229 path: &Path,
230 include_harn: bool,
231 include_prompts: bool,
232 honor_skip_marker: bool,
233 files: &mut SourceTargets,
234) {
235 if include_prompts && is_harn_prompt_file(path) {
236 files.prompts.push(path.to_path_buf());
237 } else if include_harn && is_harn_program_file(path) {
238 let skip_marker = path.with_extension("conformance-skip");
239 if !honor_skip_marker || !skip_marker.exists() {
240 files.harn.push(path.to_path_buf());
241 }
242 }
243}
244
245pub(crate) fn is_harn_program_file(path: &Path) -> bool {
246 let Some(name) = path.file_name().and_then(|name| name.to_str()) else {
247 return false;
248 };
249 if name.ends_with(".harn.prompt") || name.ends_with(".prompt") {
250 return false;
251 }
252 name.ends_with(".harn") || name.ends_with(".harn.txt")
253}
254
255pub(crate) fn is_harn_prompt_file(path: &Path) -> bool {
256 let Some(name) = path.file_name().and_then(|name| name.to_str()) else {
257 return false;
258 };
259 name.ends_with(".harn.prompt") || name.ends_with(".prompt")
260}
261
262pub(crate) fn collect_harn_files(dir: &Path, out: &mut Vec<PathBuf>) {
267 if let Ok(entries) = std::fs::read_dir(dir) {
268 let mut entries: Vec<_> = entries.filter_map(|e| e.ok()).collect();
269 entries.sort_by_key(|e| e.path());
270 for entry in entries {
271 let path = entry.path();
272 if path.is_dir() {
273 if should_skip_recursive_source_dir(&path) {
274 continue;
275 }
276 collect_harn_files(&path, out);
277 } else if should_skip_recursive_source_file(&path) {
278 continue;
279 } else if path.extension().is_some_and(|ext| ext == "harn") {
280 let skip_marker = path.with_extension("conformance-skip");
281 if skip_marker.exists() {
282 continue;
283 }
284 out.push(path);
285 }
286 }
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::nearest_rank_percentile;
293
294 #[test]
295 fn percentile_handles_empty_and_bounds() {
296 assert_eq!(nearest_rank_percentile(&[], 0.5), None);
297 let data = [10u64, 20, 30, 40, 50];
298 assert_eq!(nearest_rank_percentile(&data, 0.0), Some(10));
299 assert_eq!(nearest_rank_percentile(&data, 0.5), Some(30));
300 assert_eq!(nearest_rank_percentile(&data, 1.0), Some(50));
301 assert_eq!(nearest_rank_percentile(&data, 2.0), Some(50));
303 assert_eq!(nearest_rank_percentile(&[7u64], 0.99), Some(7));
304 }
305}