use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Section {
Decisions,
Architecture,
Debug,
Reasoning,
Feedback,
LessonsLearned,
Retrospectives,
Experiments,
AgentIssues,
Reference,
Council,
ProjectMap,
}
impl Section {
pub const PROTECTED_FORGET: &'static [Section] =
&[Section::AgentIssues, Section::Council, Section::ProjectMap];
pub const ALL: [Section; 12] = [
Section::Decisions,
Section::Architecture,
Section::Debug,
Section::Reasoning,
Section::Feedback,
Section::LessonsLearned,
Section::Retrospectives,
Section::Experiments,
Section::AgentIssues,
Section::Reference,
Section::Council,
Section::ProjectMap,
];
pub fn from_canonical_str(s: &str) -> Option<Self> {
Section::ALL.iter().find(|sec| sec.as_str() == s).copied()
}
pub fn as_str(&self) -> &'static str {
match self {
Section::Decisions => "decisions",
Section::Architecture => "architecture",
Section::Debug => "debug",
Section::Reasoning => "reasoning",
Section::Feedback => "feedback",
Section::LessonsLearned => "lessons-learned",
Section::Retrospectives => "retrospectives",
Section::Experiments => "experiments",
Section::AgentIssues => "agent-issues",
Section::Reference => "reference",
Section::Council => "council",
Section::ProjectMap => "project-map",
}
}
}
impl std::fmt::Display for Section {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
pub const fn section_to_c_kind(section: &Section) -> &'static str {
match section {
Section::Architecture => "semantic",
Section::Decisions => "episodic",
Section::Debug => "episodic",
Section::Reasoning => "semantic",
Section::Feedback => "reflective",
Section::LessonsLearned => "semantic",
Section::Retrospectives => "reflective",
Section::Experiments => "semantic",
Section::AgentIssues => "procedural",
Section::Reference => "semantic",
Section::Council => "episodic",
Section::ProjectMap => "procedural",
}
}
pub const fn section_to_doc_kind(section: &Section) -> &'static str {
match section {
Section::Debug => "Event",
Section::AgentIssues => "Event",
Section::Architecture
| Section::Decisions
| Section::Reasoning
| Section::Feedback
| Section::LessonsLearned
| Section::Retrospectives
| Section::Experiments
| Section::Reference
| Section::ProjectMap => "Static",
Section::Council => "Event",
}
}
#[cfg(test)]
mod from_canonical_str_tests {
use super::*;
#[test]
fn accepts_all_canonical_sections() {
for section in Section::ALL {
let s = section.as_str();
assert_eq!(
Section::from_canonical_str(s),
Some(section),
"from_canonical_str({s:?}) attendu Some({section:?})"
);
}
}
#[test]
fn accepts_project_map() {
assert_eq!(
Section::from_canonical_str("project-map"),
Some(Section::ProjectMap)
);
}
#[test]
fn accepts_original_eleven_sections() {
let cases = [
("decisions", Section::Decisions),
("architecture", Section::Architecture),
("debug", Section::Debug),
("reasoning", Section::Reasoning),
("feedback", Section::Feedback),
("lessons-learned", Section::LessonsLearned),
("retrospectives", Section::Retrospectives),
("experiments", Section::Experiments),
("agent-issues", Section::AgentIssues),
("reference", Section::Reference),
("council", Section::Council),
];
for (s, expected) in cases {
assert_eq!(
Section::from_canonical_str(s),
Some(expected),
"section '{s}'"
);
}
}
#[test]
fn rejects_unknown_strings() {
for bogus in ["bogus", "", "DECISIONS", "project_map", "ProjectMap"] {
assert_eq!(
Section::from_canonical_str(bogus),
None,
"from_canonical_str({bogus:?}) attendu None"
);
}
}
}
#[cfg(test)]
mod cognitive_kind_tests {
use super::*;
#[test]
fn c_kind_all_sections() {
let cases = [
(Section::Architecture, "semantic"),
(Section::Decisions, "episodic"),
(Section::Debug, "episodic"),
(Section::Reasoning, "semantic"),
(Section::Feedback, "reflective"),
(Section::LessonsLearned, "semantic"),
(Section::Retrospectives, "reflective"),
(Section::Experiments, "semantic"),
(Section::AgentIssues, "procedural"),
(Section::Reference, "semantic"),
(Section::Council, "episodic"),
(Section::ProjectMap, "procedural"),
];
for (section, expected) in cases {
assert_eq!(
section_to_c_kind(§ion),
expected,
"section_to_c_kind({section}) attendu {expected}"
);
}
}
#[test]
fn doc_kind_all_sections() {
let cases = [
(Section::Architecture, "Static"),
(Section::Decisions, "Static"),
(Section::Debug, "Event"),
(Section::Reasoning, "Static"),
(Section::Feedback, "Static"),
(Section::LessonsLearned, "Static"),
(Section::Retrospectives, "Static"),
(Section::Experiments, "Static"),
(Section::AgentIssues, "Event"),
(Section::Reference, "Static"),
(Section::Council, "Event"),
(Section::ProjectMap, "Static"),
];
for (section, expected) in cases {
assert_eq!(
section_to_doc_kind(§ion),
expected,
"section_to_doc_kind({section}) attendu {expected}"
);
}
}
#[test]
fn c_kind_matches_backfill_sql() {
fn sql_c_kind(s: &str) -> &'static str {
match s {
"architecture" => "semantic",
"decisions" => "episodic",
"council" => "episodic",
"debug" => "episodic",
"reasoning" => "semantic",
"feedback" => "reflective",
"lessons-learned" => "semantic",
"retrospectives" => "reflective",
"experiments" => "semantic",
"agent-issues" => "procedural",
"reference" => "semantic",
"project-map" => "procedural",
_ => "semantic",
}
}
fn sql_doc_kind(s: &str) -> &'static str {
match s {
"debug" => "Event",
"agent-issues" => "Event",
"council" => "Event",
"project-map" => "Static",
_ => "Static",
}
}
for section in Section::ALL {
let s = section.as_str();
assert_eq!(
section_to_c_kind(§ion),
sql_c_kind(s),
"DIVERGENCE c_kind pour section '{s}' : Rust={} SQL={}",
section_to_c_kind(§ion),
sql_c_kind(s),
);
assert_eq!(
section_to_doc_kind(§ion),
sql_doc_kind(s),
"DIVERGENCE doc_kind pour section '{s}' : Rust={} SQL={}",
section_to_doc_kind(§ion),
sql_doc_kind(s),
);
}
}
}