1use koala_core::invariant::Context;
2use std::path::PathBuf;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Severity {
6 Hard,
8 Advisory,
11}
12
13impl Severity {
14 pub fn label(&self) -> &'static str {
15 match self {
16 Self::Hard => "✗",
17 Self::Advisory => "⚠",
18 }
19 }
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum FindingKind {
24 AcceptanceTestRefMissing,
26 AdrRefDangling,
28 AdrRefSuperseded { superseder: Option<String> },
30 AdrDormant,
32 AdrIdGap { missing: u32 },
34 Tier1Tampered { expected: String, actual: String },
37 TemplatePlaceholderUnfilled,
41 AcceptanceItemUnchecked,
44 AdrGraphUnclean,
48}
49
50impl FindingKind {
51 pub fn short(&self) -> &'static str {
52 match self {
53 Self::AcceptanceTestRefMissing => "acceptance test ref missing",
54 Self::AdrRefDangling => "ADR reference does not resolve",
55 Self::AdrRefSuperseded { .. } => "ADR reference is superseded",
56 Self::AdrDormant => "ADR has no inbound references",
57 Self::AdrIdGap { .. } => "ADR id is missing — file was deleted",
58 Self::Tier1Tampered { .. } => "Tier 1 file body edited by hand",
59 Self::TemplatePlaceholderUnfilled => "scaffolded placeholder never filled in",
60 Self::AcceptanceItemUnchecked => "done feature has an unchecked acceptance item",
61 Self::AdrGraphUnclean => "ADR supersede graph is not clean",
62 }
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct Finding {
68 pub check_id: &'static str,
69 pub file: PathBuf,
71 pub line: usize,
73 pub claim: String,
75 pub kind: FindingKind,
76 pub severity: Severity,
77 pub fix_hint: Option<String>,
78}
79
80pub trait Check: Send + Sync {
81 fn id(&self) -> &'static str;
82 fn intent(&self) -> &'static str;
83 fn run(&self, ctx: &Context) -> Vec<Finding>;
84}