PipeWhy CUDA contract CV1 version1.1 | Q1 v1.0 October 30, 2026 | PW-10.01-.12 Scope and target The public CudaIntegerView and cuda_availability APIs are in pipewhy.model.cuda. The cuda extra installs the qualified driver binding, Numba-CUDA compiler and its native runtime libraries. pipewhy.fingerprint.cuda.record_contributions_from_host is the explicit host-upload GPU contribution API. fingerprint_batch_from_host adds device block/cross-block reduction. fingerprint_batches_from_host consumes bounded host streams. The local capture CLI supports explicit CUDA and host auto selection. Core/reference requires no native runtime dependencies. The first CUDA qualification target is the available Windows11 Pro x86_64 host, OS10.0.26100, CPython3.14.7, NVIDIA GTX1660 compute capability7.5, driver566.14 (CUDA driver API12070). This target was selected under the user's continued- development authorization, using the existing driver and isolated environments. No driver or system toolkit installer was run. Linux remains a qualified CPU target; Linux CUDA and other GPU/device/driver combinations require qualification. The qualified compiler profile uses Numba-CUDA0.30.4, Numba0.68.1, NumPy2.4.6, llvmlite0.50.0, NVCC/NVRTC12.6.85, runtime12.6.77, nvJitLink12.6.85, cuda-bindings12.9.10, cuda-core1.2.2 and pathfinder1.8.5. The installed driver reports API12.7; the older12.6 compiler/runtime successfully compiled and ran on this target. The small probe emitted the expected low- occupancy performance warning; it is a correctness probe, not a throughput result. Compatibility failure preserved The first isolated candidate with NumPy2.5.4 installed successfully and pip check reported no broken requirements, but Numba-CUDA failed during import when it used numpy.row_stack. NumPy's2.5 release notes document removal of that alias. Changing only the probe environment to NumPy2.4.6 allowed real compilation/execution. No monkeypatch, ignored import failure or driver upgrade was used to obtain a pass. Both optional backends now pin NumPy2.4.6 and share Numba0.68.1; the core remains dependency-free. Poetry2.5.1 retained a second transitive NumPy2.5.4 entry from its extras-inactive resolver override even after both extras used the same exact pin. Both entries acquired identical main-extra markers. Linux run125 caught this: eight compiler-profile checks failed, while2700 other units passed. The earlier local reused environment and fresh pip installs did not reveal Poetry's duplicate lock selection. This failure is preserved rather than reported as CUDA parity. Unit/native test fixtures import NumPy directly, so the dev group now declares their exact2.4.6 dependency too. Regeneration produces one NumPy package entry; its main-group marker still requires cpu or cuda, while dev tests share the same version. A new Poetry environment with --only main has no NumPy/Numba; adding only cpu installs2.4.6 successfully; the combined-extra dry run selects no second NumPy. Actual installed versions and clean Linux service checks remain required. A minimal isolated compiler installation proved that NVCC alone fails for missing cudart, adding cudart fails for missing NVRTC, and adding NVRTC runs the kernel with a missing-nvJitLink fallback warning. Adding nvJitLink runs without that fallback. The cuda extra therefore declares those four native payloads, not the larger cuda-toolkit metapackage or CCCL. Their narrow deptry DEP002 exceptions reflect DLL/SO loading rather than Python imports; no runtime audit rule is disabled. Device interface (PW-10.03/.05) CudaIntegerView accepts CUDA Array Interface version3, one-dimensional contiguous scalar integer storage: signed/unsigned8/16/32/64 bits, little-endian for multibyte values. One-byte types may use the protocol's endian-neutral marker. Logical IDs remain optional non-sentinel uint64 identifiers, independent of memory addresses. The source owner is retained for the view's lifetime; descriptor access returns a copy so a consumer cannot mutate the validated internal description. Before touching the driver, validate exact scalar dtype, shape, length/byte-count bounds, stride, aligned pointer, readonly bool and producer stream. Empty arrays have pointer0. Masks, structured types, noncontiguous layouts and ambiguous stream0 are rejected. A redundant scalar descr is accepted only when it exactly agrees with typestr; structured or conflicting descriptors fail. Host buffers and DLPack-only providers do not trigger transfers or implicit conversions. CUDA borrowing does not validate C2 record framing; that belongs to the future GPU fingerprint input boundary. The caller supplies an existing current CUDA context. Nonempty pointers are checked through the driver for device memory, unmanaged allocation, device ordinal, allocation context and allocation range. Host/managed memory and foreign devices or contexts fail explicitly. cuMemGetAddressRange must cover the declared view; allocator families that cannot supply this guarantee are outside the first scope. Empty input uses the current context without querying a null allocation pointer. Producer streams None, legacy1, per-thread2 and explicit positive handles follow CAI3 semantics. A supplied producer stream is synchronized before the view returns; the exported descriptor then carries streamNone. The view requests no payload copy. Synchronization may complete the producer's already queued transfers. Consumers must retain the view and keep mutable aliases stable until their work finishes. Completion synchronization for GPU fingerprint execution is separate. Addresses and stream handles are transient; C5 snapshots never persist this view. Lazy availability (PW-10.04) Importing the base package or pipewhy.model.cuda adds no CUDA binding or Numba CUDA imports and initializes no driver/context. Some environments pre-register the cuda namespace at Python startup. cuda_availability explicitly loads the binding, initializes the driver and queries count/version. It reports AVAILABLE, EXTRA_MISSING, IMPORT_ERROR, DRIVER_UNAVAILABLE or NO_DEVICE with a diagnostic. Unknown counts/versions are None; a successful zero-device query reports count0. Readiness is distinct from fingerprint/backend qualification. Constructing a view propagates actionable import/driver failures; there is no CPU fallback or copy. Dependency/type-check boundary The optional cuda profile is declared in pyproject.toml and locked in poetry.lock. cuda-core supplies the compiler's versioned CUDA exception base. Development/CI sync CPU and CUDA extras to check all owned Python. Isolated wheel jobs first test core-only and CPU installations, then add cuda and verify driver binary APIs, NumPy/compiler versions and compiler API imports without requesting a GPU/context. Missing APIs, binary import failures and incompatible profiles fail the check. cuda-bindings12.9.10 ships driver.pyd/driver.so, Cython sources and no Python type stubs. One exact unresolved-import suppression at the lazy SDK import documents that extension limitation. It does not exclude any owned source or disable missing imports globally. Owned CAI metadata uses a TypedDict; runtime import/API checks and real-device calls maintain evidence for this boundary. Revisit the one-line suppression when NVIDIA supplies stubs or ty understands this extension export. Initial CV1 borrowing evidence (historical revision326e1b5) Unit checks replace only driver calls, exercising descriptor validation, queried residency/context/ranges, synchronization, ownership, empty input, import failures and SDK errors. Actual GTX1660 checks verify all eight integer types, contiguous interior/empty views, pointer identity, asynchronous producer synchronization, Numba zero-copy consumption and owner lifetime. Host, strided and float inputs are rejected. Borrowing requested no payload transfer. Reproduce real-device borrowing with: poetry sync --extras cpu --extras cuda poetry run python -m pytest tests/hardware -q All17 initial borrowing cases pass on the selected target, with no skips or simulation. An unavailable device fails this explicitly selected suite. CPU-only CI jobs run units/integrations and binary import checks; they do not claim hardware parity. The final local unit gate passes2648 tests: statements5530/5551 (99.622%), combined 99.46%, zero exclusions. CV1's114 Python statements have no missing unit lines. Black, ty, deptry,57 integrations/gates, metadata and wheel/sdist builds pass; two Windows Graphviz skips are separately qualified by Linux run121. Clean core, CPU and CUDA-binding-only wheels pass isolated import/inventory checks outside the checkout. Core/CPU installed CLI workflows still pass. Final installed Numba-producer consumption preserves pointers, values and lifetime. Exact reports, source identities and prototype limitations are in CUDA_ENVIRONMENT.json. An isolated device prototype compiled the existing pure integer BLAKE2b functions with CUDA register_jitable and matched all three literal PW-13 record groups. The prototype copied digest limbs back and summed them on the host. This is useful record/arithmetic evidence, not GPU reduction, ordered-mode, device framing, full-stream or installed scan parity acceptance. No native instruction coverage, performance budget, external security review or broader hardware claim is made. Arithmetic follow-up (PW-10.06/.07) The initial simulator exposed an interpreter-only mismatch: NumPy uint8 header scalars kept their narrow dtype when shifted into message words. Native compiled CPU/GPU code already widened those bytes. message_byte now explicitly returns a Python integer for header, position and payload bytes. Two regression cases failed before the change and now match independent hashlib answers with NumPy buffers, both order modes, multiblock payloads and the maximum valid ordered position. Existing multiblock, CPU accumulation and thin-slice checks remain intact. tests/hardware/check_cuda_math.py is an explicitly selected compiler probe. Run it with the isolated recorded compiler environment and installed wheel: .tools/cuda-probe/Scripts/python.exe -I tests/hardware/check_cuda_math.py tests/fixtures --mode real Use --mode simulation only when NUMBA_ENABLE_CUDASIM=1 was set before process startup. Mode mismatch fails; simulation output has deviceNone and simulatedTrue. The probe requires Numba-CUDA0.30.4/NumPy2.4.6, compiles the shared production integer functions, checks40 independent mixing vectors and10 portable/literal record contributions, and tests both portable order modes. Real and simulated runs pass against the actual installed corrected wheel. Host reduction is still explicit; this probe alone does not establish block reduction. The public contribution API described below is qualified separately. Updated local unit gate:2650 passed; statements5530/5551 (99.622%), combined99.46%, zero exclusions. Black/ty/deptry and57 integrations/gates pass. Native Linux run123 qualified the earlier CV1 delivery:2648 units,52 integrations,7 negative gates, no skips and CUDA-binding binary/API import checks for both distributions; native statements5533/5551, combined99.50% displayed. The byte follow-up is a later revision. Public host contribution boundary (PW-10.06/.07) record_contributions_from_host accepts a normalize.batches.RecordBatch and an explicit C3 FingerprintContext, returning CudaResult[array[int]]. Its flat owned array contains four little-endian uint64 digest limbs per input record. Hashing uses the same pure integer BLAKE2b functions as CPU, compiled for CUDA; there is no second algorithm or change to C2/C3 identities. Both order modes and both strict and opt-in logical profiles retain their exact reference semantics. Validate power-of-two threads1..256, positive batch limits, typed buffers, complete C2 framing, context/profile identity and ordered start/count before runtime entry. Defaults cap supplied pools at1048576 words and65536 records. Whole supplied word pools count toward limits, including unused words. Host inputs remain stable until return. This host API does not accept borrowed CudaIntegerView inputs or conceal transfers behind a zero-copy claim. Five explicit uploads carry the context header, word pool, offsets, lengths and BLAKE2 IV. One CUDA thread hashes a record using private integer scratch. A device synchronization completes the work before download or input-owner release. There is no successful partial result after upload, allocation, launch, synchronization or download failure; expected native failures retain their cause and context. Only the compiler's known low-occupancy warning is filtered for small batches; unrelated diagnostics remain visible. Empty inputs still verify actual GPU readiness and report context uploads, then return an empty contribution array. TransferCounts records requested host_to_device_bytes, device_to_host_bytes and device_buffer_bytes (the sum of explicit input and output allocations for this API). These are payload/allocation requests, not a profiler or total process/VRAM measurement: compiler/context allocations and private kernel scratch are excluded. No throughput or total-VRAM claim follows from these counts. Unit coverage explicitly enables CUDA simulation at process startup so Coverage.py can trace the owned kernel's Python body. Tests replace the SDK boundary, compare independent hashlib and literal C3 vectors, and cover validation/failure behavior. The public runtime refuses simulation; no simulated run can establish GPU parity. Real-device tests run in a separate process and fail if simulation is enabled or hardware is unavailable. Cases include all scalar tags under both profiles/modes, empty/duplicate records, multiblock boundaries, high ordered positions, multiple thread/block shapes, exact transfer requests and persisted literal slice answers. Source coverage is Python execution only, never native GPU instruction coverage. Current exact counts, distribution checks and source identities are recorded in CUDA_ENVIRONMENT.json. Broader persisted-scan qualification, Linux GPU behavior and other hardware remain separate work. CUDA_BASELINE.txt/.json record the initial measured timing/memory/synchronization workload; no general budget is set. GPU aggregate reduction (PW-10.08/.09) fingerprint_batch_from_host shares the contribution API's validation, uploads and record kernel, then performs a deterministic uint256 sum on the device. Each block loads up to one contribution per thread into8192 bytes of shared integer scratch, using unsigned zeros for inactive lanes. A barrier separates every pairwise tree level; the shared CPU add256 primitive preserves carries modulo2^256. Blocks write private four-limb partials. Further kernel launches reduce those partials until one sum remains. threads1 is accepted for hashing; reduction uses at least two threads so every nontrivial pass makes progress. No inter-block atomic arithmetic is used. Inactive-lane zeros must be explicitly uint64. A first native prototype with signed literal0 silently joined uint64 values through float64 and lost low bits in the three-row partial-block case; simulation did not reproduce that compiler typing behavior. Independent real-device carry/random tests now guard the correct form. Both direct reduction and integrated hashing test block-size boundaries and multiple recursive levels. Flat carry tests include65536 contributions, all-limb wraparound, partial final blocks and thread shapes2/32/256. All explicitly allocated intermediate output buffers remain owned until device synchronization. TransferCounts.device_buffer_bytes includes the input uploads, record-output allocation and every intermediate allocation; shared/private kernel scratch and compiler/context memory remain excluded. Only the final four limbs (32 bytes) are downloaded for nonempty input. Empty input verifies readiness and returns the exact C3 empty state, with no digest download. The host reconstructs the integer from those four limbs and attaches the validated exact count/context/ ordered start; it does not perform contribution reduction. A partial allocation, launch, synchronization or download error returns no successful partial state. Ownership/synchronization and download ordering have explicit unit fault probes. Bounded host streams and transfer accounting (PW-10.10) fingerprint_batches_from_host consumes Iterable[RecordBatch] once, with the same explicit positive limits and thread shape as the single-batch API. It completes hash/reduction/synchronize/download before requesting the next batch. The canonical C3 host state operator composes four aggregate limbs per batch; no record hashing falls back to CPU/reference. Ordered positions advance by exact record count; empty batches do not advance them. Context/profile/count/range incompatibilities and failed later inputs/source iteration produce no successful partial aggregate. The returned transfer counters sum actual requested payload/allocation bytes across batches. They are cumulative requests, not a peak-memory measurement. Per-call buffer owners stay retained through synchronization; normal completion drops those references before processing the next batch. The native allocator may cache storage, and the source must bound its own buffering. Each nonempty batch downloads32 aggregate bytes, and an empty supplied batch uploads only context inputs. An empty iterable performs readiness checks with zero payload requests and returns the exact C3 empty identity. Invalid options/context fail before runtime/source consumption. Independent tests cover strict/logical profiles, both modes, repartitioning, maximum global positions, sequencing and source errors. Synchronization on failure (PW-10.11) Any expected execution failure attempts context synchronization while all existing input/output/intermediate owners remain retained in the call. This matters when record hashing is already queued and a subsequent partial allocation fails. The original native exception remains the cause of the public RuntimeError. If cleanup synchronization also fails, its diagnostic is included in the public message and added as a note on the original exception; neither failure becomes a success. This is best-effort completion after a device failure, not a claim that a lost or poisoned context has recovered. Unexpected programming errors still propagate. Two maintained unit regressions failed before this correction and pass afterward: they check recovery synchronization ordering, retained owners, original cause and secondary cleanup diagnostics. A real-device test injects a Python allocation failure after an actual record-kernel launch, observes real synchronization and then completes another exact GPU fingerprint successfully. This does not simulate a physical device-loss recovery or manufacture an actual device out-of-memory event. Host backend dispatch (PW-10.12) scan --backend accepts reference/cpu/cuda/auto, retaining reference as the default. Host policy is explicit: auto checks for the Numba CPU provider without importing or querying CUDA. If absent, use reference; if present, select CPU. An installed CPU backend that fails importing/compiling/executing reports failure rather than falling back. NumPy alone is not evidence that the CPU compiler is installed. This policy is for the current local-fixture host source, not a transfer-cost model or a device-resident C2 entry point. CudaIntegerView borrowing remains separate; no unsupported device input is copied implicitly to satisfy host validation. Explicit cuda verifies the complete runtime on an empty GPU stream after source/ scope/batch/parent validation and before dataset reads. Thus missing support also fails for a scope whose observations would all be unavailable. Complete datasets use bounded GPU streams; source read failures remain explicit incomplete observations, while backend failures abort publication with exit3 and no receipt. Capture metadata records the selected reference/cpu/cuda backend, never unresolved auto. The existing capture envelope and C2/C3 identities are unchanged. Historical queries accept the CUDA label and do not request a GPU or re-execute fingerprints. The repeatable PowerShell example and installed CLI workflow checker accept both new flags. CPU-only service jobs keep their reference/CPU checks; CUDA CLI success is qualified on the actual GPU separately. Unit simulation tests routing and exact saved arrays, not native instruction coverage. Real-device capture checks strict/ logical profiles, both order modes and batch sizes1/7; the actual console persists literal C3 answers and resolves host auto to CPU on the qualified GPU host. Primary references consulted October30: https://nvidia.github.io/numba-cuda/user/installation.html https://nvidia.github.io/numba-cuda/user/cuda_array_interface.html https://nvidia.github.io/cuda-python/cuda-bindings/latest/module/driver.html https://numpy.org/doc/2.5/release/2.5.0-notes.html The binding docs describe their development branch; actual12.9.10 behavior was also checked directly on this host. A successful prototype does not override the package's selected version constraints or establish an untested support matrix.