feat(release): add the nvattest native proof primitive and receipt
Bind one journal candidate to one canonical nvattest target on the executing native host: create an isolated environment, install the candidate target wheel set plus a declared bounded support set by absolute local path with index and user-site access disabled, drive nvattest resolution and install through the installed package, verify the pinned archive and companion manifest against the closed authority, and execute the absolute installed bin/nvattest --help from the fingerprinted cache root.
The receipt carries the proof challenge; observed native OS/architecture and selected target; journal version, source commit, candidate digest, core-lock digest, and ledger digest; a digest of the exact authority bytes read from the installed package; nvattest version, fork source, and upstream base; archive and manifest URL, hash, and observed size; sidecar and payload tree fingerprint; the exact seven member observations; smoke argv and exit code; and UTC observation time. It writes canonical JSON with sentinel-normalized paths and a clean public-evidence pass.
This deliberately does not add candidate orchestration, ledger integration, publisher enforcement, --recover wiring, proof-host channel changes, or live remote lane / production URL access. Those are the next sequential lode.
The companion-manifest validator is pinned to the three real published schema_version 2 documents, compares members to the authority keyed by path rather than by order, and treats the missing manifest executable bit correctly: executable state is enforced against the installed tree instead.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
refactor(release): break the nvattest proof-host import cycle
Audit follow-up to 52d03fd07, 63f871c94, and 4d95e0d45. Two findings were real;
three were assessed and rejected with reasons recorded below.
Binding nvattest into the proof-host channel created an import cycle:
release_nvattest_proof imported TARGET_POLICY from release_proof_host, and
release_proof_host then needed nvattest validators back, which was papered over
with two function-local lazy imports. TARGET_POLICY and TARGET_ENV_KEYS now live
in scripts/release_target_policy.py, a neutral module both import at top level.
The lazy imports are gone, every import site was updated directly, and nothing
is re-exported. release_nvattest_proof's import line changed; its internals did
not.
Also removed two dead locals in the candidate driver: proof_paths, which was
populated and silenced with an underscore assignment, and the unused return of
the target-proof lane call, whose receipts are written to the output paths
passed in and validated later by the shared report path.
Rejected findings, for the record:
- Transparency receipt validators are looser than the driver's inventory gate,
but that path is unreachable: transparency obtains its CandidateReport from
run_recover, which validates the evidence inventory exactly in both
directions, and CandidateReport has no other producer. Adding a redundant
guard would contradict the trust-internal-code invariant.
- The target names in docs/CHANNEL_ADAPTERS.md are pre-existing config-schema
examples naming lane keys operators must type literally, not drift-prone
prose.
- The AGENTS.md inventory-exactness statement is accurate, because the driver
enforces it.
Canonical make ci passes at 15894 passed, 17 skipped. The earlier per-slice runs
used make test-only, which runs neither the formatter check nor ruff; both are
clean now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat(release): require challenge-bound nvattest proofs in candidate evidence
Wire the landed nvattest native-proof primitive into the candidate rail. Every
fresh candidate now generates a 64-hex challenge, extracts nvattest authority
from its own root wheels, materializes a lock-verified eight-wheel support set,
and requires one challenge-bound nvattest receipt per native target alongside
the existing install receipt.
What a candidate now requires before promotion:
- a fresh challenge per attempt, so two attempts over identical version, source,
and payload inputs differ only in the challenge and the receipts bound to it
- authority extracted from the candidate's own root wheels, proven byte-identical
across all three targets, digested into the ledger, and passed to every lane
- the eight locked support wheels materialized into retained evidence and
verified against uv.lock before the ledger is written and before any host call
- one valid nvattest receipt per target in a sibling nvattest/ directory, so the
existing install-proof gates stay exact and unweakened
Evidence inventory grows from {ledger.json, proofs} to
{ledger.json, proofs, nvattest, support}, each gate exact in both directions.
The ledger gains one top-level nvattest key carrying the challenge, the
authority payload, its byte digest, and the locked support declarations. The
target list is not restated there; proofs.expected_targets stays the single
source of truth for both receipt kinds.
The proof-host seam is renamed run_target_proofs and returns both receipt paths
from a single host round-trip. release_install_smoke.run_install_proof, the
install prover the adapter harness invokes, is deliberately unchanged.
bundle_digest now binds the nvattest receipt digests. This intentionally changes
a reported candidate identity for future retained candidates; without it the
bundle digest would not cover the new evidence.
Lock verification is deliberately candidate-side only. Recovery anchors support
verification in retained bytes and the retained ledger's declarations, which are
themselves the lock-derived declarations captured at cut time. Anchoring
recovery in the checkout lock would make a retained candidate stop validating
after any support dependency bump, and would block publishing valid retained
bytes because publish_release gates on _verify_recover.
Real remote lanes, production URL reach, journal publication, and live SPP
composite acceptance remain VPE post-ship work; no test contacts a host,
downloads a wheel, or runs a real build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>