Skip to content

feat(#1000): ship 'synth verify' in released artifacts + non-vacuity smoke gate (RQ-58-SHIPVERIFY) - #1002

Merged
avrabe merged 2 commits into
mainfrom
feat/ship-verify-feature-1000
Aug 19, 2026
Merged

avrabe merged 2 commits into
mainfrom
feat/ship-verify-feature-1000

Conversation

@avrabe

@avrabe avrabe commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Closes #1000 (RQ-58-SHIPVERIFY). synth verify existed in the tree and in --help, failed closed with an exemplary message — and was in no artifact any user could obtain (confirmed on 0.55.0 and 0.57.0: all four platform tarballs lacked --features verify). For a compiler whose whole argument is verified code generation, this ships the feature and gates it so it cannot silently regress to help text.

1. Released binaries carry verify

  • release.yml: all four platform builds (x86_64/aarch64 Linux, x86_64/aarch64 macOS) now build -p synth-cli --features verify; the stale "deliberate follow-up decision" comment is replaced with the measured basis.
  • No Z3 ships. Re-measured on this base: cargo tree -p synth-cli --features verify → 0 z3 nodes; --features verify,synth-verify/z3-solver → 2. verify = ["synth-verify"] (pure-Rust ordeal since Adopt ordeal (pure-Rust QF_BV) in synth-verify; demote Z3 to differential oracle, then drop static-link-z3 #553/v0.27); the Z3 differential oracle remains a separate opt-in that links the system libz3 and is not enabled here.
  • npm channel: no change needed — the @pulseengine/synth wrapper downloads and checksum-verifies these same release tarballs at install time (npm/install.js), so it inherits the feature.
  • crates.io publish path: source-only publish, untouched. cargo install synth-cli users still choose features themselves (verify is deliberately not made a default feature — that would change every workspace build; named residual, not silently dropped).

2. The non-vacuity gate (the part that matters)

scripts/release_verify_smoke.sh runs against the binary extracted from each packaged tarball (not a dev build): compiles a module with it, runs synth verify on that module, and asserts a real verdict — exit 0, the verdict line, and a synth-verify-v1 report whose summary proves verified >= 1 && failed == 0 (counts, not sentence-greps). All four tarballs are executed: linux-x86_64 + both macOS natively (x86_64-darwin under Rosetta on the arm64 runner), the cross-built aarch64-linux tarball under qemu-aarch64 -L /usr/aarch64-linux-gnu.

Red-first evidence (local, this branch):

$ cargo build -p synth-cli   # WITHOUT verify
$ bash scripts/release_verify_smoke.sh target/debug/synth ; echo rc=$?
FAIL: synth verify exited non-zero — this artifact cannot verify
Error: this `synth` binary was built without the `verify` feature — ...
rc=1

$ cargo build -p synth-cli --features verify
$ bash scripts/release_verify_smoke.sh target/debug/synth ; echo rc=$?
verdict: 2 verified / 0 failed / 0 unknown / 3 declined across 5 applied rule kinds
PASS: released artifact runs 'synth verify' to a real verdict
rc=0

And the red direction is CI-pinned, not authoring-time-only: the fact-spec-oracle job now builds a verify-less binary, asserts the smoke script FAILS with exactly the capability error (wrong-reason failures are also red), then re-runs it green on the verify build. A gate whose red leg is not exercised goes vacuous silently — this repo has shipped that class.

3. Capability check before the banner

Previously the four Translation validation: lines — including Strategy: Per-rule SMT verification (ASIL D path) — printed before the tool discovered it could not verify, so a log-scraper found the ASIL-D strategy line in a run that verified nothing (exit code was already correct, #124). The check now runs before the banner: an incapable binary prints nothing verification-shaped before failing.

Red-first: tests/verify_banner_1000.rs (no required-features; the missing-capability half runs in the plain workspace suite) fails on the pre-fix ordering — observed: ASIL-D strategy line printed by a binary that cannot verify — and passes with the fix. The #[cfg(feature = "verify")] half (wired into fact-spec-oracle) pins that a capable binary still prints the banner and ends in All functions verified successfully..

Also de-staled the synth verify help line ("via Z3" → SMT translation validation, pure-Rust ordeal) — measured above.

Out of scope, named not silently dropped

#1000's synthesize-vs-compile description overlap, --format json, help-line width (real, minor, not this lane's subject); making verify a default cargo feature; README:98's stale Z3 sentence (corrected in #1001 — not duplicated here).

Step 5 — witness MC/DC (#978 floors)

The reordered capability check adds one decision — in synth-cli, which is outside the scored scope. The harness's full in-workspace closure is synth-mcdc-harness + synth-core/synth-cfg/synth-opt/synth-synthesis/synth-backend-riscv (per cargo tree), and git diff --name-only main...HEAD intersects it in 0 files (Cargo.lock also unchanged, rustc pinned 1.96.1): the harness wasm this branch produces is byte-for-byte main's, so the counts cannot move.

I re-derived locally anyway at rustc 1.96.1 and READ THE GAP ROWS rather than trusting the argument: every gap-row function is in the untouched crates (validate_final_allocation_rv32, ensure_supported_target, compile_function_with_opts, static_data_addr::resolve_owner/validate_reloc_resolutions*) — none is this lane's code. Honest boundary on the local numbers: my local witness-mcdc is 0.28.0, not the CI-pinned 0.42.0, and the local run reports 122 cond / 50 proved / 51 dead vs floors 130/57/≤50 — since the harness inputs are provably identical to main's, that local red is the unpinned local witness version measuring differently, not a delta from this change (it would reproduce identically on main). The PR's mcdc-structural-coverage job (pinned witness 0.42.0, pinned host) is the authoritative floors gate — read its result, not my local one.

Step 6 — sigil / attestation

Assessed, not defaulted to N/A: this PR changes what the released tarballs contain (a larger binary with the ordeal solver linked in). The existing attestation chain covers the new content without modification — SLSA provenance is generated per-release over release-assets/*.tar.gz (subject digests are computed from the new bytes) and the cosign-signed SHA256SUMS.txt transitively covers them; the smoke step runs before upload/attestation and does not modify the archives. No new artifact type or build stage is introduced, and synth's native tarballs are not sigil-signed wasm (sigil is not part of this repo's release chain). Nothing to add.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

avrabe and others added 2 commits August 19, 2026 07:16
…ategy line from a binary that cannot verify

Red-first: tests/verify_banner_1000.rs (no required-features) fails on the
pre-fix ordering — the incapable binary printed all four
'Translation validation:' lines including 'Strategy: Per-rule SMT
verification (ASIL D path)' before bailing. Exit code was already correct
(#124); now nothing verification-shaped precedes the failure. The verify
half (wired into fact-spec-oracle) pins that a capable binary still prints
the banner and ends in a real verdict. Also de-stales the 'via Z3' help
line (measured: 0 z3 nodes under --features verify).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…ity smoke gate (RQ-58-SHIPVERIFY)

release.yml: all four platform builds now pass --features verify (pure-Rust
ordeal since #553 — re-measured: 0 z3 nodes; the z3 differential oracle
stays a separate synth-verify/z3-solver opt-in). The npm channel inherits
the feature: the @pulseengine/synth wrapper downloads and checksum-verifies
these same tarballs at install time.

Non-vacuity gate: scripts/release_verify_smoke.sh runs against the binary
EXTRACTED FROM EACH PACKAGED TARBALL (aarch64-linux under qemu-user; the
x86_64 macOS tarball under Rosetta on the arm64 runner) — compiles a
module, runs 'synth verify' on it, and asserts a real verdict (exit 0,
verdict line, synth-verify-v1 report with >=1 verified / 0 failed).
Red-first, demonstrated at authoring AND pinned in CI: the
fact-spec-oracle job builds a verify-less binary and asserts the script
FAILS with exactly the capability error, then re-runs it green on the
verify build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-cli/src/main.rs 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 4f1d455 into main Aug 19, 2026
57 checks passed
@avrabe
avrabe deleted the feat/ship-verify-feature-1000 branch August 19, 2026 06:04
avrabe added a commit that referenced this pull request Aug 19, 2026
…ildcard was a LIVE executed miscompile (RQ-58-WILDCARD) (#1003)

* fix(#946): i64-width vstack drift — if/br_if/br_table/memory.copy/call args never left the width stack

The RQ-58-WILDCARD classification pass found the live hole the lane brief
predicted, and it is worse than latent: an EXECUTED cross-shape miscompile.

wasm_stack_effect listed If | BrIf | BrTable as (0, 0) although each pops
one i32 (condition / table index), let the `_ => (0, 0)` wildcard absorb
MemoryCopy/MemoryFill (which pop 3), and its Call row (0, 1) never popped
call arguments. Every one of those stale entries shifts infer_i64_locals'
width stack, so an i64 local set past one of these shapes was inferred i32,
given a 4-byte slot and a single-word STR/LDR — hi half silently dropped.
The inference is shared with the RV32 selector (#312), so the drift was
cross-backend.

Executed proof (unicorn vs wasmtime, cortex-m4 --relocatable --no-optimize),
all returning (0x1_0000_0005 >> 32), expected 1:
  br_if shape       -> 32
  if shape          -> 32
  br_table shape    -> 32
  memory.copy shape -> 0
All four return 1 after the fix.

Fix:
  * wasm_stack_effect: If | BrIf | BrTable -> (1, 0);
    MemoryCopy | MemoryFill -> (3, 0); the trailing `_ => (0, 0)` wildcard
    is GONE — the match is fully enumerated over all 279 WasmOp variants
    (SIMD rows stated per shape), so a new variant fails to COMPILE until
    its stack effect is stated (#615 expand-or-loud-reject pattern).
  * infer_i64_locals: pops call ARGUMENTS against the real arg-count tables
    (func_arg_counts / type_arg_counts, one slot per value — the decoder's
    unit) and call_indirect's table-index operand; empty tables degrade to
    the pre-#946 zero-arg approximation, matching lower_call's own #195
    fallback. Signature threaded through compute_local_layout, both ARM
    call sites, and the RV32 selector (which has no type table;
    call_indirect is Unsupported there, so the empty slice cannot under-pop
    on anything that compiles).
  * Approximation residuals STATED, not hidden, at the structural row:
    post-Br/Return/Unreachable polymorphic tracking and the Else join
    (an if-with-result double-counts its result) need a control-frame-aware
    walk — named follow-up.

Gates:
  * tests/i64_width_vstack_946.rs — 7 shape pins red on the pre-fix
    behavior (verified by temporary revert), 2 controls (i64-returning
    call #311, i32-stays-i32) green on both.
  * tests/selector_stack_effect_no_wildcard_946.rs — the #615-style
    tripwire (same scanner set as wcet_sp_no_wildcard_946.rs): no
    catch-all arm may regrow, every WasmOp variant must be NAMED in the
    body, count pinned 279, negative controls + verified red on an
    injected `_ =>` in the real function.
  * scripts/repro/i64_width_vstack_946.wat — the four executable shapes,
    picked up by the wired #973 ARM corpus sweep Phase B (ran locally:
    4 exports x 12 vectors compared, 0 mismatches, sweep PASS with the
    EXPECTED_DECLINES exact-set unchanged);
    i64_width_vstack_mem_946.wat carries the memory.copy/fill shapes
    (memory section excludes it from Phase B — Phase A compile + unit
    pins cover it).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

* chore(#946): loud-convert the silent last-variant maps + move the wildcard ratchet 56 -> 55

Classification-driven follow-through on the RQ-58-WILDCARD lane (the fix
itself is the previous commit):

* 8 silent-map refinements converted to expand-or-loud-reject: the six
  float inner matches whose `_ =>` silently lowered as the LAST variant
  (F32Div/F32Ge/F64Div/F64Sqrt/F64Ge/F64Nearest — a widened outer arm
  would have become a silent wrong lowering) now name the final variant
  and panic on drift; the two access-size fallbacks that silently widened
  any unexpected width to a WORD load/store now panic. Byte-identical by
  construction (whole workspace incl. all frozen anchors green, rc=0).
  Deliberately count-neutral: `_ => unreachable!()` still matches the
  ratchet regex, and swapping it for a bare-binding arm to game the count
  is exactly what the tripwire scanners flag.

* claims.yaml: selector_wildcard_arms_code 56 -> 55 banked (value +
  baseline; the wasm_stack_effect wildcard is gone), arms_total 91 -> 90,
  lines_code 17,897 -> 17,991 with a bound waiver (the 279-variant
  enumeration + call-arg threading: lines traded for exhaustiveness, the
  trade this release wants), lines_total 28,495 -> 28,599. The remaining
  55 arms' classification is recorded at the pin. status.json regenerated
  via --emit-status.

* Ledger re-grades: RQ-58-WILDCARD proposed -> implemented (this lane);
  RQ-58-SHIPVERIFY proposed -> implemented (PR #1002 merged 2026-08-19 —
  the ledger had drifted behind the merge).

* CLAUDE.md: the pin-rationale sentence kept v0.57 numbers as if live;
  now dated at pin creation with the live figures delegated to the pins.

Gates: cargo test --workspace rc=0, clippy -D warnings clean,
fmt --check clean, claim_check 49/49.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
avrabe added a commit that referenced this pull request Sep 23, 2026
… every PR

RQ-71-MUSL (#1349). Both refuting commands were re-run first and BOTH confirmed
the premise:
  * the v0.70.0 x86_64 asset, downloaded and measured with the issue's own two
    commands — dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, max
    of 17 GLIBC_ symbols = GLIBC_2.39. The runner image did not move under us;
  * `grep musl .github/workflows/*.yml` — one hit, in fuzz-smoke.yml, and it is
    there to FORCE the GNU target. No musl release target existed.

2.39 excludes Ubuntu 22.04, Debian 12, RHEL 9, Amazon Linux 2023, Ubuntu 20.04
and Alpine entirely. These feed the varve `pulseengine` rolling layer, whose
portability is the MAXIMUM floor across its payloads — one payload sets it for
everything pinned beside it.

`x86_64-unknown-linux-musl`, statically linked: no loader, no glibc floor at all
rather than a lower one. ADDITIVE — the gnu assets keep their names, consumers
and attestation path, so nothing that works today changes.

THE PART THAT IS NOT PLUMBING. release.yml runs only on a TAG PUSH, so a target
added there is unverified until the tag, and editing the release workflow
between an RC and its tag is the unreviewed late change this process exists to
prevent. So a per-PR job (`musl-portable-asset`) builds the same target with the
same feature set and runs the issue's two acceptance commands on the result:
statically linked, no interpreter, ZERO GLIBC_ symbols.

WITH A POTENCY CONTROL, because those assertions could pass on anything: the
same job builds the GNU target on the same runner and asserts they FAIL there.

A FALSE DOC STATEMENT FOUND ON THE WAY, corrected in place.
`docs/release-process.md` said the `verify` feature is "not" enabled in release
builds and that enabling it "remains a deliberate follow-up decision". It was
taken in v0.58 (RQ-58-SHIPVERIFY #1000/#1002, which put `--features verify` on
both build lines), so the sentence had been false for thirteen releases.
Measured on the shipped v0.70.0 asset: 20 `ordeal` strings, ZERO occurrences of
the degraded-path message.

RESIDUAL, NAMED: arm64 Linux is still glibc-floored. Only the x86_64 musl asset
is published; aarch64-unknown-linux-musl needs the cross path and its own
measurement, and is not claimed.

Refs #1349
avrabe added a commit that referenced this pull request Sep 23, 2026
…n every PR — and correct a release doc that has been false since v0.58 (#1357)

* feat(musl): publish a static musl Linux asset, and prove it builds on every PR

RQ-71-MUSL (#1349). Both refuting commands were re-run first and BOTH confirmed
the premise:
  * the v0.70.0 x86_64 asset, downloaded and measured with the issue's own two
    commands — dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, max
    of 17 GLIBC_ symbols = GLIBC_2.39. The runner image did not move under us;
  * `grep musl .github/workflows/*.yml` — one hit, in fuzz-smoke.yml, and it is
    there to FORCE the GNU target. No musl release target existed.

2.39 excludes Ubuntu 22.04, Debian 12, RHEL 9, Amazon Linux 2023, Ubuntu 20.04
and Alpine entirely. These feed the varve `pulseengine` rolling layer, whose
portability is the MAXIMUM floor across its payloads — one payload sets it for
everything pinned beside it.

`x86_64-unknown-linux-musl`, statically linked: no loader, no glibc floor at all
rather than a lower one. ADDITIVE — the gnu assets keep their names, consumers
and attestation path, so nothing that works today changes.

THE PART THAT IS NOT PLUMBING. release.yml runs only on a TAG PUSH, so a target
added there is unverified until the tag, and editing the release workflow
between an RC and its tag is the unreviewed late change this process exists to
prevent. So a per-PR job (`musl-portable-asset`) builds the same target with the
same feature set and runs the issue's two acceptance commands on the result:
statically linked, no interpreter, ZERO GLIBC_ symbols.

WITH A POTENCY CONTROL, because those assertions could pass on anything: the
same job builds the GNU target on the same runner and asserts they FAIL there.

A FALSE DOC STATEMENT FOUND ON THE WAY, corrected in place.
`docs/release-process.md` said the `verify` feature is "not" enabled in release
builds and that enabling it "remains a deliberate follow-up decision". It was
taken in v0.58 (RQ-58-SHIPVERIFY #1000/#1002, which put `--features verify` on
both build lines), so the sentence had been false for thirteen releases.
Measured on the shipped v0.70.0 asset: 20 `ordeal` strings, ZERO occurrences of
the degraded-path message.

RESIDUAL, NAMED: arm64 Linux is still glibc-floored. Only the x86_64 musl asset
is published; aarch64-unknown-linux-musl needs the cross path and its own
measurement, and is not claimed.

Refs #1349

* fix(#1337): render a MODIFIED artifact, not its Python dict, in the release notes

RQ-70-RIVETNOTES (#1337) shipped `release_notes_from_rivet.py` in v0.70 to
derive the CHANGELOG's artifact section instead of re-typing it. Its
modified-artifact path had never run.

`rivet diff` reports `added` and `removed` as bare id STRINGS but `modified` as
`{"id": ..., "changes": [...]}`. The renderer f-stringed the entry directly, so
running it for THIS release printed:

  - *(modified)* **{'changes': ['field changed: issue-scope'], 'id': 'RQ-70-NPA'}**

straight into the notes. v0.71 is the first release to modify a PRIOR release's
artifact — RQ-71-ISSUESCOPE applies `issue-scope: outlives` to RQ-70-NPA and
RQ-70-FALCONCORPUS — so the defect shipped behind a code path nothing reached.
Same shape as the four gates v0.70 found that could not fail, one layer out: a
renderer arm that could not render.

Found by running the derivation against a SIMULATION of the merged tree before
cutting, rather than discovering it in the CHANGELOG.

The `changes` list is kept rather than dropped — "which field moved on a shipped
artifact" is what a reader of a release note actually needs, and it is why a
modified entry is richer than an added one:

  - *(modified)* **RQ-70-FALCONCORPUS** — field changed: issue-scope
  - *(modified)* **RQ-70-NPA** — field changed: issue-scope

Refs #1337

* fix(#1250): name the PR in `landed:` so R10 does not depend on the squash subject

MEASURED at the #1356 merge, which reddened main. `gh pr merge --squash` uses
the PR TITLE as the subject only when the branch has MORE THAN ONE commit; with
exactly one it uses THAT COMMIT'S subject. #1356 had one commit, so main landed

  feat(issuescope): let an artifact say its ISSUE outlives its delivery ... (#1356)

while the four-check ritual's CHECK4 had simulated the PR title
"RQ-71-ISSUESCOPE (#1250): ...". The gate passed on a tree that was never
created, and main went red on R10 — a delivery-shaped commit attributable to no
artifact. Exactly v0.70's squash-simulation lesson, one level deeper: it is not
enough to simulate a squash, the simulated SUBJECT has to be the one that will
actually land.

Two fixes, and only the second is durable:

  * RQ-71-ISSUESCOPE `landed:` now names PR #1356, and RQ-71-MUSL names #1357.
    R10 accepts EITHER an artifact id / issue number in the subject OR an
    artifact whose `landed:` names the PR. Naming the PR makes attribution
    independent of whatever subject GitHub chooses — the general fix, applied
    here to both.
  * the merge ritual now passes `gh pr merge --subject "<pr title> (#N)"`, so
    the simulated tree and the real one agree BY CONSTRUCTION rather than by a
    prediction of GitHub's default. Never simulate one subject and merge
    another.

Refs #1250, #1349

* fix(#1349): the musl gate could not pass, and it was on the wrong runner pool

Two corrections to RQ-71-MUSL, one from the v0.71 cold review and one from the
maintainer. Both are recorded in the artifact rather than quietly applied.

1. THE GATE COULD NOT PASS — the class this release keeps finding, this time
   built by me. The acceptance step asserted

       file "$BIN" | grep -q 'statically linked'

   rustc's musl target NEVER produces that string. Its spec sets
   `crt-static-default` AND `static-position-independent-executables`, so the
   link is `-static-pie` and `file` reports "static-pie linked". The gate would
   have REFUSED A CORRECT ARTIFACT — and `create-release` has
   `needs: [build-binaries]`, so that is not one missing archive, it is NO
   RELEASE AT ALL. A gate that cannot pass, in the workflow that publishes the
   release, in the lane whose entire point is that the asset is checkable.

   The fix is not a better string. `file`'s wording is a presentation detail of
   a tool this repo does not control; the PROPERTY is "asks no loader to run
   it", which is exactly "no PT_INTERP program header".
   `scripts/elf_static_check.py` reads it from the ELF with nothing beyond the
   stdlib, and was validated BOTH ways before being wired:

     the real published v0.70.0 gnu asset -> PT_INTERP=1, GLIBC_ x17, rc=1
     a synthesised static-pie shape       -> PT_INTERP=0, GLIBC_ x0,  rc=0

   The potency control was widened with it: the gnu leg now runs the SAME check
   and must FAIL it, so BOTH assertions have a negative control. Previously the
   gnu block never asserted a loader was present, so the musl `interpreter`
   assertion had none.

2. THE RUNNER POOL — raised by the maintainer while this very PR sat queued.
   I put the job on `ubuntu-latest` by copying the surrounding convention
   without thinking about capacity. Measured: 56 of ci.yml's 67 jobs target
   `ubuntu-latest` and only 8 use self-hosted, which is exactly what
   RQ-61-CICAP identified as THE bottleneck — runs queue for hours beside idle
   machines because the constraint is the GitHub-hosted quota, not the fleet.
   Adding another ubuntu-latest job makes that worse for every other lane.

   Now `[self-hosted, linux, x64, rust-cpu]`. The apt step is conditional and
   non-fatal: whether rust's bundled musl crt and `rust-lld` make `musl-tools`
   unnecessary for a pure-Rust workspace is UNVERIFIED here — it cannot be
   checked on this macOS host (no musl std, no nightly for
   `--print target-spec-json`) — so it is not asserted either way, and the
   build step is left to answer it loudly.

Refs #1349

* docs(#1349): correct the `ordeal` string count — 19, not 20

The v0.71 prose-truth cold review checked this number and it is wrong in both
readings. Re-derived on the downloaded v0.70.0 x86_64 asset:

  case-sensitive   b"ordeal"  -> 19
  case-insensitive            -> 21

"20" is neither. The figure appeared twice — in RQ-71-MUSL and in the
docs/release-process.md paragraph this lane corrected — so it is fixed in both,
with the counting method stated so the next reader can reproduce it rather than
guess which convention produced the number.

The load-bearing half of that sentence is unaffected and was independently
re-verified: ZERO occurrences of the degraded-path message, which is what shows
`synth verify` genuinely works in a released binary and that the doc claiming
`--features verify` is off had been false since v0.58.

Refs #1349
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Every published synth is built without --features verify, so synth verify cannot run in any shipped artifact

1 participant