Prospective native build receipts

scripts/record_native_build.py is an opt-in public observer for a fresh out-of-source CMake/Ninja build and install. It does not reconstruct the history of cached binaries, enable runtime-slot reuse, or qualify a library by itself. Existing build commands remain unchanged unless the observer is selected.

For the already-supported unpatched FFTW recipe, select the executable observer and a fresh existing receipt directory outside the repository/build/install:

export VIBEQC_NATIVE_BUILD_COLLECTOR="$PWD/scripts/record_native_build.py"
export VIBEQC_NATIVE_RECEIPT_DIR=/absolute/external/empty-receipts
export VIBEQC_NATIVE_SOURCE_ARCHIVE=/absolute/fftw-3.3.10.tar.gz
bash scripts/build_fftw.sh

Use the source archive matching the recipe’s exact SHA-256 pin. A cache hit stays a cache hit and produces no fresh-build receipt. VIBEQC_NATIVE_SEED_CONTEXT=1 remains a separate opt-in to the conservative Darwin/Apple-clang context resolver and FFTW adapter. Its existing compiler, SDK, ABI, dependency, source and final-prefix donor-read-denied probes still apply; this observer bypasses none. No extra build is needed just to produce a receipt: select it when a fresh build is already required.

Record contract

The observer accepts record --spec SPEC.json --output RECEIPT.json -- COMMAND…. The spec has sources (name to source directory), authenticated_sources (same names, each with archive, sha256, and archive prefix), recipes (file paths), cmake_builds (configured directories), install, artifact_manifest, cwd, and explicit data_sources. Each data entry maps an install-relative destination to { "source": "source-name", "path": "source-relative-file" }. FFTW uses its authenticated api/fftw3.h as include/fftw3.h. An empty data map explicitly observes no runtime data; it must not be interpreted as qualification of a library’s data requirements.

The vibe.native-build-receipt/1 output retains pre-build observations and command, producer identity, return code, and the complete post-install inventory. Only status=recorded with integer returncode=0 denotes a successful observation. Failed commands and changed inputs leave a failed receipt; no automatic retry or unobserved fallback occurs. Source archives plus declared exact replacement programs must match source files before and after the command. Undeclared patches, source-tree generated files, archive links, and compiler wrappers requiring transitive provenance are not qualified by this contract. Output files must match the install manifest exactly. Selected installed data must match its authenticated source bytes and mode.

The receipt explicitly fingerprints six categories in inputs.semantic_inputs:

Category

Observed input

sources_patches

Authenticated archives, pinned replacement programs and complete observed inventories

recipe

Explicit recipe file bytes

toolchain

Configured compiler executable paths and bytes

options

CMake cache, compiler commands, Ninja/install graphs, selected build environment

abi

Observed host system, architecture, OS/libc and byte order

data

Explicit authenticated source data mapped to installed destinations

These hashes are observations, not universal compatibility keys. They retain absolute producing paths and are deliberately conservative. The candidate adapter must independently resolve its context, normalize only justified relocation roots, attest transitive toolchain/dependency inputs, compare semantics and run native/data probes in the final prefix with donor reads denied. Merely comparing compiler versions, six hashes or output hashes is insufficient.

The generic observer can record supplied library specs with the restricted replacement contract below; automatic recipe integration and runtime-slot admission are limited to the conservative Darwin FFTW and patched-libxc routes documented here. There is no supported libint/OpenBLAS/libecpint/spglib seed adapter yet. Those libraries’ recipe, patch, ABI, data and isolated native-probe contracts still need implementation. No live dependency reuse is established by protocol tests.

Receipts can contain local paths and environment observations. Keep them outside product sources, installers and distributable source archives; do not publish them automatically.

Focused protocol controls (no dependency build): python3 scripts/tests/test_record_native_build.py.

Authenticated exact replacements

An authenticated_sources entry may additionally contain transformations, an ordered list of { "path": "/absolute/program.json", "sha256": "<trusted pin>" }. Each program uses schema vibe.native-source-replacements/1 and a nonempty operations list. Every operation has exactly path, before and after:

  • path is a canonical contained source-relative file path.

  • before is the exact prior { "sha256": "...", "mode": 420 } identity, or null for an addition. It must match the archive inventory or preceding operation.

  • after is null for deletion, or { "sha256": "...", "mode": 420, "content_base64": "..." } for a complete regular-file replacement. Bytes must match the declared digest; special permission bits and aliases are refused.

This is an exact replacement language, not fuzzy patch application. The caller must supply reviewed transformation pins prospectively. The observer verifies the mathematical relationship between archive and transformed tree and binds the program bytes before and after execution; it does not claim to have witnessed a historical patch command. An undeclared edit remains a refusal. A changed program during execution leaves a failed receipt.

Receipts with any replacement program use source_authentication.status equal to archive_transformed. Existing unpatched receipts retain archive_matched and their semantic fingerprint shape. The current FFTW seed adapter deliberately refuses transformed receipts; this contract does not qualify a new library, transitive dependency, compiler wrapper or live runtime reuse. In particular, FFTW source version and exported runtime version remain distinct observations.

Multistage inputs and declared dependency closure

