sdk

Radroots SDK and bindings
git clone https://radroots.dev/git/sdk.git
Log | Files | Refs | README

commit 6a4f55616684d0b595ef1e0479abfd8ab2152692
parent b7cf74e494e703864fefd4a873b1177341c31721
Author: triesap <tyson@radroots.org>
Date:   Mon,  3 Aug 2026 12:14:17 +0000

sdk: migrate workspace consumers

- retire the xtask smoke consumer of removed SDK knowledge APIs
- keep only the final clean-host SDK smoke command surface
- record the intentional zero-dependent canonical workspace checkpoint
- defer separate CLI Studio mobile and daemon capsules to RCLD70

Diffstat:
Adocs/implementation/SDK_WORKSPACE_CONSUMERS.md | 27+++++++++++++++++++++++++++
Mtools/xtask/src/main.rs | 2+-
Mtools/xtask/src/smoke.rs | 234+++----------------------------------------------------------------------------
3 files changed, 35 insertions(+), 228 deletions(-)

diff --git a/docs/implementation/SDK_WORKSPACE_CONSUMERS.md b/docs/implementation/SDK_WORKSPACE_CONSUMERS.md @@ -0,0 +1,27 @@ +# SDK canonical-workspace consumer cutover + +Step 246 evaluated every Cargo package in the standalone SDK workspace before +the ordinary-user facade was activated. Cargo metadata reports no package with +a dependency on `radroots_sdk` at this checkpoint. That zero-dependent state is +intentional: + +- `radroots` remains the pulled-forward, dependency-free scaffold until Steps + 250–253 establish its curated modules, builders, and feature forwarding. +- binding and WASM packages consume their owning lower crates and do not route + through the native advanced-host SDK. +- the xtask clean-host smoke now uses only the final `ClientBuilder` and + `ErrorKind` surface. Its predecessor knowledge smoke, which referenced + removed SDK features and prefixed types, is retired. +- CLI, Studio, FFI/mobile, daemon, and other application capsules are separate + repositories/checkouts. Their ordered migrations remain assigned to Steps + 269–294 and are not folded into this standalone repository step. + +The package-owned predecessor examples and inactive integration sources under +`crates/sdk` are not Cargo consumers because the manifest sets `autotests = +false` and `autoexamples = false`. They remain quarantined only until the +ordered documentation and package-surface cleanup in Steps 247–248; they are +not compiled, exported, or authorized as compatibility APIs. + +No compatibility alias or feature was added for an external consumer. The +next consumer edge created in this repository must be the exact `radroots -> +radroots_sdk` dependency and forwarding contract specified for the facade. diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs @@ -90,7 +90,7 @@ fn command_action(args: &[String]) -> Result<CommandAction<'_>, String> { } fn usage() -> String { - "usage: cargo xtask architecture | cargo xtask architecture-ci | cargo xtask check-api-boundaries | cargo xtask check-dependency-boundaries | cargo xtask generate | cargo xtask generate ts | cargo xtask generate wasm [--package <key>] | cargo xtask generate package-metadata | cargo xtask check | cargo xtask smoke sdk-rust-local | cargo xtask smoke knowledge-rust-local | cargo xtask coverage run" + "usage: cargo xtask architecture | cargo xtask architecture-ci | cargo xtask check-api-boundaries | cargo xtask check-dependency-boundaries | cargo xtask generate | cargo xtask generate ts | cargo xtask generate wasm [--package <key>] | cargo xtask generate package-metadata | cargo xtask check | cargo xtask smoke sdk-rust-local | cargo xtask coverage run" .to_owned() } diff --git a/tools/xtask/src/smoke.rs b/tools/xtask/src/smoke.rs @@ -8,11 +8,7 @@ use crate::{ pub fn run(args: &[String]) -> Result<(), String> { match args { [target] if target == "sdk-rust-local" => sdk_rust_local(), - [target] if target == "knowledge-rust-local" => knowledge_rust_local(), - _ => Err( - "usage: cargo xtask smoke sdk-rust-local | cargo xtask smoke knowledge-rust-local" - .to_owned(), - ), + _ => Err("usage: cargo xtask smoke sdk-rust-local".to_owned()), } } @@ -86,36 +82,6 @@ radroots_sdk = {{ path = {sdk_path}, default-features = true }} Ok(manifest) } -fn knowledge_rust_local() -> Result<(), String> { - let root = workspace_root()?; - let sdk_path = root.join("crates/sdk"); - let toolchain = resolve_rust_toolchain(&root)?; - let nostr_version = exact_workspace_dependency_pin(&root.join("Cargo.toml"), "nostr")?; - let tempdir = - tempfile::tempdir().map_err(|error| format!("failed to create smoke tempdir: {error}"))?; - write_consumer( - &tempdir.path().join("Cargo.toml"), - &sdk_path, - &nostr_version, - )?; - let src_dir = tempdir.path().join("src"); - fs::create_dir_all(&src_dir) - .map_err(|error| format!("failed to create {}: {error}", src_dir.display()))?; - fs::write(src_dir.join("main.rs"), CONSUMER_MAIN) - .map_err(|error| format!("failed to write smoke consumer main.rs: {error}"))?; - let mut command = smoke_cargo_check_command(tempdir.path(), &toolchain); - let status = command - .status() - .map_err(|error| format!("failed to run smoke cargo check: {error}"))?; - if status.success() { - Ok(()) - } else { - Err(format!( - "knowledge Rust local smoke failed with status {status}" - )) - } -} - fn smoke_cargo_check_command(path: &Path, toolchain: &ResolvedRustToolchain) -> Command { let mut command = Command::new(&toolchain.cargo); command.arg("check").arg("--quiet").current_dir(path); @@ -123,164 +89,6 @@ fn smoke_cargo_check_command(path: &Path, toolchain: &ResolvedRustToolchain) -> command } -fn write_consumer(path: &Path, sdk_path: &Path, nostr_version: &str) -> Result<(), String> { - let cargo_toml = render_consumer_manifest(sdk_path, nostr_version)?; - fs::write(path, cargo_toml) - .map_err(|error| format!("failed to write {}: {error}", path.display())) -} - -fn render_consumer_manifest(sdk_path: &Path, nostr_version: &str) -> Result<String, String> { - let sdk_path = serde_json::to_string(&sdk_path.to_string_lossy()) - .map_err(|error| format!("failed to render SDK path: {error}"))?; - Ok(format!( - r#"[package] -name = "radroots_sdk_knowledge_smoke" -version = "0.1.0" -edition = "2024" -publish = false - -[dependencies] -radroots_sdk = {{ path = {sdk_path}, features = ["std", "serde", "serde_json", "nostr", "knowledge"] }} -nostr = "{nostr_version}" -"# - )) -} - -fn exact_workspace_dependency_pin(manifest_path: &Path, name: &str) -> Result<String, String> { - let contents = fs::read_to_string(manifest_path) - .map_err(|error| format!("failed to read {}: {error}", manifest_path.display()))?; - let version = workspace_dependency_version_from(&contents, name)?; - exact_dependency_pin(name, &version) -} - -fn workspace_dependency_version_from(contents: &str, name: &str) -> Result<String, String> { - let value = contents - .parse::<toml::Value>() - .map_err(|error| format!("failed to parse workspace manifest: {error}"))?; - let dependency = value - .get("workspace") - .and_then(|workspace| workspace.get("dependencies")) - .and_then(|dependencies| dependencies.get(name)) - .ok_or_else(|| format!("workspace dependency {name} is not defined"))?; - if let Some(version) = dependency.as_str() { - return Ok(version.to_owned()); - } - dependency - .get("version") - .and_then(toml::Value::as_str) - .map(str::to_owned) - .ok_or_else(|| format!("workspace dependency {name} must define version")) -} - -fn exact_dependency_pin(name: &str, version: &str) -> Result<String, String> { - let trimmed = version.trim().strip_prefix('=').unwrap_or(version.trim()); - if !is_exact_crates_version(trimmed) { - return Err(format!( - "workspace dependency {name} must use a full exact version for smoke pinning: {version}" - )); - } - Ok(format!("={trimmed}")) -} - -fn is_exact_crates_version(version: &str) -> bool { - let base = version - .split_once(['-', '+']) - .map_or(version, |(base, _)| base); - let parts = base.split('.').collect::<Vec<_>>(); - parts.len() == 3 - && parts.iter().all(|part| { - !part.is_empty() && part.chars().all(|character| character.is_ascii_digit()) - }) -} - -const CONSUMER_MAIN: &str = r#"use nostr::{EventBuilder, Keys, Kind, Tag, Timestamp}; -use radroots_event::envelope::EventEnvelopeParts; -use radroots_sdk::knowledge::prelude::*; - -const CREATED_AT: u32 = 1_800_000_000; - -fn main() -> Result<(), Box<dyn std::error::Error>> { - let keys = Keys::generate(); - let claim = claim_builder().build()?; - let parts = claim_builder().build_event()?; - let draft = claim_builder().build_draft(keys.public_key().to_hex(), CREATED_AT)?; - let decoded = verify_and_decode_radroots_event(sign_parts(parts, &keys)?)?; - let manifest = contract_manifest(); - - assert_eq!(draft.contract_id, KNOWLEDGE_CLAIM_CONTRACT_ID); - assert_eq!(manifest.contract_count, 11); - match decoded { - RadrootsDecodedEvent::KnowledgeClaim(parsed) => assert_eq!(parsed.data.data.text, claim.text), - _ => return Err("expected knowledge claim".into()), - } - Ok(()) -} - -fn sign_parts( - parts: Nip01EventWireParts, - keys: &Keys, -) -> Result<EventEnvelope, Box<dyn std::error::Error>> { - let tags = parts - .tags - .into_iter() - .map(Tag::parse) - .collect::<Result<Vec<_>, _>>()?; - let event = EventBuilder::new(Kind::Custom(parts.kind as u16), parts.content) - .tags(tags) - .custom_created_at(Timestamp::from_secs(u64::from(CREATED_AT))) - .sign_with_keys(keys)?; - Ok(EventEnvelope::new(EventEnvelopeParts { - id: event.id.to_hex(), - author: event.pubkey.to_hex(), - created_at: event.created_at.as_secs(), - kind: u32::from(event.kind.as_u16()), - tags: event - .tags - .as_slice() - .iter() - .map(|tag| tag.as_slice().to_vec()) - .collect(), - content: event.content, - sig: event.sig.to_string(), - })?) -} - -fn hex_64(character: char) -> String { - character.to_string().repeat(64) -} - -fn event_ref(character: char, kind: u32) -> EventRef { - EventRef { - id: hex_64(character), - author: radroots_identity::PublicKey::from_hex( - "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - ) - .expect("fixture public key"), - kind, - d_tag: None, - relays: Some(vec!["wss://relay.radroots.example".to_owned()]), - } -} - -fn claim_builder() -> RadrootsKnowledgeClaimBuilder { - RadrootsKnowledgeClaimBuilder::new() - .claim_type("practice_effect") - .text("Cover crops improve soil structure.") - .citation_span(KnowledgeCitationSpan { - source_ref: event_ref('4', KIND_KNOWLEDGE_SOURCE), - artifact_ref: None, - page_start: Some(12), - page_end: Some(13), - section_path: vec!["chapter-1".to_owned()], - quote_hash: Some(hex_64('5')), - chunk_id: Some("chunk-1".to_owned()), - }) - .topic("cover-crops") - .applies_to("local-food") - .author_asserted_confidence("medium") -} -"#; - const SDK_CONSUMER_MAIN: &str = r#"use radroots_sdk::{ClientBuilder, error::ErrorKind}; fn main() { @@ -298,10 +106,12 @@ mod tests { use crate::wasm::ResolvedRustToolchain; - use super::{ - exact_dependency_pin, render_consumer_manifest, render_sdk_consumer_manifest, - smoke_cargo_check_command, workspace_dependency_version_from, - }; + use super::{render_sdk_consumer_manifest, run, smoke_cargo_check_command}; + + #[test] + fn retired_sdk_knowledge_smoke_is_not_a_command_surface() { + assert!(run(&["knowledge-rust-local".to_owned()]).is_err()); + } #[test] fn sdk_smoke_consumer_uses_final_api_and_local_lower_package_patches() { @@ -321,36 +131,6 @@ mod tests { } #[test] - fn smoke_consumer_manifest_uses_exact_direct_dependency_pin() { - let manifest = - render_consumer_manifest(Path::new("/tmp/radroots_sdk"), "=0.44.2").expect("manifest"); - - assert!(manifest.contains("name = \"radroots_sdk_knowledge_smoke\"")); - assert!(manifest.contains("nostr = \"=0.44.2\"")); - assert!(!manifest.contains("nostr = \"0.44.2\"")); - } - - #[test] - fn smoke_workspace_dependency_version_reads_table_version() { - let version = workspace_dependency_version_from( - r#"[workspace.dependencies] -nostr = { version = "0.44.2" } -"#, - "nostr", - ) - .expect("version"); - - assert_eq!(version, "0.44.2"); - } - - #[test] - fn smoke_dependency_pin_rejects_floating_major_version() { - let error = exact_dependency_pin("nostr", "0.44").expect_err("floating version"); - - assert!(error.contains("full exact version")); - } - - #[test] fn smoke_command_uses_resolved_cargo_path() { let cargo = PathBuf::from("/tmp/rust-toolchain/bin/cargo"); let toolchain = ResolvedRustToolchain {