t81-foundation

Deterministic execution and provenance — Phase 1

An opt-in Candidate 3 experiment using the production interpreter, Axion policy engine, persistent CanonFS driver and SHA3-512/256 hashing. No ISA or public API changes. Audit, observations and limits.

From the repository root (C++23 compiler, CMake, Ninja and Python >=3.10):

cmake -S . -B /tmp/t81-provenance-build -G Ninja \
  -DCMAKE_BUILD_TYPE=Release -DT81_BUILD_PROVENANCE_EXPERIMENT=ON \
  -DT81_BUILD_TESTS=ON -DT81_STRICT_DETERMINISTIC_FLOAT=ON \
  -DT81_BUILD_BENCHMARKS=OFF -DT81_BUILD_EXAMPLES=OFF \
  -DT81_BUILD_TUI=OFF -DT81_ENABLE_TERNARYOS=OFF -DT81_BUILD_FUZZ_TESTS=OFF
cmake --build /tmp/t81-provenance-build --target t81_provenance_experiment -j 4
ctest --test-dir /tmp/t81-provenance-build --output-on-failure \
  -R '^deterministic_provenance_experiment$'
python3 experiments/deterministic_provenance/run.py \
  --runner /tmp/t81-provenance-build/t81_provenance_experiment \
  --output /tmp/t81-provenance-results --repetitions 30

Use a fresh output directory. CMake fetches the repo’s pinned Asio test dependency; an offline build can set FETCHCONTENT_SOURCE_DIR_ASIO to an existing asio-1-30-2 checkout. No Python packages or models are required.

Independent replay verification, without loading T81 or importing producer code:

python3 experiments/deterministic_provenance/verify.py \
  experiments/deterministic_provenance/fixture-v2.json \
  --input 40 --divisor 2 --profile allow
python3 experiments/deterministic_provenance/verify.py \
  /tmp/t81-provenance-results/allow-0/evidence.json \
  --input 40 --divisor 2 --profile allow \
  --store /tmp/t81-provenance-results/allow-0/store

The caller pins input and profile. The first command verifies the detached fixture by recomputing this admitted transformation and its exact execution sequence. The second additionally checks the actual CanonFS file inventory and payload bytes. Copy verify.py and fixture-v2.json to another directory to verify without the repository or producer executable.

run.py executes eight cases in rotating order: allowed, changed input, VM deny, storage deny, division by zero, input bounds and an unconfigured-policy baseline. Every case must repeat with exactly identical canonical bytes. The default is 30 samples per case, with five interpreter/hash warm-ups within each process. report.json contains source SHA/digests, toolchain/platform, raw samples, mean/median/stdev/min/max, verifier outcomes and explicit exclusions. The checked-in observed-arm64.json preserves one native run.

Canonical evidence is sorted compact ASCII JSON (ensure_ascii=True), with integers and strings; SHA3-512 truncated to the first 32 bytes. The experiment’s program/trace text uses decimal integers, opcode names and LF, never C++ object memory or host endianness. CanonFS references retain production Base81 encoding. record_hash covers the complete record, including program/input/policy/output hashes, actual ordered trace and policy audit, content reference and integer boundary probes. Platform, compiler, Git SHA, timings, memory and file paths live only in the measurement report. No timestamps enter canonical evidence.

A changed arithmetic input can preserve the control-flow trace hash while changing output and complete evidence hashes. Hashing only PC/opcode/trap is insufficient to bind execution to inputs; the complete record provides that link.

The evidence is unsigned. Verification establishes agreement with the pinned bounded computation, not producer identity or absence of unreported host writes. The protected storage side effect is creation of the result .blk payload; constructing a denied persistent store may create empty administrative directories. This experiment does not claim transactionality or concurrent revocation.

Floating-point conversion exclusion (evidence v2)

PR #551 included host-dependent T81Float::from_double probes in evidence v1. They fail numerical/reference checks and differ across native platforms. Version 2 explicitly excludes all these conversions from canonical evidence; the runner still emits their exact outputs as UNSUPPORTED_NONCANONICAL diagnostics and run.py preserves every observation outside the hashes. The independent verifier rejects v1 and any reintroduced floating-point fields instead of ignoring them.

The original fixture.json and observed-arm64.json remain immutable v1 research artifacts, not v2 verification targets. fixture-v2.json replaces the active integer-only contract. See the conversion audit for exact reference expectations, root causes, preserved failures and the unresolved production/API contract. A passing integer provenance gate does not resolve floating-point conversion or establish any floating-point determinism claim.