Repository navigation
plan: toolchain becomes a v0.12.0 prerequisite; scope v0.13.0 (REQ-32..38) - #308
Merged
Merged
Conversation
…s surfaced Triages the findings and blockers that came out of the v0.12.0 pre-release Mythos pass and the advisory sweep into a v0.13.0 scope, so none of them sit as untriaged issues invisible to the plan. REQ-31 / #302 Toolchain 1.94 -> 1.96 + re-qualify every formal-verification gate. SECURITY-FORCED: wasmtime 47.x carries RUSTSEC-2026-0327 (9.3 CRITICAL) plus two more, fixed ONLY in 48.0.4+/49.0.2+ which need rustc 1.95/1.96. Carries the obligation to record each MASKED gate's status before/after, so the mask cannot hide a toolchain-induced regression. REQ-32 / #303 Sign the WASM preamble. The signature does not bind the artifact bytes today, so a signed core module relabels to a component and still verifies. BREAKING — needs a SIGNATURE_VERSION bump and a migration decision. REQ-33 / #304 Stop retaining stale signed_hashes_set entries on re-sign, so v1 content cannot verify under a v2 header. Explicitly NOT fixed by v0.12.0's terminal-hash pinning. REQ-34 / #288 Device certificates must bind the device's public key — the HSM/provisioning path certifies a throwaway ephemeral key because ed25519_to_pem reads only the secret half. REQ-28 (#231 SigstoreBundle::verify + DER acceptance) and REQ-29 (#259 Ed25519 Rekor-v2 SET path) are already reassigned to v0.13.0 in the v0.12.0 release PR (#300), so v0.13.0's full scope is six requirements once that merges. Still untriaged on purpose, pending a scoping call rather than a silent guess: #289 (serial_number ignored / collisions), #280 (CycloneDX VEX per release — directly relevant now that advisories gate the release), #279 (musl builds), #272/#268 (varve's consolidated v1.0 ask). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aR3Md1h46K9wAUWMQiESH
…four issues
Per the scoping decision: ship v0.12.0 CLEAN rather than with a documented
exception, so the toolchain bump moves from next-release work into the current
release's critical path.
REQ-31 v0.13.0 -> v0.12.0. wasmtime 47.x carries RUSTSEC-2026-0327 (9.3
CRITICAL) plus -0326 and -0316, fixed ONLY in 48.0.4+/49.0.2+ which
need rustc 1.95/1.96. Shipping clean therefore requires the toolchain
bump BEFORE the tag, not after it. The alternative — excepting a 9.3
on the grounds that wasmtime is optional and absent from default
builds — was considered and declined.
Newly triaged into v0.13.0, so they stop being untriaged issues invisible to
the plan:
REQ-35 / #280 CycloneDX VEX per release, re-issued when advisories land.
Promoted by this cycle's experience: 12 advisories appeared in
one week and a workflow --ignore line carries its rationale
only in a YAML comment. A VEX states "not affected, because the
dependency is behind a non-default feature" machine-readably.
REQ-36 / #289 Honour CertificateConfig::serial_number. Also records that
ProvisioningSession writes a serial into its result that never
reaches the certificate, and that fixing REQ-34 makes the
serial collision live on the HSM path too.
REQ-37 / #279 musl Linux builds — the glibc-2.39 floor excludes Alpine and
older distros, which matters for a verification CLI meant to
run in arbitrary CI and air-gapped environments.
REQ-38 / #268 Scope what wsc still owes varve for keyless at v1.0. This whole
release line was varve-driven and their reports twice surfaced
real wrong-accepts, so their consolidated ask is the
highest-signal roadmap input available. Folds in #272.
Note v0.12.0 still lists REQ-28/29 here because this branch is cut from main;
#300 reassigns them to v0.13.0, after which v0.12.0's scope is
REQ-26/27/30 (verified) + REQ-31, and v0.13.0's is nine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aR3Md1h46K9wAUWMQiESH
avrabe
added a commit
that referenced
this pull request
Nov 1, 2026
Audited 2026-10-21: main's `required_status_checks.contexts` is `[]` and the repository has no rulesets. Every check in this repo is advisory. #305, #308, #292, #306, #310 and #297 all merged over a board nothing enforced, and the release that is being cut right now was gated by discipline rather than by CI. Requiring the individual contexts is the obvious fix and a trap in both directions: * rust.yml, formal-verification.yml and supply-chain.yml carry `on: pull_request: paths:`, so a PR touching none of their paths never gets a report from them — and a required context that never reports blocks the PR forever; * a job skipped INSIDE a workflow reports `skipped`, and on GitHub a skipped required context SATISFIES the merge button. So a paths filter is a gate-disabler either way. This adds one unfiltered workflow whose single job is the only context to require, and which judges what the other workflows actually concluded. Rules, all four failing closed: 1. every completed check-run must conclude in {success, skipped, neutral} — ONE predicate, an allowlist, so a conclusion GitHub invents tomorrow is rejected rather than inherited as green; 2. `Documentation claims hold` must be PRESENT. claim-check.yml has no paths filter, so its absence means it stopped running — the canary that keeps rule 1 from reading "everything was skipped" as mergeable; 3. everything must be finished; hitting the deadline FAILS, because a gate that gives up and passes is worse than no gate; 4. an API error, bad JSON or missing token exits non-zero. The audit's own finding is that a classifier's error path is usually its permissive one. What it deliberately does NOT do: re-derive from the path globs which workflows *should* have run. That would copy three filter lists into a fourth place, and a copy that drifts is how this class of bug starts. The honest cost is stated in the source — a job skipped by an in-workflow filter is taken at its word, with rule 2 as the floor. ## The decision table found a hole in itself `--self-test` drives 14 cases through the pure `classify()` function. Running mutations against the FIRST draft, two of six survived: BLOCKING = set() -> 14/14 still held "failure" added to OK -> 14/14 still held Both for the same reason: a failure was rejected twice, once by a blocklist and once by falling outside an allowlist, so either alone sufficed and neither could be isolated. That is the same redundant-guard pattern just reported in matrix.rs — reproduced from scratch in a new file, which is a decent argument for how easily it happens. Collapsed to the single allowlist, with the blocklist demoted to wording the error message. Re-running every mutation: allowlist gains "failure" 13/14 caught allowlist accepts everything 10/14 caught conclusion check removed 9/14 caught canary presence defeated 11/14 caught never waits for in-progress 11/14 caught waits on itself (deadlock) 12/14 caught 6 of 6. The workflow runs `--self-test` as its first step, so the gate proves itself before it judges anything. NOT wired into branch protection by this commit. A context that has never reported cannot be required without blocking every open PR, including the four in this release's stack. Protection gets set after this lands and reports once. Refs #316 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aR3Md1h46K9wAUWMQiESH
avrabe
added a commit
that referenced
this pull request
Nov 1, 2026
…#317) Audited 2026-10-21: main's `required_status_checks.contexts` is `[]` and the repository has no rulesets. Every check in this repo is advisory. #305, #308, #292, #306, #310 and #297 all merged over a board nothing enforced, and the release that is being cut right now was gated by discipline rather than by CI. Requiring the individual contexts is the obvious fix and a trap in both directions: * rust.yml, formal-verification.yml and supply-chain.yml carry `on: pull_request: paths:`, so a PR touching none of their paths never gets a report from them — and a required context that never reports blocks the PR forever; * a job skipped INSIDE a workflow reports `skipped`, and on GitHub a skipped required context SATISFIES the merge button. So a paths filter is a gate-disabler either way. This adds one unfiltered workflow whose single job is the only context to require, and which judges what the other workflows actually concluded. Rules, all four failing closed: 1. every completed check-run must conclude in {success, skipped, neutral} — ONE predicate, an allowlist, so a conclusion GitHub invents tomorrow is rejected rather than inherited as green; 2. `Documentation claims hold` must be PRESENT. claim-check.yml has no paths filter, so its absence means it stopped running — the canary that keeps rule 1 from reading "everything was skipped" as mergeable; 3. everything must be finished; hitting the deadline FAILS, because a gate that gives up and passes is worse than no gate; 4. an API error, bad JSON or missing token exits non-zero. The audit's own finding is that a classifier's error path is usually its permissive one. What it deliberately does NOT do: re-derive from the path globs which workflows *should* have run. That would copy three filter lists into a fourth place, and a copy that drifts is how this class of bug starts. The honest cost is stated in the source — a job skipped by an in-workflow filter is taken at its word, with rule 2 as the floor. ## The decision table found a hole in itself `--self-test` drives 14 cases through the pure `classify()` function. Running mutations against the FIRST draft, two of six survived: BLOCKING = set() -> 14/14 still held "failure" added to OK -> 14/14 still held Both for the same reason: a failure was rejected twice, once by a blocklist and once by falling outside an allowlist, so either alone sufficed and neither could be isolated. That is the same redundant-guard pattern just reported in matrix.rs — reproduced from scratch in a new file, which is a decent argument for how easily it happens. Collapsed to the single allowlist, with the blocklist demoted to wording the error message. Re-running every mutation: allowlist gains "failure" 13/14 caught allowlist accepts everything 10/14 caught conclusion check removed 9/14 caught canary presence defeated 11/14 caught never waits for in-progress 11/14 caught waits on itself (deadlock) 12/14 caught 6 of 6. The workflow runs `--self-test` as its first step, so the gate proves itself before it judges anything. NOT wired into branch protection by this commit. A context that has never reported cannot be required without blocking every open PR, including the four in this release's stack. Protection gets set after this lands and reports once. Refs #316 Claude-Session: https://claude.ai/code/session_012aR3Md1h46K9wAUWMQiESH Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands the release plan in rivet per the scoping decision: ship v0.12.0 clean, which makes the toolchain bump a current-release prerequisite rather than next-release work.
v0.12.0 — scope change
REQ-31 moved v0.13.0 → v0.12.0. wasmtime 47.x carries RUSTSEC-2026-0327 (9.3 CRITICAL) plus -0326 and -0316, fixed only in 48.0.4+/49.0.2+ which need rustc 1.95/1.96. Shipping clean therefore requires the toolchain bump before the tag. The alternative — excepting a 9.3 on the grounds that wasmtime is optional and absent from default builds — was considered and declined.
REQ-31 carries the obligation to record each masked gate's status before/after the bump —
kani format/merkle(tolerate_failure), Verus (assume(false), audit C-1), Rocq (stub), Bazeltest(audit C-7) — so a mask cannot hide a toolchain-induced regression.v0.13.0 — newly triaged
SIGNATURE_VERSIONbumpsigned_hashes_setentries (rollback)--ignorecarries its rationale only in a YAML comment. A VEX says "not affected, because the dep is behind a non-default feature" machine-readably.serial_number; also records thatProvisioningSessionwrites a serial that never reaches the cert, and that fixing REQ-34 makes the collision live on the HSM pathNote on the burn-down
v0.12.0 still lists REQ-28/29 here because this branch is cut from
main; #300 reassigns them to v0.13.0. After both merge: v0.12.0 = REQ-26/27/30 (verified) + REQ-31; v0.13.0 = nine requirements.Merge after #300 (both touch
artifacts/dev/features.yaml, different regions).🤖 Generated with Claude Code
https://claude.ai/code/session_012aR3Md1h46K9wAUWMQiESH