commit 7f3a3b7c221171ef310a124f1a81e14de8f935ef
parent 290b52d777967c2577cd7ab133d7d36df078fb52
Author: triesap <tyson@radroots.org>
Date: Mon, 22 Jun 2026 02:30:26 +0000
tools: guard rr-rs sdk boundary
- reject direct SDK, FFI, binding, and generated-package dependencies from rr-rs manifests
- fail hygiene when forbidden crate paths or retired top-level layout roots reappear
- block SDK package matrix, language export, and generated output paths inside rr-rs
- remove unused workspace binding dependencies from Cargo.toml
Diffstat:
2 files changed, 150 insertions(+), 5 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -164,9 +164,4 @@ uuid = { version = "1.22.0", features = ["v4", "v7"] }
x509-parser = { version = "0.17", default-features = false }
zstd = { version = "0.13", default-features = false }
zeroize = { version = "1" }
-uniffi = { version = "=0.29.4" }
-uniffi_build = { version = "=0.29.4" }
-wasm-bindgen = { version = "0.2" }
-wasm-bindgen-futures = { version = "0.4" }
-wasm-bindgen-test = { version = "0.3" }
rusqlite = { version = "0.32.1", default-features = false }
diff --git a/tools/xtask/src/hygiene.rs b/tools/xtask/src/hygiene.rs
@@ -1,6 +1,18 @@
use std::fs;
use std::path::{Path, PathBuf};
+const BINDING_DEPENDENCIES: &[&str] = &[
+ "serde-wasm-bindgen",
+ "ts-rs",
+ "typeshare",
+ "uniffi",
+ "uniffi-build",
+ "uniffi_build",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-bindgen-test",
+];
+
pub fn run(args: &[String], root: &Path) -> Result<(), String> {
match args.first().map(String::as_str) {
Some("forbidden-identifiers") => validate_forbidden_identifiers(root),
@@ -103,6 +115,38 @@ pub fn validate_forbidden_identifiers(root: &Path) -> Result<(), String> {
&["tools/xtask/src/hygiene.rs"],
&mut failures,
);
+ reject_binding_dependencies(root, &mut failures);
+ reject_forbidden_crate_paths(root, &mut failures);
+ reject_existing_paths(
+ root,
+ &[
+ "spec",
+ "policy",
+ "nix",
+ "scripts",
+ "bindings",
+ "dist",
+ "ffi",
+ "generated",
+ "packages",
+ "pkg",
+ "contracts/exports",
+ "contracts/language-exports",
+ "contracts/language-exports.toml",
+ "contracts/language_exports",
+ "contracts/language_exports.toml",
+ "contracts/package-matrix",
+ "contracts/package-matrix.toml",
+ "contracts/package_matrix",
+ "contracts/package_matrix.toml",
+ "contracts/sdk-exports",
+ "contracts/sdk_exports",
+ "spec/exports",
+ "spec/sdk-exports",
+ ],
+ "SDK, binding, generated-package, and retired layout paths must stay outside rr-rs",
+ &mut failures,
+ );
if failures.is_empty() {
println!("forbidden identifier hygiene passed");
@@ -115,6 +159,99 @@ pub fn validate_forbidden_identifiers(root: &Path) -> Result<(), String> {
}
}
+fn reject_binding_dependencies(root: &Path, failures: &mut Vec<String>) {
+ for file in manifest_files(root) {
+ let rel = display_path(root, &file);
+ let Ok(content) = fs::read_to_string(&file) else {
+ continue;
+ };
+ let Ok(manifest) = content.parse::<toml::Value>() else {
+ failures.push(format!("Cargo manifest must parse as TOML: {rel}"));
+ continue;
+ };
+ reject_binding_dependencies_in_value(&manifest, &mut Vec::new(), &rel, failures);
+ }
+}
+
+fn reject_binding_dependencies_in_value(
+ value: &toml::Value,
+ path: &mut Vec<String>,
+ manifest_rel: &str,
+ failures: &mut Vec<String>,
+) {
+ let Some(table) = value.as_table() else {
+ return;
+ };
+ if path
+ .last()
+ .is_some_and(|segment| is_dependency_table_name(segment))
+ {
+ for dependency in BINDING_DEPENDENCIES {
+ if table.contains_key(*dependency) {
+ failures.push(format!(
+ "SDK, FFI, binding, and generated-package dependencies are forbidden in rr-rs: {manifest_rel}: {dependency} in [{}]",
+ path.join(".")
+ ));
+ }
+ }
+ }
+ for (key, child) in table {
+ path.push(key.clone());
+ reject_binding_dependencies_in_value(child, path, manifest_rel, failures);
+ path.pop();
+ }
+}
+
+fn is_dependency_table_name(segment: &str) -> bool {
+ matches!(
+ segment,
+ "dependencies" | "dev-dependencies" | "build-dependencies"
+ )
+}
+
+fn manifest_files(root: &Path) -> Vec<PathBuf> {
+ let mut files = vec![root.join("Cargo.toml")];
+ files.extend(files_under(
+ root,
+ &[PathBuf::from("crates"), PathBuf::from("tools")],
+ ));
+ files.retain(|path| path.file_name().and_then(|name| name.to_str()) == Some("Cargo.toml"));
+ files.sort();
+ files.dedup();
+ files
+}
+
+fn reject_forbidden_crate_paths(root: &Path, failures: &mut Vec<String>) {
+ let Ok(entries) = fs::read_dir(root.join("crates")) else {
+ return;
+ };
+ for entry in entries.flatten() {
+ let path = entry.path();
+ if !path.is_dir() {
+ continue;
+ }
+ let name = entry.file_name().to_string_lossy().to_string();
+ if is_forbidden_crate_dir_name(&name) {
+ failures.push(format!(
+ "SDK, FFI, binding, and generated-package crate paths are forbidden in rr-rs: crates/{name}"
+ ));
+ }
+ }
+}
+
+fn is_forbidden_crate_dir_name(name: &str) -> bool {
+ let lowercase = name.to_ascii_lowercase();
+ lowercase.contains("ffi") || lowercase.contains("binding") || lowercase.contains("_wasm")
+}
+
+fn reject_existing_paths(root: &Path, rel_paths: &[&str], label: &str, failures: &mut Vec<String>) {
+ for rel_path in rel_paths {
+ if root.join(rel_path).exists() {
+ failures.push(format!("{label}: {rel_path}"));
+ }
+ }
+}
+
fn reject_substrings(
root: &Path,
rel_roots: &[PathBuf],
@@ -308,12 +445,25 @@ mod tests {
"crates/events/src/kinds.rs",
"pub const KIND_TRADE_LISTING_ORDER: u64 = 1;\n",
);
+ write_file(
+ &root,
+ "Cargo.toml",
+ "[workspace]\n[workspace.dependencies]\nwasm-bindgen = \"0.2\"\nuniffi = \"0.29\"\n",
+ );
+ fs::create_dir_all(root.join("crates/sql_wasm_bridge")).expect("create wasm crate dir");
+ fs::create_dir_all(root.join("scripts")).expect("create scripts dir");
+ fs::create_dir_all(root.join("contracts/sdk-exports")).expect("create sdk exports dir");
let err = validate_forbidden_identifiers(&root).expect_err("dirty tree");
assert!(err.contains("relay fetch must not bypass event-store verification"));
assert!(err.contains("event-store projection cursors must use last_event_seq"));
assert!(err.contains("raw commercial protocol identifier String fields are forbidden"));
assert!(err.contains("legacy identifier 'tangle' must not reappear"));
assert!(err.contains("legacy trade listing kind constants must not reappear"));
+ assert!(err.contains("wasm-bindgen"));
+ assert!(err.contains("uniffi"));
+ assert!(err.contains("crates/sql_wasm_bridge"));
+ assert!(err.contains("scripts"));
+ assert!(err.contains("contracts/sdk-exports"));
let _ = fs::remove_dir_all(root);
}