Unit
script/lib/LibCopyArtifacts.sol (contracts() / committedPath()) together with crates/bindings/src/lib.rs.
Intent oracle
contracts() doc: "Contract artifacts that the rust crate consumes via alloy::sol!. Adding a new contract here also requires the rust crate to reference it."
committedPath() doc: "Path of the committed ABI copy that the rust crate reads at compile time."
.github/workflows/copy-artifacts.yaml claim: "build from committed sources and assert the committed abi artifacts are still current."
Violated property
Every artifact the rust crate consumes stays fresh. The coupling between the Solidity list/path and the rust crate's hardcoded concat!(env!("CARGO_MANIFEST_DIR"), "/abi/<name>.json") reads is maintained by hand with no oracle in either lane.
Verified repro (mutation probe, commit bba50a7)
Both mutations below survived the full rainix-sol suite (forge build + forge script script/CopyArtifacts.sol regen + forge test, all green):
contracts() returning only ["IMetaBoardV1_2"] (dropping IDescribedByMetaV1): the copy script and the freshness test iterate the same shrunken list, so the orphaned crates/bindings/abi/IDescribedByMetaV1.json stops being refreshed and would go silently stale on the next ABI change. The rs lane also stays green: the stale file still exists and still compiles under alloy::sol!.
committedPath() returning crates/bindings/abi/<name>.alt.json: writer and checker share the function, so copy and freshness both retarget to the new location and stay green while crates/bindings/src/lib.rs keeps reading the old .json forever.
State after AMT PR
The AMT coverage PR for this group adds literal pins (testContractsAreExactlyTheRustCrateBindings, testCommittedPathIsWhereTheRustCrateReads) that kill both mutants for the CURRENT list and path. The converse direction remains unchecked in any lane: a binding added in crates/bindings/src/lib.rs without a contracts() entry, or a path change on the rust side, produces no signal anywhere.
Triage framing (neutral)
Flagged, not adjudicated: maintainers may consider the residual risk acceptable (the list changes rarely), or may want a cross-lane check — e.g. a rust test asserting the set of files under crates/bindings/abi/ equals the set alloy::sol! consumes, or a generated manifest both lanes read.
Unit
script/lib/LibCopyArtifacts.sol(contracts()/committedPath()) together withcrates/bindings/src/lib.rs.Intent oracle
contracts()doc: "Contract artifacts that the rust crate consumes via alloy::sol!. Adding a new contract here also requires the rust crate to reference it."committedPath()doc: "Path of the committed ABI copy that the rust crate reads at compile time.".github/workflows/copy-artifacts.yamlclaim: "build from committed sources and assert the committed abi artifacts are still current."Violated property
Every artifact the rust crate consumes stays fresh. The coupling between the Solidity list/path and the rust crate's hardcoded
concat!(env!("CARGO_MANIFEST_DIR"), "/abi/<name>.json")reads is maintained by hand with no oracle in either lane.Verified repro (mutation probe, commit bba50a7)
Both mutations below survived the full
rainix-solsuite (forge build +forge script script/CopyArtifacts.solregen +forge test, all green):contracts()returning only["IMetaBoardV1_2"](droppingIDescribedByMetaV1): the copy script and the freshness test iterate the same shrunken list, so the orphanedcrates/bindings/abi/IDescribedByMetaV1.jsonstops being refreshed and would go silently stale on the next ABI change. The rs lane also stays green: the stale file still exists and still compiles underalloy::sol!.committedPath()returningcrates/bindings/abi/<name>.alt.json: writer and checker share the function, so copy and freshness both retarget to the new location and stay green whilecrates/bindings/src/lib.rskeeps reading the old.jsonforever.State after AMT PR
The AMT coverage PR for this group adds literal pins (
testContractsAreExactlyTheRustCrateBindings,testCommittedPathIsWhereTheRustCrateReads) that kill both mutants for the CURRENT list and path. The converse direction remains unchecked in any lane: a binding added incrates/bindings/src/lib.rswithout acontracts()entry, or a path change on the rust side, produces no signal anywhere.Triage framing (neutral)
Flagged, not adjudicated: maintainers may consider the residual risk acceptable (the list changes rarely), or may want a cross-lane check — e.g. a rust test asserting the set of files under
crates/bindings/abi/equals the setalloy::sol!consumes, or a generated manifest both lanes read.