Skip to content

feat(#386): safety-profile gate + accurate shared-rebase messaging - #387

Merged
avrabe merged 5 commits into
mainfrom
fix/386-shared-rebase-warning-accuracy
Aug 19, 2026
Merged

feat(#386): safety-profile gate + accurate shared-rebase messaging#387
avrabe merged 5 commits into
mainfrom
fix/386-shared-rebase-warning-accuracy

Conversation

@avrabe

@avrabe avrabe commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Closes items 1–2 of #386 and adds the safety profile decided there.

1. --profile safety (SR-68) — the new feature

ADR-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": attestation defaults true and
--memory defaults auto — 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.

  • Profile::{Ecosystem (default), Safety}, FuserConfig::profile, CLI --profile.
  • Under safety, the memory strategy must be stated--memory auto is refused.
    It selects the inter-component isolation model, which decides whether a fault in one
    component can reach another's state.
  • Checked before fusion — a profile violation is a build-config error, so it must
    not depend on input shape or leave a partial artifact.
  • Ecosystem behavior unchanged. Nothing breaks.

The profile gates, it never transforms: a build passing under safety emits
byte-identical output to the same explicit invocation under ecosystem (pinned by a
test, with reproducible: true so it compares fusion output, not the attestation
nonce).

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-rebase warned "UNSOUND" unconditionally — including for
--emit-relocs inputs, where it isn't, and which is the path --pack-rebase/
--share-stack are 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 Auto
never 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_metadata proves presence, not coverage (undecidable, #339); it would
auto-escalate risk and change artifact layout; and every MCU user is already explicit
(--pack-rebase/--share-stack/--address-rebase all require --memory shared).
Instead Auto now reports what's available and lets the caller choose.

Verification

Workspace clippy --workspace --all-targets -D warnings clean, fmt clean, tests
855/0. rivet PASS (SR-68 + SWV-80). meld docs profiles. Live CLI verified
both directions of the gate.

🤖 Generated with Claude Code

…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
@github-actions

Copy link
Copy Markdown

LS-N verification gate

59/59 approved LS entries verified

count
Passed (≥1 test, all green) 59
Failed (≥1 test failure) 0
Missing (no ls_*_NN_* test found) 0

Approved loss-scenarios.yaml entries are expected to have a
regression test named ls_<letter>_<num>_* (e.g. LS-A-11
ls_a_11_*). The gate runs each prefix via cargo test --lib --no-fail-fast and aggregates pass/fail/missing.

Failed LS entries

(none)

Missing regression tests

(none)

Updated automatically by tools/post_verification_comment.py.
Source of truth: safety/stpa/loss-scenarios.yaml.

…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
@avrabe avrabe changed the title fix(386): warn about un-rebasable addresses only when an input lacks relocs feat(#386): safety-profile gate + accurate shared-rebase messaging Aug 19, 2026
avrabe and others added 2 commits August 19, 2026 11:58
…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
@avrabe
avrabe merged commit 4a63a55 into main Aug 19, 2026
19 checks passed
@avrabe
avrabe deleted the fix/386-shared-rebase-warning-accuracy branch August 19, 2026 10:18
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.

1 participant