From d50c7920c47d905d279d746edaa809eacc1d37aa Mon Sep 17 00:00:00 2001 From: mfw78 Date: Thu, 6 Aug 2026 16:31:40 +0000 Subject: [PATCH 1/5] refactor(types): derive the remaining mechanical newtype boilerplate AI Assistance: Claude Fable 5 used for the derive_more sweep and conversion --- Cargo.lock | 1 + crates/nexum-runtime/src/test_utils/manifest.rs | 16 +++------------- crates/nexum-sdk-test/Cargo.toml | 1 + crates/nexum-sdk-test/src/lib.rs | 13 +------------ 4 files changed, 6 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4519285..7fb3d7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3381,6 +3381,7 @@ dependencies = [ name = "nexum-sdk-test" version = "0.1.0" dependencies = [ + "derive_more", "nexum-sdk", "tracing", ] diff --git a/crates/nexum-runtime/src/test_utils/manifest.rs b/crates/nexum-runtime/src/test_utils/manifest.rs index 0ba19ff..62d70f0 100644 --- a/crates/nexum-runtime/src/test_utils/manifest.rs +++ b/crates/nexum-runtime/src/test_utils/manifest.rs @@ -2,13 +2,15 @@ use std::path::{Path, PathBuf}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, derive_more::From)] pub enum ManifestSource { /// No explicit path; the loader falls back to discovery beside the component. Beside, /// A path handed to the loader as-is, existing or not. + #[from] Path(PathBuf), /// Manifest text written out at boot. + #[from] Toml(String), } @@ -32,18 +34,6 @@ impl From for ManifestSource { } } -impl From for ManifestSource { - fn from(toml: String) -> Self { - Self::Toml(toml) - } -} - -impl From for ManifestSource { - fn from(path: PathBuf) -> Self { - Self::Path(path) - } -} - /// Builder for positive-path manifest TOML. #[derive(Debug, Clone)] pub struct TestManifest { diff --git a/crates/nexum-sdk-test/Cargo.toml b/crates/nexum-sdk-test/Cargo.toml index 8aea33e..7a9ca9b 100644 --- a/crates/nexum-sdk-test/Cargo.toml +++ b/crates/nexum-sdk-test/Cargo.toml @@ -12,4 +12,5 @@ description = "In-memory host mocks for nexum module unit tests. Implements nexu [dependencies] nexum-sdk = { path = "../nexum-sdk" } +derive_more.workspace = true tracing.workspace = true diff --git a/crates/nexum-sdk-test/src/lib.rs b/crates/nexum-sdk-test/src/lib.rs index 7c637c7..86869eb 100644 --- a/crates/nexum-sdk-test/src/lib.rs +++ b/crates/nexum-sdk-test/src/lib.rs @@ -883,7 +883,7 @@ pub struct CapturedEvent { } /// A field value as tracing's `Visit` delivered it. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, derive_more::Display)] pub enum FieldValue { /// A `record_str` value. Str(String), @@ -898,17 +898,6 @@ pub enum FieldValue { Debug(String), } -impl fmt::Display for FieldValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - FieldValue::Str(v) | FieldValue::Debug(v) => f.write_str(v), - FieldValue::U64(v) => write!(f, "{v}"), - FieldValue::I64(v) => write!(f, "{v}"), - FieldValue::Bool(v) => write!(f, "{v}"), - } - } -} - impl CapturedEvent { /// The value recorded for `name`, if the event carried it. pub fn field(&self, name: &str) -> Option<&FieldValue> { From d74f432a30b3f61168c02aeeea34bc16fa415372 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Thu, 6 Aug 2026 16:31:47 +0000 Subject: [PATCH 2/5] feat(sdk): carve a public logging-only bind_host entry point AI Assistance: Claude Fable 5 used for the macro split and consumer-shaped test --- crates/nexum-sdk/src/wit_bindgen_macro.rs | 19 +++++- crates/nexum-sdk/tests/wit_bindgen_logging.rs | 58 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 crates/nexum-sdk/tests/wit_bindgen_logging.rs diff --git a/crates/nexum-sdk/src/wit_bindgen_macro.rs b/crates/nexum-sdk/src/wit_bindgen_macro.rs index 39558de..66a6211 100644 --- a/crates/nexum-sdk/src/wit_bindgen_macro.rs +++ b/crates/nexum-sdk/src/wit_bindgen_macro.rs @@ -7,7 +7,8 @@ //! zero-argument form emits the full six-interface set. Either way the //! wit-bindgen output for the world must already be in scope, so //! selecting a capability the world does not import is a compile error. -//! A domain SDK layers its own interfaces on the same `WitBindgenHost`. +//! A domain SDK layers its own interfaces on the same `WitBindgenHost`, +//! or binds logging alone via [`bind_host_logging_via_wit_bindgen!`]. //! //! ```ignore //! wit_bindgen::generate!({ /* ... */ }); @@ -326,12 +327,26 @@ macro_rules! __bind_host_cap_via_wit_bindgen { } }; (logging) => { + $crate::bind_host_logging_via_wit_bindgen!(); + impl $crate::host::LoggingHost for WitBindgenHost { fn log(&self, level: $crate::Level, message: &str) { nexum::host::logging::log(nexum::host::logging::Level::from(level), message); } } + }; +} +/// Logging-only slice of [`bind_host_via_wit_bindgen!`]: needs only the +/// generated `nexum::host::logging` in scope, never `WitBindgenHost` or +/// the base block, so a domain world whose `nexum:host/types` is +/// foreign can still bind logging. +/// +/// The generated names `HostLogSink` and `install_tracing` are visible +/// in the caller's scope (`macro_rules!` is not hygienic for items). +#[macro_export] +macro_rules! bind_host_logging_via_wit_bindgen { + () => { /// Translate a `tracing_core::Level` into the wit-bindgen /// `logging::Level` wire enum. impl ::core::convert::From<$crate::Level> for nexum::host::logging::Level { @@ -355,7 +370,7 @@ macro_rules! __bind_host_cap_via_wit_bindgen { impl $crate::tracing::LogSink for HostLogSink { fn log(&self, level: $crate::Level, message: &str) { - ::log(&WitBindgenHost, level, message); + nexum::host::logging::log(::core::convert::From::from(level), message); } } diff --git a/crates/nexum-sdk/tests/wit_bindgen_logging.rs b/crates/nexum-sdk/tests/wit_bindgen_logging.rs new file mode 100644 index 0000000..59bb04b --- /dev/null +++ b/crates/nexum-sdk/tests/wit_bindgen_logging.rs @@ -0,0 +1,58 @@ +//! A domain-SDK-shaped consumer binds `bind_host_logging_via_wit_bindgen!` +//! against its own generated `nexum::host::logging`, with no base block +//! (no `WitBindgenHost`, no `nexum:host/types`) in scope. + +mod nexum { + pub mod host { + /// Stands in for the per-cdylib wit-bindgen `logging` output. + pub mod logging { + use std::sync::Mutex; + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum Level { + Trace, + Debug, + Info, + Warn, + Error, + } + + pub static RECORDED: Mutex> = Mutex::new(Vec::new()); + + pub fn log(level: Level, message: &str) { + RECORDED.lock().unwrap().push((level, message.to_owned())); + } + } + } +} + +nexum_sdk::bind_host_logging_via_wit_bindgen!(); + +use nexum::host::logging::Level as Wire; + +#[test] +fn sink_forwards_to_the_bound_logging_call() { + use nexum_sdk::tracing::LogSink as _; + + HostLogSink.log(nexum_sdk::Level::INFO, "ready"); + let recorded = nexum::host::logging::RECORDED.lock().unwrap(); + assert_eq!(recorded.as_slice(), [(Wire::Info, "ready".to_owned())]); +} + +#[test] +fn level_mapping_covers_the_wire_enum() { + for (level, wire) in [ + (nexum_sdk::Level::ERROR, Wire::Error), + (nexum_sdk::Level::WARN, Wire::Warn), + (nexum_sdk::Level::INFO, Wire::Info), + (nexum_sdk::Level::DEBUG, Wire::Debug), + (nexum_sdk::Level::TRACE, Wire::Trace), + ] { + assert_eq!(Wire::from(level), wire); + } +} + +#[test] +fn facade_install_runs_without_the_base_block() { + install_tracing(); +} From 3d247bf007651ca28e9b1f0a8512f6ef9db927c2 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Thu, 6 Aug 2026 16:55:05 +0000 Subject: [PATCH 3/5] fix(sdk): repair the logging macro intra-doc link AI Assistance: Claude (Opus 5) used for the red-team pass and the doc fix. --- crates/nexum-sdk/src/wit_bindgen_macro.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/nexum-sdk/src/wit_bindgen_macro.rs b/crates/nexum-sdk/src/wit_bindgen_macro.rs index 66a6211..585eb4d 100644 --- a/crates/nexum-sdk/src/wit_bindgen_macro.rs +++ b/crates/nexum-sdk/src/wit_bindgen_macro.rs @@ -8,7 +8,7 @@ //! wit-bindgen output for the world must already be in scope, so //! selecting a capability the world does not import is a compile error. //! A domain SDK layers its own interfaces on the same `WitBindgenHost`, -//! or binds logging alone via [`bind_host_logging_via_wit_bindgen!`]. +//! or binds logging alone via [`crate::bind_host_logging_via_wit_bindgen!`]. //! //! ```ignore //! wit_bindgen::generate!({ /* ... */ }); @@ -338,9 +338,8 @@ macro_rules! __bind_host_cap_via_wit_bindgen { } /// Logging-only slice of [`bind_host_via_wit_bindgen!`]: needs only the -/// generated `nexum::host::logging` in scope, never `WitBindgenHost` or -/// the base block, so a domain world whose `nexum:host/types` is -/// foreign can still bind logging. +/// generated `nexum::host::logging` in scope, never `nexum::host::types` +/// or `WitBindgenHost`. /// /// The generated names `HostLogSink` and `install_tracing` are visible /// in the caller's scope (`macro_rules!` is not hygienic for items). From 7df1fa80e92ed423f3d30eacfa3645d959c8a6ba Mon Sep 17 00:00:00 2001 From: mfw78 Date: Thu, 6 Aug 2026 16:55:06 +0000 Subject: [PATCH 4/5] test(sdk): assert the installed facade reaches the bound logging call AI Assistance: Claude (Opus 5) used for the mutation check and the assertion strengthening. --- crates/nexum-sdk/tests/wit_bindgen_logging.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/nexum-sdk/tests/wit_bindgen_logging.rs b/crates/nexum-sdk/tests/wit_bindgen_logging.rs index 59bb04b..d613f03 100644 --- a/crates/nexum-sdk/tests/wit_bindgen_logging.rs +++ b/crates/nexum-sdk/tests/wit_bindgen_logging.rs @@ -30,13 +30,22 @@ nexum_sdk::bind_host_logging_via_wit_bindgen!(); use nexum::host::logging::Level as Wire; +/// The recorder is process-wide, so every assertion is a containment +/// check rather than an equality on the whole log. +fn recorded(line: &str) -> Option { + let recorded = nexum::host::logging::RECORDED.lock().unwrap(); + recorded + .iter() + .find(|(_, message)| message == line) + .map(|(level, _)| *level) +} + #[test] fn sink_forwards_to_the_bound_logging_call() { use nexum_sdk::tracing::LogSink as _; HostLogSink.log(nexum_sdk::Level::INFO, "ready"); - let recorded = nexum::host::logging::RECORDED.lock().unwrap(); - assert_eq!(recorded.as_slice(), [(Wire::Info, "ready".to_owned())]); + assert_eq!(recorded("ready"), Some(Wire::Info)); } #[test] @@ -53,6 +62,8 @@ fn level_mapping_covers_the_wire_enum() { } #[test] -fn facade_install_runs_without_the_base_block() { +fn facade_install_routes_events_to_the_bound_logging_call() { install_tracing(); + tracing::warn!("through the facade"); + assert_eq!(recorded("through the facade"), Some(Wire::Warn)); } From f0ba91105dbbd810ddc1b485894bcea61e0063af Mon Sep 17 00:00:00 2001 From: mfw78 Date: Thu, 6 Aug 2026 16:55:06 +0000 Subject: [PATCH 5/5] refactor(runtime): derive the content-digest Display AI Assistance: Claude (Opus 5) used for the derive sweep and the flag census. --- crates/nexum-runtime/src/digest.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/nexum-runtime/src/digest.rs b/crates/nexum-runtime/src/digest.rs index 038e473..bace36c 100644 --- a/crates/nexum-runtime/src/digest.rs +++ b/crates/nexum-runtime/src/digest.rs @@ -1,6 +1,5 @@ //! Content digests for loaded component artifacts. -use std::fmt; use std::path::PathBuf; use std::str::FromStr; @@ -9,8 +8,10 @@ use thiserror::Error; const SCHEME: &str = "sha256"; -/// sha256 digest of an artifact's bytes. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +/// sha256 digest of an artifact's bytes; `Display` is the canonical +/// lowercase `sha256:` the manifest grammar parses back. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, derive_more::Display)] +#[display("{SCHEME}:{}", alloy_primitives::hex::encode(_0))] pub struct ContentDigest([u8; 32]); impl ContentDigest { @@ -56,13 +57,6 @@ impl FromStr for ContentDigest { } } -impl fmt::Display for ContentDigest { - /// Canonical lowercase `sha256:`. - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{SCHEME}:{}", alloy_primitives::hex::encode(self.0)) - } -} - #[derive(Debug, Error)] #[non_exhaustive] pub enum DigestParseError {