KeiSeiKit-1.0/_primitives/_rust/kei-decision/src/classifier.rs
Parfii-bot a4e667de10 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

80 lines
2.3 KiB
Rust

//! Heuristic classifier — `RawAction.title + severity + effort` → [`ActionKind`].
//!
//! Pure function; no IO. Keyword match on lowercased title decides; severity
//! and effort do NOT change the kind today (reserved for future tuning).
use serde::{Deserialize, Serialize};
use crate::parser::RawAction;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum ActionKind {
Refactor,
Migrate,
NewPrimitive,
Decompose,
Doc,
Unknown,
}
impl ActionKind {
/// Lower-snake form for use in slugs / file names.
pub fn slug(&self) -> &'static str {
match self {
Self::Refactor => "refactor",
Self::Migrate => "migrate",
Self::NewPrimitive => "new-primitive",
Self::Decompose => "decompose",
Self::Doc => "doc",
Self::Unknown => "unknown",
}
}
}
/// Pure mapping `RawAction → ActionKind`. Order of checks matters: more
/// specific keywords win.
pub fn classify(action: &RawAction) -> ActionKind {
let t = action.title.to_lowercase();
if matches_new_primitive(&t) {
return ActionKind::NewPrimitive;
}
if matches_decompose(&t) {
return ActionKind::Decompose;
}
if matches_migrate(&t) {
return ActionKind::Migrate;
}
if matches_refactor(&t) {
return ActionKind::Refactor;
}
if matches_doc(&t) {
return ActionKind::Doc;
}
ActionKind::Unknown
}
fn matches_new_primitive(t: &str) -> bool {
contains_any(t, &["new primitive", "build primitive", "implement primitive", "add primitive", "create primitive"])
|| (contains_any(t, &["new", "create", "build", "implement", "add"]) && t.contains("crate"))
}
fn matches_decompose(t: &str) -> bool {
contains_any(t, &["decompose", "split into", "break up", "extract module"])
}
fn matches_migrate(t: &str) -> bool {
contains_any(t, &["migrate", "port to", "switch to", "replace with"])
}
fn matches_refactor(t: &str) -> bool {
contains_any(t, &["refactor", "rewrite", "rework", "restructure", "wrap"])
}
fn matches_doc(t: &str) -> bool {
contains_any(t, &["document", "docs", "readme", "doc-only", "wiki", "translator"])
}
fn contains_any(haystack: &str, needles: &[&str]) -> bool {
needles.iter().any(|n| haystack.contains(n))
}