Skip to content

Enforce that [package].version, its snapshot and the deploy lib agree - #4

Open
thedavidmeister wants to merge 1 commit into
mainfrom
2026-07-25-internal-consistency-deploy-tag
Open

Enforce that [package].version, its snapshot and the deploy lib agree#4
thedavidmeister wants to merge 1 commit into
mainfrom
2026-07-25-internal-consistency-deploy-tag

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

What

A release in this repo moves three things at once:

  1. [package].version in foundry.toml,
  2. the frozen src/generated/<tag>/CloneFactory.pointers.sol snapshot it names,
  3. the LibCloneFactoryDeploy pins aliased out of that snapshot.

Nothing asserted they still name the same tag. LibCloneFactoryDeployTest
catches drift in one direction only — current source vs the aliased pins — so a
hand-edited [package].version that outruns the snapshot passed CI green, and
rainix-tag-release would then publish a version whose pins belong to a
different release.

This adds the testDeployTag analogue that st0x.deploy carries, closing that
hole.

  • script/BuildPointers.sol emits the tag it generated for as
    LibCloneFactoryDeploy.DEPLOY_TAG. That is the name the shared reusable
    already documents for a deploy-pin snapshot (rainix-tag-release's
    snapshot-generate-cmd: "regenerates the deploy-pin snapshot ... into
    src/generated/<tag>/, DEPLOY_TAG and any pointer libs"
    ), and the analogue
    of st0x.deploy's LibProdDeployCurrent.DEPLOY_TAG.
  • test/src/lib/LibCloneFactoryDeployTag.t.sol asserts all three agree:
    • testDeployTagDEPLOY_TAG equals [package].version with dots as
      underscores. The version is read from foundry.toml, not hardcoded, so
      a release moves it without hand-editing the test (same approach as
      st0x.deploy).
    • testDeployTagSnapshotExists — a src/generated/<DEPLOY_TAG>/ snapshot
      actually exists, so the version can only name a release that was really
      snapshotted.
    • testDeployTagSnapshotHoldsTheAliasedPins — that snapshot records exactly
      the address and codehash the library exposes, so the alias cannot silently
      point at a different tag's snapshot.

Frozen snapshots are untouched: regenerating writes only
src/lib/LibCloneFactoryDeploy.sol. The frozen-snapshots-append-only gate has
nothing to complain about.

Proof the test actually bites

Baseline (committed state) is green:

[PASS] testDeployTag() (gas: 6722)
[PASS] testDeployTagSnapshotExists() (gas: 3917)
[PASS] testDeployTagSnapshotHoldsTheAliasedPins() (gas: 13005)
Suite result: ok. 3 passed; 0 failed; 0 skipped

Mutant 1 — hand-edited version outruns the snapshot. [package].version
0.1.5 -> 0.1.6, snapshot still 0_1_5. Exactly the hole this closes:

[FAIL: assertion failed: 0_1_5 != 0_1_6] testDeployTag() (gas: 6716)
Suite result: FAILED. 2 passed; 1 failed; 0 skipped

Mutant 2 — library left aliasing a superseded snapshot. [package].version
and DEPLOY_TAG both say 0_1_3, while the generated import still resolves
0_1_5. testDeployTag is happy (version and tag agree) and the 0_1_3
snapshot exists, so only the third assertion can catch it — and does:

[PASS] testDeployTag() (gas: 6722)
[PASS] testDeployTagSnapshotExists() (gas: 3917)
[FAIL: DEPLOY_TAG snapshot does not record the aliased deploy address] testDeployTagSnapshotHoldsTheAliasedPins() (gas: 9884)

Mutant 3 — version names a tag that was never snapshotted. 0.9.9 /
0_9_9:

[PASS] testDeployTag() (gas: 6722)
[FAIL: no src/generated snapshot for DEPLOY_TAG] testDeployTagSnapshotExists() (gas: 4478)
[FAIL: vm.readFile: failed to open file ".../src/generated/0_9_9/CloneFactory.pointers.sol"] testDeployTagSnapshotHoldsTheAliasedPins()

Restored with git checkout HEAD -- foundry.toml src/lib/LibCloneFactoryDeploy.sol;
clean tree, suite green again.

Verification

nix develop github:rainlanguage/rainix/53e96a7d0a97d7c7c75c3b2412521324776fdac6#sol-shell:

  • forge soldeer install && forge build — clean.
  • forge test — 18 passed. The 5 LibCloneFactoryDeployProdTest fork tests
    fail locally only because no *_RPC_URL is set in this shell; they are
    untouched by this PR.
  • forge fmt --check — clean. reuse lint — clean.
  • forge script ./script/BuildPointers.sol && forge fmt from the committed
    state leaves an empty git status: the generator reproduces the committed
    LibCloneFactoryDeploy.sol byte-for-byte and rewrites no frozen snapshot.

Known CI red — do not chase

rainix-sol / test is expected to fail on testProdDeployArbitrum:

lb.drpc.live ... error code -32001: You've reached the usage limit for your current plan

