From 7a18d3168fb6672ed8ec57580e928a8352c1597c Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Mon, 27 Jul 2026 16:42:18 +0200 Subject: [PATCH 01/27] feat(hardware_backend): add native CUDA ABI type aliases --- crates/ptwm-core/src/flavor/native.rs | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crates/ptwm-core/src/flavor/native.rs b/crates/ptwm-core/src/flavor/native.rs index de5ef10..15875f3 100644 --- a/crates/ptwm-core/src/flavor/native.rs +++ b/crates/ptwm-core/src/flavor/native.rs @@ -94,6 +94,44 @@ pub type DeltaSchemeFn = unsafe extern "C" fn( usize, // output len ) -> i64; +/// `hardware_backend_v1_cuda_stream_handle` — returns this backend's own, +/// process-persistent CUDA stream (as a raw pointer value) for the given +/// device ordinal. Callers pass this same handle to `tensor.__dlpack__ +/// (stream=...)` for every tensor involved in a subsequent decode call, so +/// PyTorch's DLPack producer inserts the correct cross-stream wait before +/// handing back the device pointer. Returns 0 on failure (no such device, +/// CUDA init failed). +pub type HardwareBackendCudaStreamHandleFn = unsafe extern "C" fn(device_ordinal: u32) -> u64; + +/// `hardware_backend_v1_dispatch_decode_cuda` — decode `in_dev_ptr` into +/// `out_dev_ptr`, two distinct device buffers (this is NOT an in-place +/// transform over one buffer; "zero-copy" means no host round-trip, not a +/// shared address). `state_bytes` carries the codec's small per-tensor +/// state (e.g. a 16-entry codebook), in the same `(state_format_version, +/// state_bytes)` shape `plane_codec`'s existing `decode_stateful` path +/// already carries. `codec_id` self-describes the wire format for the +/// extension to validate against (distinct from the backend's own +/// canonical id, which the router already resolved to get here). +/// +/// Completion contract: by the time this function returns, the kernel has +/// FULLY COMPLETED (the extension synchronizes its own stream before +/// returning) — `out_dev_ptr`'s contents are valid and visible to any +/// subsequent CUDA operation on any stream, with no further caller-side +/// synchronization needed. Both launch-time and execution-time errors are +/// visible via the return code, since the synchronize() call observes both. +pub type HardwareBackendCudaDispatchDecodeFn = unsafe extern "C" fn( + state_format_version: u8, + state_ptr: *const u8, + state_len: usize, + codec_id_ptr: *const u8, + codec_id_len: usize, + in_dev_ptr: u64, + in_len: usize, + out_dev_ptr: u64, + out_len: usize, + device_ordinal: u32, +) -> i64; + pub struct NativeExtension { // Held for its drop-time side effect: keeping the dlopen handle alive // so all fn pointers in `symbols` remain valid. From a69e6515b49ea323e29a8ec5d7964c3cc145948c Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Mon, 27 Jul 2026 18:42:00 +0200 Subject: [PATCH 02/27] fix(hardware_backend): remove em-dashes from ABI doc comments --- crates/ptwm-core/src/flavor/native.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ptwm-core/src/flavor/native.rs b/crates/ptwm-core/src/flavor/native.rs index 15875f3..fbb5fb0 100644 --- a/crates/ptwm-core/src/flavor/native.rs +++ b/crates/ptwm-core/src/flavor/native.rs @@ -94,7 +94,7 @@ pub type DeltaSchemeFn = unsafe extern "C" fn( usize, // output len ) -> i64; -/// `hardware_backend_v1_cuda_stream_handle` — returns this backend's own, +/// `hardware_backend_v1_cuda_stream_handle`: returns this backend's own, /// process-persistent CUDA stream (as a raw pointer value) for the given /// device ordinal. Callers pass this same handle to `tensor.__dlpack__ /// (stream=...)` for every tensor involved in a subsequent decode call, so @@ -115,7 +115,7 @@ pub type HardwareBackendCudaStreamHandleFn = unsafe extern "C" fn(device_ordinal /// /// Completion contract: by the time this function returns, the kernel has /// FULLY COMPLETED (the extension synchronizes its own stream before -/// returning) — `out_dev_ptr`'s contents are valid and visible to any +/// returning). `out_dev_ptr`'s contents are valid and visible to any /// subsequent CUDA operation on any stream, with no further caller-side /// synchronization needed. Both launch-time and execution-time errors are /// visible via the return code, since the synchronize() call observes both. From 6eff6fd589ce6d49a72f2c5fc650d54c27c832da Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Mon, 27 Jul 2026 18:48:36 +0200 Subject: [PATCH 03/27] fix(hardware_backend): remove remaining em-dash from ABI doc comment --- crates/ptwm-core/src/flavor/native.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ptwm-core/src/flavor/native.rs b/crates/ptwm-core/src/flavor/native.rs index fbb5fb0..95a39c8 100644 --- a/crates/ptwm-core/src/flavor/native.rs +++ b/crates/ptwm-core/src/flavor/native.rs @@ -103,7 +103,7 @@ pub type DeltaSchemeFn = unsafe extern "C" fn( /// CUDA init failed). pub type HardwareBackendCudaStreamHandleFn = unsafe extern "C" fn(device_ordinal: u32) -> u64; -/// `hardware_backend_v1_dispatch_decode_cuda` — decode `in_dev_ptr` into +/// `hardware_backend_v1_dispatch_decode_cuda`: decode `in_dev_ptr` into /// `out_dev_ptr`, two distinct device buffers (this is NOT an in-place /// transform over one buffer; "zero-copy" means no host round-trip, not a /// shared address). `state_bytes` carries the codec's small per-tensor From 4bf7d4345c056501992764b4b9a0606ce10a24d1 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Mon, 27 Jul 2026 19:00:13 +0200 Subject: [PATCH 04/27] feat(hardware_backend): required native symbol probe, replacing lenient catch-all --- crates/ptwm-core/src/flavor/native.rs | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crates/ptwm-core/src/flavor/native.rs b/crates/ptwm-core/src/flavor/native.rs index 95a39c8..c6168d9 100644 --- a/crates/ptwm-core/src/flavor/native.rs +++ b/crates/ptwm-core/src/flavor/native.rs @@ -49,6 +49,9 @@ pub struct NativeSymbols { // DeltaScheme pub delta_scheme_v1_encode: Option, pub delta_scheme_v1_decode: Option, + // HardwareBackend + pub hardware_backend_v1_cuda_stream_handle: Option, + pub hardware_backend_v1_dispatch_decode_cuda: Option, } pub type PlaneCodecFn = unsafe extern "C" fn( @@ -164,6 +167,8 @@ impl NativeExtension { transform_v1_inverse: None, delta_scheme_v1_encode: None, delta_scheme_v1_decode: None, + hardware_backend_v1_cuda_stream_handle: None, + hardware_backend_v1_dispatch_decode_cuda: None, }; // Probe symbols depending on the kind. @@ -213,6 +218,23 @@ impl NativeExtension { }); } } + Kind::HardwareBackend => { + symbols.hardware_backend_v1_cuda_stream_handle = resolve::( + &library, + b"ptwm_hardware_backend_v1_cuda_stream_handle\0", + ); + symbols.hardware_backend_v1_dispatch_decode_cuda = resolve::( + &library, + b"ptwm_hardware_backend_v1_dispatch_decode_cuda\0", + ); + if symbols.hardware_backend_v1_cuda_stream_handle.is_none() + || symbols.hardware_backend_v1_dispatch_decode_cuda.is_none() + { + return Err(CodecError::Unsupported { + feature: "missing required hardware_backend_v1 symbol".into(), + }); + } + } _ => { // Other kinds: probe is lenient for v1. The dispatcher // will yield Unsupported on invocation if the symbol's @@ -436,4 +458,20 @@ mod tests { ); assert!(matches!(res, Err(CodecError::InvalidInput))); } + + #[test] + fn hardware_backend_missing_required_symbols_is_unsupported() { + // A library with neither ptwm_hardware_backend_v1_cuda_stream_handle + // nor ptwm_hardware_backend_v1_dispatch_decode_cuda must fail to load + // for Kind::HardwareBackend, same strictness as PlaneCodec/DeltaScheme. + // This test is a placeholder for Task 11's real artifact-backed test; + // the path-doesn't-exist error fires before symbol probing, so the test + // passes trivially for now. + let res = NativeExtension::load( + Path::new("/this/path/does/not/exist.so"), + &entry(Kind::HardwareBackend), + VerifiedToken::new_unchecked(), + ); + assert!(matches!(res, Err(CodecError::InvalidInput))); + } } From 5462000fd5726f2bd59613b763aff1a88d52c9d6 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Mon, 27 Jul 2026 19:27:11 +0200 Subject: [PATCH 05/27] feat(hardware_backend): invoke_hardware_backend_* methods on NativeExtension --- crates/ptwm-core/src/flavor/native.rs | 94 +++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/crates/ptwm-core/src/flavor/native.rs b/crates/ptwm-core/src/flavor/native.rs index c6168d9..074c84c 100644 --- a/crates/ptwm-core/src/flavor/native.rs +++ b/crates/ptwm-core/src/flavor/native.rs @@ -395,6 +395,62 @@ impl NativeExtension { }; decode_rc(rc, output.len()) } + + /// Invoke `ptwm_hardware_backend_v1_cuda_stream_handle`. Returns this + /// backend's process-persistent CUDA stream handle for `device_ordinal`. + pub fn invoke_hardware_backend_cuda_stream_handle( + &self, + device_ordinal: u32, + ) -> Result { + let f = self.symbols.hardware_backend_v1_cuda_stream_handle.ok_or( + CodecError::Unsupported { + feature: "hardware_backend_v1_cuda_stream_handle not present".into(), + }, + )?; + let handle = unsafe { f(device_ordinal) }; + if handle == 0 { + return Err(CodecError::Unsupported { + feature: "hardware backend failed to produce a CUDA stream handle".into(), + }); + } + Ok(handle) + } + + /// Invoke `ptwm_hardware_backend_v1_dispatch_decode_cuda`. Decodes + /// `in_dev_ptr` into `out_dev_ptr`, two distinct device buffers. + #[allow(clippy::too_many_arguments)] + pub fn invoke_hardware_backend_dispatch_decode_cuda( + &self, + state_bytes: &[u8], + codec_id: &CanonicalId, + in_dev_ptr: u64, + in_len: usize, + out_dev_ptr: u64, + out_len: usize, + device_ordinal: u32, + ) -> Result { + let f = self.symbols.hardware_backend_v1_dispatch_decode_cuda.ok_or( + CodecError::Unsupported { + feature: "hardware_backend_v1_dispatch_decode_cuda not present".into(), + }, + )?; + let codec_id_bytes = codec_id.as_bytes(); + let rc = unsafe { + f( + 1, // state_format_version + state_bytes.as_ptr(), + state_bytes.len(), + codec_id_bytes.as_ptr(), + codec_id_bytes.len(), + in_dev_ptr, + in_len, + out_dev_ptr, + out_len, + device_ordinal, + ) + }; + decode_rc(rc, out_len) + } } fn decode_rc(rc: i64, out_capacity: usize) -> Result { @@ -449,6 +505,35 @@ mod tests { } } + /// Build a bare `NativeExtension` with every `NativeSymbols` field set + /// to `None`, for exercising "symbol absent" invoke paths without + /// dlopen-ing a real contribution artifact from disk. + /// + /// `Library::this()` wraps a handle to the already-loaded host process + /// instead of opening a new shared object, which gives a real, valid + /// `Library` (fn pointers can be safely dropped/never resolved against + /// it) without depending on any particular file existing on disk. + fn native_extension_with_no_hardware_backend_symbols() -> NativeExtension { + let library: Library = libloading::os::unix::Library::this().into(); + NativeExtension { + library, + canonical_id: CanonicalId::from_bytes([0xCD; 32]), + kind: Kind::HardwareBackend, + lifecycle: Lifecycle::Thread, + symbols: NativeSymbols { + plane_codec_v1_encode: None, + plane_codec_v1_decode: None, + plane_codec_v1_decode_stateful: None, + transform_v1_forward: None, + transform_v1_inverse: None, + delta_scheme_v1_encode: None, + delta_scheme_v1_decode: None, + hardware_backend_v1_cuda_stream_handle: None, + hardware_backend_v1_dispatch_decode_cuda: None, + }, + } + } + #[test] fn missing_library_path_is_invalid_input() { let res = NativeExtension::load( @@ -459,6 +544,15 @@ mod tests { assert!(matches!(res, Err(CodecError::InvalidInput))); } + #[test] + fn hardware_backend_invoke_stream_handle_errors_when_symbol_absent() { + // A NativeExtension whose hardware_backend_v1_cuda_stream_handle symbol + // was never resolved (None) must return Unsupported, not panic/UB. + let ext = native_extension_with_no_hardware_backend_symbols(); // test-only constructor, see Step 3 + let res = ext.invoke_hardware_backend_cuda_stream_handle(0); + assert!(matches!(res, Err(CodecError::Unsupported { .. }))); + } + #[test] fn hardware_backend_missing_required_symbols_is_unsupported() { // A library with neither ptwm_hardware_backend_v1_cuda_stream_handle From 889cf773060f5e01d70f4a5fa95b12dcc6e4f795 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Mon, 27 Jul 2026 19:45:33 +0200 Subject: [PATCH 06/27] feat(hardware_backend): DispatchedHardwareBackendCuda trait + native adapter --- crates/ptwm-core/src/flavor/router.rs | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/crates/ptwm-core/src/flavor/router.rs b/crates/ptwm-core/src/flavor/router.rs index c7ea5d4..414c6a3 100644 --- a/crates/ptwm-core/src/flavor/router.rs +++ b/crates/ptwm-core/src/flavor/router.rs @@ -460,6 +460,52 @@ impl DeltaSchemeRouter { } } +// ── HardwareBackend router ───────────────────────────────────────────────────── + +/// Uniform decode surface for GPU-accelerated decode operations. +/// +/// Provides two methods: `dispatch_decode_cuda` for decoding on CUDA devices, +/// and `cuda_stream_handle` to acquire a stream for a given device ordinal. +pub trait DispatchedHardwareBackendCuda: Send + Sync { + fn dispatch_decode_cuda( + &self, + state_bytes: &[u8], + codec_id: &CanonicalId, + in_dev_ptr: u64, + in_len: usize, + out_dev_ptr: u64, + out_len: usize, + device_ordinal: u32, + ) -> Result; + + fn cuda_stream_handle(&self, device_ordinal: u32) -> Result; +} + +struct NativeHardwareBackendCudaAdapter { + inner: Arc, +} + +impl DispatchedHardwareBackendCuda for NativeHardwareBackendCudaAdapter { + fn dispatch_decode_cuda( + &self, + state_bytes: &[u8], + codec_id: &CanonicalId, + in_dev_ptr: u64, + in_len: usize, + out_dev_ptr: u64, + out_len: usize, + device_ordinal: u32, + ) -> Result { + self.inner.invoke_hardware_backend_dispatch_decode_cuda( + state_bytes, codec_id, in_dev_ptr, in_len, out_dev_ptr, out_len, device_ordinal, + ) + } + + fn cuda_stream_handle(&self, device_ordinal: u32) -> Result { + self.inner.invoke_hardware_backend_cuda_stream_handle(device_ordinal) + } +} + // ── Helpers ─────────────────────────────────────────────────────────────────── /// Find the `DiscoveredContribution` whose manifest declares a contribution @@ -719,4 +765,10 @@ mod tests { "expected Unsupported for unknown delta_scheme canonical id" ); } + + #[test] + fn native_hardware_backend_adapter_forwards_to_extension() { + fn assert_impl() {} + assert_impl::(); + } } From 55ffdb3d1255faf7b1859c75b062b4a09c055caf Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Mon, 27 Jul 2026 20:11:45 +0200 Subject: [PATCH 07/27] feat(hardware_backend): HardwareBackendRouter, native-only, capability-gated resolve() gates every lookup through capability_check::check before attempting a native dlopen, the crate's first production call site for that function. HardwareBackendRouter::new keeps the same signature as PlaneCodecRouter/DeltaSchemeRouter (no policy parameter): neither existing router threads a HostPolicy through its constructor, and HostPolicy::default().available_hardware is an empty Vec, so the default denies every hardware_class-declaring contribution unconditionally. That is documented on resolve() itself rather than left as a TODO. --- crates/ptwm-core/src/flavor/router.rs | 216 +++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 2 deletions(-) diff --git a/crates/ptwm-core/src/flavor/router.rs b/crates/ptwm-core/src/flavor/router.rs index 414c6a3..08a0cc9 100644 --- a/crates/ptwm-core/src/flavor/router.rs +++ b/crates/ptwm-core/src/flavor/router.rs @@ -39,7 +39,8 @@ use crate::flavor::wasm::{ WasmExtension, invoke_delta_scheme_decode, invoke_delta_scheme_encode, invoke_plane_codec_decode, invoke_plane_codec_decode_stateful, invoke_plane_codec_encode, }; -use crate::policy::capability_check::HostPolicy; +use crate::policy::capability_check::{CapabilityVerdict, HostPolicy, check}; +use crate::policy::native_deps::VendorTable; /// Uniform encode/decode surface exposed by the router. /// @@ -506,6 +507,129 @@ impl DispatchedHardwareBackendCuda for NativeHardwareBackendCudaAdapter { } } +/// Resolves a `CanonicalId` to a `Box`. +/// +/// Same shape as [`DeltaSchemeRouter`], with two deliberate differences. +/// First, there is no WASM branch: a `u64` device pointer cannot be +/// expressed in WASM's 32-bit linear address space, so `hardware_backend` +/// is native-only by construction. Second, [`resolve`](Self::resolve) runs +/// [`check`] against the entry's declared capabilities before attempting a +/// native load; see that method's documentation for why `HostPolicy::default()` +/// is the correct policy to use there rather than a caller-supplied one. +pub struct HardwareBackendRouter { + installed: Vec, + cache: Mutex>>, +} + +impl std::fmt::Debug for HardwareBackendRouter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("HardwareBackendRouter") + .field("installed_count", &self.installed.len()) + .finish_non_exhaustive() + } +} + +impl HardwareBackendRouter { + /// Create a new router with the given set of discovered installed + /// extensions. Pass an empty `Vec` to get `Unsupported` for every id + /// (there are no built-in hardware backends). + pub fn new(installed: Vec) -> Self { + Self { + installed, + cache: Mutex::new(HashMap::new()), + } + } + + /// Resolve `canonical_id` to a `DispatchedHardwareBackendCuda`. + pub fn get( + &self, + canonical_id: &CanonicalId, + ) -> Result, CodecError> { + { + let cache = self.cache.lock().unwrap(); + if let Some(backend) = cache.get(canonical_id) { + return Ok(Arc::clone(backend)); + } + } + + let backend = self.resolve(canonical_id)?; + let arc = Arc::from(backend); + { + let mut cache = self.cache.lock().unwrap(); + cache.insert(*canonical_id, Arc::clone(&arc)); + } + Ok(arc) + } + + /// Look up `canonical_id`, gate it through [`check`], and dlopen the + /// native artifact if admitted. + /// + /// # Policy default + /// + /// Neither [`PlaneCodecRouter`] nor [`DeltaSchemeRouter`] threads a + /// `HostPolicy` through its constructor, and until this method, + /// nothing in the crate outside of `capability_check`'s own test + /// module called [`check`] at all: this is the crate's first + /// production call site. Using `HostPolicy::default()` here rather + /// than adding a `policy` parameter to [`HardwareBackendRouter::new`] + /// is a deliberate choice, not an oversight, for one concrete reason: + /// `HostPolicy::default().available_hardware` is an empty + /// `Vec`, and `check`'s `hardware_class` rule denies any + /// contribution whose declared class is not a member of + /// `available_hardware`. An empty list therefore denies every + /// `hardware_class`-declaring contribution unconditionally, so this + /// default cannot silently admit a CUDA backend just because a real + /// policy was never wired in. Enabling this feature on a given host + /// requires an operator to explicitly list the hardware class (for + /// example `"cuda"`) in `available_hardware` through a policy file + /// consumed elsewhere in the resolution pipeline; there is no path + /// by which this default alone grants access. Threading a + /// caller-supplied `HostPolicy` into this router (for example from + /// the PyO3 binding that constructs it) is legitimate future work, + /// but is not required for this default to be safe today. + fn resolve( + &self, + canonical_id: &CanonicalId, + ) -> Result, CodecError> { + let Some(install) = find_install(canonical_id, &self.installed) else { + return Err(CodecError::Unsupported { + feature: format!("no hardware_backend registered for canonical id {canonical_id}"), + }); + }; + let bundle_dir = &install.bundle_dir; + let entry = manifest_entry_for(install, canonical_id)?; + + // Deny before dlopen, not after: see this method's doc comment + // for why HostPolicy::default() is the correct policy to check + // against here. + let policy = HostPolicy::default(); + match check(&entry, &policy, &VendorTable::default()) { + CapabilityVerdict::Denied { reason } => { + return Err(CodecError::Unsupported { + feature: format!("hardware_backend capability check denied: {reason}"), + }); + } + CapabilityVerdict::Admitted => {} + } + + if let Ok(native_path) = find_native_path(bundle_dir) { + let token = VerifiedToken::new_unchecked(); + let ext = NativeExtension::load(&native_path, &entry, token)?; + return Ok(Box::new(NativeHardwareBackendCudaAdapter { + inner: Arc::new(ext), + })); + } + + Err(CodecError::Unsupported { + feature: format!( + "hardware_backend {canonical_id} is installed but no native artifact was \ + found in {} (hardware_backend is native-only, WASM is not supported)", + bundle_dir.display() + ), + }) + } +} + // ── Helpers ─────────────────────────────────────────────────────────────────── /// Find the `DiscoveredContribution` whose manifest declares a contribution @@ -651,7 +775,10 @@ fn hex_nibble(b: u8) -> Option { #[cfg(test)] mod tests { use super::*; - use crate::extension::{CanonicalId, builtin_canonical_id}; + use crate::extension::{ + CanonicalId, CapabilityMap, ContributionDecl, Kind, Lifecycle, Manifest, + builtin_canonical_id, capability::CapabilityValue, manifest::BundleHeader, + }; #[test] fn router_returns_codec_for_builtin_identity() { @@ -771,4 +898,89 @@ mod tests { fn assert_impl() {} assert_impl::(); } + + #[test] + fn hardware_backend_router_errors_for_unknown_canonical_id() { + let router = HardwareBackendRouter::new(Vec::new()); + let id = CanonicalId::from_bytes([0xABu8; 32]); + assert!( + matches!(router.get(&id), Err(CodecError::Unsupported { .. })), + "expected Unsupported for unknown hardware_backend canonical id" + ); + } + + /// Build a `DiscoveredContribution` declaring a single `hardware_backend` + /// contribution with the given `hardware_class` capability, rooted at + /// `bundle_dir` (which need not exist on disk for capability-check + /// tests: the check must run, and deny, before any filesystem access + /// is attempted). + fn discovered_contribution_with_hardware_class( + hardware_class: &str, + bundle_dir: &str, + ) -> (DiscoveredContribution, CanonicalId) { + let id_bytes = [0xCDu8; 32]; + let id = CanonicalId::from_bytes(id_bytes); + let id_hex: String = id_bytes.iter().map(|b| format!("{:02x}", b)).collect(); + + let mut caps = CapabilityMap::new(); + caps.set( + "hardware_class", + CapabilityValue::Text(hardware_class.to_string()), + ); + + let manifest = Manifest { + bundle: BundleHeader { + name: "test-hardware-backend".into(), + version: "0.1.0".into(), + author_pubkey: "ed25519:00".into(), + description: None, + }, + contributions: vec![ContributionDecl { + id: format!("blake3:{id_hex}"), + label: "io.example.hardware_backend".into(), + kind: Kind::HardwareBackend, + abi_version: 1, + lifecycle: Lifecycle::Process, + flavors: vec!["native".into()], + capabilities: caps, + install_hint: None, + }], + }; + + let install = DiscoveredContribution { + manifest, + manifest_path: PathBuf::from(bundle_dir).join("manifest.toml"), + bundle_dir: PathBuf::from(bundle_dir), + installed_flavors: crate::discovery::FLAVOR_NATIVE, + }; + + (install, id) + } + + #[test] + fn hardware_backend_router_denies_before_native_load_when_capability_missing() { + // An installed contribution declaring hardware_class = "cuda" + // against HostPolicy::default() (available_hardware is empty) + // must be Denied before any dlopen is attempted. dlopen-ing a + // nonexistent path would itself error, so this test uses a + // bundle_dir that does not exist on disk: if capability_check + // runs first, the error message must say "capability check + // denied", not a filesystem error. + let (install, id) = discovered_contribution_with_hardware_class( + "cuda", + "/this/bundle/dir/does/not/exist", + ); + let router = HardwareBackendRouter::new(vec![install]); + let res = router.get(&id); + match res { + Ok(_) => panic!("expected capability denial, got Ok"), + Err(CodecError::Unsupported { feature }) => { + assert!( + feature.contains("capability check denied"), + "got: {feature}" + ); + } + Err(other) => panic!("expected capability denial, got {other:?}"), + } + } } From 32348abef5141f56cb3450f960954d7cadee940d Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Mon, 27 Jul 2026 20:33:06 +0200 Subject: [PATCH 08/27] fix(hardware_backend): require hardware_class capability be declared, not merely absent-safe --- crates/ptwm-core/src/flavor/router.rs | 131 ++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 16 deletions(-) diff --git a/crates/ptwm-core/src/flavor/router.rs b/crates/ptwm-core/src/flavor/router.rs index 08a0cc9..eb0cd7d 100644 --- a/crates/ptwm-core/src/flavor/router.rs +++ b/crates/ptwm-core/src/flavor/router.rs @@ -31,7 +31,8 @@ use std::sync::{Arc, Mutex}; use crate::discovery::DiscoveredContribution; use crate::extension::{ - Attestation, BuiltinKind, CanonicalId, ExtensionTableEntry, dispatch_builtin, + Attestation, BuiltinKind, CanonicalId, ExtensionTableEntry, capability::CapabilityValue, + dispatch_builtin, }; use crate::flavor::abi::CodecError; use crate::flavor::native::{NativeExtension, VerifiedToken}; @@ -561,8 +562,9 @@ impl HardwareBackendRouter { Ok(arc) } - /// Look up `canonical_id`, gate it through [`check`], and dlopen the - /// native artifact if admitted. + /// Look up `canonical_id`, gate it through a `hardware_class` + /// precondition and [`check`], and dlopen the native artifact if + /// admitted. /// /// # Policy default /// @@ -572,21 +574,33 @@ impl HardwareBackendRouter { /// module called [`check`] at all: this is the crate's first /// production call site. Using `HostPolicy::default()` here rather /// than adding a `policy` parameter to [`HardwareBackendRouter::new`] - /// is a deliberate choice, not an oversight, for one concrete reason: + /// is a deliberate choice, not an oversight. /// `HostPolicy::default().available_hardware` is an empty /// `Vec`, and `check`'s `hardware_class` rule denies any - /// contribution whose declared class is not a member of - /// `available_hardware`. An empty list therefore denies every - /// `hardware_class`-declaring contribution unconditionally, so this - /// default cannot silently admit a CUDA backend just because a real - /// policy was never wired in. Enabling this feature on a given host - /// requires an operator to explicitly list the hardware class (for - /// example `"cuda"`) in `available_hardware` through a policy file - /// consumed elsewhere in the resolution pipeline; there is no path - /// by which this default alone grants access. Threading a - /// caller-supplied `HostPolicy` into this router (for example from - /// the PyO3 binding that constructs it) is legitimate future work, - /// but is not required for this default to be safe today. + /// contribution whose *declared* class is not a member of + /// `available_hardware`. + /// + /// That rule only fires when `hardware_class` is present and typed + /// as `CapabilityValue::Text`, though: every rule in `check` inspects + /// capability keys that are present rather than requiring a key to + /// exist, so a contribution whose manifest never declares + /// `hardware_class` at all (or declares it as some other + /// `CapabilityValue` variant) would not trip that rule, or any other + /// rule in `check`, and would resolve to `Admitted` regardless of + /// policy. This method closes that gap itself, before calling + /// [`check`]: `hardware_class` must be present and + /// `CapabilityValue::Text`, or resolution is denied immediately. + /// With that precondition enforced, every `hardware_backend` + /// contribution this router can admit necessarily has a declared + /// class subject to `check`'s `hardware_class` rule, so the empty + /// default `available_hardware` denies all of them. Enabling this + /// feature on a given host requires an operator to explicitly list + /// the hardware class (for example `"cuda"`) in `available_hardware` + /// through a policy file consumed elsewhere in the resolution + /// pipeline. Threading a caller-supplied `HostPolicy` into this + /// router (for example from the PyO3 binding that constructs it) is + /// legitimate future work, but is not required for this default to + /// be safe today. fn resolve( &self, canonical_id: &CanonicalId, @@ -599,6 +613,24 @@ impl HardwareBackendRouter { let bundle_dir = &install.bundle_dir; let entry = manifest_entry_for(install, canonical_id)?; + // check()'s hardware_class rule only denies a *declared* + // mismatched class; it has no rule that fires when + // hardware_class is absent or of the wrong CapabilityValue + // variant, since every one of its rules inspects keys that are + // present. Enforce presence and type here, before check() runs, + // so omission is treated as denial rather than silently skipped + // (see this method's doc comment for the full reasoning). + if !matches!( + entry.capabilities.get("hardware_class"), + Some(CapabilityValue::Text(_)) + ) { + return Err(CodecError::Unsupported { + feature: "hardware_backend contribution must declare hardware_class as a \ + capability" + .into(), + }); + } + // Deny before dlopen, not after: see this method's doc comment // for why HostPolicy::default() is the correct policy to check // against here. @@ -983,4 +1015,71 @@ mod tests { Err(other) => panic!("expected capability denial, got {other:?}"), } } + + /// Build a `DiscoveredContribution` declaring a single `hardware_backend` + /// contribution whose capability map does not declare `hardware_class` + /// at all, rooted at `bundle_dir` (which need not exist on disk: the + /// omission precondition must deny before any filesystem access is + /// attempted). + fn discovered_contribution_missing_hardware_class( + bundle_dir: &str, + ) -> (DiscoveredContribution, CanonicalId) { + let id_bytes = [0xEFu8; 32]; + let id = CanonicalId::from_bytes(id_bytes); + let id_hex: String = id_bytes.iter().map(|b| format!("{:02x}", b)).collect(); + + let manifest = Manifest { + bundle: BundleHeader { + name: "test-hardware-backend-no-class".into(), + version: "0.1.0".into(), + author_pubkey: "ed25519:00".into(), + description: None, + }, + contributions: vec![ContributionDecl { + id: format!("blake3:{id_hex}"), + label: "io.example.hardware_backend_no_class".into(), + kind: Kind::HardwareBackend, + abi_version: 1, + lifecycle: Lifecycle::Process, + flavors: vec!["native".into()], + capabilities: CapabilityMap::new(), + install_hint: None, + }], + }; + + let install = DiscoveredContribution { + manifest, + manifest_path: PathBuf::from(bundle_dir).join("manifest.toml"), + bundle_dir: PathBuf::from(bundle_dir), + installed_flavors: crate::discovery::FLAVOR_NATIVE, + }; + + (install, id) + } + + #[test] + fn hardware_backend_router_denies_when_hardware_class_capability_missing() { + // check()'s hardware_class rule only fires on a *declared* + // mismatched class; an omitted key can't fail a rule that only + // inspects present keys. HardwareBackendRouter::resolve enforces + // presence itself, so a contribution that never declares + // hardware_class must still be denied before any dlopen is + // attempted, using the same nonexistent-bundle-dir trick as + // above so a filesystem error can't masquerade as the intended + // denial. + let (install, id) = + discovered_contribution_missing_hardware_class("/this/bundle/dir/does/not/exist"); + let router = HardwareBackendRouter::new(vec![install]); + let res = router.get(&id); + match res { + Ok(_) => panic!("expected denial for missing hardware_class, got Ok"), + Err(CodecError::Unsupported { feature }) => { + assert!( + feature.contains("hardware_class"), + "got: {feature}" + ); + } + Err(other) => panic!("expected denial for missing hardware_class, got {other:?}"), + } + } } From 7ff679c67d4915ffca8dcc86689e66b3144db23c Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 06:40:22 +0200 Subject: [PATCH 09/27] feat(hardware_backend): re-export HardwareBackendRouter from flavor module --- crates/ptwm-core/src/flavor/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ptwm-core/src/flavor/mod.rs b/crates/ptwm-core/src/flavor/mod.rs index 2077e5d..27d7ea9 100644 --- a/crates/ptwm-core/src/flavor/mod.rs +++ b/crates/ptwm-core/src/flavor/mod.rs @@ -21,7 +21,8 @@ pub use native::{ DeltaSchemeFn, NativeExtension, NativeSymbols, PlaneCodecFn, TransformFn, VerifiedToken, }; pub use router::{ - DeltaSchemeRouter, DispatchedDeltaScheme, DispatchedPlaneCodec, PlaneCodecRouter, + DeltaSchemeRouter, DispatchedDeltaScheme, DispatchedHardwareBackendCuda, DispatchedPlaneCodec, + HardwareBackendRouter, PlaneCodecRouter, }; pub use third_party::ThirdPartyPlaneCodec; pub use wasm::{ From 7d87e6727fd244e91115c8681e8c62893063783a Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 07:02:16 +0200 Subject: [PATCH 10/27] feat(hardware_backend): PyO3 binding with hand-rolled DLPack capsule parsing --- crates/ptwm-py/src/hardware.rs | 349 +++++++++++++++++++++++++++++++++ crates/ptwm-py/src/lib.rs | 2 + 2 files changed, 351 insertions(+) create mode 100644 crates/ptwm-py/src/hardware.rs diff --git a/crates/ptwm-py/src/hardware.rs b/crates/ptwm-py/src/hardware.rs new file mode 100644 index 0000000..381013e --- /dev/null +++ b/crates/ptwm-py/src/hardware.rs @@ -0,0 +1,349 @@ +//! PyO3 bridge for invoking a `hardware_backend` contribution by canonical +//! id, plus a hand-rolled DLPack capsule parser. +//! +//! There is no in-tree consumer of `hardware_backend` (unlike `plane_codec`, +//! which the container encode/decode loop calls internally): this module +//! is the only way to reach a `hardware_backend` contribution from Python. +//! Each call re-scans installed extensions and builds a fresh +//! `HardwareBackendRouter`; `scan_all_cached` already caches the discovery +//! walk, so this matches the stateless-function pattern used by +//! `delta_scheme.rs` and `ext.rs` rather than introducing a persistent +//! router object. +//! +//! # DLPack capsule parsing +//! +//! `torch.Tensor.__dlpack__()` returns a `PyCapsule` wrapping a +//! `DLManagedTensor` (frozen C ABI, defined by `dlpack.h`). No `dlpack` +//! crate dependency is used here: the pinned `pyo3 = "0.24"` in this +//! workspace conflicts with the version range the `dlpark` crate's `pyo3` +//! feature requires, so the relevant structs are reproduced below directly +//! against the stable DLPack C layout instead. + +use std::os::raw::{c_int, c_void}; + +use pyo3::prelude::*; +use pyo3::types::{PyCapsule, PyCapsuleMethods, PyDict}; + +use ptwm_core::discovery::scan_all_cached; +use ptwm_core::extension::CanonicalId; +use ptwm_core::flavor::HardwareBackendRouter; + +fn to_pyerr(e: E) -> PyErr { + pyo3::exceptions::PyValueError::new_err(e.to_string()) +} + +fn hardware_backend_router() -> PyResult { + let installed = scan_all_cached().map_err(to_pyerr)?; + Ok(HardwareBackendRouter::new(installed)) +} + +// --------------------------------------------------------------------------- +// DLPack: frozen C structs (dlpack.h) +// --------------------------------------------------------------------------- + +/// The capsule name mandated by the DLPack Python spec for an +/// unconsumed `__dlpack__()` capsule. A producer that has already +/// consumed the capsule renames it to `"used_dltensor"`; this module only +/// ever sees freshly produced capsules, so `"dltensor"` is the only name +/// accepted here. +const DLPACK_CAPSULE_NAME: &str = "dltensor"; + +/// `DLDeviceType::kDLCUDA`, per `dlpack.h`. +const DL_CUDA: c_int = 2; + +#[repr(C)] +#[derive(Clone, Copy)] +struct DLDevice { + device_type: c_int, // kDLCUDA = 2 + device_id: c_int, +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct DLDataType { + code: u8, // kDLBfloat = 4, kDLUInt = 1, etc. + bits: u8, + lanes: u16, +} + +#[repr(C)] +struct DLTensor { + data: *mut c_void, + device: DLDevice, + ndim: c_int, + dtype: DLDataType, + shape: *mut i64, + strides: *mut i64, // may be null (implies row-major contiguous) + byte_offset: u64, +} + +#[repr(C)] +struct DLManagedTensor { + dl_tensor: DLTensor, + manager_ctx: *mut c_void, + deleter: Option, +} + +/// True when `strides` (in elements, per the DLPack spec) matches the +/// row-major-contiguous layout implied by `shape`. +/// +/// A dimension of size 0 or 1 is skipped: its stride is a don't-care for +/// contiguity purposes (this mirrors the convention PyTorch's own +/// `is_contiguous()` uses for singleton dimensions), which keeps this +/// check from rejecting perfectly usable tensors that merely have an +/// arbitrary stride recorded on a size-1 axis. +fn is_row_major_contiguous(shape: &[i64], strides: &[i64]) -> bool { + debug_assert_eq!(shape.len(), strides.len()); + let mut expected: i64 = 1; + for i in (0..shape.len()).rev() { + let dim = shape[i]; + if dim < 0 { + return false; + } + if dim > 1 && strides[i] != expected { + return false; + } + expected = expected.saturating_mul(dim.max(1)); + } + true +} + +/// Extract (device pointer as u64, device ordinal, byte length) from a +/// PyCapsule returned by `tensor.__dlpack__(stream=...)`. Rejects +/// non-contiguous tensors (a non-null `strides` pointer whose values do +/// not match the row-major-contiguous stride for `shape`), matching the +/// existing `.contiguous()` convention already used on the CPU path in +/// `python/ptwm/core/_compressor.py::_to_raw_bytes`. +fn extract_device_ptr(capsule: &Bound<'_, PyAny>) -> PyResult<(u64, i32, usize)> { + let capsule: &Bound<'_, PyCapsule> = capsule + .downcast::() + .map_err(|e| to_pyerr(e.to_string()))?; + + // `PyCapsule::pointer()` fetches the capsule's own stored name and + // passes it straight back into `PyCapsule_GetPointer`, so it always + // "succeeds" for any valid capsule regardless of what that name is. + // The DLPack Python spec pins the name to a specific string; check it + // explicitly here rather than trusting an unnamed or wrongly-named + // capsule to actually contain a `DLManagedTensor`. + let name = capsule.name().map_err(to_pyerr)?; + match name { + Some(n) if n.to_str().map_err(to_pyerr)? == DLPACK_CAPSULE_NAME => {} + Some(n) => { + return Err(to_pyerr(format!( + "expected a DLPack capsule named '{DLPACK_CAPSULE_NAME}', got '{}' \ + (has this capsule already been consumed?)", + n.to_string_lossy() + ))); + } + None => { + return Err(to_pyerr(format!( + "expected a DLPack capsule named '{DLPACK_CAPSULE_NAME}', capsule has no name" + ))); + } + } + + let raw_ptr = capsule.pointer(); + if raw_ptr.is_null() { + return Err(to_pyerr("DLPack capsule pointer is null")); + } + + // SAFETY: the name check above confirms this is a "dltensor" capsule + // per the DLPack Python spec, which guarantees the capsule's opaque + // pointer references a `DLManagedTensor` laid out per the frozen + // dlpack.h C ABI. The tensor memory is kept alive by the capsule + // object itself, which the caller holds for the duration of this + // call (it is not dropped until the enclosing `#[pyfunction]` returns). + let managed: &DLManagedTensor = unsafe { &*raw_ptr.cast::() }; + let dl_tensor = &managed.dl_tensor; + + if dl_tensor.device.device_type != DL_CUDA { + return Err(to_pyerr(format!( + "expected a CUDA DLPack tensor (device_type={DL_CUDA}), got device_type={}", + dl_tensor.device.device_type + ))); + } + + if dl_tensor.ndim < 0 { + return Err(to_pyerr("DLPack tensor has negative ndim")); + } + let ndim = dl_tensor.ndim as usize; + + let shape: &[i64] = if ndim == 0 { + &[] + } else { + if dl_tensor.shape.is_null() { + return Err(to_pyerr("DLPack tensor shape pointer is null")); + } + // SAFETY: shape is non-null (checked above) and the + // `DLManagedTensor` contract guarantees `ndim` valid `i64` + // entries at that pointer. + unsafe { std::slice::from_raw_parts(dl_tensor.shape, ndim) } + }; + + if !dl_tensor.strides.is_null() { + // SAFETY: same contract as `shape` above; non-null `strides` + // points to `ndim` valid `i64` entries per the DLPack spec. + let strides = unsafe { std::slice::from_raw_parts(dl_tensor.strides, ndim) }; + if !is_row_major_contiguous(shape, strides) { + return Err(to_pyerr( + "non-contiguous tensor passed to hardware_backend dispatch; \ + call .contiguous() on the tensor before passing it in", + )); + } + } + + let elem_bits = dl_tensor.dtype.bits as u64 * dl_tensor.dtype.lanes as u64; + if elem_bits == 0 || elem_bits % 8 != 0 { + return Err(to_pyerr(format!( + "unsupported DLPack dtype: bits={} lanes={} does not divide evenly into bytes", + dl_tensor.dtype.bits, dl_tensor.dtype.lanes + ))); + } + let elem_bytes = elem_bits / 8; + + let num_elements: u64 = shape + .iter() + .try_fold(1u64, |acc, &d| { + if d < 0 { + None + } else { + acc.checked_mul(d as u64) + } + }) + .ok_or_else(|| to_pyerr("DLPack tensor shape has a negative dimension or overflows"))?; + + let byte_len = num_elements + .checked_mul(elem_bytes) + .ok_or_else(|| to_pyerr("DLPack tensor byte length overflows usize"))?; + + if dl_tensor.data.is_null() { + return Err(to_pyerr("DLPack tensor data pointer is null")); + } + let device_ptr = (dl_tensor.data as u64) + .checked_add(dl_tensor.byte_offset) + .ok_or_else(|| to_pyerr("DLPack tensor data pointer + byte_offset overflows u64"))?; + + Ok((device_ptr, dl_tensor.device.device_id, byte_len as usize)) +} + +// --------------------------------------------------------------------------- +// PyO3-visible functions +// --------------------------------------------------------------------------- + +/// Return the raw CUDA stream handle for `canonical_id`'s `hardware_backend` +/// contribution on the given device ordinal. +#[pyfunction] +pub fn hardware_backend_cuda_stream_handle(canonical_id: String, device_ordinal: u32) -> PyResult { + let router = hardware_backend_router()?; + let id = CanonicalId::parse(&canonical_id).map_err(to_pyerr)?; + let backend = router.get(&id).map_err(to_pyerr)?; + backend.cuda_stream_handle(device_ordinal).map_err(to_pyerr) +} + +/// Dispatch a CUDA decode through `canonical_id`'s `hardware_backend` +/// contribution. +/// +/// `compressed` and `out` are `torch.Tensor` objects already resident on +/// the CUDA device identified by `device_ordinal`; both must be +/// contiguous. `device_ordinal` is an explicit, caller-supplied parameter +/// rather than something inferred from the tensors: `cuda_stream_handle` +/// needs a device ordinal before any tensor has been inspected (there is +/// no tensor yet to peek a `.device.index` from at that point), and v1 of +/// this design is documented as single-GPU, so requiring the caller to +/// state the device explicitly is simpler than reaching into tensor +/// internals and keeps behavior visible rather than silently inferred. +#[pyfunction] +#[allow(clippy::too_many_arguments)] +pub fn hardware_backend_dispatch_decode_cuda<'py>( + py: Python<'py>, + canonical_id: String, + codec_id: String, + state_bytes: &[u8], + compressed: &Bound<'py, PyAny>, + out: &Bound<'py, PyAny>, + device_ordinal: u32, +) -> PyResult { + let router = hardware_backend_router()?; + let id = CanonicalId::parse(&canonical_id).map_err(to_pyerr)?; + let codec = CanonicalId::parse(&codec_id).map_err(to_pyerr)?; + let backend = router.get(&id).map_err(to_pyerr)?; + + let stream = backend.cuda_stream_handle(device_ordinal).map_err(to_pyerr)?; + + let dlpack_kwargs = PyDict::new(py); + dlpack_kwargs.set_item("stream", stream)?; + let compressed_capsule = compressed.call_method("__dlpack__", (), Some(&dlpack_kwargs))?; + let out_capsule = out.call_method("__dlpack__", (), Some(&dlpack_kwargs))?; + + let (in_dev_ptr, in_ordinal, in_len) = extract_device_ptr(&compressed_capsule)?; + let (out_dev_ptr, out_ordinal, out_len) = extract_device_ptr(&out_capsule)?; + + // Both tensors' own DLPack device ordinals must agree with the + // caller-supplied `device_ordinal` used to acquire the stream above; + // a mismatch means the stream and the tensor memory belong to + // different devices, which would silently corrupt the decode rather + // than fail loudly. + if in_ordinal != device_ordinal as i32 || out_ordinal != device_ordinal as i32 { + return Err(to_pyerr(format!( + "device_ordinal mismatch: caller supplied {device_ordinal}, but compressed \ + tensor reports device {in_ordinal} and out tensor reports device {out_ordinal}" + ))); + } + + py.allow_threads(|| { + backend + .dispatch_decode_cuda( + state_bytes, + &codec, + in_dev_ptr, + in_len, + out_dev_ptr, + out_len, + device_ordinal, + ) + .map_err(to_pyerr) + }) +} + +pub fn register(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> { + m.add_function(pyo3::wrap_pyfunction!(hardware_backend_cuda_stream_handle, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(hardware_backend_dispatch_decode_cuda, m)?)?; + Ok(()) +} + +#[cfg(test)] +mod dlpack_tests { + use super::*; + + #[test] + fn dl_data_type_bf16_matches_dlpack_spec_code() { + // DLPack's DLDataTypeCode for bfloat16 is kDLBfloat = 4, per the + // frozen DLPack C header (dlpack.h, DLDataTypeCode enum). This + // guards against a transcription error in the hand-rolled struct + // above; it is not a full DLPack conformance test. + let dt = DLDataType { code: 4, bits: 16, lanes: 1 }; + assert_eq!(dt.code, 4); + assert_eq!(dt.bits, 16); + } + + #[test] + fn row_major_contiguous_accepts_standard_layout() { + // A [2, 3] row-major tensor has strides [3, 1] (in elements). + assert!(is_row_major_contiguous(&[2, 3], &[3, 1])); + } + + #[test] + fn row_major_contiguous_rejects_transposed_layout() { + // The same [2, 3] tensor transposed (a view, not a copy) has + // strides [1, 2], which is not row-major-contiguous. + assert!(!is_row_major_contiguous(&[2, 3], &[1, 2])); + } + + #[test] + fn row_major_contiguous_ignores_singleton_dimension_stride() { + // A [1, 3] tensor's size-1 leading dimension carries an + // arbitrary/don't-care stride in many producers; only the size-3 + // trailing dimension's stride must be 1. + assert!(is_row_major_contiguous(&[1, 3], &[999, 1])); + } +} diff --git a/crates/ptwm-py/src/lib.rs b/crates/ptwm-py/src/lib.rs index 5de4642..b4abf2b 100644 --- a/crates/ptwm-py/src/lib.rs +++ b/crates/ptwm-py/src/lib.rs @@ -18,6 +18,7 @@ use ptwm_core::{PtwmCoreError, codec_tagged, delta, entropy}; mod delta_scheme; mod ext; +mod hardware; mod host_flavor; mod inspect; mod policy; @@ -61,6 +62,7 @@ fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> { ext::register(m)?; inspect::register(m)?; delta_scheme::register(m)?; + hardware::register(m)?; Ok(()) } From 80ad1458fcf0ae6f312ad0a2a8c79d149d846022 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 07:39:37 +0200 Subject: [PATCH 11/27] test(hardware_backend): capability-policy coverage for a realistic CUDA declaration --- .../ptwm-core/src/policy/capability_check.rs | 110 +++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/crates/ptwm-core/src/policy/capability_check.rs b/crates/ptwm-core/src/policy/capability_check.rs index b425daa..6bb243c 100644 --- a/crates/ptwm-core/src/policy/capability_check.rs +++ b/crates/ptwm-core/src/policy/capability_check.rs @@ -6,7 +6,7 @@ use crate::extension::{ExtensionTableEntry, capability::CapabilityValue}; use super::native_deps::{ - CommandRunner, NativeDep, NativeDepVerdict, RealCommandRunner, VendorTable, + CommandRunner, NativeDep, NativeDepVerdict, RealCommandRunner, RunResult, VendorTable, verify_native_dep_with, }; @@ -288,4 +288,112 @@ mod tests { _ => panic!("expected Denied"), } } + + /// Fake CommandRunner: programmable responses. + struct FakeRunner { + which_returns: std::collections::HashMap>, + run_returns: std::collections::HashMap<(String, Vec), RunResult>, + } + + impl FakeRunner { + fn new() -> Self { + Self { + which_returns: Default::default(), + run_returns: Default::default(), + } + } + fn with_which(mut self, prog: &str, path: Option<&str>) -> Self { + self.which_returns + .insert(prog.to_string(), path.map(std::path::PathBuf::from)); + self + } + fn with_run(mut self, prog: &str, args: &[&str], result: RunResult) -> Self { + self.run_returns.insert( + ( + prog.to_string(), + args.iter().map(|s| s.to_string()).collect(), + ), + result, + ); + self + } + } + + impl CommandRunner for FakeRunner { + fn run(&self, program: &str, args: &[&str]) -> std::io::Result { + let key = ( + program.to_string(), + args.iter().map(|s| s.to_string()).collect::>(), + ); + self.run_returns + .get(&key) + .map(|r| RunResult { + status_success: r.status_success, + stdout: r.stdout.clone(), + stderr: r.stderr.clone(), + }) + .ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("fake runner has no entry for {program} {args:?}"), + ) + }) + } + fn which(&self, program: &str) -> Option { + self.which_returns.get(program).cloned().flatten() + } + } + + #[test] + #[cfg(target_os = "linux")] + fn hardware_backend_cuda_capability_admitted_when_libcuda_present() { + let runner = FakeRunner::new() + .with_which("dpkg", Some("/usr/bin/dpkg")) + .with_run( + "dpkg-query", + &["-S", "libcuda.so*"], + RunResult { + status_success: true, + stdout: "libcuda1: /usr/lib/x86_64-linux-gnu/libcuda.so.1\n".into(), + stderr: String::new(), + }, + ) + .with_run( + "dpkg", + &["--verify", "libcuda1"], + RunResult { status_success: true, stdout: String::new(), stderr: String::new() }, + ); + + let mut caps = CapabilityMap::new(); + caps.set("hardware_class", CapabilityValue::Text("cuda".into())); + caps.set( + "native_deps", + CapabilityValue::List(vec![CapabilityValue::Map({ + let mut m = std::collections::BTreeMap::new(); + m.insert("name".to_string(), CapabilityValue::Text("cuda".into())); + m.insert("version_constraint".to_string(), CapabilityValue::Text(">=12.0".into())); + m + })]), + ); + let entry = entry_with_caps(caps); + + let mut policy = HostPolicy::default(); + policy.available_hardware.push("cuda".into()); + + let verdict = check_with(&entry, &policy, &VendorTable::default(), &runner); + assert!(matches!(verdict, CapabilityVerdict::Admitted), "got: {verdict:?}"); + } + + #[test] + fn hardware_backend_cuda_capability_denied_when_hardware_class_not_in_policy() { + let mut caps = CapabilityMap::new(); + caps.set("hardware_class", CapabilityValue::Text("cuda".into())); + let entry = entry_with_caps(caps); + let mut policy = HostPolicy::default(); + policy.available_hardware.push("cpu".into()); + match check(&entry, &policy, &VendorTable::default()) { + CapabilityVerdict::Denied { reason } => assert!(reason.contains("cuda")), + other => panic!("expected Denied, got {other:?}"), + } + } } From ee4922f6ad78bcf7f3bb398a8ce985adda5309c4 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 07:50:58 +0200 Subject: [PATCH 12/27] feat(hardware_backend): correct ext-init scaffold (was silently falling back to the wrong generic template) --- .../rust/_kind_hardware_backend/Cargo.toml | 12 +++ .../rust/_kind_hardware_backend/manifest.toml | 14 +++ .../rust/_kind_hardware_backend/src/lib.rs | 88 +++++++++++++++++++ tests/ptwm/ext_tooling/test_init.py | 19 ++++ 4 files changed, 133 insertions(+) create mode 100644 python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/Cargo.toml create mode 100644 python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/manifest.toml create mode 100644 python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/src/lib.rs diff --git a/python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/Cargo.toml b/python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/Cargo.toml new file mode 100644 index 0000000..a124534 --- /dev/null +++ b/python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "{{ name }}" +version = "{{ version }}" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +# Add what your contribution needs. PTWM provides no host-side helpers +# for hardware_backend extensions; you write the device-pointer ABI +# exports directly, per the pattern in this file. diff --git a/python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/manifest.toml b/python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/manifest.toml new file mode 100644 index 0000000..8eda6d3 --- /dev/null +++ b/python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/manifest.toml @@ -0,0 +1,14 @@ +[bundle] +name = "{{ name }}" +version = "{{ version }}" +author_pubkey = "{{ author_pubkey }}" +description = "{{ description }}" + +[[contributions]] +id = "{{ canonical_id }}" +label = "{{ label }}" +kind = "hardware_backend" +abi_version = 1 +lifecycle = "process" +flavors = ["{{ flavor }}"] +capabilities = { determinism = true, hardware_class = "cpu" } diff --git a/python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/src/lib.rs b/python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/src/lib.rs new file mode 100644 index 0000000..e7884fa --- /dev/null +++ b/python/ptwm/ext_tooling/_templates/rust/_kind_hardware_backend/src/lib.rs @@ -0,0 +1,88 @@ +//! {{ name }}: a hardware_backend contribution scaffold. +//! +//! The native branch below exports the real device-pointer-shaped ABI +//! (ptwm_hardware_backend_v1_cuda_stream_handle, +//! ptwm_hardware_backend_v1_dispatch_decode_cuda) with a CPU-passthrough +//! body: it reinterprets the u64 pointers as host pointers and memcpy's. +//! This works as a real dispatch-path exercise, not an approximation, +//! because ptwm-core never dereferences these pointers itself; it only +//! forwards them. Replace the body with real CUDA kernel launches for a +//! production backend. +//! +//! The wasm branch below is a CPU-only reference kept for structural +//! parity; it is NOT reachable through HardwareBackendRouter. Native-only +//! by design: a u64 device pointer cannot be expressed in WASM's 32-bit +//! linear address space. + +#[cfg(target_arch = "wasm32")] +mod wasm_ref { + #[unsafe(no_mangle)] + pub extern "C" fn ptwm_hardware_backend_v1_init() -> u32 { + 1 + } + + #[unsafe(no_mangle)] + pub extern "C" fn ptwm_hardware_backend_v1_cleanup(_state: u32) {} + + #[unsafe(no_mangle)] + pub extern "C" fn ptwm_hardware_backend_v1_dispatch_decode( + _state: u32, + _codec_id_ptr: u32, + _codec_id_len: u32, + in_ptr: u32, + in_len: u32, + out_ptr: u32, + out_len: u32, + ) -> i64 { + // Passthrough copy within WASM linear memory, matching + // extensions/ref_hardware_backend/rust/src/lib.rs's existing shape. + let n = in_len.min(out_len) as usize; + unsafe { + std::ptr::copy_nonoverlapping(in_ptr as *const u8, out_ptr as *mut u8, n); + } + n as i64 + } +} + +#[cfg(not(target_arch = "wasm32"))] +mod native { + #[unsafe(no_mangle)] + pub extern "C" fn ptwm_hardware_backend_v1_cuda_stream_handle(_device_ordinal: u32) -> u64 { + // CPU-passthrough scaffold: no real CUDA stream. Return a nonzero + // sentinel so callers treat this as "succeeded" for dispatch-path + // smoke-testing purposes; a real CUDA backend returns a genuine + // CUstream/cudaStream_t pointer value here. + 1 + } + + #[unsafe(no_mangle)] + #[allow(clippy::too_many_arguments)] + pub extern "C" fn ptwm_hardware_backend_v1_dispatch_decode_cuda( + _state_format_version: u8, + _state_ptr: *const u8, + _state_len: usize, + _codec_id_ptr: *const u8, + _codec_id_len: usize, + in_dev_ptr: u64, + in_len: usize, + out_dev_ptr: u64, + out_len: usize, + _device_ordinal: u32, + ) -> i64 { + // Reinterprets the u64 "device" pointers as ordinary host + // pointers and memcpy's. This is legal ONLY because ptwm-core + // never dereferences these pointers itself; it exists purely to + // exercise the real dispatch path (router, native ABI, dlopen, + // FFI call) with zero GPU/CUDA present, matching this repo's + // testing plan. + let n = in_len.min(out_len); + unsafe { + std::ptr::copy_nonoverlapping( + in_dev_ptr as *const u8, + out_dev_ptr as *mut u8, + n, + ); + } + n as i64 + } +} diff --git a/tests/ptwm/ext_tooling/test_init.py b/tests/ptwm/ext_tooling/test_init.py index 08285ea..2a4c0a5 100644 --- a/tests/ptwm/ext_tooling/test_init.py +++ b/tests/ptwm/ext_tooling/test_init.py @@ -55,3 +55,22 @@ def test_init_assemblyscript(tmp_path: Path) -> None: ) assert (target / "package.json").exists() assert (target / "src" / "index.ts").exists() + + +def test_init_hardware_backend_scaffolds_correct_native_abi(tmp_path: Path) -> None: + from ptwm.ext_tooling.init import init_extension + + target = tmp_path / "demo-hw" + init_extension( + target_dir=target, + name="demo-hw", + lang="rust", + kind="hardware_backend", + flavor="native", + ) + lib = (target / "src" / "lib.rs").read_text(encoding="utf-8") + assert "ptwm_hardware_backend_v1_dispatch_decode_cuda" in lib + assert "ptwm_hardware_backend_v1_cuda_stream_handle" in lib + # The generic template's wrong stateless 2-arg encode/decode shape + # must NOT appear for this kind. + assert "ptwm_hardware_backend_v1_encode" not in lib From 61c9b39435f8f50ccede04bd988f7d1c3edecc4b Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 08:07:13 +0200 Subject: [PATCH 13/27] feat(hardware_backend): add native-flavor CPU-passthrough export to ref_hardware_backend --- .../ref_hardware_backend/rust/manifest.toml | 2 +- .../ref_hardware_backend/rust/src/lib.rs | 66 ++++++++++++++++--- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/extensions/ref_hardware_backend/rust/manifest.toml b/extensions/ref_hardware_backend/rust/manifest.toml index 39076c8..33fdedd 100644 --- a/extensions/ref_hardware_backend/rust/manifest.toml +++ b/extensions/ref_hardware_backend/rust/manifest.toml @@ -10,5 +10,5 @@ label = "io.ptwm.ref.hardware_backend" kind = "hardware_backend" abi_version = 1 lifecycle = "thread" -flavors = ["wasm"] +flavors = ["wasm", "native"] capabilities = { determinism = true, hardware_class = "cpu" } diff --git a/extensions/ref_hardware_backend/rust/src/lib.rs b/extensions/ref_hardware_backend/rust/src/lib.rs index 9feacc2..a37a8be 100644 --- a/extensions/ref_hardware_backend/rust/src/lib.rs +++ b/extensions/ref_hardware_backend/rust/src/lib.rs @@ -1,21 +1,24 @@ //! Reference contribution for the `hardware_backend` kind (CPU passthrough). //! //! WASM ABI: ptwm_hardware_backend_v1_{init,cleanup,dispatch_decode} +//! Native ABI: ptwm_hardware_backend_v1_{cuda_stream_handle,dispatch_decode_cuda} //! Passthrough: copies input to output, ignoring the plane_codec_id. -#![no_main] +#![cfg_attr(target_arch = "wasm32", no_main)] use std::alloc::Layout; // --------------------------------------------------------------------------- -// Allocator exports. +// Allocator exports (WASM only). // --------------------------------------------------------------------------- +#[cfg(target_arch = "wasm32")] #[unsafe(no_mangle)] pub extern "C" fn ptwm_alloc(len: u32) -> u32 { let layout = Layout::from_size_align(len as usize, 1).unwrap(); unsafe { std::alloc::alloc(layout) as u32 } } +#[cfg(target_arch = "wasm32")] #[unsafe(no_mangle)] pub extern "C" fn ptwm_free(ptr: u32, len: u32) { let layout = Layout::from_size_align(len as usize, 1).unwrap(); @@ -23,24 +26,20 @@ pub extern "C" fn ptwm_free(ptr: u32, len: u32) { } // --------------------------------------------------------------------------- -// Lifecycle. +// WASM: Lifecycle and dispatch_decode. // --------------------------------------------------------------------------- +#[cfg(target_arch = "wasm32")] #[unsafe(no_mangle)] pub extern "C" fn ptwm_hardware_backend_v1_init() -> u32 { 1 } +#[cfg(target_arch = "wasm32")] #[unsafe(no_mangle)] pub extern "C" fn ptwm_hardware_backend_v1_cleanup(_state: u32) {} -// --------------------------------------------------------------------------- -// dispatch_decode: passthrough — copies input to output. -// -// Signature: (state, plane_codec_id_ptr, plane_codec_id_len, -// in_ptr, in_len, out_ptr, out_len) -> i64 -// --------------------------------------------------------------------------- - +#[cfg(target_arch = "wasm32")] #[unsafe(no_mangle)] pub extern "C" fn ptwm_hardware_backend_v1_dispatch_decode( _state: u32, @@ -59,3 +58,50 @@ pub extern "C" fn ptwm_hardware_backend_v1_dispatch_decode( } in_len as i64 } + +// --------------------------------------------------------------------------- +// Native: CUDA-shaped exports (CPU-passthrough for testing). +// --------------------------------------------------------------------------- + +#[cfg(not(target_arch = "wasm32"))] +mod native { + #[unsafe(no_mangle)] + pub extern "C" fn ptwm_hardware_backend_v1_cuda_stream_handle(_device_ordinal: u32) -> u64 { + // CPU-passthrough scaffold: no real CUDA stream. Return a nonzero + // sentinel so callers treat this as "succeeded" for dispatch-path + // smoke-testing purposes; a real CUDA backend returns a genuine + // CUstream/cudaStream_t pointer value here. + 1 + } + + #[unsafe(no_mangle)] + #[allow(clippy::too_many_arguments)] + pub extern "C" fn ptwm_hardware_backend_v1_dispatch_decode_cuda( + _state_format_version: u8, + _state_ptr: *const u8, + _state_len: usize, + _codec_id_ptr: *const u8, + _codec_id_len: usize, + in_dev_ptr: u64, + in_len: usize, + out_dev_ptr: u64, + out_len: usize, + _device_ordinal: u32, + ) -> i64 { + // Reinterprets the u64 "device" pointers as ordinary host + // pointers and memcpy's. This is legal ONLY because ptwm-core + // never dereferences these pointers itself; it exists purely to + // exercise the real dispatch path (router, native ABI, dlopen, + // FFI call) with zero GPU/CUDA present, matching this repo's + // testing plan. + let n = in_len.min(out_len); + unsafe { + std::ptr::copy_nonoverlapping( + in_dev_ptr as *const u8, + out_dev_ptr as *mut u8, + n, + ); + } + n as i64 + } +} From 913f1e06a2f5130d43ca2f31c9e22c045cb970c6 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 08:14:16 +0200 Subject: [PATCH 14/27] fix(hardware_backend): gate unused Layout import to wasm32-only build --- extensions/ref_hardware_backend/rust/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ref_hardware_backend/rust/src/lib.rs b/extensions/ref_hardware_backend/rust/src/lib.rs index a37a8be..5cbe33d 100644 --- a/extensions/ref_hardware_backend/rust/src/lib.rs +++ b/extensions/ref_hardware_backend/rust/src/lib.rs @@ -5,6 +5,7 @@ //! Passthrough: copies input to output, ignoring the plane_codec_id. #![cfg_attr(target_arch = "wasm32", no_main)] +#[cfg(target_arch = "wasm32")] use std::alloc::Layout; // --------------------------------------------------------------------------- From ed2181915d883741957c52633c7a74183df6d3ac Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 09:18:47 +0200 Subject: [PATCH 15/27] feat(hardware_backend): additive HardwareBackendRouter::new_with_policy HardwareBackendRouter::resolve() always checked capabilities against HostPolicy::default(), whose empty available_hardware denies every hardware_class-declaring contribution with no way for any caller to say "this host actually has X". new() keeps that exact default-deny behavior; new_with_policy() lets a caller supply a HostPolicy instead, so an operator-approved policy can admit a real contribution. Neither capability_check::check nor the hardware_class-presence precondition in resolve() changed. Adds a regression test proving new_with_policy actually admits what new()'s default denies, by checking that resolution fails at the "no native artifact" step rather than "capability check denied" once a matching hardware_class is granted. --- crates/ptwm-core/src/flavor/router.rs | 147 ++++++++++++++++++++------ 1 file changed, 113 insertions(+), 34 deletions(-) diff --git a/crates/ptwm-core/src/flavor/router.rs b/crates/ptwm-core/src/flavor/router.rs index eb0cd7d..ffe4937 100644 --- a/crates/ptwm-core/src/flavor/router.rs +++ b/crates/ptwm-core/src/flavor/router.rs @@ -499,12 +499,19 @@ impl DispatchedHardwareBackendCuda for NativeHardwareBackendCudaAdapter { device_ordinal: u32, ) -> Result { self.inner.invoke_hardware_backend_dispatch_decode_cuda( - state_bytes, codec_id, in_dev_ptr, in_len, out_dev_ptr, out_len, device_ordinal, + state_bytes, + codec_id, + in_dev_ptr, + in_len, + out_dev_ptr, + out_len, + device_ordinal, ) } fn cuda_stream_handle(&self, device_ordinal: u32) -> Result { - self.inner.invoke_hardware_backend_cuda_stream_handle(device_ordinal) + self.inner + .invoke_hardware_backend_cuda_stream_handle(device_ordinal) } } @@ -515,10 +522,11 @@ impl DispatchedHardwareBackendCuda for NativeHardwareBackendCudaAdapter { /// expressed in WASM's 32-bit linear address space, so `hardware_backend` /// is native-only by construction. Second, [`resolve`](Self::resolve) runs /// [`check`] against the entry's declared capabilities before attempting a -/// native load; see that method's documentation for why `HostPolicy::default()` -/// is the correct policy to use there rather than a caller-supplied one. +/// native load, using `self.policy`; see [`Self::new`] and +/// [`Self::new_with_policy`] for how that policy is chosen. pub struct HardwareBackendRouter { installed: Vec, + policy: HostPolicy, cache: Mutex>>, } @@ -532,11 +540,45 @@ impl std::fmt::Debug for HardwareBackendRouter { impl HardwareBackendRouter { /// Create a new router with the given set of discovered installed - /// extensions. Pass an empty `Vec` to get `Unsupported` for every id - /// (there are no built-in hardware backends). + /// extensions, gated by the default (empty-`available_hardware`) + /// [`HostPolicy`]. Pass an empty `Vec` to get `Unsupported` for every + /// id (there are no built-in hardware backends). + /// + /// This constructor's signature and default-deny behavior stay fixed + /// for every existing caller: it never admits a `hardware_class`- + /// declaring contribution, regardless of what that contribution + /// declares. A caller that needs a different policy (for example, an + /// operator who has confirmed a given host truly has CUDA available) + /// uses [`Self::new_with_policy`] instead; adding that constructor + /// leaves this method's own behavior unchanged. pub fn new(installed: Vec) -> Self { Self { installed, + policy: HostPolicy::default(), + cache: Mutex::new(HashMap::new()), + } + } + + /// Create a new router with the given set of discovered installed + /// extensions, gated by a caller-supplied [`HostPolicy`] instead of + /// the default. + /// + /// This is the mechanism [`Self::resolve`]'s doc comment names as + /// "legitimate future work": a caller that has independently + /// established a host's true hardware availability (for example, a + /// PyO3 binding that read a `ResolvedPolicy` produced from an + /// operator's policy file) can pass that policy here, so `resolve` + /// checks the entry's declared capabilities against it instead of + /// against the always-empty `HostPolicy::default()`. This does not + /// change what [`check`] itself does, and does not remove the + /// `hardware_class`-presence precondition enforced in + /// [`Self::resolve`]: a contribution is only ever admitted when its + /// declared capabilities pass [`check`] against whichever policy is + /// in effect. + pub fn new_with_policy(installed: Vec, policy: HostPolicy) -> Self { + Self { + installed, + policy, cache: Mutex::new(HashMap::new()), } } @@ -566,19 +608,21 @@ impl HardwareBackendRouter { /// precondition and [`check`], and dlopen the native artifact if /// admitted. /// - /// # Policy default + /// # Policy /// /// Neither [`PlaneCodecRouter`] nor [`DeltaSchemeRouter`] threads a - /// `HostPolicy` through its constructor, and until this method, - /// nothing in the crate outside of `capability_check`'s own test - /// module called [`check`] at all: this is the crate's first - /// production call site. Using `HostPolicy::default()` here rather - /// than adding a `policy` parameter to [`HardwareBackendRouter::new`] - /// is a deliberate choice, not an oversight. + /// `HostPolicy` through its constructor; [`HardwareBackendRouter`] + /// does, via `self.policy` (set by [`Self::new`] to + /// `HostPolicy::default()`, or by [`Self::new_with_policy`] to a + /// caller-supplied value). This method was the crate's first + /// production call site for [`check`] at all: previously nothing + /// outside `capability_check`'s own test module called it. /// `HostPolicy::default().available_hardware` is an empty /// `Vec`, and `check`'s `hardware_class` rule denies any /// contribution whose *declared* class is not a member of - /// `available_hardware`. + /// `available_hardware`; a router built via [`Self::new`] therefore + /// still denies every `hardware_class`-declaring contribution by + /// default, exactly as before. /// /// That rule only fires when `hardware_class` is present and typed /// as `CapabilityValue::Text`, though: every rule in `check` inspects @@ -592,15 +636,14 @@ impl HardwareBackendRouter { /// `CapabilityValue::Text`, or resolution is denied immediately. /// With that precondition enforced, every `hardware_backend` /// contribution this router can admit necessarily has a declared - /// class subject to `check`'s `hardware_class` rule, so the empty - /// default `available_hardware` denies all of them. Enabling this - /// feature on a given host requires an operator to explicitly list - /// the hardware class (for example `"cuda"`) in `available_hardware` - /// through a policy file consumed elsewhere in the resolution - /// pipeline. Threading a caller-supplied `HostPolicy` into this - /// router (for example from the PyO3 binding that constructs it) is - /// legitimate future work, but is not required for this default to - /// be safe today. + /// class subject to `check`'s `hardware_class` rule, so a + /// `self.policy` whose `available_hardware` doesn't list that class + /// denies it. Enabling this feature for a given hardware class thus + /// requires the router to be constructed via + /// [`Self::new_with_policy`] with a policy whose `available_hardware` + /// explicitly lists that class (for example `"cuda"`), an operator + /// decision made upstream of this router, not something `resolve` + /// infers on its own. fn resolve( &self, canonical_id: &CanonicalId, @@ -632,10 +675,10 @@ impl HardwareBackendRouter { } // Deny before dlopen, not after: see this method's doc comment - // for why HostPolicy::default() is the correct policy to check - // against here. - let policy = HostPolicy::default(); - match check(&entry, &policy, &VendorTable::default()) { + // for how self.policy is chosen (HostPolicy::default() via + // Self::new, or a caller-supplied policy via + // Self::new_with_policy). + match check(&entry, &self.policy, &VendorTable::default()) { CapabilityVerdict::Denied { reason } => { return Err(CodecError::Unsupported { feature: format!("hardware_backend capability check denied: {reason}"), @@ -998,10 +1041,8 @@ mod tests { // bundle_dir that does not exist on disk: if capability_check // runs first, the error message must say "capability check // denied", not a filesystem error. - let (install, id) = discovered_contribution_with_hardware_class( - "cuda", - "/this/bundle/dir/does/not/exist", - ); + let (install, id) = + discovered_contribution_with_hardware_class("cuda", "/this/bundle/dir/does/not/exist"); let router = HardwareBackendRouter::new(vec![install]); let res = router.get(&id); match res { @@ -1073,13 +1114,51 @@ mod tests { let res = router.get(&id); match res { Ok(_) => panic!("expected denial for missing hardware_class, got Ok"), + Err(CodecError::Unsupported { feature }) => { + assert!(feature.contains("hardware_class"), "got: {feature}"); + } + Err(other) => panic!("expected denial for missing hardware_class, got {other:?}"), + } + } + + #[test] + fn hardware_backend_router_new_with_policy_admits_the_case_default_denies() { + // Same "cuda" fixture shape reused with hardware_class = "cpu" to + // match the real ref_hardware_backend fixture's declared class. + // Against HardwareBackendRouter::new (HostPolicy::default(), + // available_hardware empty) this would be denied at the + // capability-check step, exactly like + // hardware_backend_router_denies_before_native_load_when_capability_missing + // above. This is the regression guard for new_with_policy: a + // policy that explicitly lists "cpu" in available_hardware must + // let resolution past the capability check. The bundle_dir still + // does not exist on disk, so resolution is expected to fail + // afterward at the "no native artifact" step, not at capability + // check: that distinction is exactly what proves admission + // happened before the (necessarily failing, since there is + // nothing to dlopen) filesystem step. + let (install, id) = + discovered_contribution_with_hardware_class("cpu", "/this/bundle/dir/does/not/exist"); + let policy = HostPolicy { + available_hardware: vec!["cpu".into()], + ..HostPolicy::default() + }; + let router = HardwareBackendRouter::new_with_policy(vec![install], policy); + let res = router.get(&id); + match res { + Ok(_) => panic!("expected a native-artifact-not-found error, got Ok"), Err(CodecError::Unsupported { feature }) => { assert!( - feature.contains("hardware_class"), - "got: {feature}" + !feature.contains("capability check denied"), + "capability check should have admitted this contribution, got: {feature}" + ); + assert!( + feature.contains("no native artifact was found"), + "expected a native-artifact-not-found error once past capability \ + admission, got: {feature}" ); } - Err(other) => panic!("expected denial for missing hardware_class, got {other:?}"), + Err(other) => panic!("expected Unsupported, got {other:?}"), } } } From 927d939379d253128cd5c0e4bf3828d5e1ad9fff Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 09:25:19 +0200 Subject: [PATCH 16/27] feat(hardware_backend): wire a caller-supplied policy into the PyO3 binding hardware_backend_cuda_stream_handle and hardware_backend_dispatch_decode_cuda were HardwareBackendRouter's only production callers, and both built the router with HardwareBackendRouter::new, so a real CUDA contribution would hit the same default-deny wall the router_native test fixture does. Both functions gain an optional trailing `policy` parameter (a ResolvedPolicy, the existing PyO3-exposed type from policy.rs). When supplied, its HostPolicy is passed to HardwareBackendRouter::new_with_policy; when omitted, behavior is unchanged (default-deny via HardwareBackendRouter::new). PyResolvedPolicy gains a pub(crate) host_policy() accessor for this, not exposed to Python. --- crates/ptwm-py/src/hardware.rs | 68 +++++++++++++++++++++++++++++----- crates/ptwm-py/src/policy.rs | 16 +++++++- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/crates/ptwm-py/src/hardware.rs b/crates/ptwm-py/src/hardware.rs index 381013e..6a830b1 100644 --- a/crates/ptwm-py/src/hardware.rs +++ b/crates/ptwm-py/src/hardware.rs @@ -28,13 +28,32 @@ use ptwm_core::discovery::scan_all_cached; use ptwm_core::extension::CanonicalId; use ptwm_core::flavor::HardwareBackendRouter; +use crate::policy::PyResolvedPolicy; + fn to_pyerr(e: E) -> PyErr { pyo3::exceptions::PyValueError::new_err(e.to_string()) } -fn hardware_backend_router() -> PyResult { +/// Build a `HardwareBackendRouter` for one call. +/// +/// `policy` is optional. When `None`, the router is built via +/// `HardwareBackendRouter::new`, which gates every `hardware_class`- +/// declaring contribution through the default (empty-`available_hardware`) +/// `HostPolicy` and therefore denies all of them: today's exact +/// behavior, unchanged. When `Some`, the caller has supplied a +/// `ResolvedPolicy` (produced from an operator's policy file via +/// `PolicyFile::resolve`, see `policy.rs`); its `HostPolicy` is passed +/// through to `HardwareBackendRouter::new_with_policy`, so a contribution +/// only resolves if its declared `hardware_class` is actually listed in +/// that policy's `available_hardware`. This never bypasses the +/// `hardware_class`-presence precondition or `check` itself; it only +/// lets a caller supply which policy `check` runs against. +fn hardware_backend_router(policy: Option<&PyResolvedPolicy>) -> PyResult { let installed = scan_all_cached().map_err(to_pyerr)?; - Ok(HardwareBackendRouter::new(installed)) + Ok(match policy { + Some(p) => HardwareBackendRouter::new_with_policy(installed, p.host_policy()), + None => HardwareBackendRouter::new(installed), + }) } // --------------------------------------------------------------------------- @@ -232,9 +251,18 @@ fn extract_device_ptr(capsule: &Bound<'_, PyAny>) -> PyResult<(u64, i32, usize)> /// Return the raw CUDA stream handle for `canonical_id`'s `hardware_backend` /// contribution on the given device ordinal. +/// +/// `policy`, when supplied, is a `ResolvedPolicy` (see `policy.rs`) whose +/// `HostPolicy` gates resolution instead of the router's own default-deny +/// policy; omitting it keeps today's default-deny behavior. #[pyfunction] -pub fn hardware_backend_cuda_stream_handle(canonical_id: String, device_ordinal: u32) -> PyResult { - let router = hardware_backend_router()?; +#[pyo3(signature = (canonical_id, device_ordinal, policy=None))] +pub fn hardware_backend_cuda_stream_handle( + canonical_id: String, + device_ordinal: u32, + policy: Option<&PyResolvedPolicy>, +) -> PyResult { + let router = hardware_backend_router(policy)?; let id = CanonicalId::parse(&canonical_id).map_err(to_pyerr)?; let backend = router.get(&id).map_err(to_pyerr)?; backend.cuda_stream_handle(device_ordinal).map_err(to_pyerr) @@ -253,6 +281,15 @@ pub fn hardware_backend_cuda_stream_handle(canonical_id: String, device_ordinal: /// state the device explicitly is simpler than reaching into tensor /// internals and keeps behavior visible rather than silently inferred. #[pyfunction] +#[pyo3(signature = ( + canonical_id, + codec_id, + state_bytes, + compressed, + out, + device_ordinal, + policy=None, +))] #[allow(clippy::too_many_arguments)] pub fn hardware_backend_dispatch_decode_cuda<'py>( py: Python<'py>, @@ -262,13 +299,16 @@ pub fn hardware_backend_dispatch_decode_cuda<'py>( compressed: &Bound<'py, PyAny>, out: &Bound<'py, PyAny>, device_ordinal: u32, + policy: Option<&PyResolvedPolicy>, ) -> PyResult { - let router = hardware_backend_router()?; + let router = hardware_backend_router(policy)?; let id = CanonicalId::parse(&canonical_id).map_err(to_pyerr)?; let codec = CanonicalId::parse(&codec_id).map_err(to_pyerr)?; let backend = router.get(&id).map_err(to_pyerr)?; - let stream = backend.cuda_stream_handle(device_ordinal).map_err(to_pyerr)?; + let stream = backend + .cuda_stream_handle(device_ordinal) + .map_err(to_pyerr)?; let dlpack_kwargs = PyDict::new(py); dlpack_kwargs.set_item("stream", stream)?; @@ -306,8 +346,14 @@ pub fn hardware_backend_dispatch_decode_cuda<'py>( } pub fn register(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> { - m.add_function(pyo3::wrap_pyfunction!(hardware_backend_cuda_stream_handle, m)?)?; - m.add_function(pyo3::wrap_pyfunction!(hardware_backend_dispatch_decode_cuda, m)?)?; + m.add_function(pyo3::wrap_pyfunction!( + hardware_backend_cuda_stream_handle, + m + )?)?; + m.add_function(pyo3::wrap_pyfunction!( + hardware_backend_dispatch_decode_cuda, + m + )?)?; Ok(()) } @@ -321,7 +367,11 @@ mod dlpack_tests { // frozen DLPack C header (dlpack.h, DLDataTypeCode enum). This // guards against a transcription error in the hand-rolled struct // above; it is not a full DLPack conformance test. - let dt = DLDataType { code: 4, bits: 16, lanes: 1 }; + let dt = DLDataType { + code: 4, + bits: 16, + lanes: 1, + }; assert_eq!(dt.code, 4); assert_eq!(dt.bits, 16); } diff --git a/crates/ptwm-py/src/policy.rs b/crates/ptwm-py/src/policy.rs index b6c100d..9a8f6d3 100644 --- a/crates/ptwm-py/src/policy.rs +++ b/crates/ptwm-py/src/policy.rs @@ -5,7 +5,7 @@ use std::collections::HashSet; use std::path::PathBuf; use ptwm_core::extension::CanonicalId; -use ptwm_core::policy::{PolicyFile, ResolvedPolicy, resolve}; +use ptwm_core::policy::{HostPolicy, PolicyFile, ResolvedPolicy, resolve}; fn to_pyerr(e: E) -> PyErr { pyo3::exceptions::PyValueError::new_err(e.to_string()) @@ -89,6 +89,20 @@ impl PyResolvedPolicy { } } +impl PyResolvedPolicy { + /// Crate-internal accessor for the resolved `HostPolicy`, used by + /// other PyO3 bindings (e.g. `hardware.rs`) that need to pass a + /// caller-supplied policy into a router such as + /// `HardwareBackendRouter::new_with_policy` instead of that router's + /// own default-deny policy. Not exposed to Python: callers on the + /// Python side only ever hand a whole `ResolvedPolicy` object back + /// into another PyO3 function, never read `HostPolicy` fields + /// directly. + pub(crate) fn host_policy(&self) -> HostPolicy { + self.inner.host.clone() + } +} + pub fn register(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; From 116772ffa010c870e0fe9e6eb2e03228d27a40c9 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 09:25:38 +0200 Subject: [PATCH 17/27] test(hardware_backend): real dlopen round-trip against the compiled reference fixture First test in the repo's history exercising the entire hardware_backend dispatch path: router resolution, capability check, dlopen, native-symbol probing, and an FFI call, against a real compiled ref_hardware_backend .so, not a mock. ref_hardware_backend's manifest declares hardware_class = "cpu", which HardwareBackendRouter::new's default-deny policy would reject before any dlopen. Uses the just-added HardwareBackendRouter::new_with_policy with a policy that explicitly grants "cpu" to available_hardware, mirroring what an operator's policy file would need to configure on a real host. #[ignore]-gated: requires building extensions/ref_hardware_backend first (cd extensions/ref_hardware_backend/rust && cargo build --release). --- .../tests/hardware_backend_router_native.rs | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 crates/ptwm-core/tests/hardware_backend_router_native.rs diff --git a/crates/ptwm-core/tests/hardware_backend_router_native.rs b/crates/ptwm-core/tests/hardware_backend_router_native.rs new file mode 100644 index 0000000..bb9efc9 --- /dev/null +++ b/crates/ptwm-core/tests/hardware_backend_router_native.rs @@ -0,0 +1,146 @@ +//! Requires extensions/ref_hardware_backend built for native flavor first: +//! cd extensions/ref_hardware_backend/rust && cargo build --release +//! Run with: cargo test -p ptwm-core --test hardware_backend_router_native -- --ignored +//! +//! This is the first test in the repo's history exercising the entire +//! `hardware_backend` dispatch path: router resolution, capability +//! check, dlopen, native-symbol probing, and an FFI call, against a +//! real compiled `.so`, not a mock. +//! +//! `ref_hardware_backend`'s manifest declares `hardware_class = "cpu"`. +//! `HardwareBackendRouter::new` gates resolution through the default +//! (empty-`available_hardware`) `HostPolicy`, which denies every +//! `hardware_class`-declaring contribution by design (see +//! `HardwareBackendRouter::resolve`'s doc comment in `router.rs`). This +//! test therefore uses `HardwareBackendRouter::new_with_policy` with a +//! policy that explicitly lists `"cpu"` in `available_hardware`, +//! mirroring what an operator's policy file would need to grant for this +//! contribution to load on a real host. + +use ptwm_core::discovery::{DiscoveredContribution, FLAVOR_NATIVE}; +use ptwm_core::extension::{CanonicalId, Manifest}; +use ptwm_core::flavor::HardwareBackendRouter; +use ptwm_core::policy::HostPolicy; + +/// Locate the compiled reference fixture's shared library. +/// +/// Falls back to a nonexistent path (rather than panicking at collection +/// time) so `cargo test` can still enumerate this file's tests when the +/// fixture hasn't been built; the `assert!` inside the test itself gives +/// the actionable error message. +fn native_so_path() -> std::path::PathBuf { + std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../extensions/ref_hardware_backend/rust/target/release") + .canonicalize() + .ok() + .and_then(|dir| { + std::fs::read_dir(&dir).ok()?.find_map(|e| { + let p = e.ok()?.path(); + let ext = p.extension()?.to_str()?; + (ext == "so" || ext == "dylib").then_some(p) + }) + }) + .unwrap_or_else(|| std::path::PathBuf::from("/nonexistent")) +} + +/// Path to the fixture's bundle directory (where `manifest.toml` and the +/// compiled `.so` both live). +fn fixture_bundle_dir() -> std::path::PathBuf { + std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../extensions/ref_hardware_backend/rust") +} + +/// Load the real `ref_hardware_backend` manifest and wrap it in a +/// `DiscoveredContribution` mirroring what filesystem discovery would +/// produce for an installed bundle. +/// +/// `manifest.toml` lives in `extensions/ref_hardware_backend/rust/`, but +/// the compiled `.so` lands in that crate's own `target/release/` (a +/// plain `cargo build` output layout, not the packaged +/// `/manifest.toml` + `/.so` layout real +/// installed bundles use (see `discovery/mod.rs`'s module doc comment). +/// `DiscoveredContribution::bundle_dir` is what `find_native_path` +/// actually scans, so it must point at the directory holding the `.so`; +/// `manifest_path` stays informational and can point at the manifest's +/// real, separate location. +fn discover_fixture() -> DiscoveredContribution { + let manifest_path = fixture_bundle_dir().join("manifest.toml"); + let manifest_src = std::fs::read_to_string(&manifest_path).unwrap_or_else(|e| { + panic!("read {manifest_path:?}: {e} (build ref_hardware_backend first?)") + }); + let manifest = Manifest::from_toml(&manifest_src) + .unwrap_or_else(|e| panic!("parse {manifest_path:?}: {e:?}")); + + let bundle_dir = native_so_path() + .parent() + .unwrap_or_else(|| panic!("build ref_hardware_backend --release first")) + .to_path_buf(); + + DiscoveredContribution { + manifest, + manifest_path, + bundle_dir, + installed_flavors: FLAVOR_NATIVE, + } +} + +#[test] +#[ignore = "requires extensions/ref_hardware_backend built for native flavor first"] +fn native_hardware_backend_round_trips_through_real_dispatch_path() { + let so_path = native_so_path(); + assert!( + so_path.exists(), + "build ref_hardware_backend --release first: {so_path:?}" + ); + + let install = discover_fixture(); + let canonical_id_str = install + .manifest + .contributions + .first() + .expect("ref_hardware_backend manifest declares one contribution") + .id + .clone(); + let canonical_id = CanonicalId::parse(&canonical_id_str) + .unwrap_or_else(|e| panic!("parse canonical id {canonical_id_str:?}: {e:?}")); + + // The fixture declares hardware_class = "cpu"; grant exactly that + // class so the capability check admits it, mirroring what an + // operator's policy file would need to configure on a real host. + // See this file's module doc comment for why HardwareBackendRouter::new + // (the default-deny policy) cannot be used here. + let policy = HostPolicy { + available_hardware: vec!["cpu".into()], + ..HostPolicy::default() + }; + let router = HardwareBackendRouter::new_with_policy(vec![install], policy); + + let backend = router + .get(&canonical_id) + .expect("should resolve native flavor"); + + let stream = backend + .cuda_stream_handle(0) + .expect("scaffold returns nonzero sentinel"); + assert_ne!(stream, 0); + + // The CPU-passthrough body does a real memcpy between two "device" + // pointers that are actually host allocations here: allocate two + // Vec buffers, pass their .as_ptr() as u64, and confirm the + // output buffer received the input bytes unchanged. + let input = vec![1u8, 2, 3, 4, 5, 6, 7, 8]; + let mut output = vec![0u8; input.len()]; + let n = backend + .dispatch_decode_cuda( + &[], + &canonical_id, + input.as_ptr() as u64, + input.len(), + output.as_mut_ptr() as u64, + output.len(), + 0, + ) + .expect("dispatch should succeed"); + assert_eq!(n, input.len()); + assert_eq!(output, input); +} From ca1ddb0552f20f93f85c0830bbe43f5cba7fa29a Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 10:11:29 +0200 Subject: [PATCH 18/27] test(hardware_backend): Python interop round-trip via the real ext-build CLI --- crates/ptwm-py/src/hardware.rs | 125 +++++++++++++++--- python/ptwm/_rust/hardware.py | 13 ++ .../ext/test_hardware_backend_dispatch.py | 106 +++++++++++++++ 3 files changed, 225 insertions(+), 19 deletions(-) create mode 100644 python/ptwm/_rust/hardware.py create mode 100644 tests/ptwm/ext/test_hardware_backend_dispatch.py diff --git a/crates/ptwm-py/src/hardware.rs b/crates/ptwm-py/src/hardware.rs index 6a830b1..b9369bb 100644 --- a/crates/ptwm-py/src/hardware.rs +++ b/crates/ptwm-py/src/hardware.rs @@ -21,6 +21,7 @@ use std::os::raw::{c_int, c_void}; +use pyo3::buffer::PyBuffer; use pyo3::prelude::*; use pyo3::types::{PyCapsule, PyCapsuleMethods, PyDict}; @@ -245,6 +246,78 @@ fn extract_device_ptr(capsule: &Bound<'_, PyAny>) -> PyResult<(u64, i32, usize)> Ok((device_ptr, dl_tensor.device.device_id, byte_len as usize)) } +// --------------------------------------------------------------------------- +// Buffer-protocol fallback: plain `bytes` / `bytearray` inputs +// --------------------------------------------------------------------------- + +/// Either a DLPack capsule (produced by `tensor.__dlpack__()`) or a plain +/// buffer-protocol object (`bytes`, `bytearray`, ...), resolved once and +/// kept alive for the duration of the dispatch call. +/// +/// `resolve_input` picks the variant based on whether the Python object +/// exposes `__dlpack__`; real `torch.Tensor` arguments always do, so the +/// DLPack path is unchanged for them. Plain `bytes`/`bytearray` do not, so +/// they fall back to `Buffer`, which reads/writes the host memory the +/// buffer protocol exposes directly. This lets a CPU-only caller (this +/// crate's own interop tests, and any other host-memory caller) exercise a +/// `hardware_backend` contribution without constructing a fake CUDA-shaped +/// DLPack tensor; a `hardware_class = "cpu"` contribution such as +/// `ref_hardware_backend` treats its "device" pointers as ordinary host +/// pointers already (see that crate's own doc comments), so passing a host +/// pointer through this path is exactly what such a contribution expects. +enum InputHandle<'py> { + Dlpack(Bound<'py, PyAny>), + Buffer(PyBuffer), +} + +impl InputHandle<'_> { + /// Returns `(pointer, device_ordinal, byte_len)`. `device_ordinal` is + /// `None` for the buffer-protocol fallback, which carries no device + /// information; callers must skip the device-ordinal-agreement check + /// in that case rather than treat `None` as a mismatch. + fn ptr_len_ordinal(&self) -> PyResult<(u64, Option, usize)> { + match self { + InputHandle::Dlpack(capsule) => { + let (ptr, ordinal, len) = extract_device_ptr(capsule)?; + Ok((ptr, Some(ordinal), len)) + } + InputHandle::Buffer(buf) => Ok((buf.buf_ptr() as u64, None, buf.len_bytes())), + } + } +} + +/// Resolve one `compressed`/`out` argument to an [`InputHandle`]. +/// +/// `dlpack_kwargs` is only used on the DLPack branch (`stream=` has no +/// meaning for a plain host buffer). `require_writable` rejects a +/// read-only buffer on the fallback branch; the DLPack branch has no +/// equivalent read-only concept at this layer; a decode into a read-only +/// tensor's backing memory is between the caller and whatever `torch` +/// enforces, unchanged from before this fallback was added. +fn resolve_input<'py>( + obj: &Bound<'py, PyAny>, + dlpack_kwargs: &Bound<'py, PyDict>, + role: &str, + require_writable: bool, +) -> PyResult> { + if obj.hasattr("__dlpack__")? { + let capsule = obj.call_method("__dlpack__", (), Some(dlpack_kwargs))?; + return Ok(InputHandle::Dlpack(capsule)); + } + + let buf = PyBuffer::::get(obj)?; + if !buf.is_c_contiguous() { + return Err(to_pyerr(format!("{role}: buffer must be C-contiguous"))); + } + if require_writable && buf.readonly() { + return Err(to_pyerr(format!( + "{role}: buffer-protocol fallback requires a writable buffer \ + (e.g. bytearray), got a read-only buffer" + ))); + } + Ok(InputHandle::Buffer(buf)) +} + // --------------------------------------------------------------------------- // PyO3-visible functions // --------------------------------------------------------------------------- @@ -271,15 +344,25 @@ pub fn hardware_backend_cuda_stream_handle( /// Dispatch a CUDA decode through `canonical_id`'s `hardware_backend` /// contribution. /// -/// `compressed` and `out` are `torch.Tensor` objects already resident on -/// the CUDA device identified by `device_ordinal`; both must be -/// contiguous. `device_ordinal` is an explicit, caller-supplied parameter -/// rather than something inferred from the tensors: `cuda_stream_handle` -/// needs a device ordinal before any tensor has been inspected (there is -/// no tensor yet to peek a `.device.index` from at that point), and v1 of -/// this design is documented as single-GPU, so requiring the caller to -/// state the device explicitly is simpler than reaching into tensor -/// internals and keeps behavior visible rather than silently inferred. +/// `compressed` and `out` are ordinarily `torch.Tensor` objects already +/// resident on the CUDA device identified by `device_ordinal`; both must +/// be contiguous, and are read via `__dlpack__()`. As a fallback, an +/// object without `__dlpack__` (plain `bytes`/`bytearray`) is accepted +/// too, read/written directly through the buffer protocol instead: see +/// [`resolve_input`]. This exists so a `hardware_class = "cpu"` +/// contribution can be exercised from Python without constructing a fake +/// CUDA-shaped DLPack tensor or requiring `torch`; a real CUDA tensor +/// still goes through `__dlpack__` exactly as before. `device_ordinal` is +/// an explicit, caller-supplied parameter rather than something inferred +/// from the tensors: `cuda_stream_handle` needs a device ordinal before +/// any tensor has been inspected (there is no tensor yet to peek a +/// `.device.index` from at that point), and v1 of this design is +/// documented as single-GPU, so requiring the caller to state the device +/// explicitly is simpler than reaching into tensor internals and keeps +/// behavior visible rather than silently inferred. The device-ordinal +/// agreement check below only applies when both arguments went through +/// the DLPack branch; the buffer-protocol fallback carries no device +/// information to check against. #[pyfunction] #[pyo3(signature = ( canonical_id, @@ -312,22 +395,26 @@ pub fn hardware_backend_dispatch_decode_cuda<'py>( let dlpack_kwargs = PyDict::new(py); dlpack_kwargs.set_item("stream", stream)?; - let compressed_capsule = compressed.call_method("__dlpack__", (), Some(&dlpack_kwargs))?; - let out_capsule = out.call_method("__dlpack__", (), Some(&dlpack_kwargs))?; + let compressed_handle = resolve_input(compressed, &dlpack_kwargs, "compressed", false)?; + let out_handle = resolve_input(out, &dlpack_kwargs, "out", true)?; - let (in_dev_ptr, in_ordinal, in_len) = extract_device_ptr(&compressed_capsule)?; - let (out_dev_ptr, out_ordinal, out_len) = extract_device_ptr(&out_capsule)?; + let (in_dev_ptr, in_ordinal, in_len) = compressed_handle.ptr_len_ordinal()?; + let (out_dev_ptr, out_ordinal, out_len) = out_handle.ptr_len_ordinal()?; // Both tensors' own DLPack device ordinals must agree with the // caller-supplied `device_ordinal` used to acquire the stream above; // a mismatch means the stream and the tensor memory belong to // different devices, which would silently corrupt the decode rather - // than fail loudly. - if in_ordinal != device_ordinal as i32 || out_ordinal != device_ordinal as i32 { - return Err(to_pyerr(format!( - "device_ordinal mismatch: caller supplied {device_ordinal}, but compressed \ - tensor reports device {in_ordinal} and out tensor reports device {out_ordinal}" - ))); + // than fail loudly. Neither ordinal is known on the buffer-protocol + // fallback branch (`None`), so the check is skipped there rather than + // treated as a mismatch. + if let (Some(in_ordinal), Some(out_ordinal)) = (in_ordinal, out_ordinal) { + if in_ordinal != device_ordinal as i32 || out_ordinal != device_ordinal as i32 { + return Err(to_pyerr(format!( + "device_ordinal mismatch: caller supplied {device_ordinal}, but compressed \ + tensor reports device {in_ordinal} and out tensor reports device {out_ordinal}" + ))); + } } py.allow_threads(|| { diff --git a/python/ptwm/_rust/hardware.py b/python/ptwm/_rust/hardware.py new file mode 100644 index 0000000..b8fc4e7 --- /dev/null +++ b/python/ptwm/_rust/hardware.py @@ -0,0 +1,13 @@ +"""Thin shim exposing the hardware_backend dispatch surface as ptwm._rust.hardware.""" + +from __future__ import annotations + +from ptwm._core import ( # type: ignore[import] + hardware_backend_cuda_stream_handle, + hardware_backend_dispatch_decode_cuda, +) + +__all__ = [ + "hardware_backend_cuda_stream_handle", + "hardware_backend_dispatch_decode_cuda", +] diff --git a/tests/ptwm/ext/test_hardware_backend_dispatch.py b/tests/ptwm/ext/test_hardware_backend_dispatch.py new file mode 100644 index 0000000..1d5ac11 --- /dev/null +++ b/tests/ptwm/ext/test_hardware_backend_dispatch.py @@ -0,0 +1,106 @@ +"""End-to-end test for hardware_backend dispatch through the real native +build path. + +Installs the ref_hardware_backend native (CPU-passthrough) reference +contribution as a local bundle, then round-trips +hardware_backend_dispatch_decode_cuda through the full stack: Python -> +PyO3 -> HardwareBackendRouter -> NativeExtension -> the actual reference +module (not a hand-rolled stand-in). + +Requires the reference module to already be built: + cd extensions/ref_hardware_backend/rust + cargo build --release + +Both calls pass an explicit `policy`: `hardware_class = "cpu"` is a +capability the router denies by default (HardwareBackendRouter::new's +HostPolicy has an empty available_hardware list), so a ResolvedPolicy +that grants "cpu" is required for resolution to succeed at all. The +policy can only be produced by writing a policy file to disk and +resolving it; there is no in-memory constructor. + +`hardware_backend_dispatch_decode_cuda`'s compressed/out arguments are +ordinarily `torch.Tensor` objects read via `__dlpack__()`. This test +passes plain `bytes`/`bytearray` instead, which fall back to the +buffer-protocol path added to that function for exactly this case: it +lets this CPU-only smoke test exercise the real dispatch path (router, +native ABI, dlopen, FFI call) without requiring `torch` or constructing +a fake CUDA-shaped DLPack tensor. Real CUDA tensors still go through +`__dlpack__` normally. +""" + +from __future__ import annotations + +import shutil +from pathlib import Path + +import pytest + +REF_DIR = Path(__file__).parents[3] / "extensions" / "ref_hardware_backend" / "rust" +REF_SO_CANDIDATES = list((REF_DIR / "target" / "release").glob("*.so")) + list( + (REF_DIR / "target" / "release").glob("*.dylib") +) +CANONICAL_ID = "blake3:" + "00" * 32 # matches ref_hardware_backend/rust/manifest.toml's id + + +@pytest.fixture +def isolated_extensions(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path: + monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data")) + monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path / "cache")) + return tmp_path + + +def _cpu_policy(tmp_path: Path): + from ptwm._rust.policy import PolicyFile + + pol = tmp_path / "policy.toml" + pol.write_text('[capabilities]\navailable_hardware = ["cpu"]\n', encoding="utf-8") + return PolicyFile.load(str(pol)).resolve([]) + + +@pytest.mark.interop +@pytest.mark.skipif(not REF_SO_CANDIDATES, reason="ref_hardware_backend native .so not built") +def test_hardware_backend_cpu_passthrough_round_trip(isolated_extensions: Path) -> None: + from ptwm._rust.ext import ext_install + from ptwm._rust.hardware import ( + hardware_backend_cuda_stream_handle, + hardware_backend_dispatch_decode_cuda, + ) + + src = isolated_extensions / "src" + src.mkdir(parents=True, exist_ok=True) + shutil.copy(REF_DIR / "manifest.toml", src / "manifest.toml") + shutil.copy(REF_SO_CANDIDATES[0], src / f"ref-hardware-backend{REF_SO_CANDIDATES[0].suffix}") + + bundle_dir = ext_install(str(src)) + assert bundle_dir + + policy = _cpu_policy(isolated_extensions) + + stream = hardware_backend_cuda_stream_handle(CANONICAL_ID, 0, policy=policy) + assert stream != 0 + + # CPU-passthrough scaffold reinterprets its "device" pointers as host + # pointers, so this test uses plain bytes/bytearray rather than a real + # CUDA tensor, via hardware_backend_dispatch_decode_cuda's + # buffer-protocol fallback for objects without __dlpack__. Real-GPU + # coverage is a separate gpu-marked test (Task 30). + payload = b"hardware backend dispatch smoke test payload" + out = bytearray(len(payload)) + n = hardware_backend_dispatch_decode_cuda( + CANONICAL_ID, CANONICAL_ID, b"", payload, out, 0, policy=policy + ) + assert n == len(payload) + assert bytes(out) == payload + + +@pytest.mark.interop +@pytest.mark.skipif(not REF_SO_CANDIDATES, reason="ref_hardware_backend native .so not built") +def test_hardware_backend_unknown_canonical_id_raises(isolated_extensions: Path) -> None: + from ptwm._rust.hardware import hardware_backend_cuda_stream_handle + + # No policy needed: an unknown canonical id is rejected by + # HardwareBackendRouter::resolve's install lookup before the + # capability check runs, so the router's default-deny HostPolicy + # never enters into it. + with pytest.raises(ValueError, match="no hardware_backend registered"): + hardware_backend_cuda_stream_handle("blake3:" + "ff" * 32, 0) From 0786e1f39c756f919382ed0b3c4115c4c473de63 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 10:21:14 +0200 Subject: [PATCH 19/27] test(hardware_backend): concurrent first-access router resolution --- .../tests/hardware_backend_router_native.rs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/crates/ptwm-core/tests/hardware_backend_router_native.rs b/crates/ptwm-core/tests/hardware_backend_router_native.rs index bb9efc9..d61870d 100644 --- a/crates/ptwm-core/tests/hardware_backend_router_native.rs +++ b/crates/ptwm-core/tests/hardware_backend_router_native.rs @@ -144,3 +144,65 @@ fn native_hardware_backend_round_trips_through_real_dispatch_path() { assert_eq!(n, input.len()); assert_eq!(output, input); } + +#[test] +#[ignore = "requires extensions/ref_hardware_backend built for native flavor first"] +fn concurrent_first_access_resolves_exactly_once_and_all_threads_succeed() { + let so_path = native_so_path(); + assert!( + so_path.exists(), + "build ref_hardware_backend --release first: {so_path:?}" + ); + + let install = discover_fixture(); + let canonical_id_str = install + .manifest + .contributions + .first() + .expect("ref_hardware_backend manifest declares one contribution") + .id + .clone(); + let canonical_id = CanonicalId::parse(&canonical_id_str) + .unwrap_or_else(|e| panic!("parse canonical id {canonical_id_str:?}: {e:?}")); + + let policy = HostPolicy { + available_hardware: vec!["cpu".into()], + ..HostPolicy::default() + }; + let router = std::sync::Arc::new(HardwareBackendRouter::new_with_policy( + vec![install], + policy, + )); + + let handles: Vec<_> = (0..16) + .map(|i| { + let router = std::sync::Arc::clone(&router); + let id = canonical_id.clone(); + std::thread::spawn(move || { + let backend = router + .get(&id) + .expect("resolve under concurrent first access"); + let input = vec![i as u8; 64]; + let mut output = vec![0u8; 64]; + let n = backend + .dispatch_decode_cuda( + &[], + &id, + input.as_ptr() as u64, + input.len(), + output.as_mut_ptr() as u64, + output.len(), + 0, + ) + .expect("dispatch under concurrent access"); + assert_eq!(n, input.len()); + assert_eq!(output, input); + }) + }) + .collect(); + + for h in handles { + h.join().expect("thread panicked"); + } +} + From 32cf7378d30330d3a380eec94fd5a184e4016e13 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 10:29:24 +0200 Subject: [PATCH 20/27] ci(hardware_backend): build and test the native reference fixture on every PR --- .github/workflows/interop.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/interop.yml b/.github/workflows/interop.yml index f0d9bdd..f90644e 100644 --- a/.github/workflows/interop.yml +++ b/.github/workflows/interop.yml @@ -41,5 +41,16 @@ jobs: - name: Install ext-dev deps (for the wasmtime Python package) run: uv pip install --system 'wasmtime>=26' 'zstandard>=0.22' 'tomli-w>=1.0.0' + - name: Build ref_hardware_backend (native flavor) + run: | + cd extensions/ref_hardware_backend/rust + cargo build --release + + - name: Run hardware_backend native dispatch tests + run: cargo test -p ptwm-core --test hardware_backend_router_native -- --ignored + - name: Run interop tests run: uv run pytest tests/interop -v --no-cov + + - name: Run hardware_backend dispatch pytest + run: uv run pytest tests/ptwm/ext/test_hardware_backend_dispatch.py -v --no-cov From 9d64d08aeeb7d025ecd7e5abca5a22e974d86217 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Tue, 28 Jul 2026 10:35:08 +0200 Subject: [PATCH 21/27] feat(ext-tooling): branch native builds to cargo-oxide when the crate depends on it --- python/ptwm/ext_tooling/build.py | 10 +++++++- tests/ptwm/ext_tooling/test_build.py | 34 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/python/ptwm/ext_tooling/build.py b/python/ptwm/ext_tooling/build.py index 14e88f5..d6fac8d 100644 --- a/python/ptwm/ext_tooling/build.py +++ b/python/ptwm/ext_tooling/build.py @@ -105,8 +105,16 @@ def _build_rust(cwd: Path, release: bool, flavor: str = "wasm") -> Path: return dst +def _uses_cuda_oxide(cwd: Path) -> bool: + cargo_toml = cwd / "Cargo.toml" + if not cargo_toml.exists(): + return False + deps = tomllib.loads(cargo_toml.read_text(encoding="utf-8")).get("dependencies", {}) + return "cuda-oxide" in deps + + def _build_rust_native(cwd: Path, release: bool) -> Path: - cmd = ["cargo", "build"] + cmd = ["cargo", "oxide", "build"] if _uses_cuda_oxide(cwd) else ["cargo", "build"] if release: cmd.append("--release") _run(cmd, cwd) diff --git a/tests/ptwm/ext_tooling/test_build.py b/tests/ptwm/ext_tooling/test_build.py index ed0f9ef..2530df0 100644 --- a/tests/ptwm/ext_tooling/test_build.py +++ b/tests/ptwm/ext_tooling/test_build.py @@ -113,3 +113,37 @@ def test_build_raises_when_subprocess_fails(tmp_path: Path) -> None: run.return_value = proc with pytest.raises(BuildError, match="boom"): build_extension(tmp_path) + + +def test_build_rust_native_uses_cargo_oxide_when_cuda_oxide_dependency_present( + tmp_path: Path, +) -> None: + _write_manifest(tmp_path) + (tmp_path / "Cargo.toml").write_text( + '[package]\nname = "demo"\n\n[dependencies]\ncuda-oxide = "0.1"\n' + ) + target_dir = tmp_path / "target" / "release" + target_dir.mkdir(parents=True) + (target_dir / "libdemo.so").write_bytes(b"\x7fELF fake") + + with patch("ptwm.ext_tooling.build._run") as runner: + out = build_extension(tmp_path, release=True, flavor="native") + assert runner.call_count == 1 + cmd = runner.call_args.args[0] + assert cmd[:3] == ["cargo", "oxide", "build"], f"got: {cmd}" + assert "--release" in cmd + assert out.name == "demo.so" + + +def test_build_rust_native_uses_plain_cargo_without_cuda_oxide(tmp_path: Path) -> None: + _write_manifest(tmp_path) + (tmp_path / "Cargo.toml").write_text('[package]\nname = "demo"\n') + target_dir = tmp_path / "target" / "release" + target_dir.mkdir(parents=True) + (target_dir / "libdemo.so").write_bytes(b"\x7fELF fake") + + with patch("ptwm.ext_tooling.build._run") as runner: + build_extension(tmp_path, release=True, flavor="native") + cmd = runner.call_args.args[0] + assert cmd[:2] == ["cargo", "build"], f"got: {cmd}" + From 8cc9b15f367d519389459812a1d3d0a19209d70f Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Wed, 29 Jul 2026 15:19:01 +0200 Subject: [PATCH 22/27] style: format Rust code with cargo fmt and Python code with ruff format --- crates/ptwm-core/src/extension/id.rs | 6 ++- crates/ptwm-core/src/flavor/native.rs | 38 ++++++++++--------- crates/ptwm-core/src/flavor/wasm.rs | 3 +- .../ptwm-core/src/policy/capability_check.rs | 16 ++++++-- .../tests/hardware_backend_router_native.rs | 1 - python/ptwm/ext_tooling/build.py | 4 +- python/ptwm/ext_tooling/init.py | 4 +- .../ext/test_hardware_backend_dispatch.py | 20 +++++++--- tests/ptwm/ext_tooling/test_build.py | 1 - 9 files changed, 58 insertions(+), 35 deletions(-) diff --git a/crates/ptwm-core/src/extension/id.rs b/crates/ptwm-core/src/extension/id.rs index 0a20967..3197250 100644 --- a/crates/ptwm-core/src/extension/id.rs +++ b/crates/ptwm-core/src/extension/id.rs @@ -41,8 +41,10 @@ impl CanonicalId { } let mut out = [0u8; 32]; for (i, chunk) in hex.as_bytes().chunks(2).enumerate() { - let hi = hex_nibble(chunk[0]).ok_or_else(|| super::ExtensionError::InvalidRef(s.to_string()))?; - let lo = hex_nibble(chunk[1]).ok_or_else(|| super::ExtensionError::InvalidRef(s.to_string()))?; + let hi = hex_nibble(chunk[0]) + .ok_or_else(|| super::ExtensionError::InvalidRef(s.to_string()))?; + let lo = hex_nibble(chunk[1]) + .ok_or_else(|| super::ExtensionError::InvalidRef(s.to_string()))?; out[i] = (hi << 4) | lo; } Ok(Self(out)) diff --git a/crates/ptwm-core/src/flavor/native.rs b/crates/ptwm-core/src/flavor/native.rs index 074c84c..47a355e 100644 --- a/crates/ptwm-core/src/flavor/native.rs +++ b/crates/ptwm-core/src/flavor/native.rs @@ -219,14 +219,16 @@ impl NativeExtension { } } Kind::HardwareBackend => { - symbols.hardware_backend_v1_cuda_stream_handle = resolve::( - &library, - b"ptwm_hardware_backend_v1_cuda_stream_handle\0", - ); - symbols.hardware_backend_v1_dispatch_decode_cuda = resolve::( - &library, - b"ptwm_hardware_backend_v1_dispatch_decode_cuda\0", - ); + symbols.hardware_backend_v1_cuda_stream_handle = + resolve::( + &library, + b"ptwm_hardware_backend_v1_cuda_stream_handle\0", + ); + symbols.hardware_backend_v1_dispatch_decode_cuda = + resolve::( + &library, + b"ptwm_hardware_backend_v1_dispatch_decode_cuda\0", + ); if symbols.hardware_backend_v1_cuda_stream_handle.is_none() || symbols.hardware_backend_v1_dispatch_decode_cuda.is_none() { @@ -402,11 +404,12 @@ impl NativeExtension { &self, device_ordinal: u32, ) -> Result { - let f = self.symbols.hardware_backend_v1_cuda_stream_handle.ok_or( - CodecError::Unsupported { - feature: "hardware_backend_v1_cuda_stream_handle not present".into(), - }, - )?; + let f = + self.symbols + .hardware_backend_v1_cuda_stream_handle + .ok_or(CodecError::Unsupported { + feature: "hardware_backend_v1_cuda_stream_handle not present".into(), + })?; let handle = unsafe { f(device_ordinal) }; if handle == 0 { return Err(CodecError::Unsupported { @@ -429,11 +432,12 @@ impl NativeExtension { out_len: usize, device_ordinal: u32, ) -> Result { - let f = self.symbols.hardware_backend_v1_dispatch_decode_cuda.ok_or( - CodecError::Unsupported { + let f = self + .symbols + .hardware_backend_v1_dispatch_decode_cuda + .ok_or(CodecError::Unsupported { feature: "hardware_backend_v1_dispatch_decode_cuda not present".into(), - }, - )?; + })?; let codec_id_bytes = codec_id.as_bytes(); let rc = unsafe { f( diff --git a/crates/ptwm-core/src/flavor/wasm.rs b/crates/ptwm-core/src/flavor/wasm.rs index 3b011b8..499c753 100644 --- a/crates/ptwm-core/src/flavor/wasm.rs +++ b/crates/ptwm-core/src/flavor/wasm.rs @@ -1085,8 +1085,7 @@ mod tests { let mut store2 = ext.make_store(&HostPolicy::default()).unwrap(); let inst2 = ext.instantiate(&mut store2).unwrap(); let mut recon = vec![0u8; 64]; - let m = - invoke_delta_scheme_decode(&inst2, &mut store2, base, &delta, &mut recon).unwrap(); + let m = invoke_delta_scheme_decode(&inst2, &mut store2, base, &delta, &mut recon).unwrap(); assert_eq!(&recon[..m], target); } } diff --git a/crates/ptwm-core/src/policy/capability_check.rs b/crates/ptwm-core/src/policy/capability_check.rs index 6bb243c..b60a826 100644 --- a/crates/ptwm-core/src/policy/capability_check.rs +++ b/crates/ptwm-core/src/policy/capability_check.rs @@ -361,7 +361,11 @@ mod tests { .with_run( "dpkg", &["--verify", "libcuda1"], - RunResult { status_success: true, stdout: String::new(), stderr: String::new() }, + RunResult { + status_success: true, + stdout: String::new(), + stderr: String::new(), + }, ); let mut caps = CapabilityMap::new(); @@ -371,7 +375,10 @@ mod tests { CapabilityValue::List(vec![CapabilityValue::Map({ let mut m = std::collections::BTreeMap::new(); m.insert("name".to_string(), CapabilityValue::Text("cuda".into())); - m.insert("version_constraint".to_string(), CapabilityValue::Text(">=12.0".into())); + m.insert( + "version_constraint".to_string(), + CapabilityValue::Text(">=12.0".into()), + ); m })]), ); @@ -381,7 +388,10 @@ mod tests { policy.available_hardware.push("cuda".into()); let verdict = check_with(&entry, &policy, &VendorTable::default(), &runner); - assert!(matches!(verdict, CapabilityVerdict::Admitted), "got: {verdict:?}"); + assert!( + matches!(verdict, CapabilityVerdict::Admitted), + "got: {verdict:?}" + ); } #[test] diff --git a/crates/ptwm-core/tests/hardware_backend_router_native.rs b/crates/ptwm-core/tests/hardware_backend_router_native.rs index d61870d..ff7c7c6 100644 --- a/crates/ptwm-core/tests/hardware_backend_router_native.rs +++ b/crates/ptwm-core/tests/hardware_backend_router_native.rs @@ -205,4 +205,3 @@ fn concurrent_first_access_resolves_exactly_once_and_all_threads_succeed() { h.join().expect("thread panicked"); } } - diff --git a/python/ptwm/ext_tooling/build.py b/python/ptwm/ext_tooling/build.py index d6fac8d..61aa5b7 100644 --- a/python/ptwm/ext_tooling/build.py +++ b/python/ptwm/ext_tooling/build.py @@ -121,9 +121,7 @@ def _build_rust_native(cwd: Path, release: bool) -> Path: sub = "release" if release else "debug" out_dir = cwd / "target" / sub try: - src = next( - p for pattern in ("*.so", "*.dylib") for p in out_dir.glob(pattern) - ) + src = next(p for pattern in ("*.so", "*.dylib") for p in out_dir.glob(pattern)) except StopIteration as e: msg = f"no native .so/.dylib produced under {out_dir}" raise BuildError(msg) from e diff --git a/python/ptwm/ext_tooling/init.py b/python/ptwm/ext_tooling/init.py index b35a4d5..88d6d71 100644 --- a/python/ptwm/ext_tooling/init.py +++ b/python/ptwm/ext_tooling/init.py @@ -35,7 +35,9 @@ def init_extension( # doesn't fit that shape (e.g. delta_scheme's two-buffer base+target / # base+delta signature) — see `_templates/rust/_kind_delta_scheme/`. kind_template_dir = _TEMPLATES_DIR / lang / f"_kind_{kind}" - template_dir = kind_template_dir if kind_template_dir.is_dir() else _TEMPLATES_DIR / lang + template_dir = ( + kind_template_dir if kind_template_dir.is_dir() else _TEMPLATES_DIR / lang + ) if not template_dir.is_dir(): msg = f"template not found: {template_dir}" raise FileNotFoundError(msg) diff --git a/tests/ptwm/ext/test_hardware_backend_dispatch.py b/tests/ptwm/ext/test_hardware_backend_dispatch.py index 1d5ac11..8953488 100644 --- a/tests/ptwm/ext/test_hardware_backend_dispatch.py +++ b/tests/ptwm/ext/test_hardware_backend_dispatch.py @@ -39,7 +39,9 @@ REF_SO_CANDIDATES = list((REF_DIR / "target" / "release").glob("*.so")) + list( (REF_DIR / "target" / "release").glob("*.dylib") ) -CANONICAL_ID = "blake3:" + "00" * 32 # matches ref_hardware_backend/rust/manifest.toml's id +CANONICAL_ID = ( + "blake3:" + "00" * 32 +) # matches ref_hardware_backend/rust/manifest.toml's id @pytest.fixture @@ -58,7 +60,9 @@ def _cpu_policy(tmp_path: Path): @pytest.mark.interop -@pytest.mark.skipif(not REF_SO_CANDIDATES, reason="ref_hardware_backend native .so not built") +@pytest.mark.skipif( + not REF_SO_CANDIDATES, reason="ref_hardware_backend native .so not built" +) def test_hardware_backend_cpu_passthrough_round_trip(isolated_extensions: Path) -> None: from ptwm._rust.ext import ext_install from ptwm._rust.hardware import ( @@ -69,7 +73,9 @@ def test_hardware_backend_cpu_passthrough_round_trip(isolated_extensions: Path) src = isolated_extensions / "src" src.mkdir(parents=True, exist_ok=True) shutil.copy(REF_DIR / "manifest.toml", src / "manifest.toml") - shutil.copy(REF_SO_CANDIDATES[0], src / f"ref-hardware-backend{REF_SO_CANDIDATES[0].suffix}") + shutil.copy( + REF_SO_CANDIDATES[0], src / f"ref-hardware-backend{REF_SO_CANDIDATES[0].suffix}" + ) bundle_dir = ext_install(str(src)) assert bundle_dir @@ -94,8 +100,12 @@ def test_hardware_backend_cpu_passthrough_round_trip(isolated_extensions: Path) @pytest.mark.interop -@pytest.mark.skipif(not REF_SO_CANDIDATES, reason="ref_hardware_backend native .so not built") -def test_hardware_backend_unknown_canonical_id_raises(isolated_extensions: Path) -> None: +@pytest.mark.skipif( + not REF_SO_CANDIDATES, reason="ref_hardware_backend native .so not built" +) +def test_hardware_backend_unknown_canonical_id_raises( + isolated_extensions: Path, +) -> None: from ptwm._rust.hardware import hardware_backend_cuda_stream_handle # No policy needed: an unknown canonical id is rejected by diff --git a/tests/ptwm/ext_tooling/test_build.py b/tests/ptwm/ext_tooling/test_build.py index 2530df0..79e64e4 100644 --- a/tests/ptwm/ext_tooling/test_build.py +++ b/tests/ptwm/ext_tooling/test_build.py @@ -146,4 +146,3 @@ def test_build_rust_native_uses_plain_cargo_without_cuda_oxide(tmp_path: Path) - build_extension(tmp_path, release=True, flavor="native") cmd = runner.call_args.args[0] assert cmd[:2] == ["cargo", "build"], f"got: {cmd}" - From 8982a422cf835dd28b4ad6df4cc30a7bef992f96 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Wed, 29 Jul 2026 15:23:42 +0200 Subject: [PATCH 23/27] fix(clippy): fix unused import and allow too_many_arguments on HardwareBackend dispatch method --- crates/ptwm-core/src/flavor/router.rs | 2 ++ crates/ptwm-core/src/policy/capability_check.rs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/ptwm-core/src/flavor/router.rs b/crates/ptwm-core/src/flavor/router.rs index ffe4937..8e7406d 100644 --- a/crates/ptwm-core/src/flavor/router.rs +++ b/crates/ptwm-core/src/flavor/router.rs @@ -469,6 +469,7 @@ impl DeltaSchemeRouter { /// Provides two methods: `dispatch_decode_cuda` for decoding on CUDA devices, /// and `cuda_stream_handle` to acquire a stream for a given device ordinal. pub trait DispatchedHardwareBackendCuda: Send + Sync { + #[allow(clippy::too_many_arguments)] fn dispatch_decode_cuda( &self, state_bytes: &[u8], @@ -488,6 +489,7 @@ struct NativeHardwareBackendCudaAdapter { } impl DispatchedHardwareBackendCuda for NativeHardwareBackendCudaAdapter { + #[allow(clippy::too_many_arguments)] fn dispatch_decode_cuda( &self, state_bytes: &[u8], diff --git a/crates/ptwm-core/src/policy/capability_check.rs b/crates/ptwm-core/src/policy/capability_check.rs index b60a826..7893615 100644 --- a/crates/ptwm-core/src/policy/capability_check.rs +++ b/crates/ptwm-core/src/policy/capability_check.rs @@ -6,7 +6,7 @@ use crate::extension::{ExtensionTableEntry, capability::CapabilityValue}; use super::native_deps::{ - CommandRunner, NativeDep, NativeDepVerdict, RealCommandRunner, RunResult, VendorTable, + CommandRunner, NativeDep, NativeDepVerdict, RealCommandRunner, VendorTable, verify_native_dep_with, }; @@ -180,6 +180,7 @@ fn parse_native_dep(value: &CapabilityValue) -> Option { #[cfg(test)] mod tests { + use super::super::native_deps::RunResult; use super::*; use crate::extension::{ Attestation, CanonicalId, CapabilityMap, ExtensionTableEntry, Kind, Lifecycle, From f9df747f5a8c3236f6ccda8a8e012f94b15d3b3b Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Wed, 29 Jul 2026 15:28:17 +0200 Subject: [PATCH 24/27] fix(clippy): resolve clippy warnings across tests and codecs --- crates/ptwm-core/src/codecs/arithmetic.rs | 4 ++-- crates/ptwm-core/src/entropy/context_mixing/model.rs | 2 +- crates/ptwm-core/tests/hardware_backend_router_native.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ptwm-core/src/codecs/arithmetic.rs b/crates/ptwm-core/src/codecs/arithmetic.rs index 473025d..8578369 100644 --- a/crates/ptwm-core/src/codecs/arithmetic.rs +++ b/crates/ptwm-core/src/codecs/arithmetic.rs @@ -419,8 +419,8 @@ mod tests { let data: Vec = (0..262144) .map(|i| match i % 16 { 0..=9 => 0x40u8, - 10 | 11 | 12 => 0x41, - 13 | 14 => 0x3F, + 10..=12 => 0x41, + 13..=14 => 0x3F, _ => 0x42, }) .collect(); diff --git a/crates/ptwm-core/src/entropy/context_mixing/model.rs b/crates/ptwm-core/src/entropy/context_mixing/model.rs index 4434b96..aeb63cf 100644 --- a/crates/ptwm-core/src/entropy/context_mixing/model.rs +++ b/crates/ptwm-core/src/entropy/context_mixing/model.rs @@ -133,7 +133,7 @@ mod tests { for &byte in data { let mut c0: u32 = 1; for i in (0..8).rev() { - let bit = ((byte >> i) & 1) as u8; + let bit = (byte >> i) & 1; let preds = models.predict(c0); out.push(preds[0]); models.update(c0, bit); diff --git a/crates/ptwm-core/tests/hardware_backend_router_native.rs b/crates/ptwm-core/tests/hardware_backend_router_native.rs index ff7c7c6..b36edb5 100644 --- a/crates/ptwm-core/tests/hardware_backend_router_native.rs +++ b/crates/ptwm-core/tests/hardware_backend_router_native.rs @@ -177,7 +177,7 @@ fn concurrent_first_access_resolves_exactly_once_and_all_threads_succeed() { let handles: Vec<_> = (0..16) .map(|i| { let router = std::sync::Arc::clone(&router); - let id = canonical_id.clone(); + let id = canonical_id; std::thread::spawn(move || { let backend = router .get(&id) From 3f3aaf9f00738ab513de6d6b20cbffcb4866fbad Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Wed, 29 Jul 2026 15:32:52 +0200 Subject: [PATCH 25/27] fix(clippy): resolve clippy 1.97 lints in wasm, huff_llm, and base64 --- crates/ptwm-core/src/codecs/huff_llm.rs | 4 ++-- crates/ptwm-core/src/flavor/wasm.rs | 4 ++-- crates/ptwm-core/src/transcode/base64.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/ptwm-core/src/codecs/huff_llm.rs b/crates/ptwm-core/src/codecs/huff_llm.rs index f1cf0f7..c331d54 100644 --- a/crates/ptwm-core/src/codecs/huff_llm.rs +++ b/crates/ptwm-core/src/codecs/huff_llm.rs @@ -64,7 +64,7 @@ impl PlaneCodec for HuffLlm5Bit { _layout: &PlaneLayout, ) -> bool { // Skip tiny planes (three table headers don't amortize) and odd lengths. - plane.len() >= MIN_WORDS * 2 && plane.len() % 2 == 0 + plane.len() >= MIN_WORDS * 2 && plane.len().is_multiple_of(2) } fn encode( @@ -82,7 +82,7 @@ impl PlaneCodec for HuffLlm5Bit { // Empty or odd-length → raw store (still total). Short-circuit empty // before allocating `words`, since trial-encode hits this path on every // candidate. - if plane.is_empty() || plane.len() % 2 != 0 { + if plane.is_empty() || !plane.len().is_multiple_of(2) { return Ok(raw_store(plane)); } let words: Vec = plane diff --git a/crates/ptwm-core/src/flavor/wasm.rs b/crates/ptwm-core/src/flavor/wasm.rs index 499c753..88c5563 100644 --- a/crates/ptwm-core/src/flavor/wasm.rs +++ b/crates/ptwm-core/src/flavor/wasm.rs @@ -97,10 +97,10 @@ impl WasmExtension { }; let mem_limit = match entry.capabilities.get("mem_factor") { Some(CapabilityValue::Float(f)) if f.0 > 0.0 => { - ((f.0 * DEFAULT_MEM_LIMIT as f64) as usize).min(usize::MAX) + (f.0 * DEFAULT_MEM_LIMIT as f64) as usize } Some(CapabilityValue::Int(i)) if *i > 0 => { - ((*i as usize) * DEFAULT_MEM_LIMIT).min(usize::MAX) + (*i as usize) * DEFAULT_MEM_LIMIT } _ => DEFAULT_MEM_LIMIT, }; diff --git a/crates/ptwm-core/src/transcode/base64.rs b/crates/ptwm-core/src/transcode/base64.rs index 762b70d..687839e 100644 --- a/crates/ptwm-core/src/transcode/base64.rs +++ b/crates/ptwm-core/src/transcode/base64.rs @@ -45,7 +45,7 @@ fn decode_char(c: u8) -> Option { /// malformed input (bad characters, wrong length, misplaced padding). pub fn decode(s: &str) -> Option> { let bytes = s.as_bytes(); - if bytes.len() % 4 != 0 { + if !bytes.len().is_multiple_of(4) { return None; } let mut out = Vec::with_capacity(bytes.len() / 4 * 3); From caa171cec69bc52d7d8e53335079d7cf3800b241 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Wed, 29 Jul 2026 15:34:45 +0200 Subject: [PATCH 26/27] style: format wasm.rs with cargo fmt --- crates/ptwm-core/src/flavor/wasm.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ptwm-core/src/flavor/wasm.rs b/crates/ptwm-core/src/flavor/wasm.rs index 88c5563..aad5c37 100644 --- a/crates/ptwm-core/src/flavor/wasm.rs +++ b/crates/ptwm-core/src/flavor/wasm.rs @@ -99,9 +99,7 @@ impl WasmExtension { Some(CapabilityValue::Float(f)) if f.0 > 0.0 => { (f.0 * DEFAULT_MEM_LIMIT as f64) as usize } - Some(CapabilityValue::Int(i)) if *i > 0 => { - (*i as usize) * DEFAULT_MEM_LIMIT - } + Some(CapabilityValue::Int(i)) if *i > 0 => (*i as usize) * DEFAULT_MEM_LIMIT, _ => DEFAULT_MEM_LIMIT, }; From c9ca35a893597283a6f3429561688c086c96fb96 Mon Sep 17 00:00:00 2001 From: "Kurt H. W. Stolle" Date: Wed, 29 Jul 2026 15:39:02 +0200 Subject: [PATCH 27/27] fix(clippy): use is_multiple_of(8) in ptwm-py hardware binding --- crates/ptwm-py/src/hardware.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ptwm-py/src/hardware.rs b/crates/ptwm-py/src/hardware.rs index b9369bb..f973f75 100644 --- a/crates/ptwm-py/src/hardware.rs +++ b/crates/ptwm-py/src/hardware.rs @@ -213,7 +213,7 @@ fn extract_device_ptr(capsule: &Bound<'_, PyAny>) -> PyResult<(u64, i32, usize)> } let elem_bits = dl_tensor.dtype.bits as u64 * dl_tensor.dtype.lanes as u64; - if elem_bits == 0 || elem_bits % 8 != 0 { + if elem_bits == 0 || !elem_bits.is_multiple_of(8) { return Err(to_pyerr(format!( "unsupported DLPack dtype: bits={} lanes={} does not divide evenly into bytes", dl_tensor.dtype.bits, dl_tensor.dtype.lanes