# Changelog: tool releases and site changes - Provael

> Markdown twin of https://www.provael.com/changelog/ - derived mechanically from the page's own rendered content at
> build time (it can state nothing the page does not). Curated, data-driven twins exist for the
> key pages; this one is generated so the twin convention has no holes.

CHANGELOG

# What changed in the Provael tool and on this site.

The current release is 0.46.0, from 21 October 2026. Site changes come first, then the tool’s releases, newest first, with their notes read from the product repository’s CHANGELOG.md each time this page is built.

last measured

2026-09-18 — the newest committed measurement is older than the seven-day window this project holds itself to, so the continuous-verification claim is not currently true. A release is not a measurement: shipping a version does not re-measure anything.

Source: [watch/freshness.json ↗](https://github.com/provael/provael/blob/main/watch/freshness.json), which the tool regenerates and which decays on its own rather than being asserted here.

59 releases [Full CHANGELOG ↗](https://github.com/provael/provael/blob/main/CHANGELOG.md) [How to cite →](https://www.provael.com/citation/) [Run the quickstart →](https://www.provael.com/quickstart/)

Site changes

## Changes to provael.com

Distinct from the tool’s releases below. A version string on a page can go stale without any release happening, which is how four of them did.

### Most pages rewritten in plain words, and the claims its own sources contradicted corrected · 1 November 2026

- Most pages now state the problem before the method, in shorter sentences and with fewer repeated limits. The entries on /errata and /changelog, the notes themselves and the product mirrors kept their words (/errata, /changelog and /notes got new introductions), the legal pages kept their policy text, and /studies, /crosswalk and /case-studies still open with their weakest link on purpose. No measurement changed: the published result, its controls, its limits and every price are what they were, and the result sentence is placed word for word. The homepage keeps its headline; the sentence under it now says the problem in plain words, and the hero has two buttons instead of three.
- Each offer has one name: Open-source tool, Free scan (it was PV-SCAN), Checkpoint Report, Red-team assessment, Assurance subscription and Fleet CI. The founding, early, standard and list figures are prices inside the Red-team assessment, not offers of their own. Each action has one button label, and a scoping-call button says “Request a scoping call” until a calendar is connected, because nothing on the site books one yet. The header item that opens /checkpoint-report now says Checkpoint Report; it said Assessment, the name of another page. /pricing shows the free tool, the Checkpoint Report and the red-team assessment side by side, with the assurance subscription below them and every price visible; the compliance add-on card is gone.
- Eleven claims the site’s own sources contradicted were corrected. The sample pack was called redacted, and nothing in it is. Every paid tier was said to deliver identical evidence, while the assessment adds to it. Pricing as a whole was called “a scoped starting range” beside a fixed-price Checkpoint Report. The /findings Meta-World card said “shipped” while the registry lists Meta-World as scaffolding. A CPU-fixture study carried a MEASURED badge. The total attack count was labelled as the adversarial attacks “plus a benign baseline”, when it also counts the harmless-variation controls: the label error erratum E-2026-13 describes. The Machinery Regulation note said every machine “meets” Annex III, which it must meet. The attack was called a reworded instruction where the data says reframed (E-2026-12). A CPU-stub run produced “a scorecard you can file”. Several pages said what an assessor or notified body asks for, which nothing sources. And the Top 10 was called a community framework, though nobody outside the project has adopted it.
- The page rewrites corrected more. /continuous-assurance said a regression beyond tolerance fails the build and the baseline only moves forward; the product fails a slice only when the rise exceeds tolerance and the 95% intervals do not overlap, and it promotes any passing run to the baseline. The /quickstart recording was captioned as a SmolVLA run at 100%; it shows a stub run. /results printed a reproduction command that runs one task, with no checkpoint and no benign control; it now prints the published run’s command. /trust said the wheel has no Sigstore signature (every release asset carries one) and that every run emits an ML-BOM (a run writes one when asked). /compare/vla-arena and /compare/vla-safety-benchmarks printed an earlier run’s benign control; they now read the pinned run’s. The /for pages put AMR and AGV fleets under UN R155, which applies to road vehicles; a fleet that is machinery sits under the Machinery Regulation. /results and the EAI03 page said the weight_integrity family cannot run against a real model; it ran on SmolVLA in the one-task breadth probe of 14 September 2026, at the benign floor, and both pages now say so, with no rate claimed. The compliance pages said the search-based attack families had only met the CPU test fixture; two of them ran in the same probe, and the line now says that none of them has a real-policy rate. /for/certifiers-insurers called its measurement comparable across policies, which a keep-out box not fitted to each policy does not establish; it now says the evidence has the same shape for every policy. The homepage deployment figure named a notebook the tool does not write.
- On /reproduce and /verification, the board-rebuild command once named a different run, so anyone who followed it got “match: False”. It has read its run from the pinned manifest since it was fixed, and the two pages no longer retell the incident beside it; this entry is where it is recorded.
- /contact now has a form beside its two addresses, and the footer gained a Company column with About, FAQ and Contact, which neither the header nor the footer linked before. The header’s GitHub button says GitHub rather than showing a lone star. Pages whose column or cards stopped short of the page width (/quickstart, /roadmap, /top10-rfc, the /pricing fine print, the cards on /faq, /errata and the Top 10) now line up with it.
- The four /for pages were rewritten without a new re-read of their claims, so each says when its claims were last checked and that the re-check is pending. /glossary, /top10-rfc and /changelog are now linked from the pages that use their terms or point at release history.
- Until 1 November a Cloudflare setting outside this repository refused the AI training crawlers, and Claude when a person asked it to read a page, while robots.txt welcomed them. The setting now matches robots.txt, which records what happened. The Markdown copies of 32 pages named an address without its closing slash as their canonical one (…/pricing, not …/pricing/), which is not the address those pages declare, and /llms.txt listed two pages the same way. Both now match, and the build fails if they disagree again. The site also gains a favicon.ico and a PNG home-screen icon, because iPhones ignore the SVG icon it pointed to before.
- The PyPI figure for the last 30 days on /adopters and /pricing had stopped moving at 1,874, the 30 days to 5 October, because the field it read froze; it is now counted from PyPI’s daily series, which puts the 30 days to 31 October at 686. Every link to the documentation now opens the page itself instead of a one-second redirect, each Markdown copy names its page in a canonical HTTP header, and the homepage, /results, /compliance, /machinery-regulation and the 14 framework pages carry titles in the words people search with. The stale-measurement notice now links to the dates on /results rather than the changelog, the footer links /for, and the site’s structured data names its Hugging Face organisation and its OECD.AI catalogue entry. /privacy and /trust now name Buttondown and Cal.com only while each is switched on, and the build refuses to switch either on without a new effective date for the privacy notice.
- Two new pages answer two questions people search with. /red-team-smolvla-libero is the how-to for testing a SmolVLA policy in LIBERO: what it needs, the commands, how to read what it prints, the published run worked through, what the first full run cost and what the test does not tell you. /sarif-robot-policy-findings shows how a red-team result is written as SARIF and uploaded to GitHub code scanning, with excerpts read from the sample file when the site is built, and what SARIF leaves out. /quickstart, /results, /sample-evidence-pack and /compliance link to them.
- The compliance pages now read every date of verification from the regulatory clock and print it one way, 19 September 2026. The Korea page’s Dates heading said “verified 19 Sept 2026” over lines saying “verified 12 Sep 2026”. The clock’s 19 September re-read covered the promulgation and in-force dates, which now say so, and the fines line, last checked on 12 September, keeps that date under a heading that says each line shows when it was checked. UN R155 splits the same way between 18 and 19 September, and the CRA, IEC 61508, ISO 13849, ISO/IEC 23894 and ISO/IEC TR 5469 pages read their own check dates from the clock too. The build fails if a compliance page prints a check date the clock does not record, in another style, or one that contradicts the date in the heading above it.

### The stale notices fold into one row, and an old measurement no longer freezes the site · 31 October 2026 [#184 ↗](https://github.com/provael/website/pull/184)

- The site no longer stops publishing when its measurement gets old. It used to refuse to build once the newest real-model measurement was 45 days old, which froze every update, corrections included, while the version already online stayed up. Now, past that age, the stale-measurement notice opens itself on every page instead, and a weekly job flags the number for re-measurement.
- The stale-measurement banner no longer takes three lines at the top of every page. It is now a small red STALE: OUR MEASUREMENT tag in a slim row above the header, and selecting it opens the same sentence in place. It still appears on every page, in the same red, as soon as the measurement goes stale, and nothing closes it. The tag says whose measurement is old because, folded, it is all a reader sees.
- On /regulatory-clock and the compliance pages the stale-clock notice sat below the header, against the left edge of the window, and read as a second banner. It is now a STALE: REGULATORY CLOCK tag beside the measurement one, in the same row, and the Markdown twins of /regulatory-clock and the CRA incident-reporting page still carry its sentence.
- /reproduce now uses the same page width as the rest of the site below its heading, instead of a narrower centred column.

### /results counts releases the way the banner does · 9 October 2026

- The provenance line on /results said the current release, 0.45.0, was “one release later” than the 0.41.2 the reference run was measured with, while the banner and the page’s own metadata said four. The version in that line was derived and the count beside it was typed; both now come from the value the banner reads.
- The site’s build checks now fail if that line and the page’s releases-since metadata disagree again.

### The measurement status gives a date, not a day count, and the site rebuilds daily · 3 October 2026

- The banner and every page’s measurement meta now say the newest real-model measurement is dated 2026-09-18 instead of how many days old it is, and /changelog and /roadmap show the same date. On 3 October every page still said 13 days old, the count from its last build, while the product’s badge said 14: the site only rebuilt when a commit landed here. A scheduled job now rebuilds and redeploys it once a day, so the stale flag and the release count follow the product within about a day. The build fails if a day count comes back into the status sentence.
- The “older tool version” notice on /leaderboard now gives the same verdict as the banner: measured with v0.41.2, 4 releases ago, past the 2-release window, current release v0.45.0. It used to print the verdict saved inside the board when it was assembled, “3 minor versions behind 0.44.0 (limit 1)”, which named a release that was no longer current and used a different limit.
- /errata now says why there is no E-2026-09 on it (the product’s log uses that number for this page’s E-2026-05), and its mirror date reads 2026-09-28, the day E-2026-17 was mirrored, instead of 2026-09-13. The build fails if a number goes missing without a reason, or if the mirror date is older than an entry on the page.

### The peer-review proof on /roadmap says the paper was submitted · 3 October 2026

- Proof 3, the SPAIS@CoRL four-pager due 1 October 2026, now carries a dated status line: submitted before the deadline, notification 17 October 2026. It used to show only the date, which had passed.
- The build now fails if that card loses its status line, so the 17 October decision replaces the line rather than removing it.

### Re-pinned at provael 0.45.0 · 28 September 2026

- Every pinned figure now reads provael 0.45.0’s evidence manifest (b4aa77d): the regulatory clock it references is ruleset 6, the suite counts are 4 runnable and 3 scaffolding (Meta-World moved to scaffolding), and the pinned ATLAS crosswalk and sample SARIF carry the corrected tactic names (E-2026-16). No measured number moved; the published measurement is still the 14 September run on 0.41.2, now four releases behind, and the banner says so.
- Two defenses now have a defended arm measured on a real policy (SmolVLA, 18 September), and /defenses says what neither shows: the canonicalized run used different seeds from the undefended one, and under the envelope the policy completed no task at all. Neither is a measured risk reduction.

### ANSI/A3 R15.06-2025 is dated to its year, the only part of its date anyone has verified · 28 September 2026

- The clock carried R15.06 at month precision (August 2025) for a few hours. ANSI’s record for it shows no date at all: August came from the approval date written into the entry when it was created, the same unverified record as the 21 August day, and secondary reporting puts it on sale in September 2025. /regulatory-clock.json now gives 2025 with datePrecision year, and /regulatory-clock prints 2025. The 21 August day is still recorded only so the build can fail a page that prints it, and a year-precision date that renders with a month now fails the build too. Recorded as an update to erratum E-2026-17.

### The machine-readable clock carries ISO 10218 and R15.06 at the month it verifies · 28 September 2026

- /regulatory-clock.json now gives ISO 10218’s date as 2025-02 and ANSI/A3 R15.06-2025’s as 2025-08, each with datePrecision month; every other dated entry says datePrecision day. The pages already printed the months, but the data behind them still carried a day its own notes doubted (5 February, 21 August), and that day is what a machine reading the JSON received. The doubted day is kept only as unverifiedDay, and the build fails any page that prints it. Recorded as erratum E-2026-17, which also corrects the product’s signed clock.
- ISO 10218’s entry was re-read on iso.org on 28 September 2026: both parts show publication date 2025-02, with no day.

### Three stale surfaces, and a check that never looked at its page · 26 September 2026

- The ATLAS and RoboJailBench crosswalk downloads now match the product at v0.44.0. Both had kept an older EAI03 coverage note, from before `weight_integrity` was added to it.
- /studies carries the π0 / π0.5 study as the study document states it: pre-registered, with a preliminary leg run on 18 September 2026 and no headline claimed. For eight days after that leg ran the card read “protocol only, no results claimed”.
- On /readiness the ISO 10218 card no longer says “in force” beside a publication date. A standard’s publication is not a deadline.
- The test report’s disclaimer and its authorising blanks are now checked on the built /sample-evidence-pack page. The check that claimed to do it ran before the build, so on a clean checkout it never looked.

### Two standards’ publication dates show the month their sources show, not a day nobody verified · 26 September 2026

- ISO 10218-1/-2:2025 now reads “published February 2025”, and ANSI/A3 R15.06-2025 “August 2025”, wherever either date appears: /compliance and its Markdown twin, the ISO 10218 page, /regulatory-clock (including its screen-reader link labels and machine-readable dates), /for/certifiers-insurers, /readiness, llms-full.txt and the sample evidence pack. Each had printed a day that the regulatory clock’s own notes said was not a verified value — one of them under a heading reading “Verified dates”.
- The date is now read from the clock at the precision the clock verified, and the build fails if any page prints a day the clock doubts. No date in the clock moved.

### Re-pinned to v0.44.0: the published measurement is now three releases behind, and the site says so · 20 September 2026

- Every pinned figure now reads provael 0.44.0’s evidence manifest (pinned at b00fbe6). No number moved: the run is still the 14 September SmolVLA × LIBERO-Object suite measured with 0.41.2. What moved is the distance — 0.44.0 is the third release since, past the product’s own two-release window — so the freshness banner, the machine-readable meta line and llms.txt now read STALE, from the product’s publish-freshness artifact, which is exactly the state that artifact exists to disclose. The product paused the scheduled re-measurement campaign until its keep-out predicate is calibrated; a shard measured under the box about to be replaced would not be a refresh.
- The compliance mirror carries the product’s new per-row tier — operative (Machinery Regulation, ISO 10218:2025, ISO 13849, IEC 61508, IEC 62443) or reference — and the delivery pack’s test report is re-copied with the corrected machinery route in its compliance rows. The product renamed `certify` to `dossier` (the old name stays as a deprecated alias); the compliance pages and twins follow.
- Each compliance page’s “From the regulatory clock” block is anchored on the clock’s own verification date rather than the evidence manifest’s: the two coincided on 19 September and diverged at this re-pin, and the date check refused a compliance page stating a day the regulatory record does not carry.
- The route inventory reads docs.provael.com’s /latest/ sitemap. The docs site is versioned; its root URLs became redirect stubs into /latest/ on 20 September after months of serving a page set frozen in August, so the root sitemap no longer describes the current docs.

### Re-pinned to v0.43.0: the evidence manifest is v2, the release decision is a named protocol’s, and the floor families are counts · 19 September 2026

- Every measured figure now reads provael 0.43.0’s evidence manifest (format v2, pinned at 6ddabfa). The numbers did not move — the run is still the 14 September SmolVLA × LIBERO-Object suite measured with 0.41.2 (42/50, task-clustered [62%, 100%], benign 1/50) — but what travels with them did: the predicate (“default (uncalibrated)”), the interval method (“wilson-score-95 (episode-level)” per attack; the headline’s is task-clustered) and the release decision are fields now, mirrored and checked against the source artifact field by field (check:evidence).
- The release decision is a statement under a NAMED acceptance protocol, never a default. Under the product’s example protocol (smolvla-libero-object-pilot, digest d72175fffd10e353) the pooled adversarial ASR (18.0%) sits inside its 50% bound and the roleplay critical slice (42/50, 84%) exceeds its 20% gate: the decision is FAIL on that slice. /results states it in the headline’s “how to read it” block; without a protocol the product reports “incomplete, not assessed”, and the site would say so.
- The registry block of the source manifest changed convention with v2 (control family excluded, as the site always did), so check:registry now asserts equality between the product’s counts and the site’s instead of a conversion: families, attacks, the real-policy split and the measured adapters (SmolVLA; π0.5 preliminary) all agree across the three artifacts.
- The visual (2/100) and injection (2/50) families were still rendered as “0%”, “honest 0% nulls” or families that “did not transfer” on /sample-evidence-pack, /readiness, the three /for pages, /eai-top-10/eai02 and /llms.txt — a zero typed when the 0.32.0 run really was zero, beside fractions the manifest had already moved. Every such surface now renders the counts from the manifest (floorTrials), says “at the benign floor”, and check:facts requires the family fraction on EAI02 and forbids the retired zero forms. The homepage hero, problem chain and method copy no longer say the policy is “hijacked” or that the attacker “redirects” it: the supported reading is fragility under a long out-of-distribution instruction (E-2026-12).
- site.ts’s RESULT block — the strings seventeen pages render — is derived from the manifest instead of typed beside it, and the headline interval is read from the manifest’s statement rather than held as a literal.
- The homepage gate example printed “✗ adversarial ASR 18.0% exceeds the 20.0% threshold” — arithmetic that was false — and a “CI gate: FAIL · threshold ASR ≤ 20%” row the artifact never carried. It now shows two lines read from the artifact: the pooled adversarial ASR within the illustrative asr-threshold (descriptive, and worded by the comparison), and the release decision under the named protocol (FAIL on the roleplay slice), with one sentence on what a PASS would and would not establish. The downloadable SARIF is the emitter’s own output for the ten-shard run, unredacted, carrying releaseVerdict / acceptanceProtocol / predicate / intervalMethod; check:evidence-pack holds those fields to the manifest, check:facts requires the decision line on /, /results and /sample-evidence-pack and forbids the retired shapes, and check:guards proves each with a fixture.
- The Checkpoint Report offer no longer promises “every attack family” on any checkpoint “in 5 business days” from “send one checkpoint … under NDA”. /checkpoint-report now opens with eligibility: a table of what the fixed price covers today (SmolVLA-family on LIBERO-Object supported; π0.5 preliminary; OpenVLA / openpi / GR00T scaffolding and other simulators quoted separately; physical robots out of scope), the work order in one screen (scope and the applicable attacks under a written protocol, controls and run budget, access defaulting to your environment, outputs and one retest allowance, a five-business-day clock that starts when scope, access and the agreed payment prerequisites are complete and pauses on customer delays, credit and refund per the executed agreement), and the sentence that a null or negative finding under the agreed protocol is a delivery. /pricing and /assessment carry the same scope; the draft terms’ 9.3 states the delivery standard instead of a one-week refund trigger; the homepage form promises a reply “usually within two working days”.
- Paid value is described as operated work — agreed execution on your own checkpoint, stack-specific integration, a controlled evaluation under a written protocol, a findings review and retesting — rather than as a signature: local signing was always in the free tool, and an Ed25519 signature establishes origin and integrity with no automatic insurer or assessor acceptance (/pricing, the homepage open-core block, /faq, /continuous-assurance). The “Most teams start here” ribbon, a popularity claim no customer record supported, is now “Recommended entry engagement” and sits on the Checkpoint Report; the retainer is described as scoped on request, with no demand implied.
- Confidential delivery and research publication are separated. The ordinary route is a confidential assessment under the engagement agreement; the design-partner programme is an opt-in publication partnership with its discount stated, item-by-item written permission (name, logo, testimonial, metrics, case study, leaderboard entry), and both sides committing before work starts to publishing a null or unflattering result. “Client packs are delivered under NDA and are never published” became “Customer assessments are confidential under the engagement agreement. Public research and case studies require a separate written agreement” on /sample-evidence-pack, its twin, /checkpoint-report, /assessment, /case-studies and the tier data; the draft terms’ 9.1 says publication needs that separate agreement.
- /trust no longer says “What crosses the boundary: nothing” as if the CLI were the only path while /checkpoint-report invited weights or an endpoint under NDA. The architecture claim now reads: the free tool transmits nothing; a booked assessment runs in your environment or against an endpoint you host by default; any transfer is a written decision under the DPA. A data-flow table, per path (the free tool · this website’s forms · a managed engagement), states what crosses, where it lands (Cloudflare KV, the AWS us-east-1 lead backend, Resend, or the agreed engagement environment), who can read it (the maintainer), retention and deletion (730 days for form submissions; 30 days after final delivery for transferred material, confirmed in writing) and training use (none). /trust.md, /privacy, the DPA template’s scope note and the draft terms’ 9.2 say the same; the homepage form states that it is for compatibility information only, never weights, tokens, traces or prompts.
- The homepage leads with one outcome. Headline: “Find and reproduce robustness regressions in your VLA checkpoint.” Supporting copy: the tool tests instructions and observations in simulation and produces reproducible evidence for engineering review; run it yourself or request a scoped assessment of a supported checkpoint. Primary action “Check assessment eligibility” (→ /checkpoint-report/#eligibility), developer action “Run the open-source quickstart”, GitHub kept beside them, and the scope line “Simulation evidence for the tested checkpoint and protocol. Physical safety and conformity require separate assessment.” under the buttons. The strip beneath the hero answers who it serves, what is measured (the pinned figures), what is delivered and how to start. The header nav is Product · Evidence · Assessment · Pricing · Docs; Top 10, Leaderboard and Compliance stay one click away in the footer, every URL unchanged. Design, domain and the illustration are as they were — no animation added.
- Support status is derived and rendered per policy. The homepage coverage chips now say measured · published (SmolVLA), preliminary (π0.5: a committed three-seed, two-arm leg that is not the published body), runnable · no committed run (π0, π0-FAST), scaffolding · never run (GR00T, OpenVLA, openpi) and fixture · CPU (stub) — read from the product’s living registry and ledger (the 0.43.0 ledger’s published flag), never typed, with the sentence that installable is not measured. /results carries a “Dates of this result” card that keeps five dates apart: executed (14 September 2026, from the ledger), measured with (provael 0.41.2), assembled at (v0.43.0 · 6ddabfa), source reviewed (19 September 2026), current release — so a release bump cannot relabel the run. /citation says the numbers were measured with 0.41.2 and the manifest is pinned at v0.43.0 (“the assembly, not the measurement”). /defenses and its twin state the product’s two counts: two registered defenses with a committed study, zero with a measured defended arm on a real policy; check:registry holds those to the source. /leaderboard was checked, not changed: it already declines to call one policy’s attack-family rows a ranking and renders the board’s own stale flag.
- /sample-evidence-pack is now a rendering of the product’s committed delivery pack (examples/delivery-pack/smolvla-libero-object-2026-09-14 at v0.43.0), identified as a public-checkpoint reference assessment rather than a customer handoff. Section 6 is that pack’s test report verbatim — its section 12 carries the FAIL decision under the example protocol instead of “not recorded”, and the authorising fields stay blank with a sentence saying why. The page lists the pack file by file (README, decision.json, evidence-manifest.json, scorecard, test report, shards.txt, REPRODUCE.md, retest.md — each verified to open at the tag), the ten shard digests behind the aggregate, the run’s limits (provenance fields unknown and not backfilled; the uncalibrated default box; digest-bound, not signed) and the next engineering step. The raw pack .md carries the same header, links and limits; check:evidence-pack requires the decision line in the test report and the pack link in the .md, and check:guards proves it.
- The inquiry path was tested in a rendered browser rather than inferred from HTML. Every offer CTA still lands on the homepage form with its intent (/?intent=checkpoint-report#request, /?intent=paid-pilot#request, design-partner, fleet-ci): heading, submit label and the pre-filled brief swap, the browser focuses the form section itself (it now carries tabindex=-1, so the fragment navigation’s focusing steps land on it instead of the viewport), a refresh keeps the offer, an empty submit reports the first invalid field, and a failed POST shows “nothing has been sent yet” with every field still enabled. Fields are explicit and minimal — name, work email, company or role (optional), policy/checkpoint family (optional, needed for eligibility, with each option’s support status), simulator / release problem / timing (optional) — with the private-data warning and a privacy link beside the button. /thanks says no payment has been taken and that a paid engagement starts only with a signed work order. One labelled owner test per commercial intent goes through the live form after this deploys; receipt in the monitored inbox closes the item.
- The contracting position is stated as it is. “Provael” is a project name; no company has been incorporated; the counterparty for any paid work is Sattyam Jain, an individual (India), operating as Provael. /terms says so under its lead, separates the website terms (in effect), the Apache-2.0 licence and the draft service terms for paid engagements (section 9, not in effect and not yet reviewed by counsel — the executed engagement agreement governs and the draft yields to it), and moves its effective date to 19 September 2026 because the text changed. The DPA and rules-of-engagement templates, /trust (GRC-04 and the entity row), /privacy (the controller) and /contact name the same individual; /about’s rail says “Entity: none yet”. No registration, insurance or certification is implied anywhere until it exists.
- /readiness is a self-reported checklist of evaluation practice, not a readiness score. The 76-point number is gone from the result: the same answers now lead with a practice statement (“No adversarial evidence yet” … “Substantiated practice”), the next useful conversation and the eight Top-10 gaps; the 0–100 heuristic is kept only inside a folded “for your own reference” note, labelled unvalidated, with its weights spelled out. The clocks block is framed as inputs for counsel to confirm, filtered by market answers and never a determination of applicability; every dated clock is asserted at build against regulatory-clock.json. The send form states exactly what is transmitted and that nothing leaves the browser until the button is pressed (checked in a rendered browser: no request before send). The W02 measurement wording applies here too.
- The regulatory clock was re-verified entry by entry against primary sources on 19 September 2026 — EUR-Lex OJ texts (Regulations 2026/1744, 2023/1230 with its corrigendum, 2024/2847, 2019/2144 consolidated, Directive 2024/2853), iso.org, iec.ch, nist.gov, law.go.kr and the ANSI webstore — and each entry’s own verification note records what was read; the top-level date is derived from the oldest entry, so the STALE CLOCK banner clears because the entries moved, not because a timestamp did (the ANSI day of month remains marked unverified). Every dated entry now renders one of three mutually exclusive phrases decided against the build’s as-of day — published · applies from (upcoming) · applies since (applied) — with entry into force kept apart in the status note, and the CRA’s Article 14 reporting date reads “applied since 11 September 2026”; the transition is tested at each entry’s own dates at build. The CRA note no longer says Provael is outside the Act’s scope: it claims neither scope nor exemption, records that the steward route (a legal person) is unavailable to the maintainer and that the commercial-activity question is one for counsel, matching the product’s SECURITY.md; the Product Liability entry says the same. Each compliance detail page now carries a “From the regulatory clock” block per instrument — kind of instrument (binding law · voluntary standard · draft · initiative), whether it applies as of the build day, the responsible actor, what Provael measures toward it, what that does not establish, source and review date — and states that the Top 10 is a community-draft taxonomy, not an instrument. The certifier/insurer and automotive pages say the rate is one input to a judgement, not an incident likelihood, a premium or a type-approval outcome.
- Adoption and verification claims are auditable. /adopters no longer grades the installs-per-star ratio against a “healthy 10:1 or 50:1” benchmark nobody published; the counters were refreshed from pypistats and the GitHub API (measured 19 September 2026) and keep their as-of date. /verification keeps signature verification, regeneration from stored traces and fresh execution as three separate levels, links the product’s reproduction request and its reproduction-result issue form, and states the empty register as requested 0 · attempted 0 · completed 0 · agrees 0 · disagrees 0. /about and the JSON-LD bio state the 2025 acquisition of pyAGI without naming the acquirers until their written permission to appear on a commercial site is recorded; the founder’s own dates are unchanged and remain his to confirm.
- Research and comparisons stay inside their evidence. /compare links each tool to its own documentation, dates when the docs were read, replaces “the part text-only tools structurally can’t reach” with the checkable shipped difference (none of the three ships a simulator harness, a policy adapter or an action-level predicate today; that says nothing about what they could be extended to) and declines a numerical ranking. /eai-top-10 calls its 8-of-10 “mapping coverage — not a share of robot risk” and its EAI02 badge “measured · at the benign floor”. /incidents says a mapping is a categorisation, not a detection, and marks every row that is wholly or partly device / network / firmware as outside what a policy harness can reach. /findings carries an evidence-class chip on every card and a second legend — real policy · committed run, preliminary run (π0.5), fixture demonstration (the stub studies), pre-registered study (not run), completed hardware result (none) — with the class carried into the RSS feed; the crosswalk JSONs already state “proposed, not endorsed” and the one that does not is described as such rather than given a status.
- Routes and machine files were verified rather than rewritten: the sitemap is still 87 URLs (81 HTML + 6), every route has a Markdown twin, /llms.txt regenerates from the build, sitemap-index.xml, sitemap-0.xml and all six feeds parse as XML, the 16 docs.provael.com links and every download on /sample-evidence-pack answer 200, provael.com and http:// redirect to https://www.provael.com with 301, /robots.txt is served as committed and the live lead endpoint captures. Nothing needed changing except the routes inventory’s timestamp. One item is outside the repository: Cloudflare’s Web Analytics beacon is injected at the edge and blocked by the site’s own CSP on every page, which is a dashboard toggle.
- The rendered reading and form experience was checked in a browser at 1280 px and 390 px on the homepage, pricing, the Checkpoint Report page, the sample pack, contact, results, the regulatory clock, readiness and trust: no horizontal page overflow at either width, every table inside its own scroll region, every form control labelled, a visible 2 px focus ring on every element in the tab order, the illustration labelled as not experiment footage. Three demonstrated defects were fixed: the scroll cue under the hero is hidden when the reader prefers reduced motion (nothing animates then; the measured result stays in static text below), a link on the navy result panel measured 2.15:1 and now uses the panel’s own light teal with an underline, and the trust page’s download links and the homepage SARIF link were distinguishable by colour alone and are now underlined; the shared sheet rail is a labelled region rather than a nested complementary landmark. One load measurement: 38 KB document, no asset over 51 KB on load, zero layout shift.
- Buying decisions are tracked in a private record, not on the site. The lead form already persists the offer chosen (intent) and the page it was sent from (source), and the site adds no analytics, tracking SDK or dashboard; the log itself lives outside every repository, keyed on those two fields, with stage, next action, amount proposed, outcome, reason lost, founder hours and repeat demand per inquiry. CLI telemetry is unchanged: there is none.

### The second architecture reaches /findings and /leaderboard, and the site now fails its own build when it lags the repository · 19 September 2026

- The product committed a second architecture’s real-model arm on 18 September at 14:53 UTC (results/pi05_libero_object_2026-09-18: π0.5 on the same ten libero_object tasks, three seeds, roleplay 1/30 against none 0/30, McNemar p = 1.0, task success 27/30 unattacked and 8/30 under the frame). This site had built 80 minutes earlier and went on saying “1 measured on SmolVLA” for a day. /findings now carries the leg as a card under AT THE FLOOR — the legend’s own words for a real-policy measurement whose arms fired no more often than the untouched instruction — with its status derived from the paired test, not chosen; the hero’s policy counter reads the count and the names from the product registry at the pinned release (realPoliciesTested / realPolicyNames, published since provael 0.42.1) and renders nothing rather than a literal when a pin lacks them. Every figure on the card comes from src/data/pi05-leg.json, which check:pi05 recomputes from the run’s ten shards and its aggregate.json whenever the product checkout is beside this one (CI clones it) and reports itself skipped, never passed, without it.
- /leaderboard gains one line beside Scope, “Outside this board”: the board still aggregates the 14 September SmolVLA suite and nothing else, and its rows must all share one measurement, so the π0.5 leg is not eligible to enter it and is named as what exists beside it, with a link to the card. The line is derived (the registry’s measured policies minus the board’s own) and disappears when the board covers them all.
- What deliberately did not move: the headline (42/50, task-clustered [62%, 100%]), the product’s publish-freshness (measured with 0.41.2, one release behind, inside the window) and the signed board. The π0.5 run is a different lineage — another policy — and the pre-registered study’s preliminary leg (three seeds, two arms of eight), so it moves no published number and claims no transfer.
- The site now fails its own build when the product’s living watch/registry.json names a measured real policy that no finding card carries (src/lib/measuredPolicies.ts) — a warning inside 48 hours of the run’s own end time, a failed build after — and check:findings:policies holds the rendered /findings page to the same list after the build. The failure names the policy and the committed results directory, not a verdict word. No second staleness sentence: the banner still carries exactly one, and this check renders nothing. The evidence pin and repo-facts moved to v0.42.1 (the same measurement; only the pinned commit changes).

### Re-pinned to v0.42.0: the headline is the 14 September re-measurement, and its controls change what it means · 18 September 2026

- Every measured figure on the site now reads the ten-task SmolVLA × LIBERO-Object suite re-run on provael 0.41.2 on 14 September 2026 (results/smolvla_libero_object_suite_2026-09-14, pinned at the v0.42.0 release commit): roleplay 42 of 50, task-clustered 95% interval [62%, 100%], benign control 1 of 50, clean-task success 48 of 50. The 9 August run it reproduces (44 of 50 on 0.32.0) stays committed in the product repository and is named wherever the site quotes history. The staleness banner that stood since 9 September is gone: the published measurement is one release behind the current one, inside the window.
- What the number means moved with it (erratum E-2026-12). The control run of the same day shows the roleplay frame with no target named leaving the envelope on 27 of 30 matched cells, as often as the attack, and the same words in scrambled order on 18 of 30, while two meaning-preserving rewordings stayed at 0 of 30. Every surface that read the result as an attacker steering the arm — the homepage hero and method card, /results, the sample pack, the compliance crosswalk sentence, the findings cards, the Markdown twins — now reads it as the policy’s fragility under a long out-of-distribution instruction. The manifest carries each arm’s paired McNemar and Holm p-values from the run’s own aggregate.
- Visual and injection are no longer called “0% nulls”. On the re-run each fired once or twice, exactly as often as the untouched instruction did, on the same task (McNemar p = 1.0), so they are published as arms that did not separate from the benign floor — derived from the p-values the manifest carries (FLOOR_FAMILIES), never typed — on /results, /case-studies, /findings (a new “AT THE FLOOR” status), /leaderboard (from each row’s own benign interval), the homepage and the twins.
- Guards moved with the pin rather than being re-typed: check:facts derives the headline interval and the forbidden pooled interval from the manifest and forbids the 0.32.0 phrasings; check:hardware reads the accelerator a workstation run’s README names, not only a Modal gpu= request; check:inventory accepts the pinned run’s own family count as a denominator; check:guards holds 44 fixtures. The sample SARIF and the clause-7.8 test report are the product’s own exporters run over the ten 0.41.2 shards. /eai-top-10/ no longer says a report identifies the checkpoint and not the deployed policy: since 0.42.0 it records both (schema 6). The registry mirror reads v0.42.0: 44 attacks in 19 families, 39 adversarial in 17, 8 families exercised against a real policy; the VLA-Arena scaffolding suite joins the coverage card.

### The machine-readable surfaces say what the pages say: the measurement meta, /llms.txt, /404, the pricing JSON-LD and the attack counts · 18 September 2026

- Every page’s <head> carried a measurement block that contradicted itself: measurement-age-days="0" beside measurement-stale="true", and a status string blaming the 7-day window when only the release window had fired — the sentence PR #110 corrected in the banner on 6 September, still alive in the meta because the meta was built from a second string. The banner and the meta now render ONE sentence (src/lib/freshness.ts), the day-count meta is gone (the date is the fact; a consumer subtracts), and check:freshness fails if the meta and the banner ever differ or the flag stops following the facts. Erratum E-2026-13.
- /llms.txt was seven days behind /adopters/: 6,630 lifetime installs and 6 stars against the page’s 6,926 and 7, with a measured-at of 2026-09-14 against the meta’s date. The figures were typed into the file’s hand-written head. That head is now a template filled from the same data the pages render (distribution.json, repo-facts.json, the pinned evidence manifest), distribution.yml regenerates the file when the counters move, and check:llms:facts fails the build if any adoption figure or measured-at disagrees across the file, the page and the meta. The measurement date and the adoption date are labelled as the different things they are.
- /404 answered HTTP 200 with the not-found body — a soft 404 a crawler records as a real page. A Pages Function on that one route now re-issues the same body with a 404 status; /design-partner still answers 404 and /design-partners still 308-redirects, and check:live asserts all three at the origin after every deploy.
- /pricing’s schema.org Offer for the standard Red-Team Assessment carried the design-partner rate as its minPrice, under the standard product’s name, so a machine reader took the discount as the base price. The Offer now carries the figure its card headlines, the design-partner rate is its own Offer with LimitedAvailability and the spot count the page states, and check:jsonld-prices asserts every price in the JSON-LD appears on the page under the same name and that the assessment Offer equals pricing.ts.
- The homepage twin and /llms.txt paired an inclusive attack count with an exclusive family count: the total attack count (42, labelled “incl. the benign baseline”) sat in the parenthesis after the adversarial family count (17). Both now state matched pairs, each convention with itself (adversarial attacks with adversarial families; total attacks with total families, counting the baseline and the controls), and check:counts fails on any attack count stated without the family count of the same convention. The counts are the pinned release’s (v0.41.2: 42 attacks); the next re-pin, at v0.42.0, brings 44.

### The automotive segment page finally ends somewhere: /compliance/un-r155-iso-21434/ · 18 September 2026

- /for/automotive-logistics-deployers/ has argued since August that a learned component in a type-approved vehicle sits inside UN R155 and ISO/SAE 21434, then pointed at a crosswalk that had no page for either. /compliance/un-r155-iso-21434/ is that page: R155 paras. 7.3.3, 7.3.6 and 7.2.2.2(e) and ISO/SAE 21434 Clause 15, with what Provael supplies to each (measurements a CSMS can hold as one input among many), what it does not (no type approval, no CSMS certificate, no conformity position, simulation only, zero real-hardware results, an uncalibrated predicate) and what is deliberately not mapped (Clause 11 vehicle-level validation; R155 paras. 7.3.4, 7.3.7, 7.3.8).
- Its dates were read on 18 September 2026 from the Official Journal text of R155, the consolidated Regulation (EU) 2019/2144 and the ISO catalogue, and the page carries that date rather than inheriting the hub stamp. The Timing cell on /compliance/ states the cycle, since the mandate has already run: the CSMS certificate is valid for a maximum of three years and is re-audited. The product catalogue the site is checked against gained the matching rows first (provael/provael#251).
- One correction on the segment page while re-reading: its R155 row said all new vehicles produced since 1 July 2024. The consolidated GSR row says registration of new vehicles is prohibited without it from 7 July 2024, which is what it says now.

### The test report an assessor reads is in the funnel · 18 September 2026

- The clause-7.8 test report shipped in the product on 14 September 2026 (provael #237) and appeared on this site only in the changelog. It is now section 6 of /sample-evidence-pack, listed and rendered first because it is the document a sign-off reader opens: the emitter’s own output over the reference run, committed as a file and rendered verbatim, with its first line intact (Provael is not an accredited laboratory, this is not an ISO/IEC 17025 report, no statement of conformity is made) and the date-of-issue and authorising-person blanks left blank. The build now checks that file’s first line and its figures against the pinned manifest.
- /pricing lists the test report first in the compliance and attestation add-on, with the reason in one sentence: it is the shape a machinery technical file is assessed in; the machine formats stay, for the engineer. /assessment names it as a deliverable, /for/certifiers-insurers names it as the deliverable that reader actually reads, and /compliance and /verification link to it in one hop.

### E-2026-11 on /errata, and the regulatory clock says how wide the machinery-standards gap is · 14 September 2026

- /errata carries E-2026-11: the product’s gradient_patch attack, as released from 0.39.1 to 0.41.2, started its search where its objective’s gradient is exactly zero and so could not move a frame; the PushT figures it shipped with are an external script’s, not the module’s. No page on this site quoted them and no published rate moves.
- The Machinery Regulation entry of the regulatory clock now records what a 13 September check found: no harmonised standard yet cited under the Regulation, the first list expected in Q4 2026, prEN 50742 (formal vote September, publication planned November 2026) as the one AI-adjacent standard on course for 20 January 2027, and ISO/IEC TS 22440 and IEC 61508 Ed. 3 both landing in 2027. The CRA entry separates the manufacturer’s binding duty from Provael’s own voluntary alignment.

### An honesty pass: the retired single-task result is gone from every surface, and a note corrected itself · 13 September 2026

- The single-task result superseded on 9 August (10 of 10, 100%, a 0 of 10 benign control) still rendered on the three /for pages, the /findings null card and its feeds, the homepage method selector and the EAI01 entry. Every one of those now derives from the pinned manifest, and check:facts forbids the retired phrasings, which is how they should have been caught the first time.
- The methodology note for the 88% figure had the reword arms backwards: it called the paraphrase attack (3 of 50) the control and said the 1 of 50 control was a draft error. The harmless-variation control (benign_reword) is 1 of 50 and always was; the note now says so, keeps a dated correction in its body, and the reversal is recorded as E-2026-10.
- Version story on /citation: the version to cite now comes from the product’s CITATION.cff (0.41.2), not from the evidence pin (v0.39.1); the numbers were measured with 0.32.0. Three quantities, labelled as three. /results no longer attributes the ten-task run to “provael 0.1.0”.
- Legal pages now describe what actually runs: the contact form’s path (Cloudflare KV, then the AWS lead store in us-east-1, then email), its 730-day retention, and one sub-processor list shared with /trust — which previously said the AWS store was in an EU region; the deployed Terraform state says us-east-1. The Cal.com embed that never shipped is no longer described as running.
- Smaller: the CRA page and SECURITY.md agreed with themselves about when open-source-steward duties start (11 December 2027); the errata ID collision on E-2026-05 is recorded on both sides with a new E-2026-09 in the product ledger; the leaderboard description no longer says “one measured row”; /submit counts adapters the way the homepage does; drawing numbers name one sheet each; the star count is derived; the sample pack’s AI Act date is the adopted one.

### The CRA clock page says the duty is live, and an upstream disclosure gets a home · 12 September 2026

- /compliance/cra-incident-reporting laid out the mechanics of the Article 14 duty in detail and never said the duty was in force. It does now, in three short paragraphs: the duty is live, it does not bind an open-source steward until 11 December 2027 under Article 24(3), and it does not make anyone non-compliant today. The 24h / 72h / 14-day breakdown below it is unchanged, because restating four deadlines at the top of the page that already carries them is how two copies start to drift.
- ActSafeGuard (arXiv:2609.11697) joins /defenses. It trains hard action feasibility into flow-matching policies rather than filtering unsafe actions at inference, and reports a 100% step safety rate on pi-0.5 and Fast-WAM. The entry states what that headline does not cover: it is a rate against the paper’s own constraint set, it says nothing about failures whose harm is not a per-step action constraint, and it reports no false-positive rate at all.
- /security now records the openpi disclosure. We reported a policy server bound to all interfaces returning tracebacks to the client on 5 September; on 11 September a contributor we have never met opened a fix, four files with tests. The page says plainly that the fix is their work and not ours, and records the one point we argued against them on in public.
- That disclosure is deliberately NOT on /findings. That page is the research stream, where every entry derives its numbers from the pinned evidence manifest and carries a measurement-maturity status. This has no measurement in it and no Provael attack family found it, so giving it one of those statuses would have diluted what they mean.

### The regulatory clock stops counting down to a date that has already passed · 12 September 2026

- On 11 September 2026 the hero of /regulatory-clock read “Next dated obligation · EU Cyber Resilience Act” above a countdown frozen at 0 days : 00 hrs : 00 min. The component pinned the digits at zero when its target passed and stopped, and it would not have recovered on any later day. It now swaps the digits for “In force since 11 September 2026” and the label above changes to “Last dated obligation”, so the two agree.
- The selection was never the bug, which is worth saying because it is the part that looks wrong. The page picks the soonest obligation after the date its evidence was last pinned, and on that pin it picked correctly. What drifted was the pin: it sat at 3 September while the obligation fell on the 11th, so a date that had passed for every visitor was still one the build could honestly call upcoming. The build carries no wall clock by design and that design is kept.
- Three checks now sit with the clock freshness gate, which is already the one place on this site allowed a real clock. One fails when the clock has no obligation left after the pin. One fails when any countdown anywhere targets a date at or before the pin, which is what will catch the two counters on the homepage and /machinery-regulation/ when 20 January 2027 arrives. One reports an obligation that has passed in the real world while the pin still calls it upcoming, and turns that into a failed build after 30 days. All three were made to fail on purpose before being trusted.
- The screen-reader path was the part most likely to be got wrong. The digits stay hidden from assistive technology in both states, as they were, because on the homepage this component sits inside a link and exposing it would rewrite that link’s name after the page loads. The status reaches a screen reader through the label instead, which is where it belongs and where it is said once.
- Separately, /compliance/cra-incident-reporting/ said ENISA’s single reporting platform was “not live yet”. ENISA deployed its initial operating capability on 11 September 2026, the morning the duty started, so the sentence was false by the time anyone read it under the new rules. Rewritten against ENISA’s own announcement, keeping their “initial operating capability” wording rather than rounding it up to “live”, and the CRA row on the clock now reads as in force with full application still ahead on 11 December 2027.

### What the CRA reporting duty changed, and a Top 10 candidate that Provael itself fails · 12 September 2026

- /compliance/eu-cyber-resilience-act now opens with what changed on 11 September 2026 rather than with a description of the instrument. Manufacturers of products with digital elements must report actively exploited vulnerabilities and severe incidents: an early warning within 24 hours, a full notification within 72, and a final report within 14 days of a corrective measure being available, or within a month of the 72-hour notification for a severe incident. Every date is quoted from the Commission’s own reporting page.
- The section is written in the past tense on purpose. One announcing an imminent deadline ages into telling a reader something is about to happen after it already has, which is the defect this cluster keeps having: the draft this was written from still said the duty bites tomorrow.
- It also says who is not caught. The duty falls on manufacturers placing products on the EU market; open-source software stewards are subject to their own obligation under Article 24(3) only from 11 December 2027, per Article 71(2). If you ship an open-source policy and nothing else, that is fifteen months away. Getting that wrong in the alarming direction would have been the easy mistake.
- /eai-top-10 carries a candidate for the next revision: under-specified policy identity. A policy identified by weights and prompt can still execute differently once action unnormalisation and controller conventions are applied, so a passing result on one deployment does not transfer to another deployment of the same policy. The framing is Jianwei Tai’s (arXiv:2606.03724), not ours, and the entry says so.
- Provael fails its own candidate check, and the entry leads with that. A Provael report identifies a policy by weights hash and config hash and records neither the resolved unnormaliser nor the controller convention, so two of our reports on the same policy can describe two different executable policies. The schema fix is tracked as provael#227. The candidate sits outside the numbered list, so the ten entries do not renumber and the published coverage artifact is unchanged.

### The Korea AI Framework Act gets the page the clock had been promising it · 12 September 2026 [#116 ↗](https://github.com/provael/website/pull/116)

- /regulatory-clock has carried a KR row since August saying what Provael supports for a high-impact system under Korea’s AI Framework Act. There was nowhere to click. /compliance/korea-ai-framework-act/ is that page: Art. 34(1) subparagraphs 1, 4 and 5 — risk-management plan, human management and supervision, and the safety-and-reliability document set — with the Act’s own wording, plus Art. 4(1) extraterritorial reach, Art. 33 advance self-review and Art. 36 domestic representative. It is the first non-EU national statute in the crosswalk; the other thirteen pages are all EU, ISO, IEC or NIST.
- Writing it meant reading the statute, and two things the clock said turned out not to be in it. The row claimed the high-impact duties include “interruption and rollback”: they do not appear in Art. 34 at all, and the Act’s only shutdown reference is a government R&D support provision pointing at a different statute. The row also said enforcement of the penalty provisions “is deferred by one year”, which reads as statutory; the Addenda contain no such provision, and the one-year grace before administrative fines is an enforcement-policy position, not law. Both are corrected on the clock as well as absent from the page.
- The scope sentence changed too, and it narrows who has to care. Art. 2(4) makes a system high-impact only in an enumerated area — energy, drinking water, health care, medical devices, nuclear, biometrics for criminal investigation, employment and loan assessment, transport, public decisions, school assessment. A VLA policy is inside this Act when it is deployed into one of those, not because it drives a robot. A general warehouse or factory arm is not enumerated.
- What the page does not claim: no Korean conformity or registration position, and not legal advice. There is no conformity assessment under this Act for a red-team result to feed. Four figures that circulate widely — the 10^26 FLOP compute threshold, the domestic-representative revenue and user thresholds, a document retention period, and the fines grace period — are set by the Enforcement Decree, whose text could not be retrieved, so the page carries them as reported and says so rather than presenting them as read.
- The clock row now links to the page, the page is inside the build-time date check against the clock instead of being skipped, and the primary source is a stable per-statute law.go.kr URL rather than the English portal the row used to point at.

### The staleness banner reads the product’s drift verdict instead of computing its own · 9 September 2026 [#114 ↗](https://github.com/provael/website/pull/114)

- The banner had its own version arithmetic: a `minorDelta` comparing the pinned board’s tool version against the current release. `provael.watch.releases_behind` already owned that rule, so there were two implementations of one staleness question in two repositories — and two implementations of a staleness rule drift in the reassuring direction by default, because a window that quietly widens looks exactly like a project keeping up. provael 0.41.2 publishes the answer as `watch/publish-freshness.json`; the banner reads it. Nothing a visitor sees changed today, and that is the point: the same sentence now updates on the product’s next release with no edit on this side.
- One thing deliberately did NOT move to the artifact. The version in "measured with v0.32.0" still comes from this site’s pinned evidence board, because that is the run whose numbers the page actually renders; the artifact’s `measuredWith` is the product’s own view, the version behind the largest campaign in its ledger. They agree today, and the build now fails if they ever stop agreeing — a mismatch would mean the banner was stating the drift of one run beside the numbers of another.
- A missing or malformed artifact fails the build rather than defaulting. That is not caution for its own sake: "not stale" is the one direction a fetch failure could push a trust page, and it is the direction that looks fine. Two build checks hold it — one that the numbers are read rather than recomputed, one that a malformed field cannot fall back to a reassuring value — and both are mutation-tested against the exact defect they name.

### The staleness banner names the window that actually fired, and stops clipping its own link · 6 September 2026 [#110 ↗](https://github.com/provael/website/pull/110)

- The banner fires when either of two windows is crossed — a 7-day measurement age or a 2-release drift — and its sentence always blamed the age. A measurement landed on 6 September, so the age went to zero while the release count stayed at seven, and the banner read "0 days old … past this project’s own 7-day window". Zero days is not past a seven-day window. It now names whichever window fired, and says so in the window’s own units.
- Worse than the wrong label: the sentence joined two different runs. The age came from the newest measurement, live; the version came from the pinned published one. Those were the same run until the scheduled GPU lane started committing, and from that day "newest real-model measurement: 0 days old, measured with v0.32.0" described one thing that did not exist. The two facts are separate clauses now, each naming its own subject.
- The banner was also clipping itself. `body` sets `overflow-x: hidden`, and the copy carried `white-space: nowrap` above 760px — so at a 760px viewport the sentence rendered 1026px wide, the document overflowed by 294px, and there was no scrollbar to reach it. What sat past the edge was the end of the sentence and the "Why, and what unblocks it" link: the only control in the banner, unreachable, on the disclosure a reader is least able to skip. Measured on the built page rather than inferred. WCAG 2.2 SC 1.4.10 Reflow. The nowrap is gone; the row still renders on one line wherever it fits.

### The release number stopped being a copy, the registry stopped being rounded down, and every measurement got a date · 6 September 2026 [#109 ↗](https://github.com/provael/website/pull/109)

- Fourteen pages rendered v0.39.3 while the tag, the GitHub release and PyPI all said 0.39.4. /security was telling a vulnerability reporter to reproduce on a superseded release before filing, and /changelog printed the superseded release as the current one in the same sentence that named the newer one as its newest entry — a sentence contradicting itself, published, green. (Not quoted here: this page is checked for exactly that shape of sentence, and the guard cannot tell a quotation from a claim.) The version had been read from a mirror refreshed by running a script on the release cadence, which is to say by someone remembering. It is now read from the product repository at build time and proven to be tagged before it renders, and /changelog fails the build if its two sources disagree.
- The homepage said 8 policies and 5 suites. The registry says 8 and 6, and the honest numbers are 5 runnable of 8 and 5 runnable of 6: three policy adapters and one suite are registered and have never been run. The suite figure had been right about the runnable count by accident while claiming to be the total. Both cards now say "5 runnable of N registered", the scaffolded entries carry the word scaffolding rather than a shade of grey, and the build checks the chips against the declared scaffolding NAMES rather than only the counts.
- /results now dates every committed measurement, not just the newest one. Twenty-six rows, each with the tool version that produced it, whether its date was observed or reconstructed, and whether it counts as a measurement at all — five came from a fixture backend and measure no policy. The banner at the top of the site answers "when was anything last measured"; this answers "how old is the specific number I am reading".
- Eleven links inside tables on /compare/published-attack-baselines, /defenses and /sample-evidence-pack rendered identically to the text beside them: no colour, no underline, findable only by hovering. WCAG 2.2 SC 1.4.1, Level A. One had been reported; a new check that reads the CSS each page actually ships found the other ten.
- Four surfaces said ISO 10218:2025 hands its detailed cybersecurity requirements to IEC 62443. It does not — Clause 2 of ISO 10218-1:2025 does not normatively reference that series, which appears only in an informative bibliography, and the 2025 Foreword says the revision ADDS cyber requirements. An assessor was being sent to a paywalled series for a requirement in the document they already hold. Recorded as erratum E-2026-07, and the four superseded phrasings are now blocked by the build.
- A source link under the CRA Article 14 sub-deadlines had been moved to the Commission’s summary of the Regulation, which is the document those four rows were deliberately moved OFF five days earlier because its phrasing does not carry the distinction they encode. The deadlines themselves were correct throughout; the citation under them was not. Erratum E-2026-06, and the link label now travels beside the URL so the two cannot be edited apart.
- The regulatory clock was 42 days past its last verification against a 30-day window, and it is now 18. Every instrument that moved was re-read against its own record — including iso.org, iec.ch and the ANSI webstore, which block automated fetching and were opened in a browser instead. That turned up a dead citation and a wrong designation: the ANSI entry pointed at a URL that 404s, and named a document ANSI marks Historical. The current designation includes a Part 3 that was developed in the United States and is not an adoption of ISO 10218 at all. Two entries were deliberately NOT re-dated, because the readings stopped before the articles carrying the dates those entries exist for.
- /for/certifiers-insurers argued that a measured attack-success rate is the kind of thing an underwriter can use, and cited nothing. It now carries what the market has actually done — a standard generative-AI exclusion for US general liability, a carrier writing specifically for autonomous robots, and underwriters’ own account of why physical AI resists pricing — each labelled with how strong the source is and each stating what it does not establish. Nothing there says an insurer requires a red-team result, and nothing rests on a robot having hurt anyone.
- /compare/gray-swan is new, and it is the comparison people make out loud the first time this project is described to them. Gray Swan runs the largest AI red-teaming arena there is; on their own published figures it is bigger than this project by three to five orders of magnitude, and that is in the first paragraph rather than a footnote. Seven axes, four of which Provael loses. The difference that survives the scale gap is what comes out: a break says a failure is possible, a rate says how often.

### A readiness checklist for the CRA clock, one price on the homepage, and a way onto the leaderboard · 4 September 2026 [#105 ↗](https://github.com/provael/website/pull/105)

- /compliance/cra-incident-reporting described the Article 14 clock and stopped there. It now ends with the four things that have to be already true before a 24-hour duty can be met: who is on call and whether they may file without sign-off, what evidence they have to hand, where the SBOM lives and whether it covers what is running rather than what was released, and how "when did we become aware" and "why did we conclude this was or was not reportable" get written down at the time rather than reconstructed. Framed as cheap now and expensive on the day — the obligation has not started and the page does not tell anyone they are late.
- The homepage showed four prices run together on one line and sent every reader who acted on them to the most expensive page on the site. /checkpoint-report was not linked from the homepage at any depth. It now names one product, shows its price, and links to it. The full ladder still lives on /pricing.
- The leaderboard told a stranger how to produce a submittable artifact and not what happens to it. It now names the exact file the PR adds, what gets published (the report, the row, the interval, the benign control beside it), what is never asked for (your weights, your episodes, anything about you beyond a directory name), and the two grounds on which a submission is refused — both failures of method, never of result. And in writing: a result that makes Provael look bad is published unchanged.
- Every count and version on the site was already derived rather than typed, and two checks guarded that. Neither could see a NEW one: typing a wrong family count into a heading on /quickstart left both reporting success, because both are allow-lists over the slots someone remembered to enumerate. check:inventory sweeps rendered page copy instead, and knows the difference between a restatement of the registry and a legitimate denominator — a sentence about how many of the families measured on a real policy came back null is true, and is not a claim about how many are registered.
- /compliance/cra-incident-reporting rendered its own <main> inside the one the layout already emits, so the page carried two main landmarks and two elements with id="main" — invalid, and meaningless to anyone navigating by region. It was the only page in the site doing it, and check:routes now counts them. Separately, the three "evidence, in three links" lists on the /for/ pages marked their links with colour and weight alone (WCAG 2.2 SC 1.4.1); they are underlined now.

### The stale-measurement banner now says what is stale, and measures it in releases · 4 September 2026 [#104 ↗](https://github.com/provael/website/pull/104)

- A red STALE MEASUREMENT banner sat directly above the pricing table. The question that raised was whether that is honest positioning or a conversion hole, and the answer turned out to be neither: the banner was honest about one thing and read as a claim about another. On the results page, "our published number is 25 days old" is exactly right. On the pricing page the reader is asking whether they will get something current, and a red banner above a price answers that question with a no it never meant. An assessment runs against your own policy on the current release.
- So the banner stays, in the same red, on every page including the ones that ask for money. What changed is the sentence. On a commercial page it now says the published benchmark result is the thing that is old, and that your assessment is not. Hiding it where money is discussed was the one option refused outright: a project that sells the willingness to publish its own bad numbers does not get to suppress them at the checkout.
- The second change is the measure. Days were always a proxy. A pinned result does not decay because a week passed, it decays when the code that produced it changes, and counting days is wrong in both directions at once - too aggressive on a run nobody invalidated, too lenient when several releases ship inside the window. The banner now also fires when more than two releases have shipped since the measurement, and it says which version measured it. On the day this shipped that read seven releases against twenty-five days, so the old measure was the less alarming of two true statements, which is the wrong way round for a staleness signal.
- The seven-day window is untouched and still fires on its own. The new condition can only make the banner appear earlier, never later, which is the test the original decision set for any change to this mechanism.

### A methodology note for the headline number, and the last three Top 10 glossary entries · 4 September 2026 [#103 ↗](https://github.com/provael/website/pull/103)

- The 88 percent figure now has a page explaining what it is: the exact setup, the benign control arm that gives it meaning, the paired reword that separates attacker control from mere brittleness, what the attack costs task completion, and the three things that would change the result. Until now that reasoning existed as homepage copy people scroll past, which is not something an academic or a journalist can cite.
- One correction worth recording. The outline this note was written from put the semantics-preserving reword at 1 of 50. The committed artifact says 3 of 50, and 3 of 50 is what the page prints. The note says so in its own body rather than quietly using the right number, because a page whose argument is "publish your control arm" does not get to hide a draft that disagreed with the run. [Reversed on 13 September 2026: the 3 of 50 was the paraphrase attack arm, not the reword control; 1 of 50 was right. See E-2026-10 and the entry for that date.]
- The glossary now has an entry for every one of the ten Embodied AI Security risks. EAI08, EAI09 and EAI10 were missing; the last of those carries no attacks and never will, because there is no policy input that attacks the absence of a process, and the entry says that rather than leaving a gap that reads as an oversight.

### The counts on this site now come from the release they claim to describe · 3 September 2026 [#102 ↗](https://github.com/provael/website/pull/102)

- Every registry count the site renders — attack families, attacks, the real-policy split — was one release behind, and so was the supported version on /security, which told a security reporter to reproduce on v0.38.0 while PyPI served 0.39.1. Both came from the same stale pin, and both are corrected — the counts on /results and the version on /security are now read from the release they claim to describe, so this note does not restate them and cannot go stale the way they did.
- No measured number moved. The attack-success rate, its confidence interval, the benign control and every per-attack row are byte-identical before and after; only the provenance pin and the registry block changed. Filed as E-2026-05 on /errata.
- The part worth reading is why nothing caught it. A build check already compares the site’s counts against the product’s generated registry file and it passed, because the copy it compared against was the same stale mirror the pages were reading. A guard that reads a cached copy of the fact it guards cannot fire. That is the third time this project has found that exact shape, after the measurement-freshness banner and the regulatory-clock anchor.
- Re-pinning also revealed that the regulatory clock has been past its own 30-day re-verification window since 26 August. The banner had been measuring its age against a frozen build anchor rather than against wall time, so the site read "current" while the underlying check already knew otherwise. The clock page now says stale, which is true, and the entries on it still need re-reading against their primary sources.
- The Unitree G1 EDU disclosure of 27 August is now on /incidents — two published CVEs, one reachable from Bluetooth range with no pairing. It is filed under EAI07 and used to state a boundary rather than to claim coverage: Provael attacks a policy through what it is shown and told, and nothing in that disclosure touches a policy. A clean Provael run says nothing about it.
- /verification now asks for the thing it exists to get. It carries a direct link to the raw artifact behind the headline result and a commitment: a result that contradicts ours gets published, with attribution, unchanged.

### Share cards render off-site, and the footer now links the company page · 2 September 2026 [#101 ↗](https://github.com/provael/website/pull/101)

- Four pages passed a root-relative path for their share image while four others passed an absolute URL, so the same layout prop meant two different things depending on which page you had copied from. Facebook, LinkedIn and X resolve og:image against nothing: a relative path is dropped and the link shares with no card image at all. /adopters and /verification were the two still doing it.
- Fixed in the layout rather than on the two pages. BaseLayout now resolves any per-page image against the site URL before rendering it, so a page can pass either form and the tag comes out absolute. Patching the two callers would have left the next page that copies one of them broken in the same way.
- A build-time check now reads the generated pages and fails the build if any og:image is not absolute, and refuses to pass if it finds no og:image tags at all — a check that silently matches nothing looks identical to one that found no problems. Swept every page afterwards: 77 of 77 carry an absolute image, none missing.
- Separately, the footer now links the Provael company page on LinkedIn, next to GitHub. The URL was already in the page’s structured data, which is the half a crawler reads; nothing on the rendered page linked to it, which is the half a person uses. Both now read the same constant.

### Correction: the CRA severe-incident final report starts from your own filing, not from the fix · 1 September 2026

- The Article 14 reporting clock on /regulatory-clock carried both final-report deadlines as one row: "Final report, due once a corrective or mitigating measure is available - 14 days for an actively exploited vulnerability, one month for a severe incident." That start point is right for the vulnerability branch and wrong for the incident branch.
- Article 14(2)(c) and Article 14(4)(c) measure from different events. The 14-day vulnerability report runs from when a corrective or mitigating measure is available. The one-month incident report runs from submission of the 72-hour notification under Article 14(4)(b) — it does not wait for a fix at all. Collapsing them into one sentence applied the first anchor to the second, which points a reader at a later start than the regulation allows. A runbook built on it could wait for a fix before starting a count that had already been running.
- Cause worth recording, because it is the reusable part: the sub-deadlines were transcribed from the Commission CRA summary rather than from the OJ text, and the summary’s phrasing does not carry the distinction. The clock entry now cites Regulation (EU) 2024/2847 on EUR-Lex as its sub-deadline source, carries the two branches as separate rows with their own anchors, and records Article 69(3) — the derogation that puts the whole installed base in scope for reporting while leaving it out of scope for the product requirements.
- The 24-hour, 72-hour and 14-day figures were correct throughout, and no measured result, signed artifact or attestation is affected. Filed as E-2026-04 on /errata.
- While checking that page: /errata was missing E-2026-03, the zero-width confidence interval published in two of the product repository’s READMEs and corrected there on 30 August. This site was never wrong about it — provael.com carried the correct non-zero upper bound the whole time — but an errata page that is missing a correction is the one page on this site that cannot afford to be behind. It is mirrored now, and the mirror date moved with it.

### The CRA Article 14 clock now has a page, ten days before it starts · 1 September 2026

- Reporting obligations under Regulation (EU) 2024/2847 apply from 11 September 2026. The site already carried that date — /regulatory-clock has announced it as the next dated obligation since 25 August, and the CRA crosswalk states it — but nothing on the site answered the question a manufacturer actually asks on the day it matters: an exploited vulnerability has just been reported to us, what do we owe and by when.
- /compliance/cra-incident-reporting is that page, written as a clock rather than an explainer: what starts it (an actively exploited vulnerability or a severe incident — not merely discovering a bug), the 24-hour early warning to the coordinating CSIRT and ENISA, the 72-hour notification that has to add what the first one was allowed not to know, the final report at 14 days for an exploited vulnerability or one month for a severe incident, and the clause most often missed — products already on the EU market are in scope.
- It exists on this site rather than a law firm’s because of the last section. A firmware CVE closes with a patch and a version number; a learned policy has neither, so "we fixed it" is not evidence that anything improved. The only honest answer to how do you know it is better is a before-and-after measurement on the same suite, the same seeds, and a benign control arm — without the control you cannot tell a policy that got safer from a predicate that got quieter.
- No deadline on the page is typed. All three windows and both dates are read from src/data/regulatory-clock.json, which already carried them with a CELEX-quoted verification note; a second hand-typed copy is exactly how this site once had two pages disagreeing about which obligation came next. The page also fails its own build if the clock ever loses those sub-deadlines, because a page about a 24-hour duty that silently renders zero deadlines is worse than a 404.
- check:dates:compliance would have skipped it. That guard maps a compliance slug to a clock entry id, and this page is not a framework crosswalk, so its slug matched nothing and every date on it would have escaped the check — on the one instrument the guard’s own header was written for. It is now mapped to the CRA entry, so the page states no date the record does not carry: nine compliance pages are checked where eight were.

### The booking control on /assessment had never worked in production · 31 August 2026

- The #book block on /assessment offered a click-to-load Cal.com calendar. It rendered its "Load booking calendar" button only when a calendar link validated, and that link came from PUBLIC_CAL_URL / PUBLIC_CALCOM_LINK — neither of which has ever been set in the deployed build. So the served page carried an empty cal-link attribute, no button, and an inline script whose first act was to look up the button by id. The lookup returned nothing. The bind was optional-chained, so nothing threw and nothing reached the console. Above it, the heading read "Pick a time".
- How long: the block has shipped in this exact shape since the first commit in this repository, on 6 July 2026, and the variable it depends on is unset in production today — eight weeks. Whether it was ever set in between cannot be established from the repository, because that value lives in the deployment environment and never in git; what can be established is the shape of the page and its state now. The email fallback beside it always worked, which is why nothing ever looked broken — every visitor who wanted a call got a mail client instead of a calendar, and the site had no way to tell that had happened.
- It was not one page. SCHEDULING_URL resolves to /assessment specifically so that every "book a scoping call" control on the site — /pricing, /continuous-assurance, the three /for/ pages, /machinery-regulation, and the booking button’s own no-calendar fallback — funnels to the one surface meant to take a booking. All of them terminated there. The Standard Assessment tier carried a source comment stating that the booking calendar already lived on /assessment; it did not.
- The calendar is gone from that page rather than repaired, because repairing it means guaranteeing an environment variable in production and this is the second time a control here has depended on one being set. #book now leads with a link into the request form carrying the paid-pilot intent, which posts to the same endpoint the rest of the site uses and captures the lead rather than handing the visitor a draft to send themselves. The email address stays visible beneath it as the path that needs no script and no endpoint. Roughly sixty lines of third-party bootstrap that could never run no longer ship to anyone.
- The class of failure is now a build check. check:controls asserts, against the built output, that every element a script looks up by id exists on the page shipping that script; that no booking attribute is present but empty; that every page carrying a conversion control still offers a form, a request-form link, an email address or a route to the booking surface; and that a form with no action has a script that references it. It was written against the broken build first and reported both halves of this defect before anything was fixed. Its entry in the guard mutation suite reintroduces the exact fault, so a future change that makes it stop noticing fails CI.

### Two pages disagreed about which obligation comes next · 25 August 2026 [#86 ↗](https://github.com/provael/website/pull/86)

- /compliance/eu-cyber-resilience-act said, in its body text, "Reporting obligations (Art. 14) apply 11 September 2026". /regulatory-clock announced the next dated obligation as the EU Product Liability Directive on 9 December 2026. September is before December, so the two pages disagreed about the one thing the clock exists to state — on a site whose pitch is catching exactly that in other people’s material.
- The date was never missing from the record. regulatory-clock.json carried it as `reportingDate` and the clock ignored it: the timeline filtered on `applicableDate` alone, and the type did not declare the other field. It was not CRA-specific either. The AI Act entry carries `additionalDate` 2 December 2026, for two further prohibited practices, and that was skipped by the same filter — also earlier than the date being announced. Fixing only the CRA would have left the page wrong about the very next obligation after it.
- Every date-shaped field in the clock is now classified exactly once: an obligation a reader must act by, or explicitly not one, with the reason recorded. An unclassified date field fails the build, which is the only property that stops this recurring — the defect was a date nothing read. `supersededStatutoryDate` is the exclusion worth naming: it is the date Regulation (EU) 2026/1744 moved, so announcing it would tell a reader to act by a date that no longer governs.
- The CRA entry now carries the Article 14 reporting clock, which appeared nowhere on this site before and is what a certifier actually asks about: 24 hours for the early warning to the coordinating CSIRT and ENISA, 72 hours for the main notification, and 14 days for the final report once a corrective or mitigating measure is available. Its scope line says plainly what a Provael run does not establish for it — a policy-attack result is not a vulnerability-handling process, and Article 14 is about the process.
- A second check asserts the mirror image: a date stated on a compliance page must exist in the clock JSON. It is scoped to pages whose instrument the clock tracks, and visibly skips the five that carry dates the clock has no opinion on — a standard’s publication date, an inspection-programme announcement. Failing on those would either block a correct build or "fix" itself by writing a vendor press release into a regulatory record.
- The clock is now held to a stated verification window, the way the measurement already is. Past 30 days — the window this site already applies to a hand-verified date — every page that states a regulatory date carries a banner giving the age of the check. Past 90 days the build fails. Inside the window nothing renders at all: the banner appearing is the signal, and a permanent "clock: fine" strip is furniture nobody reads.

### The version numbers came from the wrong single source · 24 August 2026

- PR #69 below is titled "One source of truth for every published number". For the measurements it held. For the version strings it did not, and it is worth being precise about why, because the mechanism it introduced is what broke them: #69 said derive everything from the pinned evidence manifest, and the manifest’s pin is deliberately FROZEN at whatever release produced the numbers. That is correct for a measurement and wrong for "the current release", so every surface naming a current version began falling behind the moment the next release shipped. Nothing was hardcoded — grepping for a stale string would have found none of it — and by v0.38.0 the changelog page named 0.36.2 as the current one directly above its own 0.37.0 entry (the sentence is paraphrased here on purpose: `check:versions` scans this page like any other, and spelling the claim out verbatim would make the guard flag its own account of the bug), /security told a security reporter to reproduce on a superseded release before filing, and the homepage published a copy-pasteable Action pin at a version two minors old.
- There are two version quantities, not one, so there are now two sources. The current release comes from the GitHub latest-release endpoint, fetched at build and committed to src/data/repo-facts.json so a build without network is reproducible; the measurement pin stays exactly where it was. The build refuses to run at all if the committed facts are pinned to a commit rather than a release tag, because six pages state a current release and a site that cannot name one should not be published.
- What is structurally different from #69: that PR added derivation and no check that the derivation read the right source — a derived number and a correctly-derived-from-the-wrong-place number are equally invisible to `check:figures`, which is why it caught none of this. `check:versions` now asserts against the built output that every currency CLAIM ("the current release is", "currently vX") and every copy-pasteable Action pin names the release the site is pinned to. It matches the claim rather than the number, because an old version appearing is usually correct: /citation names the release whose artifacts produced the numbers, /changelog lists every release there has ever been. It found one on its first run that had been missed by hand.
- The measurement’s age is now a build condition rather than a sentence. Past 7 days every page carries a banner stating the age in days and machine-readable meta; past 21 days the build FAILS. An age that cannot be parsed also fails, rather than defaulting to fresh — the failure to design against is a site quietly serving a number it can no longer date. The same status is written into llms.txt, read back out of the rendered page so the two cannot hold different numbers: robots.txt here opts every AI crawler in, so an answer engine can quote the attack-success rate without ever rendering a page, and the staleness has to travel with the number.
- /leaderboard now reads the board’s own staleness verdict instead of implying currency. When the published artifact says stale, the table sits behind a labelled "measured with an older tool version — not a current result" state carrying the product’s reason verbatim, and each row shows the tool version that measured it. The rows still render: hiding them behind an interaction would strip them from a crawler and from anyone on a keyboard, which makes the disclosure worse than what it discloses. The benign control column gained its counts and its own confidence interval, so the floor an attack-success rate is measured against is published to the same standard as the rate.
- llms.txt said "All 76 routes" while the sitemap carried 81 URLs. Both were right and neither said which: 81 is every indexable URL, 76 is the subset that is an HTML page with a title and description to list, and the other five are the feeds. The two already came from one source — the index is generated from the sitemap — so the fix was to state the arithmetic and check it, not to add a manifest layer that removes no failure mode.

### One source of truth for every published number · 18 August 2026 [#69 ↗](https://github.com/provael/website/pull/69)

- The homepage carried the superseded single-task result — 100% keep-out violation against a 0% benign control, "one task" — in five places, on the same page as a hero already rendering the ten-task result from the pinned manifest. The page contradicted itself and the stale half was the flattering one. All five now derive from the manifest, so a re-pin moves them.
- Two build checks were added so this class of drift fails rather than ships. `check:figures` refuses a literal percentage or fraction inside a stat slot in any template, because after rendering a derived number and a typed one are indistinguishable. `check:board` now also compares /leaderboard against the exact signed artifact it rendered from — every row, the generation date, the source commit and the digest — since a page that can silently disagree with its own signature is worse than no page on a site whose pitch is "verify the signature rather than trust this".
- /verification stopped telling people to install from git. `provael submit` has shipped on PyPI since 0.32.0; the instruction outlived the fact it described, on the page whose job is to get third parties to reproduce a result.
- /citation now names the current release and carries the Zenodo concept DOI in both plain text and BibTeX. It previously said there was no DOI, which stopped being true when one was minted and confirmed resolving.
- Added /changelog, which had no route at all — releases were visible only on GitHub and PyPI, which is exactly why four version strings drifted unnoticed. It renders the product CHANGELOG and this list side by side, with the measurement-freshness signal beside them.
- The regulatory clock was counting down to an obligation that had already arrived: "next" was measured from the date the clock was last verified rather than from anything resembling now, so 2 August 2026 stayed "next" after it applied. It is now measured against the site’s own pinned date, and the page states which anchor it used and that an obligation has come into force since the entries were last checked against primary sources.
- The five machine-readable crosswalk artifacts are downloadable from /compliance, each stating its own mapping_status — and where the artifact declares none, saying so rather than supplying one. XPolicyLab is deliberately absent: it ships no artifact, and listing it would present a planned integration as a delivered one.

Unreleased

## Merged, not yet published

On main and not in any release. Installing from PyPI does not get you these.

### Changed

- The README leads with the measured result, and the main docs pages describe themselves. Why: its first screen showed a fixture demo, badges, the evidence formats and five limits, but never the one real-policy result, so a visitor met the caveats before the number they qualify. A short block now states it as the site and errata do (42 of 50 against 1 of 50 benign, task-clustered 95% CI [62%, 100%], fragility per [E-2026-12](https://www.provael.com/errata/#E-2026-12)) above "What this is". Seven docs pages (home, quickstart, attacks, Python API, handbook, Top 10, leaderboard disclosure) carry their own meta description; every page had shared the site's one, so search results could not tell them apart.
- The docs stop contradicting the published run. Why: docs/quickstart.md's ten-task GPU command left out --seeds and --horizon, so it ran the CLI defaults (10 episodes of 8 steps) rather than the published settings; it now carries --seeds 5 --horizon 280 and says what the defaults do. docs/attacks.md still read the roleplay result as SmolVLA being redirected (88%, 44/50) and attributed the arms' separation to meaning, the reading [E-2026-12](https://www.provael.com/errata/#E-2026-12) withdrew; it now gives the 14 September run (42/50 against 1/50) and the control arms. The package docstring no longer says SmolVLA runs are gated behind PROVAEL_INTEGRATION: provael attack on SmolVLA does not read it (the studies, the GR00T adapter and the real-model tests do), and every run records it. A comment in mlbom.py no longer dates ML-BOM to CycloneDX 1.6 (it entered the spec at 1.5). Eighteen in-page links that used GitHub's heading ids now use the docs site's, so they land on their section.
- Dependabot's security jobs now read ignore rules. Why: they run as the uv package manager and read only a uv entry, so the rules under pip, 0.46.0's datasets rule included, never reached them (every job printed "ignore-conditions":[]). A security-only uv entry (open-pull-requests-limit: 0) now carries one rule for each update the lerobot 0.5.1 pin cannot install, with the blocking requirement beside it: torch, transformers, diffusers and setuptools (four dependency_file_not_resolvable jobs on 21 October 2026), datasets and fsspec. The [lerobot], [hardware] and [openvla] extras keep the advised versions until that pin moves; the default install carries none of them.
- The GPU arm's plan step refuses a calibrate dispatch that rolls out a seed the headline run scored, read from that run's records (#335). Why: the stage fitted on seeds 0-6 and the headline, still uncalibrated, scored 0-4, so a calibrated re-run on those seeds would score its benign floor on episodes the envelope was fitted on.
- Each LIBERO episode records the init state its reset loaded (init_state, report schema 8). Why: the starting scene follows a reset counter the seed never reaches, so without it the calibrate check could only place a task's first episode (#136). The adapter reads the state back from lerobot's counter after every reset, so a resumed run is recorded as it ran, and calibration artifacts record it for every rollout. Older reports load as before and their digests do not move.
- The calibrate plan step compares the fit's planned init states with the ones a scored run recorded, per task. Why: the opening-scene check (#339) could see only each task's first episode; with recorded states it refuses on any overlap and names an offset that would clear them. A run whose records carry none, the uncalibrated headline among them, keeps that check.
- provael calibrate --init-state-offset starts each task's fit at a chosen init state. Why: so a fit can begin past the states a scored run recorded. The GPU arm's stage carries it at 0; the plan step refuses a non-zero offset against a run without recorded init states, and while the image installs provael 0.46.0, whose calibrate has no such flag.
- provael attack --pin-init-states START takes each LIBERO episode's starting scene from its seed: the episode at seed s starts from init state (START + s) mod 50, in every arm. Why: otherwise lerobot's reset counter picks the scene, so a scored run's scenes depended on what the episodes before it did. Pinned, they are known before the run, and every arm at a seed shares one. The adapter sets the counter before every reset and raises if the state it reads back is not the one asked for. The GPU arm's plan step refuses a pinned stage whose states meet the calibrate fit's window. The control stage pins at 45 (states 45-49, clear of the fit's 0-12), and its plan is refused while the image installs provael 0.46.0, which lacks the flag.

### Fixed

- The calibrate stage's cost line priced a one-hour timeout (#334). Why: the timeout has been 5,400 s since the fit gained its attacked arm, so the example's table said ~$8 where the plan step prints ~$11.99. It now says ~$11.99 (10 x 1.5 h), and a test ties every ceiling line to its stage.
- The roadmap's peer-review proof still said the SPAIS notification was due (#336). Why: the workshop did not accept the paper (notified 17 October 2026). The roadmap, the paper's README and the community page now say so, and the arXiv posting in November stands.
- The roadmap says the 24 October 2026 date for #136 was missed, and gives the new target, 14 November 2026. Why: the page promises to keep each date or miss it in public. A test now fails on any past target in its Now section without "missed" or "delivered" beside it. Nothing was re-run, and the published headline is unchanged and still uncalibrated.

### Security

- hydra-core 1.3.7 to 1.3.8 in the lock (GHSA-wf6j-7hvr-gr93). It comes in only through hf-libero, in the [lerobot] and [hardware] extras, and src never calls instantiate(), so the default install was never affected.
- tornado 6.5.9 to 6.6 in the lock (GHSA-r4j5-9xpp-3985, GHSA-pcg2-g6cj-q92c, GHSA-pw9p-v7r6-gx4p). It comes in only through the docs group, and Provael never serves tornado, so no install was affected.

## 0.46.0 · 21 October 2026

A hygiene release. The per-run significance column now tests each attack against the benign control arm's own counts with a one-sided Fisher exact test ([E-2026-18](https://www.provael.com/errata/#E-2026-18)): 21 of the 49 rows the old test marked ✅ were not significant, 19 of them on real policies, and the significance sections of the 65 committed report.md files are re-rendered with a dated note. It also raises the pillow floor to >=13.0 and moves the lockfile to 13.0.0, for nine advisories against 12.3.0 published on 20 October 2026. Beside them: two Dependabot rules for updates the current pins cannot install (huggingface-hub 2.x, datasets 5), docs and docstrings that had fallen behind what 0.45.0 shipped, and the Action's SARIF uploads on github/codeql-action/upload-sarif 4.38.3. No headline number moves (the results READMEs use McNemar with Holm, and no signed artifact moves), but the q-values and check marks in those 65 reports do. The published measurement is still the 14 September run on 0.41.2, still uncalibrated, now five minor releases behind a window of two (six tags, since 0.42.1 was a patch): watch/publish-freshness.json reads releasesBehind: 5 and isStale: true, and the campaign stays paused until the predicate is calibrated (#136).

### Changed

- scoring/asr.py says which rates return 0.0 and which return None. Why: its module docstring said every rate returns 0.0 on zero attempts, but the signal-dependent rates return None when their signal is absent, and the 0.0 that attack_success_rate returns for an empty input is the serialization sentinel ASRStat.asr also stores, not a measured 0% (read ASRStat.measured_rate for N/A). Docstrings only: no behaviour and no published number moves.
- Dependabot no longer proposes huggingface-hub major versions. Why: #311 raised the leaderboard Space's floor to huggingface_hub>=2.0.0, and neither place that installs the package resolves with 2.x. The Space runs gradio 6.23.1 (sdk_version in leaderboard/README.md), which requires huggingface-hub>=1.16.0,<2.0, and lerobot 0.5.1, pinned for the GPU path, requires >=1.0.0,<2.0.0; uv pip compile with 2.1.1, the newest 2.x, is unsatisfiable against each. .github/dependabot.yml now ignores huggingface-hub majors with that reason beside the rule, and #311 is closed. The rule lifts once the Space's gradio and the lerobot pin have both moved (gradio 6.29.0 already accepts 2.x).
- Dependabot no longer proposes datasets updates the lerobot 0.5.1 pin cannot install. Why: the update for datasets failed on main (run 97), and the pin is held on purpose until the arm run. The rule ignores versions: [">=5"] rather than an update type, because update types never apply to security updates and run 97 was one; a 4.x fix would still be proposed.
- Dependency bumps, taken from Dependabot #310 and #312 into this change so they land with it: astral-sh/setup-uv 10.0.1 → 10.2.0, and 5.4.2 → 10.2.0 in the two leaderboard workflows that were still on v5; github/codeql-action/upload-sarif 4.38.1 → 4.38.2, then 4.38.3 (#322), the pin both SARIF uploads in action.yml use. The two v5 workflows are manual-only and pass setup-uv nothing but enable-cache: true, and no workflow here runs on the pull_request_target, workflow_run or release events where v10 turns the cache off.

### Fixed

- pillow floor raised >=12.3 → >=13.0, and the lockfile moved 12.3.0 → 13.0.0. Why: nine advisories against 12.3.0 were published on 20 October 2026 (OSV, all fixed in 13.0.0), among them a heap buffer overflow in the JPEG decoder and out-of-bounds reads in the TIFF decoder, and the CI audit failed on them. pillow is a direct runtime dependency and Provael decodes and perturbs camera frames, so this repeats the July raise (>=10 → >=12.3). With the old floor a fresh pip install already resolved 13.0.0 (out since 16 October), but an environment that held 12.x kept it, and the container image installs the lockfile, so it shipped 12.3.0. 13.0.0's backwards-incompatible changes (Python 3.10 dropped, fromarray's data-type mode, I-mode PNG saving, four removed attributes) touch nothing Provael calls, and every extra re-resolves on it.
- The per-run significance column no longer reads the benign rate as a known constant ([E-2026-18](https://www.provael.com/errata/#E-2026-18)). Why: fdr_by_attack and the dossier's per-family test ran a one-sample binomial against the observed benign false-positive rate, clamped to 1e-12 at 0/n, so a single success against a thin control came out "significant". 21 of the 49 rows marked ✅ in committed reports were not significant, 19 of them on real policies. Both tests now use a one-sided Fisher exact test against the control arm's own counts (provael.scoring.asr.fisher_exact_greater), and a report without benign counts gets no significance. The significance sections of the 65 committed report.md files are re-rendered from their unchanged report.json, each with a dated note. No headline number moves (the results READMEs use McNemar with Holm), and no signed artifact moves.
- The SPAIS paper README kept its 1 October to-do list after the deadline passed. Why: nothing read the date, so the repository could not say whether the paper went in, and the list still implied the calibrated re-fit could land before 1 October when the roadmap dates it to 24 October 2026. paper/spais/README.md and the roadmap's proof 3 now record the submission and the 17 October 2026 notification, the community page's two closed calls say they closed, and tests/test_docs_no_past_todo.py fails when a paper README or the roadmap's Now section keeps a to-do dated in the past. No measured number moves.
- The studies index said neither defense had a real-policy defended arm committed. Why: 0.45.0 counts two (realPolicyDefenses 0 → 2), both committed on 18 September under results/, and the index row for each still read "no real-policy defended arm committed". The rows now say an arm is committed and what it does not show, and each study page gains a dated note with the arm's result: canonicalization 20/30 against a 1/30 floor, not a reduction against the undefended 42/50 at other seeds; the envelope 0/30 with competence 0/30, so it says nothing about the attack. No measured number moves.
- Seven passages said "on main, not yet in a release" about what 0.45.0 shipped. Why: the release made them false the day it was cut — the schema-7 section of docs/attestation.md, the glossary's three-way fit, the quickstart's contact predicate, two roadmap items, suites/libero.py's module docstring, and [E-2026-16](https://www.provael.com/errata/#E-2026-16)'s status line ("in no release yet"). Each now names 0.45.0.

## 0.45.0 · 28 September 2026

The attestation-format release. A signed bundle now binds the exact bytes of report.json, and its Ed25519 signature covers a 306-byte record that names the statement, so a key held in AWS KMS can sign it (provael-attestation/v2; a 0.44.0 verifier cannot check a v2 bundle, so upgrade verifiers before issuers, and v1 bundles keep verifying). Beside it: provael calibrate splits benign rollouts three ways by default (report schema 7), a second predicate for contact and force events is wired and unmeasured, the ML-BOM describes the policy rather than the tool, Meta-World is declared scaffolding (4 runnable / 3 scaffolding suites), three ATLAS tactic names are corrected ([E-2026-16](https://www.provael.com/errata/#E-2026-16)), and the signed regulatory clock carries ISO 10218 at the month ISO gives ([E-2026-17](https://www.provael.com/errata/#E-2026-17), ruleset /6). No previously published number moves; three 18 September workstation measurements are added beside the headline, each with what it does not show. The published measurement is still the 14 September run on 0.41.2, now four releases behind a window of two: watch/publish-freshness.json reads isStale: true, and the campaign stays paused until the predicate is calibrated (#136).

### Added

- The second predicate: a contact / force event beside the envelope exit (physical_hazard endpoint, oracle contact-event/v1). Why: the first proof on the roadmap owes a safety engineer a column in the units they already read — contact and force, not a keep-out box — and the endpoint had been named since Phase 7 with no suite behind it. What changed: SuiteAdapter gains physical_hazard(state) -> bool | None (default None: the stub is arithmetic and has no contact API, and a fabricated False would read as "no hazard"); the LIBERO adapter reads, per step, robosuite's end-effector force sensor (robots[0].ee_force) and MuJoCo's contact list through the lerobot → LIBERO → robosuite chain, every read guarded so a missing sensor is an unmeasured step and never a False, and ContactRule calls an event when the force norm reaches its limit (default 140 N — ISO/TS 15066:2016 Table A.2, quasi-static, hands and fingers: a scale an engineer recognises, not a claim that LIBERO holds a person) or an arm link (robot0_*) touches a body that is not the robot's own (gripper contact with objects and the table is manipulation and is not counted); the runner latches the per-step answers into one episode outcome under AttackResult.endpoints["physical_hazard"], writing the key only when a signal was surfaced; scoring.asr.endpoint_by_attack counts events over the episodes that measured them; the Markdown report, the CLI table and the results README generator add a contact events column with its own Wilson interval when any episode surfaced the signal and say not surfaced when none did. The contact rule never reaches is_unsafe, so it cannot enter the ASR. The attribute chain and sensor names were read from the lerobot 0.5.1, LIBERO and robosuite 1.4.1 sources on 21 September 2026 and exercised against a fake graph on CPU; no committed run carries the column yet — the calibrated re-run on the GPU box is the first that will, and the endpoint stays N/A until a live simulator has produced it.
- Keep-out figures for the three 14–18 September LIBERO-Object runs (results/<run>/figures/, one SVG per task): every benign end-effector path in blue against the task's keep-out zone, the roleplay paths in red with a marker at the first unsafe step; the control run also draws the four harmless-variation arms in grey. Rendered by scripts/plot_keepout_paths.py from the shards' own trajectories, so nothing is drawn that a report.json does not carry. The pictures answer the #136 question directly: the benign paths never enter the default box, and the roleplay paths cross into it from the same start. No measured number moves.

- Three measurements from the 18 September workstation runs, and what each one does not show. Why: the shards finished while nothing was pushing them, and two of the three are easy to misread in the project's own favour. What changed, per run. (1) instruction_canonicalization on the ten libero_object tasks: the attack survives the defense at 20/30 against a benign floor of 1/30, McNemar p=3.8e-06, competence 29/30. It is not a measured reduction against the undefended 42/50 — different seed counts (3 against 5) and overlapping task-clustered intervals ([43%, 87%] against [62%, 100%]) — and a paired run at matched seeds has not happened. (2) action_envelope on the same tasks: 0/30 on the roleplay arm and 0/30 on the competence control, so the policy completed no task at all under the defense and the zero says nothing about the attack; the honest reading is "unusable at the shipped defaults". (3) Ten keep-out calibrations (20 benign rollouts each, a roleplay arm at the holdout seeds, 36 candidate faces): every fit reports a benign FPR of 0.0 and they differ only in whether the boundary catches anything — tasks 0/6/8 catch every attacked rollout, task 3 catches one in six, tasks 5 and 7 chose the opposite face and catch none. Eight would be adopted and two withheld, which is why a benign-only fit is not enough. Those detection rates are in-sample: the same six attacked rollouts chose each face (the candidate that catches the most of them), so none is a held-out rate. These are two-way fits at a 5% target and carry no binding; the three-way re-fit supersedes them. realPolicyDefenses moves 0 to 2 across watch/, the evidence manifests and the docs counts, and the two assertions that pinned it at zero now read two. No previously published number moves.

### Changed

- provael calibrate splits benign rollouts three ways by default — fit / tuning / eval — and the eval split is scored only after the predicate is chosen. The two-way path stays as --split two-way. Why: since 0.43.0 every surface has said that the second split *selects* the threshold or hazard face, so its FPR is a tuning figure and no evaluation split stood behind a calibration; his 19 September review listed wiring the three-way split end to end as the first product gap, and the 24 October re-run under a calibrated predicate would otherwise publish calibrations with no FPR anyone could call an estimate. What changed: the artifact records split, eval_seeds, eval_fpr and a binding (CalibrationBinding schema 2: endpoint, oracle version, policy, suite, task, checkpoint, predicate kind, the three seed-set digests, target and achieved eval FPR; seed overlap refused at build); an eval FPR above target is written as fitted with an invalid binding and a red row, never re-fitted on the eval seeds (a threshold moved to satisfy its own evaluation turns that split back into tuning data); the runner re-derives the binding's validity against the run's own policy, suite, task, --model and oracle version, applies the predicate either way, and says which (calibration.<task>.split / eval_fpr / binding in report.json — report schema 7, nested keys the projection strips for a report declaring less, so every earlier attestation keeps verifying; schemas/report.v7.schema.json); the Markdown report and the compliance report render one sentence from provael.calibration.describe_calibration (tuned / bound / invalid) instead of each stating the split in its own words; the CLI table gains an eval FPR column. Old artifacts without the new keys load as what they are, two-way fits with no binding. No measured number moves, and no real-policy calibration validity is claimed: the roadmap's condition — a fresh supported real-policy calibration run — is unchanged, and every published number is still measured under the uncalibrated default box.
- The brand assets under docs/assets/ (README wordmark, org picture, docs logo and favicon) now derive from the website's Proof-Path mark through scripts/gen_brand_assets.py; the teal checkmark this repository had carried since before the mark existed is retired everywhere, and the README wordmark gains a dark-scheme variant. The retired social_preview.png (the July "100 %" card) is deleted; the repository's social preview is the website's og.png.
- The ML-BOM names the model it describes, and carries a serial number. Why: metadata.component is the component a CycloneDX BOM describes, and it named provael, the tool, so a consumer read the BOM as describing the red-team harness rather than the policy it measured; and the BOM had no serialNumber, which CycloneDX says every BOM should carry. The policy under test (its model card included) is now metadata.component, provael moves to metadata.tools.components, and the model is no longer repeated in top-level components. The serial is a UUIDv5 of the report's canonical JSON rather than a random UUID, so the same report still yields a byte-identical BOM. Every emitted BOM is now validated against the official CycloneDX 1.6.2 JSON schema, vendored in tests/fixtures/cyclonedx-1.6. Consumers that read metadata.component.name == "provael", or that looked for the model under components, must read metadata.tools and metadata.component instead. No measured number moves.
- Attestations bind report.json's bytes, and the signature covers a small fixed record (provael-attestation/v2). Why: a v1 subject was the SHA-256 of the report's model projection, because the statement builder was handed a parsed report, so deleting a field whose stored value equals its default (loading restores it) left the digest intact; and a signed statement is 38 to 117 KB, while AWS KMS signs pure Ed25519 only over a raw message of at most 4,096 bytes, which ruled out a KMS-held key for the operated tier. What changed: the subject is the SHA-256 of the file as written (subject.binding: report-json-bytes/v1, the value sha256sum report.json prints), so attest --verify --report is a byte-for-byte check; the Ed25519 signature covers a 306-byte signed record naming the statement's SHA-256 and the subject's, a 363-byte message under the DSSE encoding (attest.signed_record, record_signing_message). ReportArtifact carries the bytes read from disk to every statement builder (attest, certify, submit, the assurance and insurer views), submit publishes the submitter's report.json byte for byte instead of a re-serialised copy, the hosted server binds the request body as received (post it with curl --data-binary, not --data), report.json is written as LF bytes on every OS, and .gitattributes stops a Windows checkout rewriting byte-bound files. The verifier now also rejects a bundle whose payloadType is not an attestation, or whose statement format it does not know: before, a signature by the same key over any payload type verified as an attestation. The certify dossier no longer calls the bundle a PEP 740 attestation, which it never was. The committed insurer sample is regenerated as v2. Compatibility: v1 bundles still verify, by the v1 rule (frozen fixtures in tests/fixtures/attestation_v1 pin it), and the verdict names the rule applied; 0.44.0 and earlier cannot verify a v2 bundle, so upgrade verifiers before issuers. The execution and evidence manifests keep the projection digest, which is no longer equal to the attestation subject. No measured number moves.

- Meta-World is declared scaffolding: the published counts move from 5 runnable / 2 scaffolding suites to 4 / 3. Why: no Meta-World benchmark has ever been run (nothing under results/), and its CLI path raises at reset, because the adapter refuses LIBERO's default keep-out box (it lies behind the Sawyer arm), there is no zone option, and no Meta-World calibration is committed. That is the project's own definition of scaffolding, and watch/registry.json still counted it as runnable. SCAFFOLDING_SUITES now carries it with that reason (the CLI-gating note is folded in, so SUITE_GATING_NOTES is empty); list-suites renders it as scaffolding, doctor now labels every scaffolded suite as scaffolding rather than "importable", and suite_is_ready("metaworld") is False whatever is installed. The docs and examples that described its status now call it scaffolding (quickstart, attacks, FAQ, index, roadmap, and the suites and cross-suite examples); ActionLayoutError's hint no longer suggests --suite metaworld, and examples/suites/README.md no longer calls its wiring "validated on the gated, real path", which the suite registry's own comment ("never been introspected against an installed package") contradicted. watch/registry.json and the generated inventory line in docs/quickstart.md are regenerated; the website picks the counts up at its next re-pin. That inventory line's three groups are now disjoint (3 CPU fixtures, 1 gated real simulator, 3 scaffolding): two suites are both gated and scaffolding, and counting them in both made the line sum to 9 of 7 (8 of 7 before this change). scripts/gen_doc_counts.py now refuses to render a line whose groups do not sum to the total.

### Fixed

- docs/attestation.md still said the instruction family "redirects the policy 100% (10/10)". Why: that is the single-task figure the ten-task suite superseded in August, stated in the framing [E-2026-12](https://www.provael.com/errata/#E-2026-12) corrected, on the page that tells an auditor what a bundle proves. It now gives roleplay 42/50 against a 1/50 benign floor on 0.41.2 (44/50 against 2/50 on 0.32.0) and reads it as fragility under the frame, not attacker control, as docs/index.md does. The offline real-observation study, which says "the published result is 10/10" in its motivation, gains a dated note instead of a rewrite, as the π0 study already has. [E-2026-17](https://www.provael.com/errata/#E-2026-17) records that ANSI/A3 R15.06-2025 is carried at year precision. No measured number moves.
- The signed regulatory clock dated ISO 10218 to 1 April 2025, a day no source gives ([E-2026-17](https://www.provael.com/errata/#E-2026-17)). Why: ISO's catalogue gives the publication date of both ISO 10218-1:2025 and -2:2025 as 2025-02, a month with no day, and the clock sits inside every signed statement, so every attestation from 0.7.0 to 0.44.0 vouched for the unsourced day; the hosted conformity mapping typed the same day again. What changed: the entry reads applies_from: 2025-02; every clock entry carries a date_precision (day, month or year) and the model refuses a date written more finely than it; the entry's note no longer calls a published standard "in force"; the hosted conformity rows read their dates (and applies_from_precision) from the clock instead of repeating them; the ruleset moves to provael-attest-ruleset/6, because manifests stamp it as their regulatory_clock_version and two clocks must not share one; the committed evidence manifests that stamp it (results/smolvla_libero_object, the 14 September delivery pack) and the insurer sample are regenerated, and only that string and the clock move in them. No measured number moves.
- list-suites called Meta-World ready on lerobot alone, and "horizon 280" had no stated source. Why: Meta-World is a separate lerobot extra that provael[lerobot] does not bring in, so a box with the extra was told the suite was ready and failed inside LeRobot's env factory; both readiness checks now require the metaworld module too, and the module docstring agrees with its own install hint. suites.libero.LIBERO_HORIZON records OpenVLA's per-suite evaluation budgets (220 / 280 / 300 / 520, each just above the suite's longest training demonstration), which every committed LIBERO run already used; the eai04-redirect recipe and the local sweep read it, the sweep no longer runs libero_spatial at object's 280 by default, and tests/test_libero_horizon.py holds committed runs to it. No measured number moves.
- Four published docs claims the repository no longer supports. Why: each was found stale in the 26 September re-verification of AUDIT_BACKLOG.md and ISSUE_REGISTER.md. docs/faq.md and the instruction-transfer finding quoted Predictive Red Teaming as "MAE < 0.19" without the caveat docs/sim-predicts-real.md already carries (the result is on diffusion policies, not VLAs); docs/index.md listed GR00T and OpenVLA as real policies and Meta-World as a real simulator (both policies are unrun scaffolding, and Meta-World is not runnable end to end); docs/standards/listings.md said "nothing submitted yet" after sixteen listing PRs, and now carries the ledger its own checklist asks for; pyproject.toml called lerobot 0.6.0 the newest release (0.6.1 has been on PyPI since 3 August). The glossary now links the website's. No measured number moves.
- Three module docstrings that had gone stale, and [E-2026-15](https://www.provael.com/errata/#E-2026-15) in the errata log. Why: each said something the code no longer does. combine.py said provael attack cannot resume (the ledger has been wired in as --resume since 30 August, #157); attacks/controls.py said --attacks control resolves to "both arms" (it resolves to four); scripts/gen_brand_assets.py called the website upstream "for every public number" (it is upstream for the brand only). docs/errata.md records [E-2026-15](https://www.provael.com/errata/#E-2026-15), the website's correction of two publication days its own regulatory clock does not verify; no measured number moves.
- Four docs pages and suites/libero.py's module docstring said the contact column and the three-way calibration split shipped "since 0.45"; there is no 0.45. Both are on main since 21 September 2026 (#278, #279) and in no release yet, and all six lines now say so. tests/test_counted_claims.py fails on any tracked doc or src/ docstring that names a release after provael.__version__ — the mirror of the checks that catch prose falling a release behind.
- Three ATLAS mappings named something that is not an ATLAS tactic. Why: checked on 26 September 2026 against MITRE's data release 2026.09. EAI01 said "ML Attack Staging", a retired name (AML.TA0001 is "AI Attack Adaptation" since 2026.08), and ATLAS files neither prompt injection nor jailbreak under that tactic. It now reads Execution → LLM Prompt Injection and Defense Evasion → LLM Jailbreak. EAI02 said "Evasion" for "Defense Evasion". The Top 10's own crosswalk table already had both right. EAI03 put "ML Supply Chain Compromise", a technique under its retired name, in the tactic slot: it is AI Supply Chain Compromise, under Initial Access. The catalog, results/crosswalk/crosswalk.atlas.json, every SARIF rule's atlasTechniques and docs/standards/atlas-case-study.md move together. tests/test_eai.py now fails on a mapping whose tactic is not one of ATLAS's sixteen. The 8 August submission to MITRE is left exactly as sent. No measured number moves.

## 0.44.0 · 20 September 2026

The hygiene-and-scope release: the 19 September repository backlog (sections 1, 3, 4 and 5 — story, licence hygiene, scope cuts, governance, docs consistency), worked one item per commit in its own order and cut as one minor because three behaviours change for a caller: --recipe full-sweep on a real policy narrows to the measured families, provael certify becomes provael dossier with a deprecated alias, and the compliance rows and the signed regulatory clock carry a different framing of the machinery route. No measured number moves. The published measurement is still the 14 September run on 0.41.2, now three releases behind a window of two: watch/publish-freshness.json reads isStale: true, and the scheduled campaign that would refresh it is paused until the predicate is calibrated (#136) — a stale flag that stands is the artifact working, and this entry says so rather than arranging for it not to.

### Added

- NOTICE, SPDX headers, CONTRIBUTORS.md, TRADEMARKS.md, and a DCO check. The Apache-2.0 appendix names the copyright holder; every tracked .py/.sh under src/, scripts/, tests/ and examples/ carries the two-line SPDX header (scripts/add_spdx_headers.py, guarded by tests/test_spdx_headers.py); who contributed and under which terms is a file; the mark's status is stated as it is (unregistered, no application filed) with a Mozilla-shaped policy; and the dco CI job refuses a pull request with a human commit that lacks Signed-off-by (scripts/check_dco.py, make hooks for the local trailer).
- A status per attack family and per suite. family_status() answers measured / fixture-only / control from a declared MEASURED_FAMILIES held to the committed runs by test; suite_status() answers measured / fixture / scaffolding / no run committed here the same way. list-attacks and list-suites print the column; docs/attacks.md carries a generated per-family status table.
- provael dossier, the Machinery Regulation evidence dossier; certify is a hidden, deprecated alias until 0.46.0.
- A tier on every compliance row: operative (Machinery Regulation, ISO 10218:2025, ISO 13849, IEC 61508, IEC 62443) or reference (the other ten frameworks), in the emitted compliance report, the dossier and the catalogue the website mirrors.
- A Sigstore bundle beside every release asset (<asset>.sigstore.json, cosign keyless through the release workflow's OIDC identity) and the verification recipe in the release notes and SECURITY.md; the build-provenance attestation stays.
- tests/test_workflow_permissions.py: no workflow grants a write scope at the top level.

### Changed

- --recipe full-sweep on a real policy runs the families with a committed real-policy measurement by default; --include-fixture-families adds the nine that have only met the CPU fixture. On the fixture nothing is narrowed. An explicit --attacks always wins.
- The signed regulatory clock states the machinery route after the Digital Omnibus. Regulation (EU) 2026/1744 moved the Machinery Regulation to AI Act Annex I Section B; Art. 15 does not apply to machinery directly; the AI-specific requirements arrive in Machinery Annex III by delegated act under Art. 8, third paragraph, applying by 2 August 2028, with Art. 20(10)'s presumption of conformity meanwhile. Both clock entries say so (last_verified 2026-09-19); the hosted report, the compliance rows, the committed insurer sample, the delivery pack's test report and the docs agree with them. The AI Act entry's date does not move; its meaning does.
- The README is 200 lines. Everything it used to carry moved verbatim, links re-pointed, to the docs page that owns the subject; the guards that read it moved with the sentences.
- The measured body leads with the September run everywhere (README, Top 10 EAI01, the finding, the leaderboard page — which described a board two rebuilds old and is now held to leaderboard.json row by row).
- The roadmap is the three proofs with dates, and the freeze: no new families, crosswalks, emitters, recipes or suites until the proofs and a paid engagement; one minor every two to four weeks; integration after the hardware run.
- The scheduled GPU campaign is paused (no schedule: trigger; watch/campaign.json records cadence.paused: true) until the keep-out predicate is calibrated.
- The six workflows that write main push with the repository's GitHub App token so main can require a pull request and passing checks; absent secrets fail loudly.
- GHCR is the only container registry; the unconfigured Docker Hub mirror is gone.
- Least privilege at the top of every workflow; the one job that uploads SARIF opts in.

### Fixed

- docs.provael.com served the pre-versioning tree at every cited root URL (/errata/ without [E-2026-10](https://www.provael.com/errata/#E-2026-10) to [E-2026-14](https://www.provael.com/errata/#E-2026-14), /top10/ at 88%), because the root-stub generator never replaced an existing page; it now does, and resolves the retired uppercase URLs in one hop. The docs smoke job had been red on every tag since v0.42.0.
- SECURITY.md said CVE-2026-25874 was fixed in LeRobot PR #3048; the PR is open and no released version carries a fix. The GPU-extras audit names the advisory in an allow-list with the justification.
- Stale counts and costs: examples/recipes/README.md ("all 14" of 17 families — the guard now reads through bold and line breaks), the Modal lane's "$0.02/run" (measured ~$0.49), docs/leaderboard.md's 0.1.0 board, the transcript's August download counters.
- The AI2 harness premise: the bridge notes, the scaffolding message and the roadmap said no per-step state reaches a caller; the harness maintainer corrected that on 8 September and the docs had not caught up — the model-server proxy is the supported route.

## 0.43.0 · 19 September 2026

A correction release, cut the same day as 0.42.1, and a minor rather than a patch because the wheel's behaviour changes: a release verdict is now made only under a named acceptance protocol, and every emitter says incomplete — not assessed when none is named where it used to render a default gate's pass; the evidence manifest is format v2 with the manifest's registry counts in coverage.py's convention (17 families, 39 attacks); the packaged policy status names π0.5 as measured (preliminary). The fourteen items are the 19 September 2026 correction backlog, worked in order: decision correctness (R01–R03), calibration and scope (R04–R05), evidence consistency (R06–R08), delivery and usability (R09–R11), release readiness (R12–R14). No measured number moves; the published measurement posture is unchanged (measured with 0.41.2, now two releases behind, at this project's own window). Deferred with reasons, on the roadmap: wiring the three-way calibration split into the CLI and runner (conditional on selling or publishing validated calibration), and moving the LeRobot pin (needs a validated GPU run).

The signed board's staleness flag moves with this release: its rows were measured with 0.41.2, which is two minors behind 0.43.0 (limit one), so stale must read true. That field sits inside the signed payload of a schema-6 board, so the flag is re-stamped and re-signed by the leaderboard-restamp workflow with the published key rather than edited by hand — and that workflow now reads the run it re-aggregates from leaderboard/results/source.json instead of a typed path (it still said results/smolvla_libero_object_suite, the 0.32.0 run the board no longer aggregates, and would have refused its own output as "measured_with moved").

### Added

- A customer-run protocol template and a loadable acceptance protocol under examples/assessment/: the sections to record before a paid assessment (checkpoint and simulator, tasks, intervention capabilities, endpoint, controls and the agreed benign firing rate, clean-task competence, seeds and horizon, budget and stop conditions, acceptance criteria), the rule that an attack the adapter cannot reach is unavailable and never protection, and protocol.example.yml — a worked SmolVLA × LIBERO-Object protocol. Against the committed ten-task body it fails on roleplay (42/50 against a 0.2 gate); a single shard is incomplete under it (below its minimum evidence), never a 0% and never a pass; the quickstart shows the --protocol step. The keep-out module's docstring says what the default box is (a documented default that overlaps the reachable benign workspace on four Object tasks) and is not (a safety claim).

### Fixed

- The anytime-valid interval no longer depends on the operating system. anytime_ci calls the platform's lgamma, and libm implementations differ in the last bits: the same counts gave 0.0509692617012705 on the Linux workstation that ran the 14 September shards and 0.05096926170127061 on a macOS checkout — the one field that made the combined view of a sharded run, and so the evidence manifest's source_report_sha256, differ by platform (found when CI's Linux runner regenerated the delivery pack the macOS checkout had committed). The interval is now rounded to twelve decimal places, ten orders of magnitude above the noise; every other number in a report is a ratio or a sqrt and was already stable. New reports store the rounded value; committed reports keep the values their machines recorded. The insurer sample under results/smolvla_libero_object/ (which embeds a recomputed interval) is regenerated by its drift guard; the pack's manifest digest moves once and is the same on both platforms.
- SECURITY.md no longer contradicts itself about what the project is. It opened with "published by an open-source steward" and explained ninety lines later that a steward under Article 3(14) of the Cyber Resilience Act must be a legal person and that there is no entity. The regulatory context is now one dated statement of the facts (19 September 2026: Apache-2.0 software maintained by one natural person, no legal entity, paid services offered and none sold, no legal advice taken, the steward route unavailable, the manufacturer question open and counsel's to settle — claiming neither scope nor exemption); the later scope section refers to it rather than restating it; the disclosure route, the 3-day / 90-day timings and the 24-hour REPORTABLE commitment are unchanged. tests/test_security_policy_consistency.py holds the file to that. A licence register for the supported pilot's dependencies (docs/maintainers/paid-run-dependency-licences.md) records each row as checked from metadata, stated by the upstream project, or unverified — the two checkpoints' model-card licences and the LIBERO asset terms are unverified until read at the resolved revision — and states that employment/IP provenance is recorded outside the repository.
- The evidence manifest counts what coverage.py counts: 17 adversarial families, 39 attacks. manifest._registry_counts subtracted the baseline family alone and published 18 / 43 while coverage.py — the module that exists so a count is computed once — excluded the four harmless-variation controls too and published 17 / 39; the website carried a conversion between its own upstream's two numbers. The manifest now reads coverage.registry_counts() and states the parts (families_baseline, families_control, attacks_baseline, attacks_control), so total = adversarial + baseline + control holds in one convention. The defense counts say study and real policy apart: defenses_with_study (2 — both measured on the stub fixture) replaces the ambiguous defenses_measured, and defenses_measured_real_policy (0, derived from the committed execution manifests via the new realPolicyDefenses / realPolicyDefenseNames registry keys) is the count that would be risk reduction on a real policy. The two defense studies are labelled "measured on the stub fixture" in the docs navigation and index.
- The packaged policy status names pi05 as measured — preliminary — and points SmolVLA at the published body. MEASURED_POLICIES (what provael list-policies reads in a wheel, where no results/ exists to scan) listed SmolVLA alone against a June run, so 0.42.1's registry counted two measured policies while the installed CLI said pi05 had no run committed here. It now names the 18 September π0.5 leg with its exact checkpoint, run directory and scope (three seeds, two of eight arms, at the benign floor, no transfer claimed), and SmolVLA's 14 September suite. A test holds the declared set equal to coverage().real_policy_names in a checkout, in both directions, and requires each entry to name a results directory that exists.

### Added

- A reproduction request, a result template, and a register that counts nothing until it arrives. docs/reproduction-request.md points at the delivery-pack sample and its pinned environment, states the expected qualitative behaviour with its uncertainty (the two committed runs' spread), the hardware and time, and the known limits — and keeps three activities apart: verifying digests, regenerating derived artifacts from the shards, and re-executing the policy. .github/ISSUE_TEMPLATE/reproduction-result.yml asks for the level actually done, the exact environment including the resolved checkpoint revision, per-arm counts with the benign arm, and a reading of agrees / disagrees / null / could not complete. The errata page gains a reproduction register with states requested · attempted · completed · agrees / disagrees / null; its one row is requested, and the page says no entry above that exists on 19 September 2026. A prepared request is never counted as an external validation; the adopters page links here and claims nothing.
- A private-assessment procedure, dry-run once with dummy data. docs/maintainers/private-assessment-procedure.md records, for one engagement at a time: authorisation and the named access owner and operator before any input moves; the allowed-inputs list (credentials, traces and anything not in the protocol refused); execution in the customer's environment by preference, else one isolated environment per engagement with a limited egress list and provenance variables set — never the in-repo reference server; what leaves (the delivery pack, each file digested) and what does not; retention and verified deletion. The 19 September 2026 dry run — dummy checkpoint in, stub run under the example protocol, seven deliverables out, every step digested, deletion verified, the execution manifest's env block empty — is logged on the page with what it could not exercise. .gitignore refuses private/, customer/ and *.customer.*; every issue form and the template chooser say that private checkpoints, credentials, customer traces and confidential findings never go into public GitHub and name the monitored private contact; SAFETY.md and CONTRIBUTING.md say the same; the hosted README and the hosted-production-requirements page state that a paid assessment does not run through the reference server, and the hosted README's stale family enumeration is replaced by the registered-versus-validated pointer.
- A reference delivery pack, generated from the published body. scripts/gen_delivery_pack.py renders examples/delivery-pack/smolvla-libero-object-2026-09-14/ from the ten 14 September shards using the same emitters the CLI uses: findings with every rate beside its control and both interval kinds named apart, the decision under examples/assessment/protocol.example.yml (a FAIL on roleplay, stated), the v2 evidence manifest with every shard's schema-aware digest, the scorecard and test report under the same decision, shards.txt (raw sha256 of every shard's report and manifest — a changed byte and the pack disagree), REPRODUCE.md (the pinned inputs, the expected qualitative behaviour with its uncertainty, the known limits) and retest.md (the regression diff against the earlier run of the same configuration, caveats first: 0.32.0 vs 0.41.2, no resolved checkpoint revision on either side). It states its own provenance gaps — the shards ran on 0.41.2 and record no repository, commit, lock digest or precision — as unknown and leaves them unknown. make check-delivery-pack is part of check-docs; tests/test_delivery_pack.py holds the pack current, every digest to its file, and every aggregate number to the sum of its shards.
- A provenance gate on committed runs, with a cut-over. tests/test_results_provenance.py: any run whose newest ended_at is on or after 2026-09-20 must pass campaign.check_provenance on every shard (repository, commit, dep_lock_digest, precision), with no exemption list for new runs; every earlier run is listed with its exact gaps and the test fails if a gap disappears — a historical manifest changes only by a new measurement, never by a filled-in value nobody recorded. The generated run READMEs now render the same state per shard (manifest digest, complete or the missing fields "recorded as unknown, not backfilled") and say the aggregate is derived from the shards; the local sweep driver warns when PROVAEL_REPOSITORY is unset.

### Changed

- The README opens with what a new reader needs, and the history moved to the pages that hold it. The first two screens are now: what the tool is and who it is for, the one CPU command with its output (labelled as fixture numbers, with the not-assessed verdict explained), the limits, a supported-configuration table, the one dated real-policy result (14 September 2026, with its control, both intervals named, the competence control and the decision under the example protocol), an interpretation guide, one contribution route and one link to the assessment offer. The long opening narrative and the results section's commentary moved verbatim to docs/findings/2026-instruction-transfer.md ("The README narrative, retired 19 September 2026"), every link intact; the registry-vs-evidence paragraphs sit under their own "Coverage: registered is not validated" heading; the cross-architecture section states today's status (SmolVLA measured, π0.5 preliminary at the floor, π0-via-openpi still pending) in place of a paragraph that still said the π0 leg was pending with no second architecture measured; two stale limitation bullets (the n=10 "roleplay 100%" figures; "one policy, one suite") are reconciled with [E-2026-12](https://www.provael.com/errata/#E-2026-12) and the derived policy count; the README roadmap is a short list pointing at docs/roadmap.md, which itself now leads with reliability work and customer-triggered work and labels every planned item conditional. Every guarded phrase (tests/test_counted_claims.py, the coverage breakdown, the quickstart the workflow runs, the board's measured-with claim) is unchanged.
- A checkpoint comparison says what changed, and refuses what is not like-for-like. RegressionDiff.changed records the checkpoint id and resolved revision, tool version, episodes and seeds that differ (allowed in a checkpoint comparison, on the record — a diff naming no change is comparing a run with itself). incomparable now also refuses a different recorded calibration when both runs are calibrated (the boolean matched; the predicate behind it did not), a different action unnormaliser and a different controller convention from deployed_policy; the Markdown verdict reads NOT LIKE-FOR-LIKE, treat as inconclusive, states that overlapping intervals do not show equivalence, and adds a "next investigation" line per regressed slice. examples/ci/regression-gate.md and .yml carry the protocol input and the worked pair (the delivery pack's retest.md).
- Release readiness: egress stated per path, the LeRobot pin exception bounded, and the wheel's status tested. SECURITY.md documents network egress and credentials for the CPU CLI (none), the optional model loaders (the Hub for the named checkpoint; a token from the operator's environment, never logged or written into an artifact) and customer execution (the customer's network, or the isolated environment's limited egress), and states the lerobot==0.5.1 advisory follow-up as a bounded exception: the pin moves by a run validated against the supported checkpoint on a GPU, never by a version-string edit, and the separate inference server is not a shortcut. docs/maintainers/real-policy-validation.md says which changes need a small real-policy validation (one task, one arm pair, ~1 GPU-hour, diffed against the committed task-0 shard) before the path is described as working, and that unavailable execution is reported as unverified. tests/test_wheel_status.py holds the packaged statuses and the coverage counter's unscanned behaviour to what an installed wheel must say.
- The measurement ledger carries what a consumer used to re-derive. Each watch/measurements.json row now names the run's checkpoint (model), its adversarial and benign numerators and denominators, its predicate (calibrated) and evidenceState, the intervalMethod every per-run interval uses (episode-level Wilson; a sharded run's task-clustered interval stays under its own name in its aggregate.json), and published — whether the row is part of the body behind the published headline, by the same displacement rule publish-freshness.json applies; publishedLineage says which body. watch/README.md keeps four dates apart (execution date, tool release date, assembly commit, source-review date) and states that none is written from a wall clock. tests/test_watch_consistency.py holds registry.json to the coverage counter, the ledger's rows to the committed manifests and its published rows to publish-freshness.json's body, and every generator to a no-op on a clean tree.
- A release pass needs a named acceptance protocol; with none named, nothing is decided. provael.verdict.release_verdict used to apply a default gate — a real policy plus a benign control, no threshold on anything — and every emitter rendered its answer as *the* release verdict. The committed task-0 shard of the 14 September 2026 suite (roleplay 5/5, 7/30 pooled) therefore carried release verdict: pass in its Markdown report, SARIF, OSCAL, evidence manifest and execution manifest. It now carries incomplete with assessed: false and the reason "no acceptance protocol named — release acceptance not assessed". A decision is made only against an AcceptanceProtocol — a named YAML/JSON file (or object) carrying the requirements — and a pass means exactly that the named protocol was satisfied; there is no built-in safe ASR. The decision carries the protocol's name and content digest, per-criterion outcomes, and a report.decision.json sidecar writer/loader (report.json itself is unchanged, so no attestation digest moves). ReleaseRequirements is now the body of a protocol rather than a decision on its own; the positional requirements and conditional= arguments of release_verdict are gone.
- Exceptions expire and are scoped. ConditionalException.expires is a timezone-aware datetime (a naive or malformed value is rejected when the protocol loads — it used to be an unvalidated string, and an exception that expired in 2000 returned conditional), and covers names the requirement keys it may soften. At decision time the caller passes as_of: an expired exception is refused and says so, an uncovered gap stays incomplete, an exception with no decision time is not applied, and a failed threshold is never softened.
- Critical slices are judged on their own denominators, and the CI gate follows them. ReleaseRequirements gains critical_attacks and critical_tasks (each a SliceGate: max_asr, min_attempts, estimator = point or Wilson upper bound; equality passes) and min_clean_task_success. A critical attack is compared on its own adversarial episodes, a critical task on that task's adversarial episodes only (scoring.asr.adversarial_by_task; the benign and harmless-variation arms never enter the denominator), and a slice that did not run, measured no applicable episode or ran below min_attempts is incomplete, never a 0%. Naming the benign control as a critical attack is a protocol error. The pooled max_adversarial_asr stays as an aggregate gate and the pooled rate stays descriptive: the task-0 shard now fails a protocol that names roleplay critical however many ineffective arms are added beside it. regression.diff_reports takes critical_attacks and RegressionDiff.regressed trips on a critical attack's own slice with a flat aggregate (critical_regressed, critical_unmeasured say which). provael attack and provael report take --protocol; the Action gains protocol and release-mode inputs and release-verdict, protocol, critical-regressed outputs — fail fails the job always, incomplete or no protocol fails it in release mode, and the annotation names the slice.
- Every export renders the one decision the command made, and says what it measured. to_markdown, to_sarif, to_oscal, build_evidence_manifest, build_statement/to_bundle, build_execution_manifest, to_test_report_markdown, to_compliance* and the certify dossier builders take decision=; the CLI decides once per command (--protocol on attack, report, evidence-manifest, attest, certify; a run's report.decision.json otherwise) and none of them computes its own. Each carries the verdict, whether it was assessed, the protocol's name and digest, and the reasons, under the same keys (verdict.acceptance_block). The evidence manifest is format v2: release_verdict changed meaning (the decision under acceptance_protocol, incomplete with release_assessed: false when none was named — never a default gate's answer); it adds model, predicate, endpoint (unsafe_envelope, which is an envelope exit — not task completion, not a calibrated hazard violation unless the predicate is calibrated, not physical-robot evidence), interval_method and a metric_semantics.intervals line: every wilson_ci95 is an episode-level Wilson score, and a sharded run's task-clustered interval is a different estimate kept under its own name. Report, scorecard and test-report interval columns say "Wilson (episode)". The attestation ruleset is /5 (acceptance_protocol and its digest in the statement) and the execution manifest schema is 3 (acceptance_protocol). Instruction-family rates are described everywhere as instruction-induced fragility under an out-of-distribution imperative frame, not attacker control. The two committed legacy derivatives under results/smolvla_libero_object/ (the evidence manifest, the execution manifest and the insurer sample, all regenerated from the same inputs by their drift-guard tests) are refreshed; the measurement they describe is unchanged.
- Compliance rows describe the evidence actually present. Three provael_signal strings read "calibrated redirection rate" whatever the run's predicate was, and Article 15 showed evidence-present for the uncalibrated public sample beside a footer saying the run was uncalibrated. Every entry now carries predicate (calibrated / default (uncalibrated)), the signal strings name the rate without presupposing calibration, the Markdown table gains a predicate column and states at the table — not only in the footer — that evidence-present is neither legal compliance nor satisfaction of a whole standard, and the report carries an acceptance block (verdict, protocol, reasons). N/A rows and the ten-risk table are unchanged. tests/test_decision_consistency.py renders the audited task-0 shard through every emitter, under no protocol and under one naming roleplay critical, and reads the same verdict, protocol, reasons, denominators and predicate out of each.
- The calibration's second split is described as what it is: tuning data. fit_scalar_threshold picks the tightest threshold whose fit *and* second-split FPR meet the target, and fit_spatial_zone keeps only the candidate faces whose second-split FPR is within it — the second split takes part in the selection, so the recorded benign_fpr is the target the choice was made to satisfy, not an estimate on data the selection never saw. The README, glossary, compliance pages, CLI help and the Calibration / CalibrationMeta field descriptions called it "held-out"; they now say "tuning split", state that the shipped path has no untouched final-evaluation split, and a test bans the held-out wording from those surfaces (the JSON keys holdout_seeds / holdout_fpr stay so committed artifacts load). The three-way helpers (split_seeds_three, CalibrationBinding) are the shape an untouched-evaluation claim needs; wiring them into calibrate and the runner is logged on the roadmap as conditional on selling or publishing validated calibration, with a fresh supported real-policy run.
- The scorecard leads with the protocol's release verdict, not its own. --threshold still compares the pooled adversarial ASR, on a line now labelled "Pooled threshold comparison (descriptive; not the release decision)"; the release verdict above it is the same release_verdict every other emitter renders, passed in by the caller so the page cannot disagree with the report, the SARIF or the manifest about the same run.

## 0.42.1 · 19 September 2026

A patch, one day after 0.42.0, because the fix to published_measurement's lineage matching is shipped code that 0.42.0 users have the bug of, and because the evidence surfaces moved: the workstation's two remaining runs are committed, the leaderboard aggregates the 14 September re-measurement and names its source run, and the registry counts the real policies measured — two since the π0.5 arm, stated as a count of adapters with a committed arm and nothing more. The published measurement posture is unchanged from 0.42.0: measured with 0.41.2, one release behind, inside the window.

### Added

- The two runs the workstation finished on 18 September 2026 after the crash, committed. results/smolvla_libero_10_2026-09-14/ is now all ten LIBERO-10 tasks (shards 3–9 relaunched from their ledgers): roleplay 0/30, none 0/30, clean task success 12/30 and 0/30 under the frame — on the long-horizon suite the frame breaks completion without an envelope exit, on an uncalibrated box shaped for the Object scenes. results/pi05_libero_object_2026-09-18/ is an attack arm on a second architecture: π0.5 (lerobot/pi05_libero_finetuned_v044, native pi05 adapter) on the ten Object tasks at three seeds, roleplay 1/30 against none 0/30 (McNemar p = 1.0, clustered [0%, 10%]), task success 27/30 unattacked and 8/30 under the frame. It is the pre-registered study's *preliminary* leg (three seeds, two arms of eight), so no transfer of the envelope-exit effect is claimed and no headline moves; the study doc records the result against its own falsifiers and notes that the per-arm lower-bound criterion is degenerate against a zero benign floor. The cross-architecture findings page, the README's backend sentence and the roadmap say pi05 has a committed real-model result now. Both runs are other lineages than the published Object body (a different task suite; a different policy), so watch/publish-freshness.json does not move. The ledger grows to 96 rows.

- The committed leaderboard names the run it aggregates, and a workflow can move it to a newer one. leaderboard/results/source.json sits beside the signed board and says which committed results directory produced it; tests/test_leaderboard_version_claim.py reads the run from there instead of from a path typed into the test, and holds the pointer's measuredWith and generatedAt to the board's own fields. leaderboard-rebuild.yml (dispatch-only, signs with the repository secret, commits to the ref it is dispatched on) rebuilds the board from a named run and writes the pointer with it. It is a different act from leaderboard-restamp.yml, which re-aggregates the same run and refuses a moved measured_with: a rebuild expects measured_with to move, and refuses instead a run that is not a committed directory, a board whose measured_with is not what the run's shards say, a source older than the current one, a board that drops a (policy, suite) or its attribution, and a signature that does not verify against the published key. Written because the ten-task suite had been re-measured on 0.41.2 (#255) while the board still said 0.32.0, and the key that could move it lives only in the secret.

- The registry counts the distinct real policies measured, beside the families. provael coverage and watch/registry.json gain realPoliciesTested and realPolicyNames, derived from the same scan as realPolicyFamilies (same fixture exclusions, same applicability test; a policy counts with at least one applicable adversarial episode in a committed run), so the count rose to 2 (pi05, smolvla) the day the π0.5 arm landed rather than the day someone edited a constant. The meaning string says what it is not: a scaffolding adapter never run does not count, a registered adapter is not a measured one, a policy is not an architecture, and one checkpoint is not a survey. The README's coverage breakdown carries the count and the names, held to the counter by the same test that holds its family counts, and the generated list-policies inventory line states it beside the registered and scaffolding counts.

### Changed

- The published leaderboard aggregates the 14 September 2026 run. leaderboard-rebuild.yml rebuilt and re-signed leaderboard/results/leaderboard.json from results/smolvla_libero_object_suite_2026-09-14: measured_with ["0.41.2"], stale: false against 0.42.0, rows instruction 50/150, injection 2/50, visual 2/100, baseline 1/50 (not_applicable: mcp_tool_desc), attribution unchanged, signature verified against the published key. The 0.32.0 board's rows (62/150, 44/50) stay committed under their own run. leaderboard/method-equivalence.json records the 0.32.0 entry as settled by that re-run and argues, from the diff between the tags, why 0.41.2 → 0.42.0 moves none of these rows; the README's published-board paragraph and the Space card describe the board as it is now. The staleness gate and its tests judge the board by whether its verdict is declared and names its measuring version, not by which way it currently reads. Two sentences the results made false are corrected: the EAI03 coverage note no longer says weight_integrity has never met a real policy (the breadth probe ran it at n = 3), which moves the committed crosswalk, manifest and assurance goldens; and the control run's notes cite the reframe erratum by its number, [E-2026-12](https://www.provael.com/errata/#E-2026-12).

### Fixed

- published_measurement picks the record that represents the published body from the body's own lineage. It matched on (policy, suite, version); the LIBERO-10 shards that finished on the morning of 18 September 2026 shared all three with the Object body and were newer, so the record standing for the published body would have been one from another task suite. It matches the full lineage now, and watch/publish-freshness.json's measuredAt stays the Object body's.

## 0.42.0 · 18 September 2026

The first release since 0.41.2 on 9 September, and the first whose published measurement was made on a current release. It carries everything merged in between: the two extra control arms, the white-box surfaces on the real LeRobot adapter, the vla_arena scaffolding suite, the 17025-shaped test report, the Hugging Face Community Evals export, the clip and side-by-side video tooling, the keep-out path figures, the Machinery Regulation Annex III page, the Korea AI Framework Act crosswalk (#226), which the published tool had been missing since it reached main and the website on 12 September, the UN R155 / ISO/SAE 21434 rows (#251), the deployed-policy identity in the report (schema 6, #249), the priced white-box GPU arm (#252), the scheduled lane's campaign arithmetic (#254) and the workstation's results of 14 September (#255). Those results change the published measurement posture: the ten-task SmolVLA x LIBERO-Object headline reproduces on 0.41.2 (42/50 against the published 44/50), its controls change what the number means ([E-2026-12](https://www.provael.com/errata/#E-2026-12)), and watch/publish-freshness.json reads measuredWith: 0.41.2, one release behind this one and inside the two-release window. The stale banner www.provael.com carried since 9 September clears when the site re-pins to the new suite, which is its own sweep.

### Added

- The published measurement was re-run on a current release, and it reproduces. Eight results directories from the office workstation (RTX 2000 Ada, 14 September 2026) land under results/*_2026-09-14, each with its own generated README and a hand-written NOTES.md. The ten-task SmolVLA × LIBERO-Object suite on provael 0.41.2: roleplay 42/50 against the published 44/50, floor 1/50, clean task success 96 %; the ten-task control run; the three LIBERO suites the headline had never been measured on (Spatial and Goal: roleplay 0/30 and none 0/30 each, clean success 77 %; LIBERO-10: three of ten shards so far, 0/9); a breadth probe of every runnable family on one task at three seeds (only the instruction family fired; the weight-flip ladder, gradient_patch, the visual, patch and action families 0/3 each, N/A where the policy has no channel); the first π0.5 rollouts through the native pi05 adapter (benign 10/10, 0.39 s/step); and the demonstration clip's measured pair. With these committed, the Object body of real runs at 0.41.2 (the suite, its control run, the breadth probe, the clip and the canary: 656 attempts over the same ten tasks) covers and exceeds the 0.32.0 campaign under provael.watch.displacement, so watch/publish-freshness.json reads measuredWith: 0.41.2, releasesBehind: 0, isStale: false, and test_the_committed_ledger_is_inside_the_window_today now guards the closed window rather than the open one. The Spatial, Goal and LIBERO-10 runs are their own lineages (Campaign.task_suite) and neither join that body nor raise its bar. The scheduled lane's plan is re-modelled on the new body before its first shard: eight seeds, 880 planned attempts against 656, the margin left for the 0.41.2 runs still to land from the workstation. The ledger grows from 31 rows to 79. The website re-pins to the new suite in its own sweep.

- The white-box arm is registered and priced, not run. whitebox-pilot joins the stages of examples/gpu-ci/modal_libero_suite.py and the gpu-arm.yml dispatch list: weight_integrity (the ten-rung flip ladder) and gradient_patch against the real SmolVLA x LIBERO adapter, 2 tasks x 12 arms x 2 seeds, hard ceiling ~$4.00 from scripts/gpu_arm_plan.py. It needs provael 0.42.0 in the image, it is dispatched by hand and never scheduled, and registering it moves no figure: the registry counts committed results, not priced arms. While registering it: the Modal recipe's docstring had said for several releases that sharding "composes with --resume", and the container command never passed the flag; it does now, with the ledger beside the report on the Volume.

- UN Regulation No. 155 and ISO/SAE 21434:2021 enter the compliance catalogue. Five rows, all indicative: R155 para. 7.2.2.2(e) (the CSMS testing process), 7.3.3 (the exhaustive risk assessment against Annex 5 Part A) and 7.3.6 (testing before approval); ISO/SAE 21434 Clause 15 (TARA methods) and Clause 10 (product-development verification). Clause 11 vehicle-level validation and the R155 mitigation, detection and cryptography paragraphs are left out on purpose: a simulated result about one learned component produces nothing on-point for them. The rows exist because the automotive segment page already argued this mapping while the catalogue the site is checked against did not carry it. Facts read from the OJ text of R155 (CELEX 42021X0387: in force 22 January 2021, CSMS certificate valid for a maximum of three years), the consolidated Regulation (EU) 2019/2144 (Annex II row D4: 6 July 2022 and 7 July 2024) and the ISO catalogue (Edition 1, August 2021), all on 18 September 2026. docs/compliance/index.md gains the matching section; the committed insurer sample is regenerated (its summary counts move).

- Two more control arms, scrambled_text and roleplay_no_target, because the first two left one objection standing. nonsense_text is three tokens; roleplay renders as twenty. A 0/50 on three tokens never showed that a twenty-token out-of-distribution string is harmless, so the 44/50 headline was still consistent with "a long unfamiliar sentence derails SmolVLA" — the generic fragility RobustVLA (arXiv:2510.00037) and LIBERO-PRO already document, not attacker control. scrambled_text is the roleplay prompt's own tokens with the target noun replaced by a one-token filler and the order destroyed, whitespace-token count identical, seeded per episode. roleplay_no_target is the roleplay frame intact with only the graspable target replaced by the same filler, so a firing there is the imperative frame driving the policy and not the object. Both are built *from* RolePlayAttack.TEMPLATE, so the match survives a template change; both carry the control role and enter neither the ASR nor the benign FPR. --attacks control now resolves to four arms. The registry is 44 attacks (39 adversarial, unchanged); watch/registry.json, the inventory lines and the checked-in evidence manifest are regenerated.

- The weight-integrity family and the gradient-patch attack can now run against a real policy. LeRobotAdapter implements WeightAccessible over the weight matrix of model.action_out_proj — the last linear map before the action chunk on every flow-matching checkpoint lerobot 0.5.1 loads through it (SmolVLA: 15,360 parameters) — through a symmetric per-tensor INT8 view. A flipped INT8 value is applied to the live float weights as the delta the same flip would make in an INT8 deployment, and the clean vector restores the original floats bit-for-bit; the runner hands the adapter its reference operating point (the benign first frame of the run's first task at the run seed, via the new SensitivityReferencePolicy protocol) so d(danger)/d(weight) is one autograd pass at one fixed point per run. The danger proxy — translation energy of the executed chunk in normalized action space — is documented as a ranking heuristic, not the predicate's derivative. LeRobotAdapter.input_gradient backpropagates ||enc(x) − enc(x_clean)||² through the backend's own vision tower to the suite-frame image, and the runner attaches it to every attack exposing attach_gradient_oracle (the new InputGradientProvider / GradientOracleAttack protocols) — without a reset callback, because the oracle touches no per-episode state and a reset per refinement would make the attacked arm re-plan every step. pi0-FAST has no action_out_proj and reports the family *not applicable*; the deterministic stub enters neither path, so no CPU report moves. Verified on the real HuggingFaceVLA/smolvla_libero checkpoint on a CPU with a synthetic frame (no simulator): finite sensitivities, exact restore, finite input gradients, and the attack landing at its budget. No rate is claimed until the GPU run is committed.

- A vla_arena suite (registered scaffolding): the first suite whose unsafe predicate is DECLARED rather than fitted. VLA-Arena (PKU-Alignment, ICML 2026) evaluates each task's own (:cost …) clause every step and reports it in info["cost"]; the adapter's is_unsafe() is exactly "that clause fired", and calibration_signal() returns None so provael calibrate cannot replace a declared predicate with a threshold on its own output. Task names are <benchmark>/L<level>/<id> across the sixteen registered benchmarks; reset() refuses a task from another benchmark or level. The raw observation is handed to the policy adapter in lerobot's LIBERO-wrapper shape and features() returns the LIBERO env config — the same Panda, cameras, 8-dim state and 180-degree camera convention, and what VLA-Arena's own SmolVLA evaluator does — so a LIBERO checkpoint runs unchanged. Written against the package's source (14 Sep 2026) and tested on a fake shaped by it; VLA-Arena pins Python 3.11 / robosuite 1.5.1 / numpy 1.26.4 and needs its own environment. No run has been made through it, list-suites says so, and the coverage counts exclude it; the registry is 7 suites (3 fixtures, 3 gated simulators, 2 scaffolding) and watch/registry.json, the README and the quickstart inventory lines are regenerated.

- provael report --format test-report: a test report in the shape of ISO/IEC 17025:2017 clause 7.8. The 13 September regulatory re-read found that no certifier, notified body or insurer publishes acceptance of SARIF, OSCAL or an ML-BOM; what an assessor of a machinery technical file reads is a clause-7.8-shaped test report. The emitter lays a run out that way — identification, item under test (checkpoint, revision, digest), method, dates and location, conditions (hardware, precision, OS, lock digest), results with Wilson intervals and the benign floor, uncertainty (per-seed spread, anytime interval, the stochastic-sampling caveat), deviations (not-applicable arms, replayed episodes, skipped checks, missing manifest fields), scope, external providers, evidence state and verdict, authorisation — and an Annex A clause map rendered from provael.compliance, the single source of every framework mapping. It reads execution-manifest.json beside report.json when present. The report says in its first line that Provael is not an accredited laboratory, that this is not an ISO/IEC 17025 report and that no statement of conformity is made; the date of issue and the authorising person are conspicuous blanks for the human who signs, so the emitter introduces no wall-clock value and stamps no signature that never happened.

- provael export --format hf-eval --dataset <benchmark>: Hugging Face Community Evals entries. One .eval_results/*.yaml entry per *measured* arm — treatments, the benign baseline and the harmless-variation controls alike — with value the episode-level unsafe fraction and notes carrying the role, the counts, the 95% Wilson interval, the predicate state, the tool version and the checkpoint, so the Hub page shows the floor beside the rate. Arms with no applicable episode are omitted, never published as 0; date comes from the execution manifest when present and is otherwise left to the Hub's commit time; no verifyToken is ever emitted (that badge is for HF Jobs + inspect-ai runs). Task ids are the stable <suite>--<attack>, and provael.hf_eval.benchmark_eval_yaml writes the benchmark dataset's eval.yaml declaring every arm the registry can measure. The Hub side — registering the benchmark dataset and adding provael to huggingface.js's evaluation_framework enum — is a pull request a person opens; the emitter only writes the file. Schema read from huggingface.co/docs/hub/eval-results on 14 September 2026 (a work-in-progress feature).

- scripts/plot_keepout_paths.py draws every episode's end-effector path against the task's keep-out zone (top-down and side panels, one SVG per task, no plotting dependency): benign paths in blue, the chosen attack arm in red with a dot at the first unsafe step, the committed calibration or the documented default box shaded. It reads the trajectories every episode record has carried since 0.40, so a picture of forty benign paths against the box answers the #136 question — does the default box sit inside the benign workspace — faster than any rate. Reports written before 0.40 carry no trajectory; the script says so and draws nothing.

- docs/compliance/machinery-annex-iii-corruption.md: Regulation (EU) 2023/1230 Annex III §1.1.9 (protection against corruption) and §1.2.1 (safety and reliability of control systems) quoted verbatim from EUR-Lex (read 14 September 2026) and mapped clause by clause — under each paragraph and lettered point, the Provael artifact that speaks to it (injection arms for the connected-device paragraph, checkpoint integrity and the weight-corruption ladder for critical software, the keep-out predicate for "beyond its defined task and movement space", the harmless-variation controls for foreseeable human error, the measured defenses for "correct the machinery at all times") and what it does not establish; the start/stop, protective-device and assembly points are named as out of scope. Linked from the machinery-regulation page and from --format test-report's Annex A. Four remaining double-hyphen doc anchors fixed.

- The π0.5 cross-architecture study is publicly timestamped: the amended protocol and its falsifiers were deposited on Zenodo (10.5281/zenodo.22751558, 14 September 2026) before the π0.5 attack arms ran; docs/studies/pi0-openpi-transfer.md links the deposit.

- provael attack --video-dir DIR writes one MP4 per episode: the frames the policy actually saw (after the attack and any defense), red-bordered from the first step the suite's predicate fired. A runner argument, not a RunConfig field, so a run with recording on and off produce byte-identical reports and no attestation digest moves — the same rule audit_sink follows. imageio + imageio-ffmpeg (in the [lerobot] extra) are imported lazily; the CPU core gains no dependency. Episodes replayed from a --resume ledger have no clip. provael.video exposes FrameList for callers composing their own output. provael compose-video LEFT RIGHT --out puts two of those clips side by side, step-aligned — the benign twin beside the attacked episode at the same task and seed — with the shorter clip holding its last frame so an episode the predicate stopped early stays on screen at its verdict. Clips are written from the suite's new display_frame, which the LIBERO suite overrides to turn robosuite's 180-degree-rotated raw frame the right way up — the policy's processor does the same flip before inference, so the clip shows what the policy saw; the attack surface and every committed visual result stay on the raw frame.

- Release assets carry signed SLSA build provenance. release.yml runs actions/attest-build-provenance over the wheel, the sdist, the CycloneDX SBOM and SHA256SUMS before gh release create, so gh attestation verify <asset> --repo provael/provael names the workflow, tag and commit that built it. PyPI already had PEP 740 attestations for the wheel and sdist; the GitHub release assets carried nothing, which is what OpenSSF Scorecard's Signed-Releases check scored 0 on.

- The execution manifest's hardware names the CPU count and the CUDA device, not just the ISA — x86_64 described every run ever recorded and distinguished nothing.

- examples/gpu-ci/local_libero_sweep.py — the sharded LIBERO screen on one machine (N resumable shards, per-shard logs and timeouts, --commit provenance) with an aggregate that reproduces the published cross-shard statistics from the committed shards.

- docs/studies/pi0-openpi-transfer.md carries its design (n = 50 per arm, 5 seeds, horizon 280) and Amendment 1: the first leg runs π0.5 through the native pi05 adapter, with what that narrows and what it leaves open.

- A report now names the policy that executed, not only the checkpoint that was requested (report schema 6; closes #227). RunReport.deployed_policy is resolved by the adapter at load — concrete class, the checkpoint loaded and its Hub revision read from the local cache, the action unnormaliser actually applied (mode plus a digest over its statistics), the controller convention (action dim, chunk, executed steps, clamp, post-processing pipeline) — under one digest, beside model, which stays exactly what it was. Two deployments can agree on model and be executable-inequivalent (Tai 2026, arXiv:2606.03724); now the report shows it, combine refuses to pool shards whose deployed digests differ, the execution manifest's checkpoint_repo / checkpoint_revision are filled from it, and the 17025-shaped test report lists it as the item under test. Registered in attest._REPORT_FIELDS_ADDED_IN[6], so every attestation issued before schema 6 still verifies; schemas/report.v6.schema.json published.

- The reference server bounds what it accepts, where it binds and what it says (closes #229). The three serving-layer concerns found on another project's policy server, applied to provael's own provael serve first: request bodies are capped at MAX_BODY_BYTES (16 MiB) and refused with 413 — before they are read when Content-Length says so, at the first byte over the limit when streamed — never buffered; the bind host is loopback only unless the new --allow-remote flag is given, checked before anything is imported or started; and no response body carries a traceback, a path or a module name — validation errors return only location, message and type (FastAPI's default echoes the input), and an unhandled failure returns {"error": {"code": "internal_error", "request_id": …}} with the traceback logged under that id. The limiter is plain ASGI and unit-tested on the CPU core; the wired app is tested where the hosted extra is installed.

- The campaign's shards combine into one artifact that names exactly which runs it rests on. After each scheduled run, scripts/combine_campaign.py combines every shard under results/gpu-scheduled/campaign-<version>/ into campaign.json beside them, through provael.combine.combine_reports and shard_digests — the combiner provael evidence-manifest already uses — never a second one. The rule, written in provael.campaign's docstring and pinned by tests/test_campaign.py: the artifact is written continuously and says complete: false, designation: campaign-in-progress and preliminary: true until every planned shard is present; it is never report.json (a combined view has no single execution behind it) and never a ledger row (the campaign directory carries no execution manifest, so provael.watch counts the shards and only the shards, exactly as the 0.32.0 campaign is recorded); and it is refused outright when any shard lacks the four required provenance fields. STALE_AFTER_RELEASES and published_measurement are untouched: a complete campaign at a newer version displaces the old one on its own. make check-campaign joins make check-docs, with no wall-clock field anywhere.

- watch/campaign.json publishes the re-measurement's progress with a denominator. A page could say the published measurement was nine releases old and that something was measured today, and could not say the one thing that changes how both read: that a like-for-like campaign is being measured on a current release, how much is banked, and when it completes. scripts/gen_campaign_progress.py derives it from the plan, the lane's own PROVAEL_PIN (by AST, so the two cannot disagree), the committed shards, the attempts the published body sets as the bar (provael.watch.displacement) and the workflow's own cron: plan, toolVersion, target.attemptsToDisplace, banked.attempts and shards done and remaining, cadence, and projected.completion, dated from the newest shard's ended_at plus the runs still needed — a fact about the run, not about when the file was written, and null until the first shard lands. Documented in watch/README.md; the website is unchanged in this release and can render it in place of a flat STALE banner when it chooses.

### Changed

- The 44/50 result is no longer described as the attack redirecting the policy ([E-2026-12](https://www.provael.com/errata/#E-2026-12)). The control run of 14 September 2026 shows the roleplay frame with no target named exiting the envelope in 27/30 cells and the roleplay prompt's own tokens in destroyed order in 18/30, against 0/30 for two meaning-preserving rewordings. The number stands and was reproduced; the reading changes: SmolVLA leaves its safety envelope under a long, imperative, out-of-distribution string, which is a fragility finding about the policy and not a demonstration that the attacker's chosen object is acted on. The README's finding paragraph and results section, the cross-architecture findings page and the status of the semantic-vs-mechanical page say so now, and the erratum records the sentence that was wrong and the falsification clause that was written too narrowly to catch it.

- A family counts as exercised against a real policy only with an applicable episode. provael.coverage counted a family the moment any real-policy report contained it, whatever the episode said. The 14 September breadth probe ran every runnable family against SmolVLA, and eight came back with every episode marked not applicable — the policy has no channel those attacks reach — which would have moved the published figure from 3 families to 16 on runs that measured nothing. The counter now requires at least one applicable episode: 8 of 17 adversarial families exercised against a real policy (instruction, visual, injection, gradient_patch, optimized_instruction, optimized_patch, universal_patch, weight_integrity; one transferred, seven are measured nulls, five of those at n = 3), 9 stub-validated only, six of which were run and found not applicable — an absence of a surface, which is neither a measurement nor untested. watch/registry.json, the README breakdown and the counted-claims guard move with it, and the sentences that restated "three families" and "fourteen families" as facts about the registry are rewritten or, where they describe a study's own run or a board, named as such.

- The published measurement is displaced only by a re-measurement of the same thing, and the artifact now says how close one is. provael.watch.published_measurement summed attempts per exact tool version and took the largest bucket. That was right about probes and wrong in two ways that only show up once a lane runs on a schedule: a bucket keyed by exact version resets on every release, so a canary that re-pinned each release accumulated toward nothing; and a bucket counts a single-task run and a ten-task run in the same unit, so a long enough run on one task would have replaced the ten-task headline. The rule is now provael.watch.displacement: bodies are every real recorded run at one lineage and version (Campaign; a lineage is the policy, the suite and, where the task ids carry one, the task suite — smolvla x libero x libero_object), and a body supersedes the published one only by covering every task it covered with at least as many attempts, a tie going to the newer version. Stricter, never looser. The task suite is part of the key because the LIBERO adapter itself refuses to run two of them as one job ("one run is one suite"): a Spatial or Goal null measured the same night is a different measurement, not part of the Object campaign, and does not raise the bar for re-measuring it. Bodies stay one version each on purpose: combine.py refuses to pool shards across tool versions, so a body pooled across releases would be a number no evidence manifest can be built over and no site can re-pin. MeasurementRecord and watch/measurements.json rows carry the run's tasks; watch/publish-freshness.json gains published (the body behind measuredWith, with its taskSuite) and challenger (the newer body nearest to superseding it, with attemptsNeeded and tasksMissing); provael doctor prints the same as a re-measurement row. On the committed tree the published body is the 0.41.2 Object body of 14 September 2026 (656 attempts over ten tasks) and nothing newer challenges it yet; the lane's first shards at the next pin will appear there. Existing fields and their meanings are unchanged, so the site's reader needs nothing.

- The scheduled GPU lane measures a declared campaign, shard by shard, instead of a probe. Each gpu-scheduled.yml run measured one task, eight arms, two seeds: sixteen episodes that fed the age badge and could not move the published measurement. The arithmetic, plainly: the published body is 550 attempts over ten tasks at 0.32.0; the lane added fourteen attempts on one task per run, in a version bucket that reset every time a release re-pinned it. It measured on the current release twice a week and could not have displaced the published campaign this decade, while its own header priced that at a seventh of the credit. studies/scheduled_campaign/plan.json now declares the grid: the same checkpoint, suite, ten tasks and horizon as the published body (read out of its committed shards, and held to them by tests/test_campaign.py), every arm it ran plus the control family, and enough seeds that the completed campaign exceeds the published attempts rather than tying them — declared against the 0.32.0 body with six seeds, re-modelled the same day on the 0.41.2 Object body with eight (880 planned against 656), before any shard ran. Eighty (task, seed) shards, one Modal L4 container each, twelve episodes, inside a 45-minute timeout; provael.campaign.next_shards selects the next five from what is committed under results/gpu-scheduled/campaign-<pin>/, so a missed run costs a week and not correctness and a re-run of a slot that already landed selects the shards after it. The cadence stays Tuesday and Friday, which the age badge derives; at five shards a run the ceiling is $3.00 a run and $26.05 a month of the $30 credit (tests/test_gpu_scheduled_plan.py holds it), about $1.98 a run expected, sixteen runs, eight weeks. Shards that raise no longer take the run with them: the ones that landed are recorded and committed, then the job fails naming the rest. The pin is held for the campaign (tests/test_gpu_image_pin.py allows it to lag while a challenger accumulates there and requires every lane to pin the same version); do not bump it in a release PR while publish-freshness.json shows a challenger at it. Stated in the workflow header rather than implied: at the August-to-September cadence of nine minors in thirty-two days, the campaign completes a dozen or more minors behind. The lane makes the number move; it cannot make it current.

### Fixed

- The scheduled lane's provenance fix never took effect, and the lane recorded the gap anyway. Every execution manifest the scheduled GPU lane committed reports repository, commit, dep_lock_digest and precision under missing_fields. The 2026-09-15 run did so the day after gpu-scheduled.yml gained fetch-depth: 0 and a comment saying the commit gap was fixed: the driver resolved the tag and passed PROVAEL_COMMIT correctly, and the container installed the released 0.41.2 wheel, which reads no such variable — the fix lived in main, the lane runs releases. repository and dep_lock_digest had never been supplied by any caller in any version; precision was declared on the adapter base class and set by one adapter nobody has run. provael attack now fills all four: repository from PROVAEL_REPOSITORY or the checkout's origin, commit as before, dep_lock_digest as uv.lock:sha256:<hex> from a checkout's lock file or installed:sha256:<hex> over the running interpreter's distribution set (a container that pip-installed a release has no lock file; its installed set is the fact), and precision read off the loaded policy's parameters (fp32/bf16/fp16) for LeRobot and fp32 for the stub, which computes in float32. The image now states PROVAEL_REPOSITORY. And the workflow's "Record the measurement" step runs scripts/check_provenance.py before provael watch --record and refuses any shard that lacks one of the four, naming the shard and the field — so until PROVAEL_PIN names a release that carries these fields, the lane runs and records nothing, loudly, which is the honest state. tests/test_provenance_fields.py holds each source and the end-to-end fact that a stub run from this checkout carries none of the four gaps.

- benchmark_eval_yaml declared libero--none twice. The registry already carries the none baseline, and the README's recipe prepends it for emphasis, so the committed benchmark eval.yaml and tasks.jsonl listed the benign task twice. Arms are now de-duplicated in order of first appearance, the example files are regenerated (44 tasks, 440 rows), and a test reads every committed examples/hf-benchmark/*/eval.yaml so a duplicate cannot be committed again.

- gradient_patch could not move a frame ([E-2026-11](https://www.provael.com/errata/#E-2026-11)). Its objective's gradient is exactly zero at the clean frame and the search started from a zero perturbation, so against a smooth encoder the released module (0.39.1–0.41.2) never left the clean frame — found the first time it met a real vision tower. The loop now starts from a uniform draw inside the ε-ball, seeded from the episode seed and the step; a regression test uses an oracle shaped like the real objective. No published rate moves: the arm never ran against a VLA. The Diffusion Policy × PushT figures it shipped with came from a script outside this repository and are now labelled as that script's result, not this module's, in PRIOR_ART.md and the errata ledger.

- Three stale sentences in the compliance docs (found by the 13 Sep regulatory re-read): docs/compliance/index.md called ISO 25785-1 a "Working Draft… expected 2026–2027" (it is a Committee Draft since 8 May 2026 with no committed date; trackers read ~2028) and opened the routing box with the pre-adoption "political agreement May 2026" clause (it is Regulation (EU) 2026/1744, OJ 24 July 2026, in force 27 July); machinery-reg-2027.md said ISO 10218:2025 was "in force" (a standard is published, not in force).

- A LIBERO run is built for the task suite its tasks name. LiberoSuiteAdapter.reset() parsed the suite out of a "libero_spatial/3" task name and then discarded it: the environment came from the adapter's constructor default (libero_object) and the episode was recorded as libero_spatial/3 — the suite-level twin of the task-level misattribution _build_env already refuses, and the CLI had no other way to ask for spatial, goal or 10. make_suite() now takes the run's tasks and builds the adapter for the one suite they name (a list mixing suites is refused), reset() raises on a prefix that does not match the adapter — before the lerobot gate, so the failure reproduces on a machine without the simulator — and a bare "3" is task 3, not task 0. RunConfig is unchanged, so no report or attestation digest moves.

- Korea's AI Framework Act (Act No. 20676) enters the compliance catalogue — the first non-EU national statute in it. Three rows against Article 34(1), the duties on an operator "providing high-impact AI or AI-based products and services": subparagraph 1 (risk management plan), subparagraph 4 (human management and supervision) and subparagraph 5 (preparation and storage of documents demonstrating safety and reliability measures). Article numbers are the Act's own, read from the CSET English translation of Law No. 20676 (promulgated 21 January 2025, in force 22 January 2026 under its own Addenda Art. 1).

Three of the six subparagraphs are deliberately absent. 34(1)2 (explainability), 34(1)3 (user protection) and 34(1)6 (matters the Committee resolves) have no on-point Provael signal, and a row per subparagraph would read as coverage of the whole Article rather than of the part a red-team result speaks to.

34(1)4 carries required_eai=("EAI04",), so it is a gap unless the action channel actually ran. A human-supervision duty is about the commanded motion a supervisor has to catch, so citing it from a run that never touched the action channel would cite a measurement nobody made — the same gate the functional-safety rows already use. Its provael_signal says in as many words that the row evidences neither the supervisory arrangement itself nor any stop, interruption or rollback mechanism: those are system-design duties over the deployed system, and Provael exercises the policy, not the stop.

Scope here is sectoral, not "any physical machine". Art. 2(4) enumerates the areas that make a system high-impact — energy, drinking water, health care, medical devices, nuclear, biometrics for criminal investigation, employment and loan assessment, transport, public decisions, school assessment. A VLA policy is inside this Act when it is deployed in one of those, not merely because it drives a robot; a general warehouse or factory arm is not enumerated. The constant's docstring in compliance.py says so, because the opposite reading is the easy one.

results/smolvla_libero_object/attestation.insurer.json is regenerated from the unchanged source report: the assurance payload embeds the catalogue, so its payloadSha256 moves. The subject digest over report.json does not, and no attestation over a run report is invalidated.

- The freshness badge's colour comes from the day count it prints. It came from the fractional age, so at 2.3 days the badge read "2 days ago" in orange — a number inside the fresh window painted stale — and the guard that recomputes the badge at the age its own message asserts read 2 days as green and failed every pull request for the ~17 hours until the count ticked over (observed 14 Sep 2026 on the badge committed 13 Sep). Message and colour now derive from the same whole-day count; watch/freshness.json regenerated.

- The Python versions this package claims, and the date its citation names, are now checked rather than asserted. Three surfaces stated something nothing verified (#220, #221, #222).

The CPU gate runs on 3.13 as well as 3.12. requires-python = ">=3.12" admits 3.13 and 3.14, while .github/workflows/ci.yml's check job pinned python-version: "3.12" and nothing else — so "works on 3.13" was untested rather than merely unadvertised. check is now a matrix over ["3.12", "3.13"] with fail-fast: false, because a break on one interpreter is a fact *about* that interpreter and the other leg's result is what says whether it is version-specific. Only that job changed: release, docs, freshness, coverage-badge, gpu-arm, gpu-scheduled and leaderboard-submission stay on 3.12, since each publishes an artifact and a matrix there multiplies artifacts rather than coverage.

Programming Language :: Python :: 3.13 is added to the trove classifiers, on the strength of that leg and nothing else. PyPI's sidebar renders the classifier list rather than requires-python, so the page read 3.12-only against an open floor — someone skimming it could reasonably conclude 3.13 was unsupported.

3.14 is in neither list, and not because provael fails on it. The suite passes there — 1418 passed, 15 skipped on CPython 3.14.2 — but uv.lock pins numpy==2.2.6, which publishes a cp314 macOS wheel and no cp314 manylinux wheel, so uv sync --locked cannot install on ubuntu-latest under 3.14. It is the only cp314 gap in the locked CPU set. The lane and the classifier land together when the lock moves to a numpy that ships one (2.5.3 does), rather than by loosening --locked and giving up the property that every lane installs the set a release is built from. requires-python keeps its open upper bound for a separate reason: a cap is baked into every published sdist and wheel permanently, so a <3.14 added today would make v0.41.2 uninstallable on 3.14 even once 3.14 is verified — and CITATION.cff exists precisely so a citation names a version a reader can still install.

CITATION.cff said date-released: "2026-09-06"; the v0.41.2 tag was created 2026-09-09. The file's own comment already described this failure from the 0.25.0 release — a citation that "named a date three days before the artifact existed" — and then concluded that the date "is not machine-checkable against a tag, so it is on the release author". It recurred, by three days again. It *is* checkable, because the tag carries its creation date. tests/test_version_consistency.py::test_citation_date_matches_its_tag now compares date-released against git tag -l --format='%(creatordate:short)' v<version>, and skips when that tag is not in the clone so that neither a shallow checkout nor the release-prep commit — where version names the release about to be cut — fails for the wrong reason. test_the_tag_date_lookup_actually_resolves holds the other end, so a broken lookup goes red instead of turning the skip permanent. The date is no longer guarded by the release author's memory.

#220 reported the file at 0.29.1 / 2026-07-31. That part was already fixed and has read 0.41.2 since the 0.41.2 release; the date is what the report did not catch, and what the guard it asked for now covers.

- provael submit advised a command that does not exist. The generated PR body told reviewers to "verify the bundle offline with provael verify …"; the command is provael attest --verify. docs/errata.md's [E-2026-01](https://www.provael.com/errata/#E-2026-01) carried the same phantom command (provael verify … --print-payload) in its "how to tell whether a bundle is affected" block, now replaced with the jq | base64 -d pipeline that actually reads the payload.

- Meta-World was listed as runnable with nothing saying it cannot complete a CLI run. MetaworldSuiteAdapter refuses LIBERO's default keep-out box (it lies behind the Sawyer arm), the CLI has no zone option and no Meta-World calibration is committed, so --suite metaworld raised at reset. list-suites and doctor now print the gating note beside the suite (suites.SUITE_GATING_NOTES). Counts are unchanged: the suite is implemented and runnable from the Python API with a zone derived from its own benign envelope.

- Execution manifests from the Modal GPU lanes recorded commit: null. The container pip-installs the pinned release and has no git checkout. The drivers now resolve the pinned tag's commit on the runner and pass it as PROVAEL_COMMIT, which _emit_execution_manifest honours when it is hex-shaped (cli/_shared.py); the two GPU workflows check out full history so the tag resolves.

- SECURITY.md contradicted itself on when the CRA's open-source-steward duties start (11 September 2026 in one paragraph, 11 December 2027 in another). 11 December 2027, per Article 71(2), throughout.

- action.yml's Marketplace description was 172 characters against a 125-character cap. Shortened; tests/test_action_scripts.py now pins the cap.

- Errata ledger: the numbering note claimed the ledger and the provael.com mirror agreed entry-for-entry; the mirror had minted its own E-2026-05 on 3 September for a different correction. That correction is now recorded here as E-2026-09, the collision is stated, and [E-2026-10](https://www.provael.com/errata/#E-2026-10) records the reword-arm mix-up in the site's methodology note. Next free ID: E-2026-11.

- Stale docstrings that narrated a 29-attack / 15-family registry (coverage.py, tests/test_coverage.py), "controls not wired into the registry yet" (tests/test_controls.py), "90 test modules" (ci.yml) and "33 released versions" (tests/test_changelog_gate.py) now describe the tree as it is; the assertions beneath them were already right.

Earlier releases

## 53 earlier releases

- 0.41.2 · 9 September 2026
- 0.41.1 · 8 September 2026
- 0.41.0 · 8 September 2026
- 0.40.0 · 8 September 2026
- 0.39.5 · 6 September 2026
- 0.39.4 · 6 September 2026
- 0.39.3 · 4 September 2026
- 0.39.1 · 1 September 2026
- 0.39.0 · 1 September 2026
- 0.38.1 · 31 August 2026
- 0.38.0 · 24 August 2026
- 0.37.0 · 22 August 2026
- 0.36.2 · 21 August 2026
- 0.36.1 · 21 August 2026
- 0.36.0 · 21 August 2026
- 0.35.0 · 18 August 2026
- 0.34.0 · 18 August 2026
- 0.33.2 · 15 August 2026
- 0.33.1 · 13 August 2026
- 0.33.0 · 10 August 2026
- 0.32.0 · 8 August 2026
- 0.31.1 · 3 August 2026
- 0.31.0 · 2 August 2026
- 0.30.0 · 1 August 2026
- 0.29.1 · 31 July 2026
- 0.29.0 · 31 July 2026
- 0.28.0 · 30 July 2026
- 0.27.0 · 30 July 2026
- 0.26.1 · 28 July 2026
- 0.26.0 · 28 July 2026
- 0.25.1 · 27 July 2026
- 0.25.0 · 26 July 2026
- 0.22.0 · 23 July 2026
- 0.21.0 · 22 July 2026
- 0.20.0 · 21 July 2026
- 0.19.0 · 20 July 2026
- 0.18.0 · 19 July 2026
- 0.17.0 · 18 July 2026
- 0.16.0 · 15 July 2026
- 0.15.0 · 13 July 2026
- 0.14.0 · 13 July 2026
- 0.13.0 · 8 July 2026
- 0.12.0 · 8 July 2026
- 0.11.0 · 6 July 2026
- 0.10.0 · 5 July 2026
- 0.9.0 · 4 July 2026
- 0.8.0 · 4 July 2026
- 0.7.0 · 3 July 2026
- 0.6.0 · 30 June 2026
- 0.5.0 · 29 June 2026
- 0.4.0 · 28 June 2026
- 0.3.0 · 27 June 2026
- 0.1.0 · 27 June 2026

Full notes for these are in [CHANGELOG.md ↗](https://github.com/provael/provael/blob/main/CHANGELOG.md).

---
Canonical: https://www.provael.com/changelog/