That is an exhausted drpc quota on the RPC_URL_ARBITRUM_FORK secret, not a
code failure — it needs a human RPC swap. 19/20 pass on main today for the
same reason. This PR adds 3 passing tests and changes nothing that a fork test
reads.

Summary by CodeRabbit

  • New Features

    • Added a readable release tag to identify the snapshot used for deployment values.
  • Documentation

    • Clarified which release snapshot provides the pinned deployment information.
  • Tests

    • Added validation that the release tag matches the package version.
    • Added checks confirming the corresponding snapshot exists and contains the expected deployment values.

A release moves three things together: `[package].version`, the frozen
`src/generated/<tag>/` snapshot it names, and the `LibCloneFactoryDeploy` pins
aliased out of that snapshot. Nothing asserted they still name the same tag, so
a hand-edited version silently publishes a release whose pins belong to another
one.

`BuildPointers` now emits the tag it generated for as `LibCloneFactoryDeploy.
DEPLOY_TAG` (the name `rainix-tag-release` already documents for a deploy-pin
snapshot), and `LibCloneFactoryDeployTagTest` asserts:

- `DEPLOY_TAG` equals `[package].version` with dots as underscores, read from
  `foundry.toml` rather than hardcoded so it survives a release untouched;
- a `src/generated/<DEPLOY_TAG>/` snapshot exists;
- that snapshot records exactly the address and codehash the lib exposes.

The frozen snapshots are untouched: regenerating writes only the lib.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Jul 25, 2026
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 200e8f2d-49ed-4d2b-a422-5334aa0ceb93

📥 Commits

Reviewing files that changed from the base of the PR and between 6173d3e and 17b69fb.

📒 Files selected for processing (4)
  • CLAUDE.md
  • script/BuildPointers.sol
  • src/lib/LibCloneFactoryDeploy.sol
  • test/src/lib/LibCloneFactoryDeployTag.t.sol

Walkthrough

The generated deployment library now exposes its release snapshot tag. A new test contract verifies that the tag matches the package version, the corresponding snapshot exists, and its pinned address and bytecode hash match the library aliases.

Changes

Deployment tag pinning

Layer / File(s) Summary
Generate and expose deployment tag
script/BuildPointers.sol, src/lib/LibCloneFactoryDeploy.sol, CLAUDE.md
The generator reuses the deployment tag for snapshot imports and emits the DEPLOY_TAG constant and related documentation in the generated library.
Validate tag and snapshot consistency
test/src/lib/LibCloneFactoryDeployTag.t.sol
Tests compare the library tag with the package version, verify the snapshot path exists, and match its pinned address and bytecode hash against the library aliases.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: claude

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main goal: keeping package version, generated snapshot, and deploy library in sync.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 2026-07-25-internal-consistency-deploy-tag

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

Copy link
Copy Markdown
Contributor Author

Superseded by #5, which migrates this repo to the rolling-candidate model (mirroring S01-Issuer/st0x.deploy#283).

That migration removes the premise this PR enforces: [package].version deliberately stops naming a snapshot dir, so testDeployTag would become false by design rather than merely redundant.

The other two assertions are carried forward, not lost:

  • testDeployTagSnapshotExists becomes a compile-time guarantee — the new test imports src/generated/candidate/CloneFactory.pointers.sol directly, so a missing snapshot is a compile error.
  • testDeployTagSnapshotHoldsTheAliasedPins becomes testCandidateIsTheAliasedSnapshot, asserting the identity in Solidity against the imported constants instead of grepping file text. It is mutation-proven in Migrate to the rolling-candidate snapshot model #5 (repointing the generated lib at 0_1_3 turns it red).

The DEPLOY_TAG constant this PR added to the generated lib is kept, emitted as "candidate".

Recommend closing this in favour of #5. Not closing it here — that is yours to call.

@thedavidmeister thedavidmeister added the ai:needs-work AI vetter: needs rework (code issue) label Jul 28, 2026
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

🤖 ai:vetter
Reviewed 17b69fb: reject — Missing QA evidence block (QA-GUIDE §8) — no ## QA section in body or trusted producer comment. Code itself audits sound: DEPLOY_TAG matches [package].version 0.1.5, test's exact-string snapshot matching verified against the committed 0_1_5 pointers rendering, fs_permissions cover the reads, three mutant kills shown in body. Add the §8 block and re-push.
cost 332 — release-tag agreement suite

@thedavidmeister thedavidmeister added ai:blocked-on AI producer: blocked on a dependency PR and removed ai:needs-work AI vetter: needs rework (code issue) labels Jul 28, 2026
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

🤖 ai:producer
Blocked-on: Blocked on rainlanguage/rainix#289 (fork-RPC failover preflight), which is now ai:design pending a human ruling. The single failing test is testProdDeployArbitrum and it fails entirely upstream of this diff: vm.createSelectFork could not instantiate a forked environment because drpc returned error code -32001, You've reached the usage limit for your current plan. 23 of 24 tests pass. Foundry maps one rpc_endpoints alias to exactly one URL and --fork-retries only re-hits that same dead URL, so no code change in this repo can route around an exhausted vendor quota. Goes green once the failover lands on rainix main.

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

Labels

ai:blocked-on AI producer: blocked on a dependency PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant