feat(#386): safety-profile gate + accurate shared-rebase messaging - #387
Merged
Conversation
…relocs `--memory shared --address-rebase` warned "UNSOUND" unconditionally — including for `--emit-relocs` inputs, where it is not. That text predates the reloc CONSUMER (#326→#340): a reloc-covered input is rebased at the SOURCE point (every reloc-flagged i32.const address relocated at its origin), so a pointer computed from it is correct by construction — proven by rebasing_end_to_end::test_326_reloc_const_rebasing_end_to_end, which executes on wasmtime. It fired on exactly the path meld's MCU line is built on: --pack-rebase (SR-57) and --share-stack (SR-66) both REQUIRE shared+rebase, and the falcon supplier validated that pipeline on real components (#370, an executing F100 image at 51% of budget). So every gale production build was told its validated flow is unsound. In a functional-safety tool that is worse than noise — it trains users to ignore warnings, including the precise one that matters. The warning was also redundant: address_strategy.rs already warns per-module, only on the no-reloc path, naming the actual residual (#339) and the remedy; and the genuinely dangerous case (no relocs WITH direct load/store) is already a hard error there (path-F). So it added no coverage, only noise. - lib.rs: gate the warning on `any_component_lacks_reloc_metadata()`, using the same `reloc::has_reloc_metadata` probe `resolve_address_plan` gates on (so the warning cannot disagree with the strategy that follows it); reword to describe the real risk. - Doc accuracy: the PUBLIC `MemoryStrategy::Auto` rustdoc said "Both hold -> SharedMemory with address rebasing" but Auto never picks shared — corrected, along with the module-level docs and the internal rationale (which still cited the pre-consumer #326 reasoning). Removed the dangling reference to `warn_if_unsound_rebasing` (no such function). Auto's message demoted warn->info and now points at the explicit MCU invocation. - Behavior unchanged: strategy selection, fusion output and the per-module address_strategy warnings are untouched. This is messaging accuracy only. Test: tests/shared_rebase_warning_386.rs — reloc-covered inputs fuse QUIETLY (no UNSOUND, no no-reloc warning); an input without relocs still warns. Proven RED->GREEN by revert (pre-fix it fails, capturing the spurious warning verbatim). Full suite 851/0, clippy --all-targets -D warnings clean, fmt clean. Item 3 of #386 (should Auto upgrade when EVERY input is reloc-covered?) is left open — a defaults question, with ADR-4 arguing for explicit selection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WrmMqf1iuTS1UBEes5TmdC
LS-N verification gate✅ 59/59 approved LS entries verified
Approved Failed LS entries(none) Missing regression tests(none) Updated automatically by |
…ting to it Part (a) of the #386 defaults decision. `auto` keeps selecting multi-memory — the strategy that is sound for every input with no producer contract — but now says what is available: - every input reloc-covered -> "these inputs support a single-address-space (MCU) build: select it explicitly with `--memory shared --address-rebase` (+ --pack-rebase / --share-stack)". - some input not reloc-covered -> names the missing precondition (`--emit-relocs`) instead. Deliberately a HINT, not an escalation. `has_reloc_metadata` proves reloc PRESENCE, not COVERAGE (undecidable in general, #339), so auto-switching to the strategy that carries an input contract would rest on a proxy — and it would escalate risk automatically and change artifact layout for existing builds, both of which a functional-safety tool should not do silently. ADR-4: explicit, not auto. The tool informs; the caller chooses. Full suite 851/0, clippy clean, fmt clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WrmMqf1iuTS1UBEes5TmdC
…properties
ADR-7 decided meld has a dual identity (generic RFC-46 reference fuser AND
sealed-safety product, both first-class) and specified TWO ATTESTED PROFILES.
Nobody had built the signal, so ADR-4 ("explicit, not auto") could only warn.
Why a profile rather than "strict when attested": `attestation` defaults TRUE
(lib.rs) and `--memory` defaults to `auto` (CLI), so "attested + Auto" is the
DEFAULT invocation — erroring there would fail plain `meld fuse a.wasm b.wasm`
for everyone and break the ecosystem identity ADR-7 declared first-class. The
profile is the explicit signal that separates the two.
- `Profile::{Ecosystem (default), Safety}` + `FuserConfig::profile` + CLI
`--profile <ecosystem|safety>`.
- `Error::SafetyProfileViolation`.
- `check_safety_profile()` runs BEFORE fusion (a profile violation is a build
configuration error: it must not depend on input shape, nor leave a partially
produced artifact). First enforced property = the MEMORY STRATEGY (ADR-4):
Auto selects the inter-component isolation model, which decides whether a fault
in one component can reach another's state, so a safety build must state it.
Error names both the violation and the remedy.
- Ecosystem behavior is UNCHANGED — nothing breaks.
The profile GATES, it never transforms: a build that passes under Safety emits
byte-identical output to the same explicit invocation under Ecosystem (pinned by
a test using `reproducible: true` so the comparison is fusion output, not the
attestation nonce).
Scope held deliberately: only ADR-4 is enforced. ADR-7's other binding
requirements (per-boundary strategy declared/attested; silent-downgrade = hard
error) are documented as intended future consumers of this seam, not
silently half-done.
Tests: tests/safety_profile_386.rs (4) — refuses Auto naming the remedy; accepts
explicit; ecosystem still infers (the default path); gates-but-does-not-transform.
Live CLI verified both directions. rivet SR-68 + SWV-80 (PASS). `meld docs
profiles`. Workspace: clippy --workspace --all-targets -D warnings clean, fmt
clean, tests 855/0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WrmMqf1iuTS1UBEes5TmdC
…saging (#386) Bumps workspace 0.49.0 -> 0.50.0 for SR-68 (`--profile safety`) and the #386 messaging/doc accuracy fixes (PR #387). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WrmMqf1iuTS1UBEes5TmdC
…ot-transforms test
CI's Coverage job (`cargo test --all-features`) caught a real gap: the byte-identity
assertion passed on default features but failed under `--all-features`. Root cause is
a KNOWN, documented limitation, not a new defect — under the optional `attestation`
(wsc) feature the emitted section carries `HashMap` fields whose serialization ORDER
meld cannot control from its side, so byte-identity does not hold there. The existing
`test_reproducible_attestation_is_byte_stable` is `#[cfg(not(feature = "attestation"))]`
for exactly this reason (lib.rs doc note), and release builds do not enable the
feature, so shipped artifacts are unaffected.
Fix: set `attestation: false` in the test config so the comparison is of PURE FUSION
OUTPUT. Better than cfg-gating the test — it is feature-independent AND compares
exactly what the claim says ("the profile gates, it does not transform"), instead of
relying on `reproducible` to tame attestation noise.
Verified under BOTH configs: default 4/4, `--all-features` 4/4; full `--all-features
--workspace` 854/0.
LESSON: run `cargo test --all-features` locally, not just `--workspace` — CI runs both
and the feature-gated paths are where the surprises are.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WrmMqf1iuTS1UBEes5TmdC
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.
Closes items 1–2 of #386 and adds the safety profile decided there.
1.
--profile safety(SR-68) — the new featureADR-7 decided meld has a dual identity (generic RFC-46 fuser and sealed-safety
product, both first-class) and specified two attested profiles. Nobody had built
the signal, so ADR-4 ("explicit, not auto") could only warn.
Why a profile and not "strict when attested":
attestationdefaults true and--memorydefaults auto — so "attested + Auto" is the default invocation.Erroring there would fail plain
meld fuse a.wasm b.wasmfor everyone and break theecosystem identity ADR-7 declared first-class.
Profile::{Ecosystem (default), Safety},FuserConfig::profile, CLI--profile.safety, the memory strategy must be stated —--memory autois refused.It selects the inter-component isolation model, which decides whether a fault in one
component can reach another's state.
not depend on input shape or leave a partial artifact.
The profile gates, it never transforms: a build passing under
safetyemitsbyte-identical output to the same explicit invocation under
ecosystem(pinned by atest, with
reproducible: trueso it compares fusion output, not the attestationnonce).
Scope held deliberately to ADR-4. ADR-7's other binding requirements (per-boundary
strategy declared/attested; silent-downgrade = hard error) are documented as intended
future consumers of this seam, not silently half-done.
2. Accurate shared+rebase messaging (the original #386 fix)
--memory shared --address-rebasewarned "UNSOUND" unconditionally — including for--emit-relocsinputs, where it isn't, and which is the path--pack-rebase/--share-stackare built on and that the falcon supplier validated on real components(#370). It also duplicated a better-targeted per-module warning in
address_strategy.rs, while the genuinely dangerous case is already a hard error(path-F). Now gated on
any_component_lacks_reloc_metadata().Proven RED→GREEN by revert — pre-fix the test fails, capturing the spurious warning
verbatim.
3. Docs that contradicted the code
MemoryStrategy::Auto's public rustdoc said "Both hold →SharedMemory" — but Autonever picks shared. Fixed there, in the module docs, and in the internal rationale;
dropped a dangling reference to a function that doesn't exist.
4. Discoverability hint (#386 item 3, resolved as "no auto-escalation")
Auto-escalating to shared when inputs "look" reloc-covered is ruled out permanently:
has_reloc_metadataproves presence, not coverage (undecidable, #339); it wouldauto-escalate risk and change artifact layout; and every MCU user is already explicit
(
--pack-rebase/--share-stack/--address-rebaseall require--memory shared).Instead Auto now reports what's available and lets the caller choose.
Verification
Workspace
clippy --workspace --all-targets -D warningsclean, fmt clean, tests855/0. rivet PASS (SR-68 + SWV-80).
meld docs profiles. Live CLI verifiedboth directions of the gate.
🤖 Generated with Claude Code