A stage may declare dependencies, mapping names to exact predecessor { "receipt": "/absolute/receipt.json", "sha256": "<trusted receipt pin>", "install": "/absolute/preceding-install" } entries. Every predecessor must be an authenticated successful prospective receipt. Its complete installed inventory must still match, before and after the new command. All predecessors referenced by those receipts must also be explicitly declared with matching identities; missing transitive edges, duplicate identities, cycles and changed outputs refuse. The new install and receipt cannot overlap protected predecessor installations.

For generated source, declare generated_sources in place of an archive entry: { "generated": { "dependency": "generator-stage", "subdir": "include" } }. The matching sources.generated directory must be exactly that predecessor’s output subtree, with its complete files and modes unchanged. There is no fallback from an unmatched archive to an assumed generated input. Installed data mappings and compiler translation units use these same authenticated source inventories.

Such receipts use status archive_with_dependencies in source authentication, and include the declared predecessor graph, input semantics, producer identities and installed output inventories. This authenticates the declared graph; it does not discover undeclared dynamic libraries, arbitrary compiler-wrapper behavior or system include dependencies. Unknown historical receipts remain unqualified. The existing FFTW adapter continues to reject these new receipt classes until a library-specific semantic resolver and isolated final-prefix probe qualify them.

Grouped recipe disposition

Library

Remaining source/lifecycle contract

FFTW

Existing unpatched Darwin context, serial/threaded native and header-data probe; actual fresh-build then slot reuse still required. Exported3.3.9 from pinned3.3.10 remains legitimate.

libxc

Patched Darwin source/SDK/options resolver and donor-denied native/header probe implemented below; actual fresh-build then following-slot acceptance remains required.

spglib

Bind its configured single-stage recipe and candidate ABI/native probe; no automatic admission from a generic receipt.

libecpint

Bind generated/multiple-stage inputs and separate build/install commands, then its relocated ECP data operation.

libint

Bind generator/generated-source stages and configured integral feature options; preserved historical installs cannot supply a fresh receipt.

OpenBLAS

Its Make-based build/install is outside this CMake/Ninja observer; add an actual Make/toolchain/options resolver and candidate BLAS operation.

These are explicit dispositions, not declarations that the recipes already emit or consume the multistage contract. No missing receipt authorizes a cold rebuild.

Patched libxc on Darwin (opt-in)

The library-specific scripts/native_libxc_seed.py route handles Libxc 7.0.0 with the pinned stable-spin-fraction patch on the existing conservative Apple-clang/system-runtime context. It does not enable Linux qualification. Its source is the exact reproducible Git archive of the recipe-pinned upstream commit, not a reconstructed archive of an installed or patched source tree:

git -C /path/to/authenticated/libxc-repository archive --format=tar \
  --prefix=libxc-7.0.0/ 7bd5bb41415968db94c499a2f093309c9a2dcf53 \
  > /absolute/libxc-7.0.0.tar
# Required SHA256: 6cb75c9315de67310a2ceb35f5984c107f795e1235de724d37ea028885f9cc55
export VIBEQC_NATIVE_BUILD_COLLECTOR="$PWD/scripts/record_native_build.py"
export VIBEQC_NATIVE_RECEIPT_DIR=/absolute/external/fresh-receipts
export VIBEQC_NATIVE_SOURCE_ARCHIVE=/absolute/libxc-7.0.0.tar
export VIBEQC_NATIVE_SEED_CONTEXT=1
bash scripts/build_libxc.sh

Use this only when a fresh dependency build is already authorized and needed. Existing installs still follow the normal cache path and never acquire a fresh receipt. The existing builder retains its lifecycle lock and original CMake options. In this opt-in route it stages the pinned archive directly, without Git administrative files; an existing different source tree (including a Git checkout) is preserved and refused, never deleted or retrospectively qualified. The collector checks the archive’s complete source inventory against three exact replacement operations generated from the tracked, recipe-pinned patch. A successful build must preserve source, observer, recipes, compiler, SDK/toolchain, configured options and generated config.h/xc_version.h. The installed patch marker, complete install manifest, authenticated xc.h and xc_funcs.h, and generated version header are checked before the exclusive .vq-native-libxc.json envelope is written. Failure is retained; no fallback, retry or retrospective qualification occurs.

Libxc’s upstream README and ChangeLog aliases require explicit allow_sibling_aliases: true in the authenticated-source spec. This permits only a sibling regular member of the same pinned archive; missing targets, chains, directories, escaping paths and hardlinks remain refused. The default archive contract and FFTW extraction still reject all links.

The Queue adapter command is python3 scripts/native_libxc_seed.py; it accepts the existing plan/verify JSON protocol on stdin. The pinned source archive must be available in the adapter environment. Planning extracts and applies the same authenticated transformation, runs only candidate configuration and compares actual source/patch/options/compiler/SDK/ABI semantics. Verification requires unchanged donor and candidate manifests and runs the tracked probe_libxc_prefix.py in a separate process with donor reads denied. It binds all called symbols and the patch marker to the candidate image, reads the two candidate headers, and checks unpolarized LDA exchange energy and potential at two densities against the analytic expression. The probe binds its actual library/header hashes before and after execution and scrubs loader overrides. Missing donor paths never count as isolation. This is a bounded native/header operation, not general libxc functional or gradient scientific acceptance.

The source route does not establish live reuse: the authorized fresh build and following-slot donor-denied acceptance remain required. Other library routes and Linux compiler/sysroot/loader closure remain outside this implementation.