#![allow(
dead_code,
reason = "consumed by relation engine + command modules in PHASE-02/03"
)]
pub(crate) const SL: &str = "SL";
pub(crate) const PRD: &str = "PRD";
pub(crate) const SPEC: &str = "SPEC";
pub(crate) const CM: &str = "CM";
pub(crate) const REQ: &str = "REQ";
pub(crate) const ADR: &str = "ADR";
pub(crate) const POL: &str = "POL";
pub(crate) const STD: &str = "STD";
pub(crate) const RV: &str = "RV";
pub(crate) const REC: &str = "REC";
pub(crate) const REV: &str = "REV";
pub(crate) const RFC: &str = "RFC";
pub(crate) const ISS: &str = "ISS";
pub(crate) const IMP: &str = "IMP";
pub(crate) const CHR: &str = "CHR";
pub(crate) const RSK: &str = "RSK";
pub(crate) const IDE: &str = "IDE";
pub(crate) const ASM: &str = "ASM";
pub(crate) const DEC: &str = "DEC";
pub(crate) const QUE: &str = "QUE";
pub(crate) const CON: &str = "CON";
pub(crate) const EVD: &str = "EVD";
pub(crate) const HYP: &str = "HYP";
pub(crate) const CPT: &str = "CPT";
pub(crate) const DISPATCH_REF_PREFIX: &str = "refs/heads/dispatch/";
pub(crate) const PHASE_REF_PREFIX: &str = "refs/heads/phase/";
pub(crate) const REVIEW_REF_PREFIX: &str = "refs/heads/review/";
pub(crate) const CANDIDATE_REF_PREFIX: &str = "refs/heads/candidate/";
pub(crate) const GOV: &[&str] = &[ADR, POL, STD];
pub(crate) const BACKLOG: &[&str] = &[ISS, IMP, CHR, RSK, IDE];
pub(crate) const RECORD: &[&str] = &[ASM, DEC, QUE, CON, EVD, HYP, CPT];
pub(crate) const SEARCH_DEFAULT: &[&str] = &[
SL, ADR, PRD, SPEC, RFC, ISS, IMP, CHR, RSK, IDE, ASM, DEC, QUE, CON, EVD, HYP, CPT,
];
pub(crate) const ALL_KINDS: &[&str] = &[
SL, ADR, POL, STD, PRD, SPEC, REQ, ISS, IMP, CHR, RSK, IDE, RV, REC, ASM, DEC, QUE, CON, EVD,
HYP, CPT, CM, REV, RFC,
];
pub(crate) const TAGGABLE: &[&str] = &[
SL, ADR, POL, STD, RFC, ISS, IMP, CHR, RSK, IDE, ASM, CM, DEC, QUE, CON, EVD, HYP, CPT, PRD,
SPEC, REQ, REC, REV, RV,
];
pub(crate) const WORK_LIKE: &[&str] = &[SL, ISS, IMP, CHR, RSK, IDE, REV];
pub(crate) const ADMISSIBLE_DEP_TARGETS: &[&str] = &[
SL, ISS, IMP, CHR, RSK, IDE, REV, ASM, DEC, QUE, CON, EVD, HYP, CPT,
];
pub(crate) const VALUE_BEARING: &[&str] = &[SL, ISS, IMP, CHR, RSK, IDE];
pub(crate) fn is_value_bearing(prefix: &str) -> bool {
VALUE_BEARING.contains(&prefix)
}
pub(crate) fn is_record(prefix: &str) -> bool {
RECORD.contains(&prefix)
}
#[derive(serde::Serialize)]
pub(crate) struct Kind {
pub dir: &'static str,
pub prefix: &'static str,
#[serde(skip)]
pub stem: &'static str,
}
pub(crate) const SLICE_DIR: &str = ".doctrine/slice";
pub(crate) const CONCEPT_MAP_DIR: &str = ".doctrine/concept-map";
pub(crate) const REV_DIR: &str = ".doctrine/revision";
pub(crate) const REC_DIR: &str = ".doctrine/rec";
pub(crate) const REVIEW_DIR: &str = ".doctrine/review";
pub(crate) const REQUIREMENT_DIR: &str = ".doctrine/requirement";
pub(crate) const RFC_DIR: &str = ".doctrine/rfc";
const ADR_DIR: &str = ".doctrine/adr";
const POLICY_DIR: &str = ".doctrine/policy";
const STANDARD_DIR: &str = ".doctrine/standard";
const PRODUCT_SPEC_DIR: &str = ".doctrine/spec/product";
const TECH_SPEC_DIR: &str = ".doctrine/spec/tech";
const ASSUMPTION_DIR: &str = ".doctrine/knowledge/assumption";
const DECISION_DIR: &str = ".doctrine/knowledge/decision";
const QUESTION_DIR: &str = ".doctrine/knowledge/question";
const CONSTRAINT_DIR: &str = ".doctrine/knowledge/constraint";
const EVIDENCE_DIR: &str = ".doctrine/knowledge/evidence";
const HYPOTHESIS_DIR: &str = ".doctrine/knowledge/hypothesis";
const CONCEPT_DIR: &str = ".doctrine/knowledge/concept";
const ISSUE_DIR: &str = ".doctrine/backlog/issue";
const IMPROVEMENT_DIR: &str = ".doctrine/backlog/improvement";
const CHORE_DIR: &str = ".doctrine/backlog/chore";
const RISK_DIR: &str = ".doctrine/backlog/risk";
const IDEA_DIR: &str = ".doctrine/backlog/idea";
pub(crate) const SLICE_KIND: Kind = Kind {
dir: SLICE_DIR,
prefix: SL,
stem: "slice",
};
pub(crate) const CONCEPT_MAP_KIND: Kind = Kind {
dir: CONCEPT_MAP_DIR,
prefix: CM,
stem: "concept-map",
};
pub(crate) const REV_KIND: Kind = Kind {
dir: REV_DIR,
prefix: REV,
stem: "revision",
};
pub(crate) const REC_KIND: Kind = Kind {
dir: REC_DIR,
prefix: REC,
stem: "rec",
};
pub(crate) const REVIEW_KIND: Kind = Kind {
dir: REVIEW_DIR,
prefix: RV,
stem: "review",
};
pub(crate) const REQUIREMENT_KIND: Kind = Kind {
dir: REQUIREMENT_DIR,
prefix: REQ,
stem: "requirement",
};
pub(crate) const PRODUCT_SPEC_KIND: Kind = Kind {
dir: PRODUCT_SPEC_DIR,
prefix: PRD,
stem: "spec",
};
pub(crate) const TECH_SPEC_KIND: Kind = Kind {
dir: TECH_SPEC_DIR,
prefix: SPEC,
stem: "spec",
};
pub(crate) const ASSUMPTION_KIND: Kind = Kind {
dir: ASSUMPTION_DIR,
prefix: ASM,
stem: "record",
};
pub(crate) const DECISION_KIND: Kind = Kind {
dir: DECISION_DIR,
prefix: DEC,
stem: "record",
};
pub(crate) const QUESTION_KIND: Kind = Kind {
dir: QUESTION_DIR,
prefix: QUE,
stem: "record",
};
pub(crate) const CONSTRAINT_KIND: Kind = Kind {
dir: CONSTRAINT_DIR,
prefix: CON,
stem: "record",
};
pub(crate) const EVIDENCE_KIND: Kind = Kind {
dir: EVIDENCE_DIR,
prefix: EVD,
stem: "record",
};
pub(crate) const HYPOTHESIS_KIND: Kind = Kind {
dir: HYPOTHESIS_DIR,
prefix: HYP,
stem: "record",
};
pub(crate) const CONCEPT_KIND: Kind = Kind {
dir: CONCEPT_DIR,
prefix: CPT,
stem: "record",
};
pub(crate) const ISSUE_KIND: Kind = Kind {
dir: ISSUE_DIR,
prefix: ISS,
stem: "backlog",
};
pub(crate) const IMPROVEMENT_KIND: Kind = Kind {
dir: IMPROVEMENT_DIR,
prefix: IMP,
stem: "backlog",
};
pub(crate) const CHORE_KIND: Kind = Kind {
dir: CHORE_DIR,
prefix: CHR,
stem: "backlog",
};
pub(crate) const RISK_KIND: Kind = Kind {
dir: RISK_DIR,
prefix: RSK,
stem: "backlog",
};
pub(crate) const IDEA_KIND: Kind = Kind {
dir: IDEA_DIR,
prefix: IDE,
stem: "backlog",
};
pub(crate) const ADR_KIND: Kind = Kind {
dir: ADR_DIR,
prefix: ADR,
stem: "adr",
};
pub(crate) const POLICY_KIND: Kind = Kind {
dir: POLICY_DIR,
prefix: POL,
stem: "policy",
};
pub(crate) const STANDARD_KIND: Kind = Kind {
dir: STANDARD_DIR,
prefix: STD,
stem: "standard",
};
pub(crate) const RFC_KIND: Kind = Kind {
dir: RFC_DIR,
prefix: RFC,
stem: "rfc",
};
mod resolve;
pub(crate) use resolve::*;
pub(crate) struct KindRef {
pub(crate) kind: &'static Kind,
pub(crate) state_dir: Option<&'static str>,
}
pub(crate) const KINDS: &[KindRef] = &[
KindRef {
kind: &SLICE_KIND,
state_dir: Some(".doctrine/state/slice"),
},
KindRef {
kind: &ADR_KIND,
state_dir: None,
},
KindRef {
kind: &POLICY_KIND,
state_dir: None,
},
KindRef {
kind: &STANDARD_KIND,
state_dir: None,
},
KindRef {
kind: &PRODUCT_SPEC_KIND,
state_dir: None,
},
KindRef {
kind: &TECH_SPEC_KIND,
state_dir: None,
},
KindRef {
kind: &REQUIREMENT_KIND,
state_dir: None,
},
KindRef {
kind: &ISSUE_KIND,
state_dir: None,
},
KindRef {
kind: &IMPROVEMENT_KIND,
state_dir: None,
},
KindRef {
kind: &CHORE_KIND,
state_dir: None,
},
KindRef {
kind: &RISK_KIND,
state_dir: None,
},
KindRef {
kind: &IDEA_KIND,
state_dir: None,
},
KindRef {
kind: &REVIEW_KIND,
state_dir: Some(".doctrine/state/review"),
},
KindRef {
kind: &REC_KIND,
state_dir: None,
},
KindRef {
kind: &ASSUMPTION_KIND,
state_dir: None,
},
KindRef {
kind: &DECISION_KIND,
state_dir: None,
},
KindRef {
kind: &QUESTION_KIND,
state_dir: None,
},
KindRef {
kind: &CONSTRAINT_KIND,
state_dir: None,
},
KindRef {
kind: &EVIDENCE_KIND,
state_dir: None,
},
KindRef {
kind: &HYPOTHESIS_KIND,
state_dir: None,
},
KindRef {
kind: &CONCEPT_KIND,
state_dir: None,
},
KindRef {
kind: &CONCEPT_MAP_KIND,
state_dir: None,
},
KindRef {
kind: &REV_KIND,
state_dir: None,
},
KindRef {
kind: &RFC_KIND,
state_dir: None,
},
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn groupings_match_documented_membership() {
assert_eq!(GOV, &[ADR, POL, STD]);
assert_eq!(BACKLOG, &[ISS, IMP, CHR, RSK, IDE]);
assert_eq!(RECORD, &[ASM, DEC, QUE, CON, EVD, HYP, CPT]);
}
#[test]
fn combined_constants_cover_record() {
for c in [SEARCH_DEFAULT, ALL_KINDS, TAGGABLE, ADMISSIBLE_DEP_TARGETS] {
for &r in RECORD {
assert!(c.contains(&r), "{r} missing from combined constant");
}
}
}
#[test]
fn value_bearing_is_sl_plus_backlog_strict_subset_of_work_like() {
let expected: &[&str] = &[SL, ISS, IMP, CHR, RSK, IDE];
assert_eq!(VALUE_BEARING, expected);
let work_like: &[&str] = &["SL", "ISS", "IMP", "CHR", "RSK", "IDE", "REV"];
for &prefix in VALUE_BEARING {
assert!(work_like.contains(&prefix), "{prefix} must be work-like");
}
assert!(
!VALUE_BEARING.contains(&"REV"),
"REV is work-like for dep/seq but NOT value-bearing"
);
for &prefix in &[ADR, POL, STD, ASM, DEC, QUE, CON, EVD, HYP, CPT] {
assert!(
!is_value_bearing(prefix),
"{prefix} must NOT be value-bearing"
);
}
}
#[test]
fn kinds_table_covers_the_numbered_kinds() {
assert_eq!(KINDS.len(), 24, "add/remove a KindRef row? bump this count");
let prefixes: Vec<_> = KINDS.iter().map(|k| k.kind.prefix).collect();
assert_eq!(
prefixes,
[
"SL", "ADR", "POL", "STD", "PRD", "SPEC", "REQ", "ISS", "IMP", "CHR", "RSK", "IDE",
"RV", "REC", "ASM", "DEC", "QUE", "CON", "EVD", "HYP", "CPT", "CM", "REV", "RFC"
]
);
let stateful: Vec<_> = KINDS
.iter()
.filter(|k| k.state_dir.is_some())
.map(|k| k.kind.prefix)
.collect();
assert_eq!(stateful, ["SL", "RV"]);
}
#[test]
fn kinds_prefixes_are_corpus_wide_disjoint() {
use std::collections::BTreeSet;
let prefixes: Vec<_> = KINDS.iter().map(|k| k.kind.prefix).collect();
let distinct: BTreeSet<_> = prefixes.iter().copied().collect();
assert_eq!(
distinct.len(),
prefixes.len(),
"all KINDS prefixes are distinct: {prefixes:?}"
);
}
}