UK Legislation runtime acceptance
The release gate uses the production Explorer build and the generated local
okf-uk-legislation/bundle/ publication. It does not substitute a small test
fixture for the corpus.
Run from apps/okf-explorer/:
pnpm acceptance:legislation
The runner first creates private, byte-copy snapshots of the complete production build and legislation publication. Files in those snapshots are read-only and are not hard links back to either checkout. The temporary same-origin server serves only the snapshots, with real gzip transfer and the byte ranges used by release datapacks. It executes the same journey in Chrome, Firefox and WebKit:
- load the Whole-Law federation and confirm its 36 source classes;
- open the declared UK Legislation child and wait for static search to finish initialising;
- reload the legislation descriptor in a fresh context and measure compressed startup transfer;
- measure a first cold search and a second warm search;
- inspect the real facet inventory, coloured distributions and workspace geometry;
- verify that the official-effects reconciliation panel exposes agreement, live-addition, superseded and inaccessible states, including explicit zero counts;
- open the Consumer Credit Act graph, verify official and derived relationship line styles, and activate an edge with the keyboard;
- search for The Air Navigation (Amendment) Order 2026, hydrate its aligned governed v3 accepted model-enrichment shard, verify distinct official, derived and model-assisted relationship lines plus ordered evidence provenance, then hide and restore only the model-assisted class through the URL-backed authority filter;
- run WCAG 2.2 axe checks and capture the Chrome renderer JavaScript heap.
The limits are 1 MiB compressed startup transfer, 3 seconds for cold search, 1 second for warm search, and 256 MiB for the nominated Chrome CDP JavaScript heap measure. Firefox and WebKit do not expose that Chromium-only memory metric; the receipt records those measurements as unavailable rather than passing them.
The v0.6.2 release-bound canonical destination is
release-assurance/explorer-runtime-acceptance.json. It is intentionally
absent until the clean Explorer commit, annotated v0.6.2 tag and exact
legislation candidate can be bound by the v2 release gate. The byte-preserved
26 July v1 receipt
is retained only as historical evidence under the
archive classification; its legacy
overall passed field is not current release authority.
An unbound local run emits an okf-explorer-runtime-acceptance.v1 diagnostic
receipt. Diagnostic evidence is deliberately assigned overall status failed
even when all its individual browser checks pass, so it cannot be mistaken for
a release gate. A release gate supplies all candidate and Explorer revision
arguments plus a fresh external --output path whose basename must be
explorer-runtime-acceptance.json; that run emits
okf-explorer-runtime-acceptance.v2.
Release evidence is pinned to Explorer v0.6.2. The runner reads the package
version, derives the Explorer HEAD, tree and v0.6.2 tag object from Git, and
requires an annotated tag that resolves to that HEAD. It independently derives the
legislation candidate HEAD, tree and the complete bundle inventory digest.
Both checkouts must be clean. The supplied revision arguments are expectations,
not evidence: every supplied value must exactly equal the corresponding
derived value. See the Git object model
for the distinction between commits, trees and tag objects.
The public pnpm acceptance:legislation command acquires the checkout lock and
performs the deterministic build before it starts this inner runner. Do not
invoke the inner runner or start a separate preliminary build: both would
bypass or duplicate the governed build-first sequence. A release-bound run
requires the validated on-disk manifest and does not use the local in-memory
fallback.
The wrapper records a completed-build attestation in its live lock only after
the deterministic command succeeds. Before opening the acceptance server, the
runner verifies the exact command digest, build-script bytes, canonical
manifest bytes and app-build tree. It binds the runner, runtime contract,
build-manifest module, wrapper, lock module, deterministic-build script,
package metadata and dependency lock before the browser work, then rereads all
of them afterwards. It also revalidates the live completed-build lock after all
browser contexts and the server have closed. The receipt projects only safe
repository-relative paths and digests from that proof; it does not expose the
owner token, absolute local paths, URL query strings or credentials.
Both live-lock verifications receive the runner's deadline. Lock and build
material reads are byte-bounded, check the deadline before allocation and each
read, and pass that same deadline into canonical build inspection.
The complete source bundle and build are scanned within bounded entry, file, depth, per-file and aggregate-byte limits while being copied. Directory entries are consumed incrementally and the entry ceiling is checked before a name is retained. Files are size-checked before any result buffer is allocated, opened without following links, read in bounded chunks and checked for truncation, growth, replacement and identity changes. The shared canonical build inspector applies the same finite deadline and ceilings before it reads the manifest or any build material, and checks it again before allocating read buffers and before and after canonical parsing or serialisation. The runner rejects symbolic links, hard links and special files. It computes the bundle digest with the same canonical JSON inventory algorithm as the legislation release tooling. After the browser journeys it rehashes both live sources and both snapshots and requires all four identities to remain exact.
On a successful path the private snapshot directory is removed and its absence verified before the canonical receipt is constructed or published. A cleanup failure therefore cannot leave a passing receipt. Failure paths also attempt bounded cleanup, while the wrapper's process-group timeout remains the final hard stop.
Every invocation stages a self-contained, write-once evidence tree beside the receipt:
- the exact bytes of its runner, runtime contract, build-manifest module, wrapper, lock module, deterministic-build script, package metadata and dependency lock beneath their repository-relative paths;
- both descriptors under the safe relative
bundle/root; - the canonical production-build manifest at
explorer-build/okf-explorer-build-manifest.json; - every file described by that manifest beneath
explorer-build/, including the unique production index atexplorer-build/index.html; and - both current Chrome captures under
output/playwright/.
Every material has an exact positive byte count and SHA-256 digest. The receipt
reports only these safe paths, never a ../ path back into an Explorer or
legislation checkout. Its outputs.receipt value is exactly
explorer-runtime-acceptance.json, matching the Whole-Law pre-RC controller.
The deployed okf-explorer-build-manifest.json is canonical UTF-8 JSON plus a
final line feed. It has schema okf-explorer-app-build-manifest.v1, algorithm
sha256-canonical-json-materials-v1, a positive file_count, tree_sha256
and materials strictly sorted by raw Unicode code-point path order. Each
material is a unique, safe, build-root-relative POSIX path with positive bytes
and SHA-256. Directories are not included. The manifest excludes itself to
avoid recursive hashing, and the tree digest is SHA-256 over the UTF-8 bytes
JSON.stringify(materials) + "\n" with material key order
path, bytes, sha256.
The release-bound runner requires that deployed manifest, canonical-parses and
re-renders it, scans the complete app build without following links, and
rejects missing, extra, duplicate, unsafe, linked or tampered files. It stages
the manifest and every described file independently with write-once semantics.
inputs.explorer_build exposes root, manifest, the exactly matching
index, files, sha256, algorithm and the complete ordered staged
materials array so a downstream finalizer can enumerate and independently
rehash the evidence tree. The integrity projection also reconstructs the
canonical manifest from those rows and requires the staged manifest byte count
and digest to match it. A mutable local v1 run may derive the same manifest in
memory when a plain local build predates the deployed manifest.
Publication is durable and write-once: the runner synchronises a private
temporary file, publishes it atomically without replacement, removes the
temporary name, synchronises the containing directory and then independently
rereads the exact expected byte count through a no-follow file descriptor. It
verifies and synchronises the destination inode itself, then verifies the
complete bytes and physical parent for both a newly created file and an
already-present identical file. It fails rather than replacing a
divergent, symbolic or linked destination. The receipt and
output/playwright/ captures must be physically contained by the same evidence
root. A release evidence root must be a new, absent directory whose parent
already exists, and must be disjoint from both source checkouts: it may neither
contain a source checkout nor be contained by one. Use a different external
directory for each measured attempt. This follows the durability distinction
documented for Node file handles and fsync in the
Node.js file-system API.
The hard aggregate limits are 19 minutes for the runner, 20,000 bundle entries, 12,000 bundle files, 2 GiB of bundle bytes, 2,000 build entries, 1,000 build files, 128 MiB of build bytes, 128 MiB per file, a 16 MiB canonical build manifest, 50,000 HTTP transfer rows, 8 GiB each of wire and decoded transfer bytes, 16 concurrent requests, 256 MiB of in-flight decoded source bytes, 8 MiB of retained telemetry, 64 KiB per retained string, 10,000 graph edges, two screenshots of at most 20 MiB each, and a 32 MiB receipt. The snapshot cleanup allowance is 15 seconds. At least 10 seconds of the governed runner deadline must remain before receipt publication begins. Receipt and telemetry sizes are measured incrementally before their complete JSON representations are allocated, with deadline checks during measurement and immediately before and after complete serialisation and durable publication. The final post-publication deadline check prevents an earlier duration observation from authorising late evidence. Every admitted HTTP request holds a concurrency reservation until its response finishes or closes; successful, ranged, compressed and not-found responses also reserve their transfer row and byte budgets before material is read or a response is queued. Exceeding any bound puts the same-origin acceptance server into a sticky fatal state or otherwise fails the run; a later request cannot erase that failure, and no bound is reported merely as an advisory observation.
The receipt binds every browser version, exact measurements, resource ceilings, gate decisions and limitations. Live official full-text search is replaced with an empty deterministic Atom response so source availability cannot change the static local-search measurement. The accessibility gate names its assessed standard as WCAG 2.2 AA and uses the axe-core Playwright integration.
The detailed gates and browsers evidence remains authoritative. Stable
release-facing projections additionally expose overall runtime counts plus
cross_engine, accessibility, performance and integrity statuses. These
paths are the contract consumed by the UK Legislation clean-room reproduction
gate. The integrity projection fails closed unless both descriptors, the
canonical build manifest, its complete staged material set and the exact
two-current-Chrome-screenshot set have canonical paths, positive byte counts
and valid SHA-256 evidence. A stale, missing, duplicated, extra or internally
inconsistent material cannot satisfy a passing receipt.
The projection contract can be tested without building the application or running the real corpus:
pnpm test:acceptance-contract