cli

Command-line interface for Radroots
git clone https://radroots.dev/git/cli.git
Log | Files | Refs | README | LICENSE

commit d3f18e4000ce1cd733321bde1a06063a4004a2fe
parent 1809489d6ebbb7a19baf5242c93ca7060a863a68
Author: triesap <tyson@radroots.org>
Date:   Mon, 13 Jul 2026 07:47:01 +0000

cli: guard foundation hardening surfaces

- add Foundation Hardening retired-name scans across CLI source and docs
- reject ambiguous timestamp and strict-delivery implication wording
- keep guard matching explicit for retired identifiers and doc phrases
- validate full CLI tests after rerunning transient HYF timeouts

Diffstat:
Mtests/target_cli.rs | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 201 insertions(+), 0 deletions(-)

diff --git a/tests/target_cli.rs b/tests/target_cli.rs @@ -61,6 +61,92 @@ use support::{ const ORDERABLE_LISTING_RELAY: &str = "wss://relay.example.com"; +const FORBIDDEN_FOUNDATION_HARDENING_RETIRED_CONCEPTS: &[(&str, &str)] = &[ + ( + "SignedNostrEvent", + "generic signed-event surfaces must use product-neutral signed-event names", + ), + ( + "RadrootsEventIndexIndexCheckpoint", + "event-index checkpoint names must not duplicate the index noun", + ), + ( + "RadrootsEventsIndexed", + "event-indexed APIs must use the singular event-index crate family", + ), + ( + "RADROOTS_EVENTS_VERSION", + "event contract version constants must use the current singular event namespace", + ), + ( + "radroots_events", + "CLI surfaces must use the current singular event crate names", + ), + ( + "radroots_events_codec", + "CLI event codec surfaces must use the current singular event-codec name", + ), + ( + "radroots_events_indexed", + "CLI event index surfaces must use the current singular event-index name", + ), + ( + "radroots_local_events", + "CLI storage surfaces must not reintroduce retired local-events names", + ), + ( + "radroots_local_store", + "CLI storage surfaces must not reintroduce retired local-store names", + ), + ( + "radroots_types", + "CLI type surfaces must use current crate ownership instead of retired types crates", + ), + ( + "radroots_types_bindings", + "CLI generated surfaces must not reintroduce retired types-binding names", + ), + ( + "radroots_nostr_ndb", + "CLI Nostr database surfaces must not reintroduce retired ndb names", + ), + ( + "radroots_replica_db", + "CLI replica surfaces must use current replica-store ownership", + ), + ( + "radroots_replica_db_schema", + "CLI replica schema surfaces must use current replica-schema ownership", + ), + ( + "radroots_sp1_guest_trade", + "CLI trade SP1 surfaces must use the current trade_sp1 crate names", + ), + ( + "radroots_sp1_host_trade", + "CLI trade SP1 surfaces must use the current trade_sp1 crate names", + ), +]; + +const FORBIDDEN_FOUNDATION_HARDENING_DOC_CONCEPTS: &[(&str, &str)] = &[ + ( + "Nostr event timestamp", + "CLI docs must describe event-envelope timestamps without generic Nostr wording", + ), + ( + "Forwarded satisfies Delivered", + "CLI docs must not imply forwarded evidence is strict delivery", + ), + ( + "StoredByGateway satisfies Delivered", + "CLI docs must not imply gateway storage evidence is strict delivery", + ), + ( + "Seen satisfies Delivered", + "CLI docs must not imply seen evidence is strict delivery", + ), +]; + const LISTING_ADDR: &str = "30402:1111111111111111111111111111111111111111111111111111111111111111:AAAAAAAAAAAAAAAAAAAAAg"; const DERIVED_PROJECTION_SYNC_PUSH_FARM_D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAA"; @@ -2196,6 +2282,45 @@ fn transport_source_boundary_rejects_removed_relay_and_publish_proxy_surfaces() } #[test] +fn foundation_hardening_sources_reject_retired_names_and_ambiguous_docs() { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let mut findings = Vec::new(); + + for path in cli_foundation_hardening_guard_files(manifest_dir) { + let source = fs::read_to_string(path.as_path()).expect("read source"); + let relative_path = path + .strip_prefix(manifest_dir) + .expect("guard path under manifest") + .to_string_lossy() + .replace('\\', "/"); + + for (pattern, reason) in FORBIDDEN_FOUNDATION_HARDENING_RETIRED_CONCEPTS { + if contains_foundation_hardening_concept(source.as_str(), pattern) { + findings.push(format!( + "{relative_path} contains retired Foundation Hardening concept `{pattern}`: {reason}" + )); + } + } + + if is_foundation_doc_surface(path.as_path()) { + for (pattern, reason) in FORBIDDEN_FOUNDATION_HARDENING_DOC_CONCEPTS { + if source.contains(pattern) { + findings.push(format!( + "{relative_path} contains ambiguous Foundation Hardening wording `{pattern}`: {reason}" + )); + } + } + } + } + + assert!( + findings.is_empty(), + "CLI Foundation Hardening V1 source-boundary violations:\n{}", + findings.join("\n") + ); +} + +#[test] fn sync_transport_status_source_boundary_rejects_retired_relay_shaped_generic_output() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let view_source = @@ -2306,6 +2431,82 @@ fn sync_transport_status_source_boundary_rejects_retired_relay_shaped_generic_ou } } +fn cli_foundation_hardening_guard_files(manifest_dir: &Path) -> Vec<PathBuf> { + let mut paths = Vec::new(); + + for path in [ + manifest_dir.join("Cargo.toml"), + manifest_dir.join("README"), + manifest_dir.join("README.md"), + ] { + if path.exists() { + paths.push(path); + } + } + + for relative_root in ["src", "docs"] { + let root = manifest_dir.join(relative_root); + if root.exists() { + collect_foundation_hardening_guard_files(root.as_path(), &mut paths); + } + } + + paths.sort(); + paths +} + +fn collect_foundation_hardening_guard_files(root: &Path, paths: &mut Vec<PathBuf>) { + for entry in fs::read_dir(root) + .unwrap_or_else(|error| panic!("failed to read {}: {error}", root.display())) + { + let path = entry.expect("guard entry").path(); + if path.is_dir() { + collect_foundation_hardening_guard_files(path.as_path(), paths); + continue; + } + + let guarded_extension = matches!( + path.extension().and_then(|extension| extension.to_str()), + Some("rs") | Some("toml") | Some("md") + ); + if guarded_extension + || matches!( + path.file_name().and_then(|file_name| file_name.to_str()), + Some("README") | Some("README.md") + ) + { + paths.push(path); + } + } +} + +fn contains_foundation_hardening_concept(source: &str, pattern: &str) -> bool { + if !pattern.chars().all(is_foundation_identifier_character) { + return source.contains(pattern); + } + + source.match_indices(pattern).any(|(index, _)| { + let before = source[..index].chars().next_back(); + let after = source[index + pattern.len()..].chars().next(); + before.is_none_or(|character| !is_foundation_identifier_character(character)) + && after.is_none_or(|character| !is_foundation_identifier_character(character)) + }) +} + +fn is_foundation_identifier_character(character: char) -> bool { + character == '_' || character.is_ascii_alphanumeric() +} + +fn is_foundation_doc_surface(path: &Path) -> bool { + matches!( + path.file_name().and_then(|file_name| file_name.to_str()), + Some("README") | Some("README.md") + ) || matches!( + path.extension().and_then(|extension| extension.to_str()), + Some("md") + ) +} + fn source_window<'source>( source: &'source str, start_marker: &str,