From c1c08bc49e4fc979b1b6e90236daaeaace5add51 Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Wed, 21 Oct 2026 21:11:45 +0200 Subject: [PATCH 1/4] One validator for a TARBALL_TOOLS entry, used by the deposit and its systest (#267) An edit in #266 fused two entries; the deposit would have dropped the varve-producer payload and the systest's live-pin check (which read two fields) passed. tools/tarball-entry.sh now decides an entry's shape (field count, plain names, a whole version, a platform token in any template). build-deposit-spec.sh checks every entry before fetching anything, and the live-pin check uses the same function. New negative control: fuse two live entries; the validator and the assembler must refuse it, the assembler before any gh call. Its first run caught a loose version check (`v0.35.0spar` passed), now strict. deposit-layer.sh passes locally, all three negative controls included. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019TNtfRjLNhEz82G2ggeeNu --- tools/build-deposit-spec.sh | 13 ++++++++ tools/systest/deposit-layer.sh | 32 +++++++++++++++++++ tools/tarball-entry.sh | 58 ++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 tools/tarball-entry.sh diff --git a/tools/build-deposit-spec.sh b/tools/build-deposit-spec.sh index 44f9c606..9c0cc388 100755 --- a/tools/build-deposit-spec.sh +++ b/tools/build-deposit-spec.sh @@ -97,6 +97,19 @@ WORK="${1:?usage: build-deposit-spec.sh }" PLATFORMS="${PLATFORMS:-aarch64-apple-darwin x86_64-apple-darwin aarch64-unknown-linux-gnu x86_64-unknown-linux-gnu}" +# Every TARBALL_TOOLS entry's shape, checked before anything is fetched and by +# the same function the systest's live-pin check uses (varve#267): a fused +# entry otherwise parses as a plausible first tool and silently drops the +# second. Sourced before the `cd` below, while $0 still resolves. +. "$(dirname "$0")/tarball-entry.sh" +for entry in $TARBALL_TOOLS; do + fault="$(tarball_entry_fault "$entry")" + if [ -n "$fault" ]; then + echo "::error::TARBALL_TOOLS entry '$entry' $fault" + exit 1 + fi +done + mkdir -p "$WORK" WORK="$(cd "$WORK" && pwd)" cd "$WORK" diff --git a/tools/systest/deposit-layer.sh b/tools/systest/deposit-layer.sh index ad66853e..7472e6d9 100755 --- a/tools/systest/deposit-layer.sh +++ b/tools/systest/deposit-layer.sh @@ -474,6 +474,15 @@ LIVE_WSC="$(wf_env WSC_VERSION)" { [ -n "$LIVE_TARBALL" ] && [ -n "$LIVE_VSIX" ] && [ -n "$LIVE_WSC" ]; } \ || fail "could not read TARBALL_TOOLS/VSIX_PACKAGES/WSC_VERSION out of $WORKFLOW — this check \ silently stops checking if the env block is reshaped" +# Each live entry's SHAPE, by the deposit's own validator (varve#267). The pin +# extraction below reads only the first two fields, so on its own it passed a +# fused `kiln:v0.6.0:kilndpulseengine/varve:...` as a valid `kiln v0.6.0`. +. "$REPO/tools/tarball-entry.sh" +for entry in $LIVE_TARBALL; do + fault="$(tarball_entry_fault "$entry")" + [ -z "$fault" ] || fail "the shipping TARBALL_TOOLS entry '$entry' $fault" +done +echo " $(set -- $LIVE_TARBALL; echo $#) live TARBALL_TOOLS entries, each well formed" PINS="$WORK/live-pins.txt" : > "$PINS" for entry in $LIVE_TARBALL; do @@ -496,6 +505,27 @@ another release's sums" fi echo " $(sort -u "$PINS" | wc -l | tr -d ' ') distinct repo pins, no repo at two versions" +# The shape check must be able to go red on the defect it exists for: fuse two +# real live entries exactly as #266 did, and require a refusal from BOTH the +# validator and the assembler that would have shipped it. +echo "== negative control: a fused TARBALL_TOOLS entry is refused" +set -- $LIVE_TARBALL +[ $# -ge 2 ] || fail "fewer than two live entries to fuse — this control proves nothing" +FUSED="$1$2" +[ -n "$(tarball_entry_fault "$FUSED")" ] \ + || fail "the shape check accepted the fused entry '$FUSED' — it cannot catch varve#267" +reset_layer_env +export TARBALL_TOOLS="$FUSED" +if run_assembler fused; then + fail "the assembler accepted the fused entry '$FUSED'" +fi +grep -qF "TARBALL_TOOLS entry '$FUSED'" "$WORK/logs/fused.log" \ + || { cat "$WORK/logs/fused.log"; fail "the assembler refused the fused entry, but not for its shape \ +— the control is not controlling what it claims"; } +[ ! -s "$WORK/logs/fused.gh" ] \ + || fail "the assembler called gh before refusing the fused entry — the shape check must come first" +echo " refused by the validator and by the assembler" + # ── 6. negative control: the gate must be able to go red ───────────────────── # selfhost.sh ends by proving its isolation can fail. The equivalent here is to # reintroduce the defect this gate exists for: delete the idempotence guard @@ -530,6 +560,8 @@ if removed != len(MARKERS): open(sys.argv[2], 'w').write('\n'.join(out)) PY chmod +x "$MUTANT" +# The assembler sources its entry validator from beside itself. +cp "$REPO/tools/tarball-entry.sh" "$WORK/" reset_layer_env if run_assembler mutated "$MUTANT"; then fail "the assembler built the layer with the idempotence guard REMOVED — this gate would not \ diff --git a/tools/tarball-entry.sh b/tools/tarball-entry.sh new file mode 100644 index 00000000..b4bcaca1 --- /dev/null +++ b/tools/tarball-entry.sh @@ -0,0 +1,58 @@ +# shellcheck shell=bash +# The shape of one TARBALL_TOOLS entry, decided in ONE place. +# +# [owner/]tool : version [: binary [: asset-template [: payload-version]]] +# +# Sourced by tools/build-deposit-spec.sh (the deposit) and by +# tools/systest/deposit-layer.sh (the live-pin check), so the two cannot +# disagree about what an entry is. They did (varve#267): an edit in #266 +# dropped the space between two entries, producing +# +# kiln:v0.6.0:kilndpulseengine/varve:v0.32.0:varve-producer:varve-producer-v%V-%T.tar.gz +# +# The deposit would have lost the varve-producer payload and asked the kiln +# archive for a binary named `kilndpulseengine/varve`; the systest read only the +# first two fields, saw a valid `kiln v0.6.0`, and passed. + +# Print why ENTRY is malformed, or nothing if it is well formed. +tarball_entry_fault() { + local entry="$1" fields + fields="$(printf '%s' "$entry" | awk -F: '{print NF}')" + if [ "$fields" -lt 2 ]; then + echo "has no ':version' — an entry names at least a tool and a version" + return + fi + if [ "$fields" -gt 5 ]; then + echo "has $fields ':'-separated fields; an entry has at most 5 — two entries fused by a missing space look exactly like this" + return + fi + local tool version binary template payload + IFS=':' read -r tool version binary template payload < Date: Wed, 21 Oct 2026 21:12:17 +0200 Subject: [PATCH 2/4] Land #256, #272 and #273 in rivet as proposed requirements REQ-ROLLBACK-002 (v0.44.0): a digest-pinned layer its line still supports installs on a shared machine (gale's CI, #256). REQ-OWNYAML-001 and REQ-SHIMFALLBACK-001: backlog, each needing a maintainer decision recorded in its description. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019TNtfRjLNhEz82G2ggeeNu --- artifacts/requirements.yaml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/artifacts/requirements.yaml b/artifacts/requirements.yaml index c77bc7e5..7394fd2f 100644 --- a/artifacts/requirements.yaml +++ b/artifacts/requirements.yaml @@ -5104,3 +5104,37 @@ artifacts: created-by: ai model: claude-opus-5-5 timestamp: 2026-10-21T16:52:38Z + + - id: REQ-ROLLBACK-002 + type: requirement + title: A deliberate, digest-pinned layer that its line still supports installs on any machine, whatever that machine has seen + status: proposed + release: v0.44.0 + description: "varve#256 (gale, reproduced on one machine and on ~11 shared CI runners; gale's CI red since 2026-10-08): the anti-rollback high-water mark is per MACHINE, but a pin is per PROJECT. Once any project on a shared runner installs a newer layer of a line, every other project's older pin on that line is refused as a rollback, although varve status reports it unyanked and in support. Refusing is correct against a per-machine baseline; the scope is what is wrong. REQ-ROLLBACK-001 protects against a stale validly-signed layer being PASSED OFF as current, and a digest written into a reviewed varve.toml is not that. Clauses (to be decided, see the issue's three remedies): (1) A layer selected by digest in the project's pin, which the line's signed line-status reports as neither yanked nor out of support, shall install regardless of the machine's high-water mark, and the install shall say that it did so and why. (2) Resolution by NAME, with no digest, keeps today's refusal, because that is the replay REQ-ROLLBACK-001 exists for. (3) The high-water mark shall still advance from what is installed, so the protection for name-resolved pins is not weakened. Alternative scopes to decide between: digest-as-consent (above), a per-project baseline, or an explicit --allow-stale. Kill-criterion from the issue: a layer varve status reports unyanked and in support cannot be installed on a machine that has seen a newer layer on the same line." + links: + - type: derives-from + target: REQ-ROLLBACK-001 + provenance: + created-by: ai + model: claude-opus-5-5 + timestamp: 2026-10-21T19:12:07Z + + - id: REQ-OWNYAML-001 + type: requirement + title: varve reads the inputs whose shape it defines through rivet's owned YAML stack + status: proposed + description: "varve#272, org policy (maintainer, 2026-10-13): a repository reads its OWN inputs through rivet-yaml; other formats stay only for exports and formats others define. varve's own inputs are all TOML today: varve.toml, varve-realms.toml, layer.toml, deposit-spec.toml, the previous-deposit state, mutation-scope.toml, and JSON state files. UNDECIDED, and the reason this is proposed, not approved: varve.toml and varve-realms.toml are files USERS write and commit, so moving them to YAML is a breaking change to every pinned project and every realm. Decide: migrate the user-facing files (with a transition that reads both), only the internal ones (deposit-spec, state), or keep TOML for user-facing inputs as a recorded exception to the policy." + provenance: + created-by: ai + model: claude-opus-5-5 + timestamp: 2026-10-21T19:12:07Z + + - id: REQ-SHIMFALLBACK-001 + type: requirement + title: A shim asked for a tool the pinned layer does not carry says what it can do instead + status: proposed + description: "varve#273 (maintainer): in a pinned project, a shim for a tool the layer does not carry refuses, so a tool installed locally cannot be used without editing PATH, which then shadows the layer. Asked for: fall through to the tool on PATH with a warning that it ran outside the layer; and where nothing is on PATH but another installed layer carries it, list the options. UNDECIDED, because it cuts against a stated property: a shim that falls back to an ambient binary is the mixed-toolchain hazard varve exists to close, and REQ-SHADOW-001 reports exactly that shadowing as a defect. Decide: fall-through with a warning (per project opt-in, or default), list-only (refuse but name what is on PATH and which layers carry it), or keep the refusal." + provenance: + created-by: ai + model: claude-opus-5-5 + timestamp: 2026-10-21T19:12:07Z From f6d53eed61fb1ebee0a7d087713b8d93126e82b1 Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Wed, 21 Oct 2026 21:15:25 +0200 Subject: [PATCH 3/4] Record the maintainer's decisions on #256, #272 and #273 REQ-ROLLBACK-002 (v0.44.0): digest-as-consent. REQ-SHIMFALLBACK-001 (v0.44.0): refuse, but list the options. REQ-OWNYAML-001 (v0.45.0): all of varve's own inputs through rivet-yaml, with a TOML transition. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019TNtfRjLNhEz82G2ggeeNu --- artifacts/requirements.yaml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/artifacts/requirements.yaml b/artifacts/requirements.yaml index 7394fd2f..205bab98 100644 --- a/artifacts/requirements.yaml +++ b/artifacts/requirements.yaml @@ -5108,9 +5108,9 @@ artifacts: - id: REQ-ROLLBACK-002 type: requirement title: A deliberate, digest-pinned layer that its line still supports installs on any machine, whatever that machine has seen - status: proposed + status: approved release: v0.44.0 - description: "varve#256 (gale, reproduced on one machine and on ~11 shared CI runners; gale's CI red since 2026-10-08): the anti-rollback high-water mark is per MACHINE, but a pin is per PROJECT. Once any project on a shared runner installs a newer layer of a line, every other project's older pin on that line is refused as a rollback, although varve status reports it unyanked and in support. Refusing is correct against a per-machine baseline; the scope is what is wrong. REQ-ROLLBACK-001 protects against a stale validly-signed layer being PASSED OFF as current, and a digest written into a reviewed varve.toml is not that. Clauses (to be decided, see the issue's three remedies): (1) A layer selected by digest in the project's pin, which the line's signed line-status reports as neither yanked nor out of support, shall install regardless of the machine's high-water mark, and the install shall say that it did so and why. (2) Resolution by NAME, with no digest, keeps today's refusal, because that is the replay REQ-ROLLBACK-001 exists for. (3) The high-water mark shall still advance from what is installed, so the protection for name-resolved pins is not weakened. Alternative scopes to decide between: digest-as-consent (above), a per-project baseline, or an explicit --allow-stale. Kill-criterion from the issue: a layer varve status reports unyanked and in support cannot be installed on a machine that has seen a newer layer on the same line." + description: "varve#256 (gale, reproduced on one machine and on ~11 shared CI runners; gale's CI red since 2026-10-08): the anti-rollback high-water mark is per MACHINE, but a pin is per PROJECT. Once any project on a shared runner installs a newer layer of a line, every other project's older pin on that line is refused as a rollback, although varve status reports it unyanked and in support. Refusing is correct against a per-machine baseline; the scope is what is wrong. REQ-ROLLBACK-001 protects against a stale validly-signed layer being PASSED OFF as current, and a digest written into a reviewed varve.toml is not that. Clauses (to be decided, see the issue's three remedies): (1) A layer selected by digest in the project's pin, which the line's signed line-status reports as neither yanked nor out of support, shall install regardless of the machine's high-water mark, and the install shall say that it did so and why. (2) Resolution by NAME, with no digest, keeps today's refusal, because that is the replay REQ-ROLLBACK-001 exists for. (3) The high-water mark shall still advance from what is installed, so the protection for name-resolved pins is not weakened. Kill-criterion from the issue: a layer varve status reports unyanked and in support cannot be installed on a machine that has seen a newer layer on the same line. . DECIDED 2026-10-21 by the maintainer: digest-as-consent (clauses 1-3), over a per-project baseline (a fresh checkout has none and would trust its first sight) and an --allow-stale flag (copied into every CI config until it means nothing)." links: - type: derives-from target: REQ-ROLLBACK-001 @@ -5122,19 +5122,21 @@ artifacts: - id: REQ-OWNYAML-001 type: requirement title: varve reads the inputs whose shape it defines through rivet's owned YAML stack - status: proposed - description: "varve#272, org policy (maintainer, 2026-10-13): a repository reads its OWN inputs through rivet-yaml; other formats stay only for exports and formats others define. varve's own inputs are all TOML today: varve.toml, varve-realms.toml, layer.toml, deposit-spec.toml, the previous-deposit state, mutation-scope.toml, and JSON state files. UNDECIDED, and the reason this is proposed, not approved: varve.toml and varve-realms.toml are files USERS write and commit, so moving them to YAML is a breaking change to every pinned project and every realm. Decide: migrate the user-facing files (with a transition that reads both), only the internal ones (deposit-spec, state), or keep TOML for user-facing inputs as a recorded exception to the policy." + status: approved + description: "varve#272, org policy (maintainer, 2026-10-13): a repository reads its OWN inputs through rivet-yaml; other formats stay only for exports and formats others define. varve's own inputs are all TOML today: varve.toml, varve-realms.toml, layer.toml, deposit-spec.toml, the previous-deposit state, mutation-scope.toml, and JSON state files. UNDECIDED, and the reason this is proposed, not approved: varve.toml and varve-realms.toml are files USERS write and commit, so moving them to YAML is a breaking change to every pinned project and every realm. Decide: migrate the user-facing files (with a transition that reads both), only the internal ones (deposit-spec, state), or keep TOML for user-facing inputs as a recorded exception to the policy. . DECIDED 2026-10-21 by the maintainer: everything, with a transition. Clauses: (1) varve.toml, varve-realms.toml, layer.toml and deposit-spec.toml shall be read as YAML through rivet-yaml, and for a transition of at least one release the TOML form is still read, with a deprecation note naming the YAML equivalent. (2) Internal state (previous-deposit state, mutation-scope, JSON state files) moves without a transition. (3) The removal of TOML is announced in the release before it happens. (4) A file present in both forms is refused rather than one silently preferred." provenance: created-by: ai model: claude-opus-5-5 timestamp: 2026-10-21T19:12:07Z + release: v0.45.0 - id: REQ-SHIMFALLBACK-001 type: requirement title: A shim asked for a tool the pinned layer does not carry says what it can do instead - status: proposed - description: "varve#273 (maintainer): in a pinned project, a shim for a tool the layer does not carry refuses, so a tool installed locally cannot be used without editing PATH, which then shadows the layer. Asked for: fall through to the tool on PATH with a warning that it ran outside the layer; and where nothing is on PATH but another installed layer carries it, list the options. UNDECIDED, because it cuts against a stated property: a shim that falls back to an ambient binary is the mixed-toolchain hazard varve exists to close, and REQ-SHADOW-001 reports exactly that shadowing as a defect. Decide: fall-through with a warning (per project opt-in, or default), list-only (refuse but name what is on PATH and which layers carry it), or keep the refusal." + status: approved + description: "varve#273 (maintainer): in a pinned project, a shim for a tool the layer does not carry refuses, so a tool installed locally cannot be used without editing PATH, which then shadows the layer. Asked for: fall through to the tool on PATH with a warning that it ran outside the layer; and where nothing is on PATH but another installed layer carries it, list the options. UNDECIDED, because it cuts against a stated property: a shim that falls back to an ambient binary is the mixed-toolchain hazard varve exists to close, and REQ-SHADOW-001 reports exactly that shadowing as a defect. Decide: fall-through with a warning (per project opt-in, or default), list-only (refuse but name what is on PATH and which layers carry it), or keep the refusal. . DECIDED 2026-10-21 by the maintainer: refuse, but list the options. Clauses: (1) A shim for a tool the pinned layer does not carry keeps refusing, and no ambient binary ever runs through a shim. (2) The refusal names the matching executable on PATH (outside the shim directory) and the exact command that runs it directly, and every installed layer that carries the tool, with the pin change that would select it. (3) The refusal still exits 1." provenance: created-by: ai model: claude-opus-5-5 timestamp: 2026-10-21T19:12:07Z + release: v0.44.0 From 6d4ced95f7e8b6edb80e02f2d0199118e49ea6e4 Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Thu, 22 Oct 2026 09:59:12 +0200 Subject: [PATCH 4/4] REQ-MUTANTDEBT-001 is verified by the first scheduled run on main Run 39736152885 at e7019ad completed all four shards inside the cap, each at its planned count (314/300/299/32): 945 mutants, 111 missed, read MEASURED by the report job on the pool. VER-MUTANTDEBT-001 and the v0.43.0 review record say so; the v0.43.0 changelog moves it to the verified list and adds the ureq cap and the #267 entry-shape check. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019TNtfRjLNhEz82G2ggeeNu --- CHANGELOG.md | 28 +++++++++++++++++++--------- artifacts/requirements.yaml | 2 +- artifacts/verification.yaml | 8 +++++--- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd0ffad2..59267b95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,12 @@ *An advisory arrives as a prepared line-status, a composition's support is capped by what it composes, and two independent reviews found what this release fixes.* -`rivet release status v0.43.0`: REQ-ENVQUOTE-001, REQ-NOPINNOTOOL-001 and -REQ-SBOMFIELDS-001 are **verified**. REQ-ADVISORYWATCH-001, -REQ-COMPOSEWINDOW-001 and REQ-MUTANTDEBT-001 ship **implemented**, not verified, -by the maintainer's decision: each has a clause that can only be shown after this -release, or open work moved to v0.44.0 (see "Not verified in this release"). +`rivet release status v0.43.0`: REQ-ENVQUOTE-001, REQ-NOPINNOTOOL-001, +REQ-SBOMFIELDS-001 and REQ-MUTANTDEBT-001 are **verified**. +REQ-ADVISORYWATCH-001 and REQ-COMPOSEWINDOW-001 ship **implemented**, not +verified, by the maintainer's decision: each has a clause that can only be shown +after this release, with the open work moved to v0.44.0 (see "Not verified in +this release"). ### Upgrading: behaviour that changes @@ -40,6 +41,16 @@ release, or open work moved to v0.44.0 (see "Not verified in this release"). refused. See "Upgrading". * rustls-webpki 0.103.16 and webpki-roots 1.0.10, for RUSTSEC-2026-0381 and RUSTSEC-2026-0382 (distrusted emSign intermediates). +* `ureq` is capped below 3.5: ureq 3.5.0 broke `wsc` 0.11.0, so a build + without varve's lockfile (`cargo install varve` without `--locked`) failed to + compile (pulseengine/sigil#319). + +### The deposit refuses a malformed tool entry + +Two `TARBALL_TOOLS` entries fused by a missing space used to parse as one +plausible tool and silently drop the other (#267). One validator now decides an +entry's shape, used by the deposit before it fetches anything and by its system +test; a negative control fuses two real entries and requires both to refuse. ### An advisory arrives as a prepared line-status @@ -83,8 +94,9 @@ The nightly mutation run shards the `[not-yet]` files of `mutation-scope.toml` (read at run time, never copied into the workflow) into jobs that finish inside the cap. A shard with no summary, or one that tested a different number of mutants than planned, is INCOMPLETE and turns the run red; it is never a zero. -The first full measurement: 939 mutants across 21 files, 111 surviving, the debt -that was declared and is now counted. +The first scheduled run on main (run 39736152885) completed all four shards +inside the cap, each testing exactly its planned count: 945 mutants across 21 +files, 111 surviving. That is the debt that was declared, now counted. ### Not verified in this release @@ -95,8 +107,6 @@ that was declared and is now counted. * **REQ-COMPOSEWINDOW-001.** The cap holds for direct includes only. An expired include is not refused, the override reason is not signed, and no layers repository passes `--manifest` yet. Moved to REQ-COMPOSEWINDOW-002. -* **REQ-MUTANTDEBT-001.** It is verified once a scheduled nightly run of the - merged workflow completes on main; the evidence so far is one dispatch run. * **#277:** a binary changed in the store after install still runs, for every pin, until `varve verify` is run. It is a design question, not a regression. diff --git a/artifacts/requirements.yaml b/artifacts/requirements.yaml index 205bab98..cafe9995 100644 --- a/artifacts/requirements.yaml +++ b/artifacts/requirements.yaml @@ -5055,7 +5055,7 @@ artifacts: - id: REQ-MUTANTDEBT-001 type: requirement title: Declared mutation debt is measured in full, not cut off at a time cap - status: implemented + status: verified release: v0.43.0 description: "varve#181: the nightly advisory mutation run is cancelled at its 90-minute cap every night after ~32% of 2,675 mutants, so 11 of the 22 not-yet files are never reached. The instrument that makes declared debt counted rather than hidden counts half of it. Clauses: (1) Every not-yet file in mutation-scope.toml shall be tested by some scheduled run that completes. (2) A run that does not complete shall be reported as incomplete, never as a measurement." provenance: diff --git a/artifacts/verification.yaml b/artifacts/verification.yaml index 35dee4d9..9cdcdd26 100644 --- a/artifacts/verification.yaml +++ b/artifacts/verification.yaml @@ -3429,11 +3429,11 @@ artifacts: - id: VER-MUTANTDEBT-001 type: verification title: Declared mutation debt is sharded from mutation-scope.toml and an unfinished shard reports INCOMPLETE, never zero - status: draft + status: verified release: v0.43.0 fields: method: test - baseline: "Measured 2026-10-21 with cargo mutants --list: the 21 current not-yet files hold 939 mutants and pack into 4 shards of 314, 300, 298 and 27 (the whole workspace was 2,675 in one job). An earlier version of this record said 850, which disagrees with its own shard sizes (they sum to 939) and with the run below. Falsified by negative control: a mutants.log truncated before its summary makes 'mutant-debt-plan.py report' exit 1 and print INCOMPLETE, a full log exits 0; a shard whose artifact is missing is INCOMPLETE too; and a log whose summary names a different number of mutants than the shard was planned with (fewer, or more) is INCOMPLETE too, with both numbers shown. The workflow has run on GitHub once, by workflow_dispatch: run 39541389293 on pulseengine/varve completed all 4 shards (939 mutants over 21 files, 111 missed, longest shard 66 minutes) and its report job printed MEASURED for all four. That run predates the planned-versus-tested comparison, so it did not exercise it. It measured about 4.75 mutants per minute (shards of 314, 300, 298 and 27 mutants took 66, 61, 59 and 7 minutes), not the ~7 the plan assumed; a 300-mutant target leaves about 24 minutes under the 90-minute cap. No SCHEDULED run of the merged workflow has completed yet, so REQ-MUTANTDEBT-001 clause 1, which asks for a scheduled run that completes, is not yet evidenced by a scheduled run." + baseline: "Measured 2026-10-21 with cargo mutants --list: the 21 current not-yet files hold 939 mutants and pack into 4 shards of 314, 300, 298 and 27 (the whole workspace was 2,675 in one job). An earlier version of this record said 850, which disagrees with its own shard sizes (they sum to 939) and with the run below. Falsified by negative control: a mutants.log truncated before its summary makes 'mutant-debt-plan.py report' exit 1 and print INCOMPLETE, a full log exits 0; a shard whose artifact is missing is INCOMPLETE too; and a log whose summary names a different number of mutants than the shard was planned with (fewer, or more) is INCOMPLETE too, with both numbers shown. The workflow has run on GitHub once, by workflow_dispatch: run 39541389293 on pulseengine/varve completed all 4 shards (939 mutants over 21 files, 111 missed, longest shard 66 minutes) and its report job printed MEASURED for all four. That run predates the planned-versus-tested comparison, so it did not exercise it. It measured about 4.75 mutants per minute (shards of 314, 300, 298 and 27 mutants took 66, 61, 59 and 7 minutes), not the ~7 the plan assumed; a 300-mutant target leaves about 24 minutes under the 90-minute cap. No SCHEDULED run of the merged workflow has completed yet, so REQ-MUTANTDEBT-001 clause 1, which asks for a scheduled run that completes, is not yet evidenced by a scheduled run. SCHEDULED RUN on main, 2026-10-22: run 39736152885 at e7019ad (the v0.43.0 release commit) completed all 4 shards inside the 90-minute cap (58/60/60/7 min). Each shard tested exactly its planned count (314/300/299/32), the report job on the self-hosted pool read all four MEASURED, 945 mutants across 21 files, 111 missed, 5 timed out, and the shards with survivors stayed green as an advisory run should. Clause 1 (every not-yet file tested by a scheduled run that completes) is met." steps: python3 -m unittest discover -s tools -p 'test_*.py' links: - type: verifies @@ -3450,7 +3450,7 @@ artifacts: release: v0.43.0 description: "The recorded independent review for v0.43.0 (REQ-INDEP-001, DD-019). Advisory at v0.x: a missing or dissenting review is visible, not blocking. Both reviews dissented, and the fixes landed before any requirement was promoted." fields: - baseline: "Two independent clean-room reviews (fresh context, refute-framed) of the v0.43.0 scope, 2026-10-21. REVIEW 1 at 8a3c26e: DISSENT on all six requirements. It reproduced a shell injection through varve.toml's [[export]] out (a newline ended the comment varve env prints, and eval ran the rest), and realmless dispatch with no trust root while status refused. It also found REQ-COMPOSEWINDOW-001's clauses 1-3 lost at merge, no end-to-end test of the cap, payloads the advisory watch dropped silently, the SBOM supplier ignoring the proof signer, the SBOM author depending on a local realm alias, and the mutation report not comparing tested against planned. All were fixed test-first in #278. REVIEW 2 at the #278 branch: no requirement REFUTED; four PARTIAL. It reproduced that ANY well-formed VARVE_TRUST_ROOT let a realmless pin dispatch a layer verify refused, and that shim install printed a dot-source line with command substitution live. Both were fixed test-first and negative-controlled in #278 (the layer's retained envelope must verify against the root in scope; the printed line is quoted literally). It also found fish never executed, so a CI job now installs fish and runs the round trip, failing rather than skipping when fish is absent. Outcome: REQ-ENVQUOTE-001, REQ-NOPINNOTOOL-001 and REQ-SBOMFIELDS-001 are verified, with clause amendments recorded on each artifact. REQ-ADVISORYWATCH-001 and REQ-COMPOSEWINDOW-001 stay implemented, their open clauses moved to REQ-ADVISORYWATCH-002 and REQ-COMPOSEWINDOW-002 (v0.44.0), by the maintainer's decision. REQ-MUTANTDEBT-001 waits for a scheduled nightly run on main. Tampering after install, which affects every pin, is #277." + baseline: "Two independent clean-room reviews (fresh context, refute-framed) of the v0.43.0 scope, 2026-10-21. REVIEW 1 at 8a3c26e: DISSENT on all six requirements. It reproduced a shell injection through varve.toml's [[export]] out (a newline ended the comment varve env prints, and eval ran the rest), and realmless dispatch with no trust root while status refused. It also found REQ-COMPOSEWINDOW-001's clauses 1-3 lost at merge, no end-to-end test of the cap, payloads the advisory watch dropped silently, the SBOM supplier ignoring the proof signer, the SBOM author depending on a local realm alias, and the mutation report not comparing tested against planned. All were fixed test-first in #278. REVIEW 2 at the #278 branch: no requirement REFUTED; four PARTIAL. It reproduced that ANY well-formed VARVE_TRUST_ROOT let a realmless pin dispatch a layer verify refused, and that shim install printed a dot-source line with command substitution live. Both were fixed test-first and negative-controlled in #278 (the layer's retained envelope must verify against the root in scope; the printed line is quoted literally). It also found fish never executed, so a CI job now installs fish and runs the round trip, failing rather than skipping when fish is absent. Outcome: REQ-ENVQUOTE-001, REQ-NOPINNOTOOL-001 and REQ-SBOMFIELDS-001 are verified, with clause amendments recorded on each artifact. REQ-ADVISORYWATCH-001 and REQ-COMPOSEWINDOW-001 stay implemented, their open clauses moved to REQ-ADVISORYWATCH-002 and REQ-COMPOSEWINDOW-002 (v0.44.0), by the maintainer's decision. REQ-MUTANTDEBT-001 waits for a scheduled nightly run on main. Tampering after install, which affects every pin, is #277. ADDENDUM 2026-10-22: both reviews' only open point on REQ-MUTANTDEBT-001 was clause 1's missing scheduled run. Scheduled run 39736152885 on main completed all four shards, each at its planned count, so it is verified and linked here." method: review steps: cargo test --workspace; VARVE_REQUIRE_FISH=1 cargo test -p varve --test cli -- --exact env_quotes_a_hostile_root_for_fish_so_eval_cannot_run_embedded_commands (CI job 'varve env round trip in fish') links: @@ -3460,6 +3460,8 @@ artifacts: target: REQ-NOPINNOTOOL-001 - type: verifies target: REQ-SBOMFIELDS-001 + - type: verifies + target: REQ-MUTANTDEBT-001 provenance: created-by: ai model: claude-opus-5-5