diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef736fa..e6001e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,14 @@ env: # 0.25.0 / 0.32.0 / 0.34.0 / 0.36.0 -> all PASS (266 warnings), coverage 63.9% >= 60. # So this closes a latent hazard, not an active defect. tools/varve/check-drift.sh now # reports ci.yml-vs-pin disagreement as BLOCKING, separately from PATH staleness. - RIVET_VERSION: v0.34.0 + # Tracks the varve LAYER, which is the authority for rivet (artifacts.pins does not name + # it). The 2026.08.4 -> 2026.09.2 layer migration moved rivet 0.34.0 -> 0.35.0, and + # check-drift reported the gap as BLOCKING until this followed. Measured on the same + # unchanged store before bumping: 0.34.0 / 0.35.0 / 0.36.0 all `Result: PASS (277 warnings)` + # and all clear the floor — but the coverage NUMBER moved, 63.9% under 0.34.0 and 64.8% + # under 0.35.0+. The metric is version-dependent, so a coverage figure is only meaningful + # beside the binary that produced it. + RIVET_VERSION: v0.35.0 # v0.24.0 adds `codegen --format wit` (AADL -> WIT, spar#319) used by the # WIT-derivation gate below; verified backward-compatible with jess's model # (parse + instance green). Bumped from v0.17.0. diff --git a/artifacts/findings.yaml b/artifacts/findings.yaml index 086469b..a5073dc 100644 --- a/artifacts/findings.yaml +++ b/artifacts/findings.yaml @@ -6689,3 +6689,78 @@ artifacts: detected-by: per-piece release-watch of meld v0.55.1 against the real fused cascade, 2026-09-09 severity: major triage-status: confirmed + + - id: AFD-118 + type: ai-found-defect + title: "varve realm migration (jess#261) executed: root rotated AND registry moved, layer 2026.08.4 -> 2026.09.2 — but it needed THREE coordinated changes, not the two the issue listed, plus a varve binary upgrade, and it would have made check-drift silently green on a toolchain it could not read" + status: open + description: |- + 2026-09-09. varve filed jess#261: the pulseengine rolling trust root rotated and the layer + registry moved, so varve-realms.toml and varve.toml had to change together. Executed, with + three things the issue did not mention — each found by doing it rather than by reading it. + + *** THE GUARD BUILT FOR THIS MOMENT WORKED *** + AFD-112 added tools/varve/check-realm.sh two days ago, encoding "a realm/pin pairing whose + root cannot verify the pinned layer is refused, in either direction". Exercised against the + REAL migration values before touching anything, all four states correct: + old root + 2026.08.4 exit 0 (before) + NEW root + 2026.08.4 exit 1 THE TRAP the issue warns about + old root + 2026.09.2 exit 1 the other half-state + NEW root + 2026.09.2 exit 0 (after) + A first run of that check reported exit 0 for all four because the harness piped through + `tail` and captured the PIPE's status — this repo's own standing rule, in the verification + of the guard that enforces it. Re-run bare, the guard is correct. + + *** THREE COORDINATED CHANGES, NOT TWO *** + The issue showed varve-realms.toml (registry + trust-root) and varve.toml `layer`. jess's + pin also carries a `digest`, because jess had hit two 2026.08.4 entries under different + digests and varve refuses to guess. Moving only the two produced: + error: source has no layer matching sha256:c1e6a418... + i.e. the OLD layer's digest, which does not exist in the new registry. Resolved by removing + the digest, installing BY NAME, and recording what varve actually verified — + sha256:67d585476be898dcd6c7f0e31fb8abe9c6548f708b3cb44804ad61cc5d999025 — never guessed. + + *** AND A VARVE BINARY UPGRADE THE ISSUE DID NOT STATE *** + The new varve-realms.toml carries `retired-roots`. varve 0.29.0 (what was on PATH) cannot + parse it: "unknown field `retired-roots`". That is not a niche failure — FIVE jess scripts + shell out to varve (tools/wit/regen.sh, tools/fusion-matrix/run.sh, + tools/appcompose/build-and-verify.sh, tools/varve/check-drift.sh, + hardware/renode/cascade-invoke/build.sh), and every one of them breaks until the binary is + >= 0.33.0. The error names the FIELD, not the cause. + The feature itself is good and visible: `varve list` now annotates every pre-rotation layer + "retired root, 2026-09-07 — layers here do not verify against the realm's current root" + instead of a bare "No valid signatures". That is the improvement the realms file promises. + + *** THE MIGRATION WOULD HAVE MADE A CHECKER SILENTLY GREEN *** + With varve 0.29.0 and the new realms file, every `varve run` fails, so check-drift.sh read + `-` for the entire VARVE-LAYER column and reported "no blocking drift", EXIT 0. A green + verdict produced by a toolchain the script could not read — the exact vacuity class this + campaign keeps finding in checkers rather than in code, and the migration would have + introduced it silently. check-drift now REFUSES (exit 2) when it cannot resolve the layer at + all, quoting varve's own error and naming the >= 0.33.0 requirement. + + *** WHAT THE LAYER JUMP MOVED, AND THE ONE REAL DISAGREEMENT IT SURFACED *** + `varve verify`: layer 2026.09.2 verified, signature OK, 12 tools match their signed digests + (was 11), in a disjoint realm namespace (a8ca9eb8... vs fdf90b77...) — the old realm is + frozen, not broken, exactly as the issue said. `varve inspect`: 44 payloads, 39 dispatched, + 5 held. rivet 0.34.0 -> 0.35.0, synth 0.58.0 -> 0.61.0, meld 0.52.0 -> 0.53.0, + loom 1.4.0 -> 1.4.1, spar unchanged. + That made rivet DRIFT-BLOCKING: ci.yml pinned 0.34.0 against a layer carrying 0.35.0, and + the varve layer is rivet's authority because artifacts.pins does not name it. Aligned + ci.yml to 0.35.0, measured first on the same unchanged store: + rivet 0.34.0 / 0.35.0 / 0.36.0 -> all `Result: PASS (277 warnings)`, all clear the floor + BUT the coverage NUMBER moved: 63.9% under 0.34.0, 64.8% under 0.35.0 and 0.36.0 + A compliance figure is only meaningful beside the binary that produced it; AFD-100's 63.9% + is a 0.34.0 number. + Also surfaced: jess's loom campaign pin (1.4.0) is BEHIND the layer (1.4.1). check-drift + only reported the campaign-AHEAD direction, so a pin that had fallen behind was invisible — + and behind is the direction that silently misses an upstream fix. Now reported both ways. + + NOT URGENT AND NOT TREATED AS SUCH: the issue states the old realm still serves every layer + it served yesterday. Nothing was deleted; this was done because the migration is cheap now + and expensive to discover later, not because anything was failing. + tags: [varve, supply-chain, trust-root, migration, vacuous-gate, drift, afd-112, afd-117, jess-261] + fields: + detected-by: executing the varve realm migration requested in jess#261, 2026-09-09 + severity: major + triage-status: confirmed diff --git a/tools/varve/check-drift.sh b/tools/varve/check-drift.sh index cae7bd5..d2c66d7 100755 --- a/tools/varve/check-drift.sh +++ b/tools/varve/check-drift.sh @@ -38,7 +38,22 @@ campaign_pin() { # tool -> version named by artifacts.pins, or empty "$ROOT/tools/deps/artifacts.pins" 2>/dev/null | head -1 } -compared=0; single=0; advisory=0; cipin=0; nocipin=""; ahead="" +# REFUSE if varve cannot resolve the layer at all. +# +# The 2026.09.2 migration made this concrete: the new varve-realms.toml carries `retired-roots`, +# which a varve < 0.33.0 cannot parse, so EVERY `varve run` failed and the VARVE-LAYER column +# read `-` for every tool — and this script reported "no blocking drift", exit 0. A green +# verdict produced by a broken toolchain rather than by agreement, which is the vacuity class +# this repo keeps finding in checkers. Fail loudly and name the cause instead. +if ! varve which rivet >/dev/null 2>&1 && ! varve which meld >/dev/null 2>&1; then + echo "CANNOT RESOLVE THE VARVE LAYER — refusing to report a drift verdict." >&2 + varve which rivet 2>&1 | head -3 | sed 's/^/ /' >&2 + echo " A 'no drift' result here would be a verdict about a toolchain this script could not" >&2 + echo " read. If the realms file mentions retired-roots, varve must be >= 0.33.0." >&2 + exit 2 +fi + +compared=0; single=0; advisory=0; cipin=0; nocipin=""; ahead=""; behind="" printf '%-8s %-11s %-11s %-11s %-11s %s\n' TOOL PATH VARVE-LAYER CAMPAIGN CI-YML STATUS for t in rivet spar meld synth loom sigil; do p="$(ver "$t")" @@ -99,7 +114,14 @@ for t in rivet spar meld synth loom sigil; do # unnoticed until removing ci.yml's SYNTH_VERSION made synth and loom read `-` here while # the prose still asserted agreement for them. Found by clean-room verification. if [ -n "$auth" ] && [ -n "$c" ]; then cipin=$((cipin+1)); else nocipin="$nocipin $t"; fi - [ -n "$cp" ] && [ -n "$v" ] && [ "$cp" != "$v" ] && ahead="$ahead $t($v->$cp)" + # Report the campaign/layer gap in BOTH directions. Only reporting "ahead" made a + # campaign pin that had fallen BEHIND the layer invisible — and behind is the direction + # that silently misses a fix, which is the worse one. + if [ -n "$cp" ] && [ -n "$v" ] && [ "$cp" != "$v" ]; then + newest="$(printf '%s\n%s\n' "$cp" "$v" | sort -V | tail -1)" + if [ "$newest" = "$cp" ]; then ahead="$ahead $t(layer $v -> campaign $cp)" + else behind="$behind $t(campaign $cp < layer $v)"; fi + fi fi printf '%-8s %-11s %-11s %-11s %-11s %s\n' "$t" "${p:--}" "${v:--}" "${cp:--}" "${c:--}" "$st" done @@ -138,6 +160,11 @@ if [ -n "$ahead" ]; then echo "CAMPAIGN AHEAD OF THE VARVE LAYER (deliberate, see artifacts.pins):$ahead" echo " Not drift: artifacts.pins is the authority for these, and the reason is recorded there." fi +if [ -n "$behind" ]; then + echo "CAMPAIGN PIN BEHIND THE VARVE LAYER:$behind" + echo " Not blocking — artifacts.pins is the authority — but this is the direction that" + echo " silently misses an upstream fix. Worth a release-watch, not a shrug." +fi if [ -n "$nocipin" ]; then echo "NOT COMPARED against ci.yml (no *_VERSION there):$nocipin" echo " These rows say nothing about CI. Claiming agreement for them would be a verdict" diff --git a/varve-realms.toml b/varve-realms.toml index 51971f9..0abb0e7 100644 --- a/varve-realms.toml +++ b/varve-realms.toml @@ -7,5 +7,20 @@ # the rolling channel makes no qualification promise. [realm.pulseengine] -registry = "oci://ghcr.io/pulseengine/varve/layers" -trust-root = "4e771dc62a08be89e3450f8cd807da58ff70af4a4e124ebf2d2b71684cfd9973" +registry = "oci://ghcr.io/pulseengine/layers" +trust-root = "7d3b892e6a33c70043becc708e08042e1cef0d54dd5ae6f23d7d4c68de1da1a0" + +# Roots this realm has retired (REQ-ROTATE-002). DIAGNOSTIC ONLY — nothing +# verifies against these, ever. They exist so that a consumer whose pin was +# signed by an old root is told WHICH root and WHEN, instead of getting +# "No valid signatures", which is indistinguishable from a forgery. +# +# This is not key rotation and must not be read as one: nothing signs "this new +# root replaces the old one" (`varve docs threat-model`). It only means the +# failure explains itself. +retired-roots = [ + # Retired in v0.32.1. It existed ONLY as a write-only CI secret, so nobody + # could back it up, move it, or rotate with it — varve#110. Every layer from + # 2026.08.0 to 2026.09.1 was signed by it and none of them verify here. + { key = "4e771dc62a08be89e3450f8cd807da58ff70af4a4e124ebf2d2b71684cfd9973", retired = "2026-09-07", last-layer = "2026.09.1" }, +] diff --git a/varve.toml b/varve.toml index 5f3683e..e6ba42c 100644 --- a/varve.toml +++ b/varve.toml @@ -13,9 +13,16 @@ manifest-version = 1 [toolchain] realm = "pulseengine" channel = "rolling" -layer = "2026.08.4" -# The layer NAME is not sufficient on its own: two entries for 2026.08.4 exist in this +layer = "2026.09.2" +# The layer NAME is not sufficient on its own: two entries for 2026.08.4 existed in the old # realm under different digests, and varve refuses to guess between them rather than -# silently picking one. That refusal is the feature. This digest is the one `varve -# install` fetched and verified against the realm's trust root on 2026-08-27. -digest = "sha256:c1e6a418f87dedfb97caca3d983cb7a17b3365492f66de920487a8470f064fe1" +# silently picking one. That refusal is the feature. +# +# MIGRATED 2026-09-09 (jess#261). The rolling trust root rotated AND the registry moved, so +# THREE things had to change together, not the two the issue listed: varve-realms.toml +# (registry + trust-root), this `layer`, and this `digest`. Moving the first two and leaving +# the digest gave `error: source has no layer matching sha256:c1e6a418...` — the old layer's +# digest, which does not exist in the new registry. +# This digest is the one `varve install` fetched and verified against the NEW trust root on +# 2026-09-09; it was resolved by name and then recorded, never guessed. +digest = "sha256:67d585476be898dcd6c7f0e31fb8abe9c6548f708b3cb44804ad61cc5d999025"