const TOPIC_METHODOLOGY: &str = include_str!("../data/explain/methodology.md");
const TOPIC_DOMAIN: &str = include_str!("../data/explain/domain.md");
const TOPIC_SENSOR: &str = include_str!("../data/explain/sensor.md");
const TOPIC_HAT: &str = include_str!("../data/explain/hat.md");
const TOPIC_SCORING: &str = include_str!("../data/explain/scoring.md");
const TOPIC_FEDERATION: &str = include_str!("../data/explain/federation.md");
const TOPIC_CLI: &str = include_str!("../data/explain/cli.md");
const TOPIC_CULTURE: &str = include_str!("../data/explain/culture.md");
pub const BUNDLED_VERSION: &str = "v3.2";
pub const CANONICAL_SOURCE: &str =
"neurogrim/crates/neurogrim-mcp/data/explain/<topic>.md";
pub fn topics() -> &'static [(&'static str, &'static str, &'static str)] {
&[
(
"methodology",
"What is LSP Brains; the overlay framing; the 5-piece model",
TOPIC_METHODOLOGY,
),
(
"domain",
"Anatomy of a domain; weight tiers; when to add one",
TOPIC_DOMAIN,
),
(
"sensor",
"Sensor authoring contract; CMDB envelope; score formula patterns",
TOPIC_SENSOR,
),
(
"hat",
"The 8 declared hats and when to wear each",
TOPIC_HAT,
),
(
"scoring",
"Unified score, confidence, trajectory, floor gates",
TOPIC_SCORING,
),
(
"federation",
"A2A peers, fractal composition, read-only siblings",
TOPIC_FEDERATION,
),
(
"cli",
"All commands grouped by purpose (introspection / authoring / execution / bookkeeping)",
TOPIC_CLI,
),
(
"culture",
"culture.yaml — five values as floor-only invariants",
TOPIC_CULTURE,
),
]
}
pub fn lookup(name: &str) -> Option<&'static str> {
topics().iter().find(|(k, _, _)| *k == name).map(|t| t.2)
}
pub fn topic_names() -> Vec<&'static str> {
topics().iter().map(|(k, _, _)| *k).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn topic_count_is_8() {
assert_eq!(topics().len(), 8);
}
#[test]
fn topic_names_are_unique() {
let mut names = topic_names();
names.sort();
let n = names.len();
names.dedup();
assert_eq!(n, names.len(), "duplicate topic name");
}
#[test]
fn all_topics_have_non_empty_bodies() {
for (name, _summary, body) in topics() {
assert!(
body.len() > 200,
"topic '{name}' bundled body is too short ({} bytes)",
body.len()
);
}
}
#[test]
fn all_topics_carry_version_header() {
for (name, _summary, body) in topics() {
let first = body.lines().next().unwrap_or("");
assert!(
first.starts_with("<!-- topic:") && first.contains(BUNDLED_VERSION),
"topic '{name}' is missing the bundled-version header marker; first line: {first:?}"
);
}
}
#[test]
fn methodology_topic_mentions_overlay() {
assert!(TOPIC_METHODOLOGY.contains("overlay"));
assert!(TOPIC_METHODOLOGY.contains("nervous system"));
}
#[test]
fn domain_topic_describes_weight_tiers() {
assert!(TOPIC_DOMAIN.contains("Weighted"));
assert!(TOPIC_DOMAIN.contains("Advisory"));
assert!(TOPIC_DOMAIN.contains("Stub"));
}
#[test]
fn cli_topic_lists_command_families() {
assert!(TOPIC_CLI.contains("Introspection"));
assert!(TOPIC_CLI.contains("Authoring"));
assert!(TOPIC_CLI.contains("Execution"));
assert!(TOPIC_CLI.contains("Bookkeeping"));
}
#[test]
fn lookup_finds_known_topic() {
assert!(lookup("methodology").is_some());
assert!(lookup("nonexistent").is_none());
}
}