Skip to content

test: pin the deploy script's target set and the registries' dependency lists - #186

Merged
thedavidmeister merged 3 commits into
mainfrom
amt-2026-09-14-registry-deploy-suites
Sep 15, 2026
Merged

thedavidmeister merged 3 commits into
mainfrom
amt-2026-09-14-registry-deploy-suites

Conversation

@thedavidmeister

Copy link
Copy Markdown
Contributor

What

Two assertions about this repo's own deploy declaration and the script that
broadcasts it, plus the harness one of them reads through.

  • test/src/abstract/RegistryDeploySuites.t.sol pins both candidates'
    dependency lists as empty.
  • test/script/Deploy.t.sol pins the network set a dispatch of
    script/Deploy.sol reaches, against foundry.toml rather than against the
    body it inherits.
  • test/concrete/DeployHarness.sol exposes deployNetworks() externally, the
    way BuildHarness exposes Build's internal declarations.

No source changed.

Why

Every other field of a candidate has something behind it. The recorded address,
code hash and runtime code are checked against what the recorded creation code
derives; the suite key, the artifact path and the candidate set are checked
against the generator's list and against the committed snapshots; the source
anchor checks the recorded creation code against current source.

dependencies had none of that. Nothing derives it, the generator neither
writes it nor reads it for a rolling candidate, and the only consumer is
deployToNetworks, on a fork, behind an RPC. An entry added to either list
takes that registry off every network where the named address has no code --
per network, after the fork, at dispatch time -- and both of these contracts are
declared to have no such address at all: AddressRegistry reads nothing and
calls nothing at construction, and MigrationRegistry takes no constructor
argument and resolves its namespace from msg.sender.

script/Deploy.sol was reached by no test at all. Its empty body is a claim
that a dispatch goes to every network this repo is configured for, and half of
that claim is already a compile-time guarantee -- RegistryDeploySuites spells
releasedSuites and candidateSuites as override without virtual, so a
script broadcasting a different declaration than the verification contracts
check does not build. deployNetworks is the half that is left: it IS virtual,
because a repo bootstrapping onto one chain at a time overrides it, so this repo
taking the inherited answer is a decision and a narrowed one here is a dispatch
that silently skips chains with the whole suite green.

The network assertion is written against the [rpc_endpoints] keys rather than
against supportedNetworks(), which is the value the inherited body returns.
Written against that value it would be the implementation compared to itself and
would pass for any override that happened to call it. foundry.toml is a
separate statement of the same set, and
testSupportedNetworksAreFullyConfigured is what holds the two together.

QA

  • Discriminating tests: testCandidatesDeclareNoDependencies, testDeployBroadcastsToEveryConfiguredNetwork - each fails on base once the behaviour it names is broken, verified by running the whole suite against each mutation one at a time on the unmutated tree; both mutations they kill survived the suite before this branch.
  • Mutations applied: see the three tables below - 27 mutations across src/abstract/RegistryDeploySuites.sol, script/Deploy.sol and script/CreditHyperCore.sol, 19 of 27 killed before this branch and 22 of 27 after, with the killing test named per row.
  • Oracle: the contract NatSpec on RegistryDeploySuites, RainDeploySuitesBase and CreditHyperCore, plus the README sections on deploying and on crediting the deployer's HyperCore account. The network assertion reads foundry.toml rather than supportedNetworks(), so its expected value is config the implementation does not produce; the dependency assertion reads the two contracts' own documented constructor behaviour rather than the declaration it checks.
  • Category check: the worker scope is src/abstract/RegistryDeploySuites.sol, script/Deploy.sol and script/CreditHyperCore.sol; all three are covered, with the two unfillable rows named and explained below rather than dropped.

Suite script: forge test minus the 61 fork tests that need RPC env vars absent
on this machine. check-nopin.sh is not used and is not needed here: nothing in
this unit is compiled into AddressRegistry or MigrationRegistry, so no
mutation of it moves a pinned bytecode and the pin tests carry real behaviour
for it. Baseline before: 397 passed, 0 failed. Baseline after: 399 passed, 0
failed.

Mutations were applied one at a time to the unmutated tree and the whole suite
run against each.

src/abstract/RegistryDeploySuites.sol

