use super::{SettingKey, ValueKind};
pub const SETTING_KEYS: &[SettingKey] = &[
SettingKey {
key: "agent_surface.max_items",
default: Some("0"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "agent_surface.max_output_bytes",
default: Some("0"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "agent_surface.truncate_content",
default: Some("0"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "cache.dir",
default: None,
kind: ValueKind::Path,
},
SettingKey {
key: "cli.max_instances",
default: None,
kind: ValueKind::Unsigned,
},
SettingKey {
key: "cli.no_input",
default: Some("false"),
kind: ValueKind::Bool,
},
SettingKey {
key: "cli.stdin_timeout_secs",
default: Some("60"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "db.busy_base_delay_ms",
default: Some("300"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "db.busy_retries",
default: Some("5"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "db.path",
default: None,
kind: ValueKind::Path,
},
SettingKey {
key: "db.query_timeout_ms",
default: Some("5000"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "display.tz",
default: Some("UTC"),
kind: ValueKind::Tz,
},
SettingKey {
key: "embedding.batch_size",
default: Some("32"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "embedding.dim",
default: Some("1024"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "embedding.model",
default: None,
kind: ValueKind::Text,
},
SettingKey {
key: "embedding.backend",
default: None,
kind: ValueKind::OneOf(&["auto", "openrouter", "open-router"]),
},
SettingKey {
key: "llm.backend",
default: None,
kind: ValueKind::OneOf(&["none", "openrouter", "open-router"]),
},
SettingKey {
key: "search.hybrid.max_graph_results",
default: Some("50"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "embedding.entity_cache_max_entries",
default: Some("10000"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "embedding.entity_cache_ttl_secs",
default: Some("3600"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "embedding.timeout_secs",
default: Some("300"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.circuit_breaker_reset_secs",
default: Some("60"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_connect.default_limit",
default: Some("100"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_connect.large_ns_limit",
default: Some("25"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_description.corpus_top_k",
default: Some("8"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_description.domain",
default: Some("auto"),
kind: ValueKind::Text,
},
SettingKey {
key: "enrich.entity_description.grounding_threshold",
default: Some("0.30"),
kind: ValueKind::Float,
},
SettingKey {
key: "enrich.entity_description.neighbour_top_k",
default: Some("12"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_description.min_corpus_chars",
default: Some("40"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_description.quality_sample",
default: Some("50"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_description.snippet_chars",
default: Some("2000"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_type.allowed_types",
default: None,
kind: ValueKind::Text,
},
SettingKey {
key: "enrich.entity_type.on_unknown_type",
default: Some("keep"),
kind: ValueKind::Text,
},
SettingKey {
key: "enrich.entity_type_validate.corpus_top_k",
default: Some("8"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_type_validate.min_corpus_chars",
default: Some("40"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_type_validate.neighbour_top_k",
default: Some("12"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.entity_type_validate.snippet_chars",
default: Some("2000"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.rate_limit_deadline_secs",
default: Some("3600"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.reembed_claim_batch",
default: Some("32"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.scan_page_size",
default: Some("512"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "enrich.yield_every_n_items",
default: Some("10"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "i18n.lang",
default: Some("en"),
kind: ValueKind::OneOf(&[
"en",
"EN",
"english",
"pt",
"PT",
"pt-BR",
"pt-br",
"portugues",
"portuguese",
]),
},
SettingKey {
key: "ingest.low_memory",
default: Some("false"),
kind: ValueKind::Bool,
},
SettingKey {
key: "limits.max_entities_per_memory",
default: Some("50"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "limits.max_relations_per_memory",
default: Some("50"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "llm.fallback",
default: Some("none"),
kind: ValueKind::OneOf(&["none"]),
},
SettingKey {
key: "llm.max_host_concurrency",
default: None,
kind: ValueKind::Unsigned,
},
SettingKey {
key: "llm.model",
default: None,
kind: ValueKind::Text,
},
SettingKey {
key: "llm.openrouter_timeout_secs",
default: Some("600"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "llm.probe_timeout_ms",
default: Some("800"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "llm.skip_embedding_on_failure",
default: Some("false"),
kind: ValueKind::Bool,
},
SettingKey {
key: "llm.slot_no_wait",
default: Some("false"),
kind: ValueKind::Bool,
},
SettingKey {
key: "llm.slot_wait_secs",
default: Some("300"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "llm.worker_rss_mb",
default: Some("350"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "log.format",
default: Some("pretty"),
kind: ValueKind::OneOf(&["pretty", "json"]),
},
SettingKey {
key: "log.level",
default: Some("warn"),
kind: ValueKind::LogDirective,
},
SettingKey {
key: "log.retention_days",
default: Some("7"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "log.rotation",
default: Some("daily"),
kind: ValueKind::OneOf(&["daily", "hourly", "never"]),
},
SettingKey {
key: "log.to_file",
default: Some("false"),
kind: ValueKind::Bool,
},
SettingKey {
key: "namespace.default",
default: Some("global"),
kind: ValueKind::Text,
},
SettingKey {
key: "network.chat_url",
default: None,
kind: ValueKind::Url,
},
SettingKey {
key: "network.embed_url",
default: None,
kind: ValueKind::Url,
},
SettingKey {
key: "network.openrouter.chat_url",
default: Some(crate::constants::DEFAULT_OPENROUTER_CHAT_URL),
kind: ValueKind::Url,
},
SettingKey {
key: "network.openrouter.embeddings_url",
default: Some(crate::constants::DEFAULT_OPENROUTER_EMBEDDINGS_URL),
kind: ValueKind::Url,
},
SettingKey {
key: "parallelism.embed_runtime_threads",
default: None,
kind: ValueKind::Unsigned,
},
SettingKey {
key: "parallelism.max_total_workers",
default: Some("64"),
kind: ValueKind::Unsigned,
},
SettingKey {
key: "parallelism.rayon_threads",
default: None,
kind: ValueKind::Unsigned,
},
SettingKey {
key: "retry.disable",
default: Some("false"),
kind: ValueKind::Bool,
},
SettingKey {
key: "shutdown.ignore",
default: Some("false"),
kind: ValueKind::Bool,
},
SettingKey {
key: "system.max_load_per_ncpu",
default: Some("2.0"),
kind: ValueKind::Float,
},
];
pub fn setting_key_names() -> impl Iterator<Item = &'static str> {
SETTING_KEYS.iter().map(|entry| entry.key)
}
pub(super) const LEGACY_SETTING_KEYS: &[(&str, &str)] =
&[("db.default_path", "db.path"), ("paths.cache", "cache.dir")];
const SUGGESTION_THRESHOLD: f64 = 0.7;
pub fn is_known_setting(key: &str) -> bool {
setting_key_names().any(|known| known == key)
}
pub fn nearest_setting_key(key: &str) -> Option<&'static str> {
setting_key_names()
.map(|candidate| {
let score = rapidfuzz::distance::jaro_winkler::normalized_similarity(
key.chars(),
candidate.chars(),
);
(candidate, score)
})
.filter(|(_, score)| *score >= SUGGESTION_THRESHOLD)
.max_by(|a, b| a.1.total_cmp(&b.1))
.map(|(candidate, _)| candidate)
}
#[cfg(test)]
mod setting_keys_drift_tests {
use super::SETTING_KEYS;
const SCANNED_SOURCES: &[&str] = &[
include_str!("../commands/enrich/extraction_descriptions.rs"),
include_str!("../commands/enrich/extraction_graph.rs"),
include_str!("../commands/enrich/events/entity_type_policy.rs"),
include_str!("../commands/enrich/prompts.rs"),
include_str!("../commands/enrich/quality_sample.rs"),
include_str!("../commands/enrich/scheduler.rs"),
include_str!("../commands/enrich/status.rs"),
include_str!("../commands/ingest/args.rs"),
include_str!("../constants/mod.rs"),
include_str!("../constants/identity.rs"),
include_str!("../constants/network.rs"),
include_str!("../constants/exit_codes.rs"),
include_str!("../constants/storage.rs"),
include_str!("../constants/embedding.rs"),
include_str!("../constants/enrich.rs"),
include_str!("../constants/search.rs"),
include_str!("../constants/runtime.rs"),
include_str!("../constants/limits.rs"),
include_str!("../embedder/getters.rs"),
include_str!("../i18n/mod.rs"),
include_str!("../lib.rs"),
include_str!("../llm_slots.rs"),
include_str!("../lock.rs"),
include_str!("../main.rs"),
include_str!("../namespace.rs"),
include_str!("../paths.rs"),
include_str!("../retry.rs"),
include_str!("../system_load.rs"),
include_str!("../tracing_init.rs"),
include_str!("../tz.rs"),
];
const KEY_CALL_MARKERS: &[&str] = &["get_setting(\""];
const KEY_RESOLVER_MARKERS: &[&str] = &[
"resolve_u64(",
"resolve_usize(",
"resolve_bool(",
"resolve_string(",
];
fn normalize(src: &str) -> String {
src.lines()
.filter(|line| !line.trim_start().starts_with("//"))
.flat_map(|line| line.chars().filter(|c| !c.is_whitespace()))
.collect()
}
pub(super) fn scan_keys(src: &str) -> Vec<String> {
let normalized = normalize(src);
let mut found = Vec::new();
for marker in KEY_CALL_MARKERS {
for cap in normalized.split(marker).skip(1) {
if let Some(end) = cap.find('"') {
if !cap[..end].is_empty() {
found.push(cap[..end].to_string());
}
}
}
}
for marker in KEY_RESOLVER_MARKERS {
for cap in normalized.split(marker).skip(1) {
let Some(comma) = cap.find(',') else { continue };
let close = cap.find(')').unwrap_or(usize::MAX);
if comma > close {
continue;
}
let rest = &cap[comma + 1..];
if !rest.starts_with('"') {
continue;
}
if let Some(end) = rest[1..].find('"') {
if end > 0 {
found.push(rest[1..1 + end].to_string());
}
}
}
}
found
}
#[test]
fn setting_keys_covers_every_literal_key_in_src() {
let registered: std::collections::HashSet<&str> =
SETTING_KEYS.iter().map(|e| e.key).collect();
let mut missing: Vec<String> = SCANNED_SOURCES
.iter()
.flat_map(|src| scan_keys(src))
.filter(|key| !registered.contains(key.as_str()))
.collect();
missing.sort();
missing.dedup();
assert!(
missing.is_empty(),
"setting keys read at runtime but missing from SETTING_KEYS: {missing:?}"
);
}
const COMPLETENESS_EXEMPT_DIRS: &[&str] = &["config"];
const COMPLETENESS_EXEMPT_FILES: &[&str] = &["runtime_config.rs"];
#[test]
fn scanned_sources_lists_every_file_that_reads_a_setting_key() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
if !root.is_dir() {
eprintln!("skipping: {} is not a directory", root.display());
return;
}
let mut rust_files = Vec::new();
collect_rust_files(&root, &mut rust_files);
assert!(
!rust_files.is_empty(),
"walk found zero .rs files under {} — the walk itself is broken, \
which is exactly how this guard would go silently blind",
root.display()
);
let listed: String = SCANNED_SOURCES.concat();
let mut unlisted = Vec::new();
for path in &rust_files {
let Ok(rel) = path.strip_prefix(&root) else {
continue;
};
let exempt_dir = rel.components().any(|c| {
COMPLETENESS_EXEMPT_DIRS.contains(&c.as_os_str().to_string_lossy().as_ref())
});
let exempt_file = rel
.file_name()
.is_some_and(|f| COMPLETENESS_EXEMPT_FILES.contains(&f.to_string_lossy().as_ref()));
if exempt_dir || exempt_file {
continue;
}
let Ok(body) = std::fs::read_to_string(path) else {
continue;
};
if scan_keys(&body).is_empty() {
continue;
}
if !listed.contains(body.as_str()) {
unlisted.push(rel.display().to_string());
}
}
unlisted.sort();
assert!(
unlisted.is_empty(),
"these files read a setting key but are absent from SCANNED_SOURCES: {unlisted:?}"
);
}
fn collect_rust_files(dir: &std::path::Path, out: &mut Vec<std::path::PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_rust_files(&path, out);
} else if path.extension().is_some_and(|e| e == "rs") {
out.push(path);
}
}
}
#[test]
fn scanner_sees_resolve_calls_wrapped_across_lines() {
let src = r#"
let v = crate::runtime_config::resolve_u64(
None,
"made.up.key",
7,
);
"#;
assert_eq!(scan_keys(src), vec!["made.up.key".to_string()]);
}
#[test]
fn scanner_ignores_keys_quoted_inside_whole_line_comments() {
let src = " /// scans for get_setting(\"placeholder\") call sites\n";
assert!(scan_keys(src).is_empty());
}
}