From 7651f722f128241b4a5f440bbcc0bf2b618279bb Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 26 Jul 2026 16:07:38 +0100 Subject: [PATCH 1/6] chore: update guix.scm from squisher-corpus --- guix.scm | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/guix.scm b/guix.scm index 83e4fe8..c6dd7be 100644 --- a/guix.scm +++ b/guix.scm @@ -1,5 +1,5 @@ ; SPDX-License-Identifier: MPL-2.0 -;; guix.scm — GNU Guix package definition for AcceleratorGate.jl +;; guix.scm — GNU Guix package definition for squisher-corpus ;; Usage: guix shell -f guix.scm (use-modules (guix packages) @@ -7,12 +7,12 @@ (guix licenses)) (package - (name "AcceleratorGate.jl") + (name "squisher-corpus") (version "0.1.0") (source #f) (build-system gnu-build-system) - (synopsis "AcceleratorGate.jl") - (description "AcceleratorGate.jl — part of the hyperpolymath ecosystem.") - (home-page "https://github.com/hyperpolymath/AcceleratorGate.jl") - (license ((@@ (guix licenses) license) "MPL-2.0" - "https://www.mozilla.org/MPL/2.0/"))) + (synopsis "squisher-corpus") + (description "squisher-corpus — part of the hyperpolymath ecosystem.") + (home-page "https://github.com/hyperpolymath/squisher-corpus") + (license ((@@ (guix licenses) license) "PMPL-1.0-or-later" + "https://github.com/hyperpolymath/palimpsest-license"))) From a224a23ef322b0378dc37e7255cd68ad4840fc61 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 9 Aug 2026 07:02:19 +0100 Subject: [PATCH 2/6] feat: add operation-first Julia accelerator gate --- ARCHITECTURE.md | 93 +++++++++++++ Project.toml | 1 + src/AcceleratorGate.jl | 9 +- src/operations.jl | 307 +++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 75 ++++++++++ 5 files changed, 484 insertions(+), 1 deletion(-) create mode 100644 ARCHITECTURE.md create mode 100644 src/operations.jl diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..857c9f1 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,93 @@ +# Architecture + +## Overview + +AcceleratorGate is the Julia admission and compatibility layer for the estate's +operation-first coprocessor runtime. It does not own numerical kernels and a +backend type name is not a capability claim. + +Enaction owns runtime semantics, authoritative/advisory/remote-job policy, and +the Idris2-defined native ABI. AcceleratorGate translates Julia requests into +that operation vocabulary, admits providers using explicit evidence, and +returns execution evidence to Julia consumers. + +## Operation path + +```text +Julia/domain value + | + v +OperationRequest (version, layout, lane, evidence floors) + | + v +deterministic provider planning + | + +-- pure-Zig Enaction native provider + +-- real hardware/provider adapter + +-- explicit reference provider + `-- explicit simulation (only when allow_simulation=true) + | + v +result + ExecutionEvidence +``` + +Provider claims are per operation and include support, determinism, execution +lanes, implementation kind, device class, and optional conformance digest. +Simulations are refused by default. Authoritative claims must be +`canonical_exact`. Remote providers may claim only `remote_job` execution. +Once a provider has been planned, its runtime failure is returned to the +caller; the registry never silently retries another provider. + +`EnactionZigProvider` loads the shared form of the same pure-Zig library used by +the Rust adapter. It validates ABI version, layouts, capability records, status +and execution evidence. There is no C implementation or Julia-owned copy of a +kernel. + +The older device hierarchy and operation-specialty table remain as a legacy +compatibility surface while consumers migrate. Environment flags and a type +such as `TPUBackend` are discovery hints only and must not be presented as +runnable hardware evidence. + +## Directory Structure + +``` +. +├── src/ +│ ├── AcceleratorGate.jl # legacy compatibility and module surface +│ └── operations.jl # central requests, evidence, planner, providers +├── tests/ # Test suites +├── docs/ # Documentation +├── scripts/ # Utility scripts +├── config/ # Configuration files +├── LICENSE # License file +├── LICENSES/ # Full license texts +└── README.adoc # Project documentation +``` + +## Design Principles + +- **Separation of Concerns**: Each module has a single responsibility +- **Testability**: Code is written to be easily testable +- **Documentation**: All public APIs are documented +- **Configuration**: Environment-specific settings are externalized + +## Dependencies + +- External dependencies are minimized and clearly declared +- Version pinning is used for reproducibility + +## Security Considerations + +- Sensitive data is never committed to the repository +- Secrets are managed through environment variables or secure vaults +- Regular dependency audits are performed + +## Maintainability + +- Code follows consistent style guidelines +- Pull requests require review and CI checks +- Issues and discussions are tracked transparently + +--- + +*Last updated: 2026-07-18* diff --git a/Project.toml b/Project.toml index bdb078c..c534dda 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ license = "MPL-2.0" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" [compat] julia = "1.10" diff --git a/src/AcceleratorGate.jl b/src/AcceleratorGate.jl index 2421e3c..bd74a68 100644 --- a/src/AcceleratorGate.jl +++ b/src/AcceleratorGate.jl @@ -28,6 +28,7 @@ backend = select_backend(:matmul, 1_000_000) module AcceleratorGate using Dates +using Libdl export AbstractBackend, JuliaBackend, RustBackend, ZigBackend, CUDABackend, ROCmBackend, MetalBackend, @@ -59,7 +60,13 @@ export AbstractBackend, JuliaBackend, RustBackend, ZigBackend, # Operation registry register_operation!, supports_operation, supported_operations, # Backend specialties - BACKEND_SPECIALTIES, is_specialized + BACKEND_SPECIALTIES, is_specialized, + OperationRequest, CapabilityEvidence, ExecutionEvidence, + AbstractOperationProvider, FunctionProvider, EnactionZigProvider, + register_provider!, clear_provider_registry!, registered_providers, + provider_capabilities, plan_operation, execute_operation + +include("operations.jl") # ============================================================================ # Backend Type Hierarchy diff --git a/src/operations.jl b/src/operations.jl new file mode 100644 index 0000000..f37f098 --- /dev/null +++ b/src/operations.jl @@ -0,0 +1,307 @@ +# SPDX-License-Identifier: MPL-2.0 +# Operation-first admission and execution evidence for estate coprocessors. + +const _SUPPORT_RANK = Dict( + :declared => 1, :discoverable => 2, :loadable => 3, :runnable => 4, + :conformant => 5, :resilient => 6, :deterministic => 7, + :benchmarked => 8, :production_supported => 9, +) +const _DETERMINISM_RANK = Dict(:advisory_only => 1, :tolerance_bounded => 2, :canonical_exact => 3) +const _LANES = (:authoritative, :advisory, :remote_job) +const _FALLBACKS = (:require_named_backend, :require_deterministic_equivalent, :allow_reference, :prefer_accelerated) +const _IMPLEMENTATIONS = (:reference, :native, :hardware, :simulation, :remote) + +"""A backend-neutral, versioned operation request admitted by AcceleratorGate.""" +struct OperationRequest + operation::String + version::VersionNumber + layout::NamedTuple + lane::Symbol + minimum_support::Symbol + minimum_determinism::Symbol + fallback::Symbol + named_provider::Union{Nothing,String} + allow_simulation::Bool + function OperationRequest(operation::AbstractString; + version=v"1.0.0", layout=(;), lane=:advisory, + minimum_support=:conformant, minimum_determinism=:tolerance_bounded, + fallback=:prefer_accelerated, named_provider=nothing, + allow_simulation=false) + isempty(operation) && throw(ArgumentError("operation id must not be empty")) + lane in _LANES || throw(ArgumentError("invalid execution lane: $lane")) + haskey(_SUPPORT_RANK, minimum_support) || throw(ArgumentError("invalid support level: $minimum_support")) + haskey(_DETERMINISM_RANK, minimum_determinism) || throw(ArgumentError("invalid determinism: $minimum_determinism")) + fallback in _FALLBACKS || throw(ArgumentError("invalid fallback policy: $fallback")) + fallback === :require_named_backend && named_provider === nothing && + throw(ArgumentError("require_named_backend needs named_provider")) + new(String(operation), VersionNumber(version), layout, lane, minimum_support, + minimum_determinism, fallback, + named_provider === nothing ? nothing : String(named_provider), allow_simulation) + end +end + +"""Auditable support claim for one provider/operation pair.""" +struct CapabilityEvidence + provider_id::String + provider_version::VersionNumber + operation::String + operation_version::VersionNumber + lanes::Tuple{Vararg{Symbol}} + support::Symbol + determinism::Symbol + implementation::Symbol + device_class::Symbol + conformance_digest::Union{Nothing,String} + function CapabilityEvidence(provider_id, provider_version, operation, operation_version; + lanes=(:advisory,), support=:declared, determinism=:advisory_only, + implementation=:simulation, device_class=:cpu, conformance_digest=nothing) + isempty(provider_id) && throw(ArgumentError("provider id must not be empty")) + isempty(operation) && throw(ArgumentError("operation id must not be empty")) + isempty(lanes) && throw(ArgumentError("at least one execution lane is required")) + all(lane -> lane in _LANES, lanes) || throw(ArgumentError("invalid execution lane")) + haskey(_SUPPORT_RANK, support) || throw(ArgumentError("invalid support level: $support")) + haskey(_DETERMINISM_RANK, determinism) || throw(ArgumentError("invalid determinism: $determinism")) + implementation in _IMPLEMENTATIONS || throw(ArgumentError("invalid implementation kind: $implementation")) + implementation === :remote && !all(==(:remote_job), lanes) && + throw(ArgumentError("remote providers may claim only the remote_job lane")) + implementation !== :remote && :remote_job in lanes && + throw(ArgumentError("only remote providers may claim remote_job")) + :authoritative in lanes && determinism !== :canonical_exact && + throw(ArgumentError("authoritative capability must be canonical_exact")) + new(String(provider_id), VersionNumber(provider_version), String(operation), + VersionNumber(operation_version), Tuple(lanes), support, determinism, + implementation, device_class, + conformance_digest === nothing ? nothing : String(conformance_digest)) + end +end + +"""Evidence returned only after successful execution by the planned provider.""" +struct ExecutionEvidence + provider_id::String + provider_version::VersionNumber + operation::String + operation_version::VersionNumber + lane::Symbol + support::Symbol + determinism::Symbol + implementation::Symbol +end + +abstract type AbstractOperationProvider end + +"""Provider backed by a Julia callback; useful for real adapters and explicit simulators.""" +struct FunctionProvider <: AbstractOperationProvider + id::String + version::VersionNumber + claims::Vector{CapabilityEvidence} + callback::Function + function FunctionProvider(id, version, claims, callback) + all(claim -> claim.provider_id == id && claim.provider_version == VersionNumber(version), claims) || + throw(ArgumentError("every capability must name the provider and version")) + new(String(id), VersionNumber(version), collect(claims), callback) + end +end + +FunctionProvider(callback::Function, id, version, claims) = FunctionProvider(id, version, claims, callback) + +provider_id(provider::FunctionProvider) = provider.id +provider_capabilities(provider::FunctionProvider) = copy(provider.claims) +_execute_provider(provider::FunctionProvider, request::OperationRequest, args...) = provider.callback(request, args...) + +const _OPERATION_PROVIDERS = AbstractOperationProvider[] +const _OPERATION_PROVIDER_LOCK = ReentrantLock() + +registered_providers() = lock(_OPERATION_PROVIDER_LOCK) do + copy(_OPERATION_PROVIDERS) +end +clear_provider_registry!() = lock(_OPERATION_PROVIDER_LOCK) do + empty!(_OPERATION_PROVIDERS) +end + +function register_provider!(provider::AbstractOperationProvider) + id = provider_id(provider) + isempty(provider_capabilities(provider)) && throw(ArgumentError("provider must claim at least one operation")) + lock(_OPERATION_PROVIDER_LOCK) do + any(existing -> provider_id(existing) == id, _OPERATION_PROVIDERS) && + throw(ArgumentError("duplicate operation provider id: $id")) + push!(_OPERATION_PROVIDERS, provider) + end + provider +end + +function _compatible(claim::CapabilityEvidence, request::OperationRequest) + claim.operation == request.operation || return false + claim.operation_version.major == request.version.major || return false + claim.operation_version.minor >= request.version.minor || return false + request.lane in claim.lanes || return false + _SUPPORT_RANK[claim.support] >= _SUPPORT_RANK[request.minimum_support] || return false + _DETERMINISM_RANK[claim.determinism] >= _DETERMINISM_RANK[request.minimum_determinism] || return false + request.lane === :authoritative && claim.determinism !== :canonical_exact && return false + claim.implementation === :simulation && !request.allow_simulation && return false + request.named_provider !== nothing && claim.provider_id != request.named_provider && return false + request.fallback === :require_deterministic_equivalent && claim.determinism !== :canonical_exact && return false + request.fallback === :allow_reference && claim.implementation !== :reference && return false + true +end + +function _preference(claim::CapabilityEvidence) + kind = Dict(:hardware => 5, :native => 4, :remote => 3, :reference => 2, :simulation => 1)[claim.implementation] + (kind, _SUPPORT_RANK[claim.support], _DETERMINISM_RANK[claim.determinism]) +end + +"""Select one provider deterministically. Registration order never breaks ties.""" +function plan_operation(request::OperationRequest) + candidates = Tuple{AbstractOperationProvider,CapabilityEvidence}[] + lock(_OPERATION_PROVIDER_LOCK) do + for provider in _OPERATION_PROVIDERS, claim in provider_capabilities(provider) + _compatible(claim, request) && push!(candidates, (provider, claim)) + end + end + isempty(candidates) && throw(ErrorException("no compatible provider for $(request.operation)")) + sort!(candidates; by=entry -> begin + preference = _preference(entry[2]) + (-preference[1], -preference[2], -preference[3], entry[2].provider_id) + end) + candidates[1] +end + +"""Execute exactly the planned provider. A runtime failure is never a fallback signal.""" +function execute_operation(request::OperationRequest, args...) + provider, claim = plan_operation(request) + value = _execute_provider(provider, request, args...) + evidence = ExecutionEvidence(claim.provider_id, claim.provider_version, + claim.operation, request.version, request.lane, claim.support, + claim.determinism, claim.implementation) + value, evidence +end + +# Enaction v1 C-compatible structs. Idris2 remains their authority; these +# assertions make drift fail at Julia load rather than corrupting a call. +struct _EnactionRequest + abi_major::UInt16; abi_minor::UInt16; operation_major::UInt16; operation_minor::UInt16 + operation::UInt32; lane::UInt32; minimum_support::UInt32; minimum_determinism::UInt32 + layout::UInt32; reserved::UInt32; dim0::UInt64; dim1::UInt64; dim2::UInt64 +end +struct _EnactionBufferIn + data::Ptr{Float32}; len::UInt64 +end +struct _EnactionBufferOut + data::Ptr{Float32}; len::UInt64 +end +struct _EnactionCapability + abi_major::UInt16; abi_minor::UInt16; operation_major::UInt16; operation_minor::UInt16 + operation::UInt32; support::UInt32; determinism::UInt32; backend_id::UInt32 + device_class::UInt32; flags::UInt32 +end +struct _EnactionEvidence + abi_major::UInt16; abi_minor::UInt16; operation_major::UInt16; operation_minor::UInt16 + operation::UInt32; backend_id::UInt32; support::UInt32; determinism::UInt32 +end + +sizeof(_EnactionRequest) == 56 || error("Enaction Request ABI drift") +sizeof(_EnactionBufferIn) == 16 || error("Enaction buffer ABI drift") +sizeof(_EnactionCapability) == 32 || error("Enaction Capability ABI drift") +sizeof(_EnactionEvidence) == 24 || error("Enaction Evidence ABI drift") + +const _OP_CODE = Dict( + "enaction.tensor.f32.relu" => UInt32(3), "enaction.tensor.f32.relu6" => UInt32(4), + "enaction.tensor.f32.matmul" => UInt32(5), "enaction.tensor.f32.add" => UInt32(6), + "enaction.tensor.f32.mul" => UInt32(7), +) +const _CODE_OP = Dict(value => key for (key, value) in _OP_CODE) +const _SUPPORT_CODE = Dict(key => UInt32(value) for (key, value) in _SUPPORT_RANK) +const _DETERMINISM_CODE = Dict(key => UInt32(value) for (key, value) in _DETERMINISM_RANK) + +mutable struct EnactionZigProvider <: AbstractOperationProvider + id::String + version::VersionNumber + path::String + handle::Ptr{Cvoid} + claims::Vector{CapabilityEvidence} + unary::Ptr{Cvoid} + binary::Ptr{Cvoid} +end + +provider_id(provider::EnactionZigProvider) = provider.id +provider_capabilities(provider::EnactionZigProvider) = copy(provider.claims) + +function EnactionZigProvider(path::AbstractString) + isfile(path) || throw(ArgumentError("Enaction Zig library not found: $path")) + handle = Libdl.dlopen(path) + abi_version = ccall(Libdl.dlsym(handle, :enaction_accel_abi_version), UInt32, ()) + abi_version == 0x00010000 || throw(ErrorException("unsupported Enaction accelerator ABI: $abi_version")) + count = ccall(Libdl.dlsym(handle, :enaction_accel_capability_count), UInt32, ()) + at = Libdl.dlsym(handle, :enaction_accel_capability_at) + claims = CapabilityEvidence[] + for index in UInt32(0):(count - UInt32(1)) + raw = Ref{_EnactionCapability}() + status = ccall(at, UInt32, (UInt32, Ref{_EnactionCapability}), index, raw) + status == 0 || throw(ErrorException("capability query failed with status $status")) + raw[].abi_major == 1 && raw[].abi_minor == 0 && + raw[].operation_major == 1 && raw[].operation_minor == 0 && + raw[].backend_id == 1 && raw[].device_class == 1 || + throw(ErrorException("invalid Enaction capability evidence at index $index")) + operation = get(_CODE_OP, raw[].operation, nothing) + operation === nothing && continue # fixed-i32 uses a different Julia buffer surface + support = Symbol(first(key for (key, value) in _SUPPORT_CODE if value == raw[].support)) + determinism = Symbol(first(key for (key, value) in _DETERMINISM_CODE if value == raw[].determinism)) + push!(claims, CapabilityEvidence("enaction.cpu.zig.scalar", v"1.0.0", operation, v"1.0.0"; + lanes=(:advisory,), support, determinism, implementation=:native, + device_class=:cpu, conformance_digest=nothing)) + end + EnactionZigProvider("enaction.cpu.zig.scalar", v"1.0.0", String(path), handle, claims, + Libdl.dlsym(handle, :enaction_accel_execute_f32), + Libdl.dlsym(handle, :enaction_accel_execute_f32_binary)) +end + +function _native_layout(request::OperationRequest) + if request.operation == "enaction.tensor.f32.matmul" + all(key -> haskey(request.layout, key), (:m, :k, :n)) || throw(ArgumentError("matmul layout needs m, k and n")) + UInt32(2), UInt64(request.layout.m), UInt64(request.layout.k), UInt64(request.layout.n) + else + haskey(request.layout, :len) || throw(ArgumentError("vector layout needs len")) + UInt32(3), UInt64(request.layout.len), UInt64(0), UInt64(0) + end +end + +function _execute_provider(provider::EnactionZigProvider, request::OperationRequest, args...) + code = get(_OP_CODE, request.operation, nothing) + code === nothing && throw(ArgumentError("unsupported native operation: $(request.operation)")) + layout, dim0, dim1, dim2 = _native_layout(request) + raw_request = Ref(_EnactionRequest(1, 0, UInt16(request.version.major), UInt16(request.version.minor), + code, 2, _SUPPORT_CODE[request.minimum_support], _DETERMINISM_CODE[request.minimum_determinism], + layout, 0, dim0, dim1, dim2)) + evidence = Ref{_EnactionEvidence}() + inputs = if request.operation == "enaction.tensor.f32.matmul" + map(value -> vec(permutedims(Float32.(value))), args) + else + map(value -> vec(Float32.(value)), args) + end + expected = request.operation == "enaction.tensor.f32.matmul" ? Int(dim0 * dim2) : Int(dim0) + output = Vector{Float32}(undef, expected) + output_buffer = Ref(_EnactionBufferOut(pointer(output), UInt64(length(output)))) + status = GC.@preserve inputs output begin + buffers = map(value -> Ref(_EnactionBufferIn(pointer(value), UInt64(length(value)))), inputs) + if length(buffers) == 1 + ccall(provider.unary, UInt32, + (Ref{_EnactionRequest}, Ref{_EnactionBufferIn}, Ref{_EnactionBufferOut}, Ref{_EnactionEvidence}), + raw_request, buffers[1], output_buffer, evidence) + elseif length(buffers) == 2 + ccall(provider.binary, UInt32, + (Ref{_EnactionRequest}, Ref{_EnactionBufferIn}, Ref{_EnactionBufferIn}, Ref{_EnactionBufferOut}, Ref{_EnactionEvidence}), + raw_request, buffers[1], buffers[2], output_buffer, evidence) + else + throw(ArgumentError("native tensor operations take one or two inputs")) + end + end + status == 0 || throw(ErrorException("Enaction Zig operation failed with ABI status $status")) + raw_evidence = evidence[] + raw_evidence.abi_major == 1 && raw_evidence.abi_minor == 0 && + raw_evidence.operation_major == request.version.major && + raw_evidence.operation_minor == request.version.minor && + raw_evidence.operation == code && raw_evidence.backend_id == 1 && + raw_evidence.support >= _SUPPORT_CODE[request.minimum_support] && + raw_evidence.determinism >= _DETERMINISM_CODE[request.minimum_determinism] || + throw(ErrorException("invalid Enaction execution evidence")) + request.operation == "enaction.tensor.f32.matmul" ? reshape(output, Int(dim2), Int(dim0))' |> Matrix : output +end diff --git a/test/runtests.jl b/test/runtests.jl index 8ad0756..c7d83a8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -288,6 +288,81 @@ using AcceleratorGate @test is_specialized(JuliaBackend(), :matmul) == false end + @testset "Operation-first provider admission" begin + clear_provider_registry!() + simulation_claim = CapabilityEvidence( + "test.simulated.tpu", v"1.0.0", "enaction.tensor.f32.mul", v"1.0.0"; + lanes=(:advisory,), support=:conformant, + determinism=:tolerance_bounded, implementation=:simulation, + device_class=:tpu, + ) + simulation = FunctionProvider("test.simulated.tpu", v"1.0.0", [simulation_claim]) do _, left, right + left .* right + end + register_provider!(simulation) + + refused = OperationRequest("enaction.tensor.f32.mul"; + layout=(len=2,), allow_simulation=false) + @test_throws ErrorException plan_operation(refused) + + admitted = OperationRequest("enaction.tensor.f32.mul"; + layout=(len=2,), allow_simulation=true) + value, evidence = execute_operation(admitted, Float32[2, 3], Float32[4, 5]) + @test value == Float32[8, 15] + @test evidence.provider_id == "test.simulated.tpu" + @test evidence.implementation == :simulation + @test evidence.lane == :advisory + + @test_throws ArgumentError CapabilityEvidence( + "bad.tpu", v"1.0.0", "enaction.tensor.f32.mul", v"1.0.0"; + lanes=(:authoritative,), determinism=:tolerance_bounded, + ) + @test_throws ArgumentError register_provider!(simulation) + clear_provider_registry!() + end + + @testset "Runtime failure never silently retries" begin + clear_provider_registry!() + claim(id, implementation) = CapabilityEvidence( + id, v"1.0.0", "enaction.tensor.f32.add", v"1.0.0"; + lanes=(:advisory,), support=:resilient, + determinism=:tolerance_bounded, implementation, + ) + failing = FunctionProvider("a.hardware", v"1.0.0", [claim("a.hardware", :hardware)]) do _, _... + error("planted provider failure") + end + reference = FunctionProvider("z.reference", v"1.0.0", [claim("z.reference", :reference)]) do _, left, right + left .+ right + end + register_provider!(reference) + register_provider!(failing) + request = OperationRequest("enaction.tensor.f32.add"; layout=(len=1,)) + @test_throws ErrorException execute_operation(request, Float32[1], Float32[2]) + clear_provider_registry!() + end + + if haskey(ENV, "ENACTION_ACCELERATOR_LIB") + @testset "Enaction pure-Zig provider" begin + clear_provider_registry!() + provider = EnactionZigProvider(ENV["ENACTION_ACCELERATOR_LIB"]) + register_provider!(provider) + @test length(provider_capabilities(provider)) == 5 + + request = OperationRequest("enaction.tensor.f32.matmul"; + layout=(m=2, k=3, n=2), minimum_support=:resilient) + output, evidence = execute_operation(request, + Float32[1 2 3; 4 5 6], Float32[7 8; 9 10; 11 12]) + @test output == Float32[58 64; 139 154] + @test evidence.provider_id == "enaction.cpu.zig.scalar" + @test evidence.implementation == :native + + overflow = OperationRequest("enaction.tensor.f32.mul"; + layout=(len=1,), minimum_support=:resilient) + @test_throws ErrorException execute_operation(overflow, Float32[floatmax(Float32)], Float32[2]) + clear_provider_registry!() + end + end + # ======================================================================== # Platform Detection Tests # ======================================================================== From 5bc13dc0af32e7b72c88eac97f0f2f0a8e4379da Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 9 Aug 2026 07:17:44 +0100 Subject: [PATCH 3/6] docs: add BerryWiki coprocessor notebook --- .machine_readable/6a2/STATE.a2ml | 8 ++++++++ docs/berrywiki/ABI.md | 18 ++++++++++++++++++ docs/berrywiki/Architecture.md | 18 ++++++++++++++++++ docs/berrywiki/Axiom-Kernels.md | 18 ++++++++++++++++++ docs/berrywiki/Backends.md | 18 ++++++++++++++++++ docs/berrywiki/Evidence.md | 18 ++++++++++++++++++ docs/berrywiki/Home.md | 19 +++++++++++++++++++ docs/berrywiki/Kernels.md | 18 ++++++++++++++++++ docs/berrywiki/Machine-Index.md | 18 ++++++++++++++++++ docs/berrywiki/Operations.md | 18 ++++++++++++++++++ docs/berrywiki/Performance.md | 18 ++++++++++++++++++ docs/berrywiki/Roadmap.md | 18 ++++++++++++++++++ docs/berrywiki/Runtime.md | 18 ++++++++++++++++++ docs/berrywiki/Security.md | 18 ++++++++++++++++++ docs/berrywiki/Simulation.md | 18 ++++++++++++++++++ docs/berrywiki/Testing.md | 18 ++++++++++++++++++ docs/berrywiki/UMS-Integration.md | 18 ++++++++++++++++++ docs/berrywiki/_Sidebar.md | 18 ++++++++++++++++++ 18 files changed, 315 insertions(+) create mode 100644 docs/berrywiki/ABI.md create mode 100644 docs/berrywiki/Architecture.md create mode 100644 docs/berrywiki/Axiom-Kernels.md create mode 100644 docs/berrywiki/Backends.md create mode 100644 docs/berrywiki/Evidence.md create mode 100644 docs/berrywiki/Home.md create mode 100644 docs/berrywiki/Kernels.md create mode 100644 docs/berrywiki/Machine-Index.md create mode 100644 docs/berrywiki/Operations.md create mode 100644 docs/berrywiki/Performance.md create mode 100644 docs/berrywiki/Roadmap.md create mode 100644 docs/berrywiki/Runtime.md create mode 100644 docs/berrywiki/Security.md create mode 100644 docs/berrywiki/Simulation.md create mode 100644 docs/berrywiki/Testing.md create mode 100644 docs/berrywiki/UMS-Integration.md create mode 100644 docs/berrywiki/_Sidebar.md diff --git a/.machine_readable/6a2/STATE.a2ml b/.machine_readable/6a2/STATE.a2ml index e09f348..1e7c42c 100644 --- a/.machine_readable/6a2/STATE.a2ml +++ b/.machine_readable/6a2/STATE.a2ml @@ -7,8 +7,16 @@ project = "AcceleratorGate.jl" version = "0.1.0" last-updated = "2026-03-15" status = "active" +wiki = "docs/berrywiki/" +wiki-pages = 16 +wiki-tool = "metadatastician/berrywiki" [project-context] name = "AcceleratorGate.jl" completion-percentage = 5 phase = "alpha" + +[coprocessor-documentation] +human-index = "docs/berrywiki/Home.md" +machine-index = "docs/berrywiki/Machine-Index.md" +scope = "provider admission, deterministic planning, execution evidence, and shared Zig ABI" diff --git a/docs/berrywiki/ABI.md b/docs/berrywiki/ABI.md new file mode 100644 index 0000000..d759058 --- /dev/null +++ b/docs/berrywiki/ABI.md @@ -0,0 +1,18 @@ + + +# ABI + +Stable Idris2-facing and Zig FFI layout rules. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Architecture.md b/docs/berrywiki/Architecture.md new file mode 100644 index 0000000..62ebd2e --- /dev/null +++ b/docs/berrywiki/Architecture.md @@ -0,0 +1,18 @@ + + +# Architecture + +Boundaries, ownership, and data flow. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Axiom-Kernels.md b/docs/berrywiki/Axiom-Kernels.md new file mode 100644 index 0000000..1787574 --- /dev/null +++ b/docs/berrywiki/Axiom-Kernels.md @@ -0,0 +1,18 @@ + + +# Axiom-Kernels + +Axiom-derived pointwise, binary, and attention coverage. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Backends.md b/docs/berrywiki/Backends.md new file mode 100644 index 0000000..f441343 --- /dev/null +++ b/docs/berrywiki/Backends.md @@ -0,0 +1,18 @@ + + +# Backends + +Backend/provider registration and selection. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Evidence.md b/docs/berrywiki/Evidence.md new file mode 100644 index 0000000..b4741e9 --- /dev/null +++ b/docs/berrywiki/Evidence.md @@ -0,0 +1,18 @@ + + +# Evidence + +Capability and execution evidence requirements. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Home.md b/docs/berrywiki/Home.md new file mode 100644 index 0000000..2984779 --- /dev/null +++ b/docs/berrywiki/Home.md @@ -0,0 +1,19 @@ + + +# AcceleratorGate.jl + +Julia operation-first admission and evidence control plane. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +This BerryWiki notebook is the human-facing index; the repository's machine-readable state file is authoritative for automation. + diff --git a/docs/berrywiki/Kernels.md b/docs/berrywiki/Kernels.md new file mode 100644 index 0000000..10d1c9d --- /dev/null +++ b/docs/berrywiki/Kernels.md @@ -0,0 +1,18 @@ + + +# Kernels + +Kernel families, layouts, and numerical contracts. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Machine-Index.md b/docs/berrywiki/Machine-Index.md new file mode 100644 index 0000000..650cba7 --- /dev/null +++ b/docs/berrywiki/Machine-Index.md @@ -0,0 +1,18 @@ + + +# Machine-Index + +Machine-readable inventory and automation entry point. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Operations.md b/docs/berrywiki/Operations.md new file mode 100644 index 0000000..3d302ba --- /dev/null +++ b/docs/berrywiki/Operations.md @@ -0,0 +1,18 @@ + + +# Operations + +Canonical operation names and request semantics. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Performance.md b/docs/berrywiki/Performance.md new file mode 100644 index 0000000..0a25477 --- /dev/null +++ b/docs/berrywiki/Performance.md @@ -0,0 +1,18 @@ + + +# Performance + +Benchmarking, determinism, and tuning guidance. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Roadmap.md b/docs/berrywiki/Roadmap.md new file mode 100644 index 0000000..b0f0a31 --- /dev/null +++ b/docs/berrywiki/Roadmap.md @@ -0,0 +1,18 @@ + + +# Roadmap + +Near-term implementation and admission milestones. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Runtime.md b/docs/berrywiki/Runtime.md new file mode 100644 index 0000000..600f00f --- /dev/null +++ b/docs/berrywiki/Runtime.md @@ -0,0 +1,18 @@ + + +# Runtime + +Lifecycle, loading, and failure terminality. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Security.md b/docs/berrywiki/Security.md new file mode 100644 index 0000000..b65d8f2 --- /dev/null +++ b/docs/berrywiki/Security.md @@ -0,0 +1,18 @@ + + +# Security + +Trust boundaries, validation, and unsafe inputs. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Simulation.md b/docs/berrywiki/Simulation.md new file mode 100644 index 0000000..7628d31 --- /dev/null +++ b/docs/berrywiki/Simulation.md @@ -0,0 +1,18 @@ + + +# Simulation + +Rules for simulation, refusal, and conformance claims. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/Testing.md b/docs/berrywiki/Testing.md new file mode 100644 index 0000000..9d751f0 --- /dev/null +++ b/docs/berrywiki/Testing.md @@ -0,0 +1,18 @@ + + +# Testing + +Local tests, integration tests, and acceptance gates. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/UMS-Integration.md b/docs/berrywiki/UMS-Integration.md new file mode 100644 index 0000000..7059d79 --- /dev/null +++ b/docs/berrywiki/UMS-Integration.md @@ -0,0 +1,18 @@ + + +# UMS-Integration + +How the universal model space contract is consumed. + +AcceleratorGate is the Julia-side policy plane: it chooses providers, records evidence, and refuses unsupported or unauthorised simulation. + +See [[Home]] for the project boundary and the repository machine-readable state for exact fields. diff --git a/docs/berrywiki/_Sidebar.md b/docs/berrywiki/_Sidebar.md new file mode 100644 index 0000000..a2eadce --- /dev/null +++ b/docs/berrywiki/_Sidebar.md @@ -0,0 +1,18 @@ +# Notebook + +- [AcceleratorGate.jl](Home) +- [Architecture](Architecture) +- [Operations](Operations) +- [Backends](Backends) +- [Kernels](Kernels) +- [Evidence](Evidence) +- [ABI](ABI) +- [UMS-Integration](UMS-Integration) +- [Simulation](Simulation) +- [Axiom-Kernels](Axiom-Kernels) +- [Runtime](Runtime) +- [Testing](Testing) +- [Security](Security) +- [Performance](Performance) +- [Roadmap](Roadmap) +- [Machine-Index](Machine-Index) From 2fce2ce37bb48282582edf1cf9652ff212ffc1ca Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 9 Aug 2026 07:50:30 +0100 Subject: [PATCH 4/6] chore: refresh governance metadata --- GOVERNANCE.md | 2 +- MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index e27364c..3d41c87 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -57,4 +57,4 @@ By submitting a pull request, you agree to license your contributions accordingl --- -*Last updated: 2026-07-18* +*Last updated: 2026-08-09* diff --git a/MAINTAINERS b/MAINTAINERS index 37f6411..c0b34bf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -40,4 +40,4 @@ To become a maintainer: --- -*Last updated: 2026-07-18* +*Last updated: 2026-08-09* From a94cd1be5bfce702247d7aefb7fd23d2c13cdfcb Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 9 Aug 2026 08:13:26 +0100 Subject: [PATCH 5/6] ci: align Julia action and cache pins --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4d2834..a686cee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,9 +25,9 @@ jobs: os: macos-latest steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: julia-actions/setup-julia@4c0cb0fce8556fdb04a90347310e5db8b1f98fb9 # v2 + - uses: julia-actions/setup-julia@fa02766e078afaaf09b14210362cee14137e6a32 # v3.0.2 with: version: ${{ matrix.julia-version }} - - uses: julia-actions/cache@e33b4bfa0ea7cd9caedd7cb82b0e36956ef40285 # v2 + - uses: julia-actions/cache@a45e8fa8be21c18a06b7177052533149e61e9b38 # v3.1.0 - name: Install, build, test run: julia --project=. -e 'using Pkg; Pkg.instantiate(); Pkg.build(); Pkg.test()' From 4c9fb635d587da4f5e8f92a871a9090ca00b3cc9 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 9 Aug 2026 09:49:28 +0100 Subject: [PATCH 6/6] fix: record Libdl in manifest --- Manifest.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Manifest.toml b/Manifest.toml index b36722a..caa4de5 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -15,6 +15,10 @@ deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" version = "1.11.0" +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" + [[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"