behaviour mutation before after killed by
releasedSuites returns the generated aggregate return an empty array KILLED KILLED testEveryFrozenSnapshotIsReleased
releasedSuites returns the WHOLE aggregate drop the last release KILLED KILLED testEveryFrozenSnapshotIsReleased
candidateSuites declares exactly 2 declare 1 KILLED KILLED testGeneratedContractsAreExactlyTheDeclaredCandidates, testEveryCandidateHasASnapshot
candidateSuites declares exactly 2 declare 3, the last a duplicate KILLED KILLED testGeneratedContractsAreExactlyTheDeclaredCandidates, testSnapshotInternallyConsistent
slot 0 is the address candidate make it the migration candidate KILLED KILLED testGeneratedContractsAreExactlyTheDeclaredCandidates, testSnapshotInternallyConsistent
slot 1 is the migration candidate make it the address candidate KILLED KILLED testGeneratedContractsAreExactlyTheDeclaredCandidates, testSnapshotInternallyConsistent
address candidate key is address-registry use the migration key KILLED KILLED testGeneratedContractsAreExactlyTheDeclaredCandidates, testSnapshotInternallyConsistent
address candidate key is unique across releases use a released tag key KILLED KILLED testSnapshotInternallyConsistent
address candidate records the generated creation code use type(AddressRegistry).creationCode SURVIVED SURVIVED equivalent: the two are byte identical, which is what testSnapshotMatchesSource asserts
address candidate records the aliased deploy address use address(0) KILLED KILLED testSnapshotInternallyConsistent
address candidate records the aliased code hash use bytes32(0) KILLED KILLED testSnapshotInternallyConsistent
address candidate records the generated runtime code use its creation code KILLED KILLED testSnapshotInternallyConsistent
address candidate artifact path names AddressRegistry name MigrationRegistry after the colon KILLED KILLED testGeneratedContractNameMatchesTheArtifactPath, testEveryCandidateArtifactPathResolves, testEveryCandidateHasASnapshot
address candidate declares no dependency declare one SURVIVED KILLED testCandidatesDeclareNoDependencies
address candidate anchors to AddressRegistry source anchor to MigrationRegistry KILLED KILLED testSnapshotMatchesSource, testEveryCandidateArtifactPathResolves
migration candidate key is migration-registry use the address key KILLED KILLED testGeneratedContractsAreExactlyTheDeclaredCandidates, testSnapshotInternallyConsistent
migration candidate records the generated creation code use type(MigrationRegistry).creationCode SURVIVED SURVIVED equivalent, as above
migration candidate records its own deploy address use the address registry pin KILLED KILLED testSnapshotInternallyConsistent
migration candidate records its own code hash use the address registry pin KILLED KILLED testSnapshotInternallyConsistent
migration candidate records its own runtime code use the address registry snapshot KILLED KILLED testSnapshotInternallyConsistent
migration candidate artifact path names MigrationRegistry name AddressRegistry after the colon KILLED KILLED testGeneratedContractNameMatchesTheArtifactPath, testEveryCandidateArtifactPathResolves, testEveryCandidateHasASnapshot
migration candidate declares no dependency declare one SURVIVED KILLED testCandidatesDeclareNoDependencies
migration candidate anchors to MigrationRegistry source anchor to AddressRegistry KILLED KILLED testSnapshotMatchesSource, testEveryCandidateArtifactPathResolves

script/Deploy.sol

behaviour mutation before after killed by
overrides nothing, so deployNetworks stays supportedNetworks() override it to a single chain SURVIVED KILLED testDeployBroadcastsToEveryConfiguredNetwork
overrides nothing, so the suites stay the registry's not expressible: releasedSuites and candidateSuites are override without virtual, so an override in Deploy does not compile n/a n/a the compiler

script/CreditHyperCore.sol

All three mutations survive, before and after. They are left surviving rather
than filled, because the contract's own NatSpec argues that filling them is the
worse trade and this PR agrees with it: driving run() means writing
DEPLOYMENT_KEY, vm.setEnv writes the forge PROCESS environment, forge runs
test contracts concurrently, and RainDeployBroadcastTest already documents
that every vm.setEnv in this repo is one of its own four sequenced writes --
after that contract was seen losing exactly this race. A fifth writer is a race
against a suite that is currently green. The three are reported rather than
filled, and are in the worker notes as a hazard.

behaviour mutation before after
the key comes from DEPLOYMENT_KEY read HYPERCORE_CREDIT_WEI for it SURVIVED SURVIVED
the amount is required, never defaulted vm.envOr with a zero default SURVIVED SURVIVED
credits through the forking variant call LibHyperCore.creditCore instead SURVIVED SURVIVED
argument order is (vm, address, uint256) not expressible: transposing them does not compile n/a n/a

🤖 Generated with Claude Code

https://claude.ai/code/session_01V8ViHcKLVk2YoS2joH4HdN

David Meister and others added 3 commits September 14, 2026 19:12
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V8ViHcKLVk2YoS2joH4HdN
…ependency lists

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V8ViHcKLVk2YoS2joH4HdN
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V8ViHcKLVk2YoS2joH4HdN
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 44 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 3035c922-637c-436f-bbcd-2bca56034305

📥 Commits

Reviewing files that changed from the base of the PR and between e9a922a and 5f8432c.

📒 Files selected for processing (3)
  • test/concrete/DeployHarness.sol
  • test/script/Deploy.t.sol
  • test/src/abstract/RegistryDeploySuites.t.sol

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thedavidmeister
thedavidmeister merged commit 912b8e2 into main Sep 15, 2026
6 checks passed
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