KeiSeiKit-1.0/_primitives/_rust/kei-agent-runtime/src/registry.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

167 lines
6.8 KiB
Rust

//! Registry — `&str → &'static dyn Capability` lookup for all 14
//! capability implementations.
//!
//! `get(name)` is the single dispatch point used by both the
//! `kei-agent-runtime verify` binary and the `kei-capability` hook adapter.
//!
//! ## Aliases (v0.17)
//!
//! Two capabilities were renamed in v0.17 for clarity. Their old names
//! still resolve here via a small alias table; a deprecation warning is
//! emitted to stderr on lookup (once per process via `OnceLock`).
//!
//! - `tools::read-only` → `tools::deny-tools`
//! - `tools::cargo-only-bash` → `tools::bash-allowlist`
//!
//! Alias resolution is transparent: `get()` / `get_gate()` / `get_verify()`
//! return the new implementation when queried with the old name. The new
//! name is what the impl reports via `Capability::name()`.
//!
//! ## Convergence wave v0.18
//!
//! 5 of 6 gates + 3 of 8 verifies are now `const PatternGate { … }` /
//! `const CommandVerify { … }` declarations. Registry points at the
//! const by reference (`&POLICY_NO_GIT_OPS_GATE`) — same `&'static dyn Capability`
//! dispatch shape as before.
use crate::capability::Capability;
use crate::gates;
use crate::verifies;
use std::collections::HashSet;
use std::sync::{Mutex, OnceLock};
/// Alias table — (old name → new name). Checked before every resolution.
/// v0.17 renames: `tools::read-only` and `tools::cargo-only-bash`.
fn alias_target(name: &str) -> Option<&'static str> {
match name {
"tools::read-only" => Some("tools::deny-tools"),
"tools::cargo-only-bash" => Some("tools::bash-allowlist"),
_ => None,
}
}
/// Resolve an alias (if any) and emit a one-shot deprecation warning.
/// Returns the canonical name the caller should look up.
fn resolve_alias(name: &str) -> &str {
match alias_target(name) {
Some(target) => {
warn_deprecated_once(name, target);
target
}
None => name,
}
}
/// Log a deprecation warning to stderr at most once per (old, new) pair
/// per process. Non-fatal; aliases still resolve.
fn warn_deprecated_once(old: &str, new: &str) {
static SEEN: OnceLock<Mutex<HashSet<String>>> = OnceLock::new();
let seen = SEEN.get_or_init(|| Mutex::new(HashSet::new()));
let mut guard = match seen.lock() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
};
if guard.insert(old.to_string()) {
eprintln!(
"[kei-agent-runtime] deprecation: capability `{old}` is an alias for `{new}` (v0.17); \
update your role.toml / task.toml to use the new name. Alias retained through v2."
);
}
}
/// Look up a capability by its canonical `<category>::<slug>` name.
/// Returns `None` if the name is unknown. Gate-only and verify-only
/// capabilities share the same name; registry returns the *gate* impl for
/// 6 capabilities that have gates, and the *verify* impl for 8 that have
/// verifies. The two lookups below partition cleanly — no name holds both
/// a gate and a verify in this phase's inventory.
///
/// Deprecated aliases (see module docs) are resolved transparently and
/// a one-shot stderr warning is emitted.
pub fn get(name: &str) -> Option<&'static dyn Capability> {
let canonical = resolve_alias(name);
if let Some(c) = get_gate_canonical(canonical) {
return Some(c);
}
get_verify_canonical(canonical)
}
/// Look up only the gate-side impl. Used by `kei-capability check`.
/// Aliases resolve transparently.
pub fn get_gate(name: &str) -> Option<&'static dyn Capability> {
let canonical = resolve_alias(name);
get_gate_canonical(canonical)
}
/// Look up only the verify-side impl. Used by `kei-capability verify`.
/// Aliases resolve transparently.
pub fn get_verify(name: &str) -> Option<&'static dyn Capability> {
let canonical = resolve_alias(name);
get_verify_canonical(canonical)
}
/// Gate-only lookup by canonical name (no alias resolution, no warning).
fn get_gate_canonical(name: &str) -> Option<&'static dyn Capability> {
static TOOLS_DENY_TOOLS: gates::tools_deny_tools::DenyTools = gates::tools_deny_tools::DenyTools;
match name {
"policy::no-git-ops" => Some(&gates::policy_no_git_ops::NO_GIT_OPS),
"scope::files-whitelist" => Some(&gates::scope_files_whitelist::FILES_WHITELIST),
"scope::files-denylist" => Some(&gates::scope_files_denylist::FILES_DENYLIST),
"safety::no-dep-bump" => Some(&gates::safety_no_dep_bump::NO_DEP_BUMP_GATE),
"tools::deny-tools" => Some(&TOOLS_DENY_TOOLS),
"tools::bash-allowlist" => Some(&gates::tools_bash_allowlist::BASH_ALLOWLIST),
_ => None,
}
}
/// Verify-only lookup by canonical name (no alias resolution, no warning).
fn get_verify_canonical(name: &str) -> Option<&'static dyn Capability> {
static CP: verifies::quality_constructor_pattern::ConstructorPattern =
verifies::quality_constructor_pattern::ConstructorPattern;
static WL_V: verifies::scope_files_whitelist::FilesWhitelistVerify =
verifies::scope_files_whitelist::FilesWhitelistVerify;
static DL_V: verifies::scope_files_denylist::FilesDenylistVerify =
verifies::scope_files_denylist::FilesDenylistVerify;
static RF: verifies::output_report_format::ReportFormat =
verifies::output_report_format::ReportFormat;
static SG: verifies::output_severity_grade::SeverityGrade =
verifies::output_severity_grade::SeverityGrade;
match name {
"quality::constructor-pattern" => Some(&CP),
"quality::cargo-check-green" => Some(&verifies::quality_cargo_check_green::CARGO_CHECK_GREEN),
"quality::tests-green" => Some(&verifies::quality_tests_green::TESTS_GREEN),
"safety::no-dep-bump" => Some(&verifies::safety_no_dep_bump::NO_DEP_BUMP_VERIFY),
"scope::files-whitelist" => Some(&WL_V),
"scope::files-denylist" => Some(&DL_V),
"output::report-format" => Some(&RF),
"output::severity-grade" => Some(&SG),
_ => None,
}
}
/// All known canonical capability names (union of gate + verify). Used by
/// smoke tests. Deprecated aliases are NOT included — see `deprecated_aliases()`.
pub fn all_names() -> Vec<&'static str> {
vec![
"policy::no-git-ops",
"scope::files-whitelist",
"scope::files-denylist",
"safety::no-dep-bump",
"tools::deny-tools",
"tools::bash-allowlist",
"quality::constructor-pattern",
"quality::cargo-check-green",
"quality::tests-green",
"output::report-format",
"output::severity-grade",
]
}
/// List of (old-name, new-name) pairs still honored as aliases. Used by
/// smoke tests to assert every deprecated name still resolves.
pub fn deprecated_aliases() -> Vec<(&'static str, &'static str)> {
vec![
("tools::read-only", "tools::deny-tools"),
("tools::cargo-only-bash", "tools::bash-allowlist"),
]
}