KeiSeiKit-1.0/_primitives/_rust/kei-agent-runtime/tests/capability_trait_smoke.rs
Parfii-bot e4b64418fc feat(convergence/u2): capability renames + back-compat aliases
Pre-unlock wave U2. Task 3 from CONVERGENCE-PLAN — rename misleading
capability names, keep old names as deprecated aliases.

Renames:
- tools::read-only → tools::deny-tools (mechanism is tool-name denial,
  not "read-only" metaphor)
- tools::cargo-only-bash → tools::bash-allowlist (mechanism is Bash
  pattern allow-list; cargo-only is one config value)

Back-compat via registry.resolve_alias():
- Old dir _capabilities/tools/{read-only,cargo-only-bash}/ retained with
  capability.toml-only stub: `alias = "<new-name>"` + `deprecated` field
- registry.rs loads alias stubs, redirects lookup before dispatch
- warn_deprecated_once() emits single-shot stderr per alias per process
  via OnceLock<Mutex<HashSet>>
- Zero breaking change to existing manifests / task.toml referencing
  old names

Rust impl files renamed in place:
- gates/tools_read_only.rs → gates/tools_deny_tools.rs (struct
  DenyTools)
- gates/tools_cargo_only_bash.rs → gates/tools_bash_allowlist.rs
  (struct BashAllowlist)
- gates/mod.rs + registry.rs + gate_smoke.rs updated

Roles updated (3): read-only.toml, explorer.toml, edit-local.toml —
reference new names directly.

Tests: kei-agent-runtime 41/41 (was 40, +1 deprecated_aliases_resolve
_to_new_names), _assembler 40/40 unchanged (substrate role expansion
follows new paths).

Docs updated: AGENT-ROLES.md, AGENT-SUBSTRATE-SCHEMA.md, 4 _manifests
referencing the old names (comment-only annotations).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:43:40 +08:00

88 lines
3.3 KiB
Rust

//! Registry smoke tests — every declared capability name resolves; unknown
//! names return None; gate-only and verify-only capabilities route correctly.
use kei_agent_runtime::registry;
#[test]
fn all_registered_names_resolve() {
for name in registry::all_names() {
assert!(
registry::get(name).is_some(),
"registry::get({name}) returned None"
);
}
}
#[test]
fn unknown_names_return_none() {
assert!(registry::get("bogus::nothing").is_none());
assert!(registry::get_gate("bogus::nothing").is_none());
assert!(registry::get_verify("bogus::nothing").is_none());
assert!(registry::get("").is_none());
}
#[test]
fn gate_only_capabilities_route_to_gate_table() {
let cap = registry::get_gate("tools::deny-tools").expect("deny-tools gate");
assert_eq!(cap.name(), "tools::deny-tools");
// deny-tools has no verify module — get_verify must miss
assert!(registry::get_verify("tools::deny-tools").is_none());
}
#[test]
fn verify_only_capabilities_route_to_verify_table() {
let cap = registry::get_verify("quality::cargo-check-green").expect("ccg verify");
assert_eq!(cap.name(), "quality::cargo-check-green");
assert!(registry::get_gate("quality::cargo-check-green").is_none());
}
#[test]
fn dual_capabilities_register_in_both_tables() {
// scope::* have both gate and verify impls under the same name
assert!(registry::get_gate("scope::files-whitelist").is_some());
assert!(registry::get_verify("scope::files-whitelist").is_some());
assert!(registry::get_gate("scope::files-denylist").is_some());
assert!(registry::get_verify("scope::files-denylist").is_some());
assert!(registry::get_gate("safety::no-dep-bump").is_some());
assert!(registry::get_verify("safety::no-dep-bump").is_some());
}
#[test]
fn registry_total_count_matches_spec() {
// 11 unique names in inventory; 3 of them (scope whitelist, scope
// denylist, safety::no-dep-bump) are dual gate+verify.
assert_eq!(registry::all_names().len(), 11);
}
/// v0.17 — deprecated aliases still resolve. Old callers querying
/// `tools::read-only` / `tools::cargo-only-bash` must land on the new
/// impl without breakage. The returned `Capability::name()` reports the
/// CANONICAL name, so callers that stored the string are now on the
/// migration path.
#[test]
fn deprecated_aliases_resolve_to_new_names() {
for (old, new) in registry::deprecated_aliases() {
let cap = registry::get(old)
.unwrap_or_else(|| panic!("alias {old} must resolve to some capability"));
assert_eq!(
cap.name(),
new,
"alias {old} should resolve to impl reporting canonical name {new}"
);
// Old name must also work through the typed entry points so
// hook binaries that call `get_gate` / `get_verify` directly
// see the same resolution.
if registry::get_gate(new).is_some() {
assert!(
registry::get_gate(old).is_some(),
"alias {old} must resolve through get_gate"
);
}
if registry::get_verify(new).is_some() {
assert!(
registry::get_verify(old).is_some(),
"alias {old} must resolve through get_verify"
);
}
}
}