Skip to content

rainix owns the deploy-repo release freeze: rainix-static cut-release - #302

Open
thedavidmeister wants to merge 4 commits into
mainfrom
2026-08-13-cut-release-in-rainix
Open

rainix owns the deploy-repo release freeze: rainix-static cut-release#302
thedavidmeister wants to merge 4 commits into
mainfrom
2026-08-13-cut-release-in-rainix

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Closes #301.

rainix-tag-release owned the release lifecycle but delegated the step where the
release's correctness lives — the snapshot freeze — to a required free-text
shell input
, snapshot-generate-cmd. Every deploy repo hand-rolled it, and the
first consumer to write it inverted the ordering.

This moves the freeze into rainix-static as a cut-release subcommand, so it
lands once for every deploy repo and the ordering is a property of the tool
rather than a comment in a script.

Where the logic lives

rainix-static/src/cut_release.rs — Rust, alongside soldeer-gate and
frozen_snapshots, invoked from the new
.github/actions/cut-release composite, which rainix-tag-release uses in place
of its old "Regenerate the deploy-pin snapshot" step.

It does what rain.factory.deploy's script/cut-release.sh did, generically:

  1. read [package].version from foundry.tomlthrough
    soldeer_gate::read_local_version
    , the parser the publish lifecycle already
    uses, so the two can never disagree about what that line is;
  2. refuse anything not strict X.Y.Z — checked against
    frozen_snapshots::is_tag, the append-only gate's own predicate, rather
    than a restatement of it. 0.1.7-rc1 maps to 0_1_7-rc1, which that gate
    ignores forever: an orphan snapshot nothing protects. The rejection criterion
    is literally "the gate would not recognise this dir";
  3. refuse when src/generated/candidate/ is missing (or empty — the property is
    "there is something to freeze", not "the dir exists");
  4. refuse to overwrite an existing src/generated/<TAG>/, twice: as a pre-flight
    check with a readable message, and again at the point of writing, because the
    copy uses create_dir rather than create_dir_all;
  5. regenerate candidate, forge fmt, then copy;
  6. assert the frozen dir and candidate are byte-identical afterwards, both
    sides re-read from disk (the diff -r the shell version ended with).

How the wrong ordering is made unrepresentable

The consumer no longer supplies a freeze script; it supplies a generate
command
, and cut() is the only thing that sequences the release:

let plan = plan(repo, root)?;   // guards, before any side effect
generate()?;                    // 1. REGENERATE (and format) FIRST
...
write_tree(&plan.frozen, &tree)?;  // 3. FREEZE, from the just-regenerated candidate

There is no input that runs after the copy, so "freeze, then regenerate" has
nowhere to be written. And the one way it could sneak back — a consumer passing a
leftover cut-release.sh as the generate command — is refused explicitly: if the
generate step created the numbered dir itself, cut() fails rather than
proceeding.

generate is a &mut dyn FnMut() -> Result<(), String> purely so the sequence
is unit-testable without forge; production passes the consumer command followed
by forge fmt.

What consumer input survives, and why that shape

One optional input: pointers-generate-cmd, e.g.
forge script ./script/BuildPointers.sol — which is also the default when it is
empty, so a repo on the convention passes nothing at all.

Both existing deploy repos, and every repo #296 migrates, generate pins with
exactly that invocation, so the default is the convention rather than a guess.
Keeping the override rather than hard-coding it is deliberate: rainix does not own
the consumer's script filename, and the failure mode of a wrong guess (forge script: file not found, mid-release) is worse than one input. The default string
lives in exactly one place — cut_release::DEFAULT_GENERATE_CMD — and the
workflow input defaults to '' rather than restating it.

What it is not is as important: it is a generate command, so its only
position in the lifecycle is "before the freeze".

snapshot-generate-cmd is deleted, not deprecated

Gone from the inputs entirely — no note, no ignored knob, no default. An earlier
revision of this PR deprecated it the way rainix-autopublish deprecates its
soldeer-generate-cmd; that reasoning was circular, since that input is an
instance of the same debt rather than a precedent for more of it. A permanent
no-op input in a shared reusable propagates by copy-paste: #296
is about to create eight or more deploy repos whose authors will start from an
existing caller's package-release.yaml, and each would inherit a knob that does
nothing and has to be discovered as such.

The cost of deleting is one line in each of two repos, both already open:

Merge order: this PR FIRST, then the two caller PRs

This matters, so here is the actual constraint rather than an assumption.
snapshot-generate-cmd is required: true on main today, so both
directions fail inside the window — there is no free ordering:

window state what GitHub does
caller PRs merged first: caller omits an input still declared required: true run fails at startup — "Input is required, but not provided while calling"
this PR merged first: caller passes an input no longer declared run fails at startup — "Invalid input, snapshot-generate-cmd is not defined in the referenced workflow"

So the callers cannot land first "harmlessly"; that would only hold if this PR
left the input declared-but-optional, which is precisely the dead knob being
deleted. Both windows being the same class of failure, this PR first is the
better order for one reason: the dependency then runs from a finished, reviewed
PR to two one-line follow-ups that merge in seconds. The reverse couples two
production release paths to whether this PR survives review unchanged — if it is
sent back, both callers sit broken for as long as that takes.

Neither failure can corrupt a release. Both are input validation, raised before
any step runs: nothing is generated, published, tagged or committed, and recovery
is re-pushing the tag once the set is complete. The one real precaution is that no
sol-v* tag should be pushed on either caller between the merges.

(For completeness: a genuinely zero-window sequence exists — land this with the
input optional-and-ignored, merge the callers, then a third PR deleting it. Three
merges and a transient dead knob, to buy a window that only matters if someone
tags during it. Not taken; recorded so the trade is explicit rather than implied.)

Why a composite action, not a run: step

RAINIX_SHA (53e96a7) predates frozen_snapshots and rpc_preflight
git ls-tree 53e96a7 rainix-static/src/ is three files. So
nix develop …/$RAINIX_SHA#sol-shell -c rainix-static cut-release would be
"unknown subcommand" on merge and stay broken until a separate sha bump landed. The
composite runs the tool out of its own checkout via a path: flake ref — the
pattern no-submodules, frozen-snapshots-append-only and rpc-preflight already
use, and for this same reason. path: also makes no api.github.com call, so it
cannot hit the 429 that sha-pinning exists to avoid. It enters that checkout's
sol-shell (which carries both forge and rainix-static), so the tool and the
forge it drives are the same commit; flake.lock is byte-identical between
53e96a7 and main, so there is no toolchain skew today either.

Blast radius: the two live callers

Consumers pin @main, so this is live on merge. Both callers, checked on their
default branches just now:

caller passes today after this merges
S01-Issuer/st0x.deploy snapshot-generate-cmd: bash script/cut-release.sh must drop that line (S01-Issuer/st0x.deploy#305), then releases run the correct order with no other change — it is already on the rolling-candidate model. Its script/cut-release.sh — which freezes candidate and only then runs forge script + forge fmt, i.e. the live inverted order this issue is about — becomes dead code
rainlanguage/rain.factory.deploy snapshot-generate-cmd: forge script ./script/BuildPointers.sol && forge fmt must drop that line (rainlanguage/rain.factory.deploy#9); the same command then runs as the default. But main has no src/generated/candidate/ (its BuildPointers still writes src/generated/<version>/ straight from foundry.toml), so a sol-v* tag fails loud at the candidate guard, publishing nothing, until #5 lands too. That repo has never published a Soldeer revision

Stating it plainly rather than hedging: this makes rain.factory.deploy's release
path depend on #5 being merged. The alternative — a second code path for repos not
yet on the rolling-candidate model — would preserve exactly the mode #296 exists to
delete, and #301's guard list is explicit that a missing candidate/ is a refusal.

What rain.factory.deploy#5 drops once this lands

Not touched here; that is its own PR.

  • script/cut-release.sh — all 72 lines, entirely.
  • package-release.yaml's snapshot-generate-cmd: bash script/cut-release.sh
    the whole with: entry, since that repo is on the conventional script path and
    needs no pointers-generate-cmd either. (add gmp dep for rust #9 removes main's copy of that line;
    debug deploy #5 carries its own and will need the same edit, whichever lands second.)
  • Its QA section's stubbed-forge exercise of that bash, and the "still open for
    the human: cut-release.sh hand-rolls the freeze in bash rather than a shared
    mechanism" note, which this closes.
  • One comment edit rather than a drop: script/BuildPointers.sol's docstring
    points readers at script/cut-release.sh for what creates a numbered dir; that
    becomes rainix-static cut-release.

S01-Issuer/st0x.deploy should drop its script/cut-release.sh too — its copy is
the one with the inverted order — as a follow-up to #305, which is deliberately
just the one release-call line.

Out of scope, found while here

  • pre-commit run --all-files cannot pass on main. The rustfmt-conditional
    hook's guard is [ -f Cargo.toml ] || [ -f */Cargo.toml ], which matches
    rainix-static/Cargo.toml, and it then runs cargo-fmt from the repo root:
    `cargo metadata` exited with an error: could not find `Cargo.toml`.
    Reproduced on clean main (8af258f) before touching anything, and unchanged by
    this PR. Not fixed here.
  • S01-Issuer/st0x.deploy's inverted freeze is live right now. Until this (or
    a change on its side) lands, any sol-v* tag there can freeze a drifted
    candidate into an append-only dir. It already carries one frozen tag,
    src/generated/0_1_1/.
  • rainix-autopublish's soldeer-generate-cmd is the same dead input, and has
    ZERO live callers.
    All 29 repos calling rainix-autopublish.yaml@main were
    fetched and grepped: not one passes it, and the only occurrence anywhere is its
    own declaration. So it can be deleted outright with no caller PRs and no
    ordering window at all — strictly cheaper than this deletion. Not touched here;
    it wants its own one-line PR.
  • The pinned RAINIX_SHA is two rainix-static subcommands behind main. Any
    future workflow step that reaches for a new subcommand from
    github:rainlanguage/rainix/$RAINIX_SHA#… will be an unknown-subcommand failure
    on merge. Only the path: composites are immune. Worth a lint or a bump policy;
    not this PR.

End to end, not just unit tests

A minimal deploy-repo shape (a foundry.toml at 0.1.5 and a deliberately
unformatted src/generated/candidate/X.pointers.sol pinning 0xAAAA), run
through the composite's exact invocation —
nix develop "path:<rainix>#sol-shell" -c rainix-static cut-release … — with a
generate command that rewrites the pin to 0xBBBB:

$ rainix-static cut-release --generate-cmd 'printf "…address(0xBBBB);…" > src/generated/candidate/X.pointers.sol'
Formatted …/src/generated/candidate/X.pointers.sol
cut-release: froze src/generated/candidate -> src/generated/0_1_5

$ cat src/generated/0_1_5/X.pointers.sol
address constant DEPLOYED_ADDRESS = address(0xBBBB);

$ diff -r src/generated/candidate src/generated/0_1_5
IDENTICAL

$ rainix-static cut-release --generate-cmd true
::error::cut-release: ./src/generated/0_1_5 already exists — refusing to overwrite a frozen release snapshot (snapshots are append-only; release a new version instead)
exit=1

Three things that transcript shows, which the unit tests assert separately: the
frozen dir holds 0xBBBB, the address the generator produced, not the 0xAAAA
that was committed — freeze-first would have recorded the wrong one; the frozen
bytes are the formatted form, so forge fmt ran before the copy, not after;
and a second cut refuses rather than clobbering.

QA

  • Discriminating tests: 13 new tests in rainix-static/src/cut_release.rs
    (suite: 53 passed, 0 failed — they run inside the nix build via doCheck, so
    every job that enters a shell runs them).

    • regenerates_before_freezing — the generator rewrites candidate from
      0xAAA to 0xBBB and adds a file; the frozen dir must hold 0xBBB and the
      new file. Freezing first records the address the release does not publish.
    • nothing_is_frozen_while_the_generator_runs — the same ordering from the
      generator's side: the numbered dir does not exist while it runs.
    • refuses_when_the_generator_freezes_the_numbered_dir_itself — a leftover
      consumer cut-release.sh passed as the generate command is refused, and its
      bytes are not adopted as the release.
    • a_failing_generator_freezes_nothing, and
      a_command_that_fails_anywhere_fails_the_cut (false, false; true,
      false | true, unset var — -e, -o pipefail, -u respectively).
    • rejects_versions_the_append_only_gate_would_ignore0.1.7-rc1, 0.1,
      1.2.3.4, 0.1.5+build, v0.1.5, ""; each must refuse, must not run the
      generator, and must leave src/generated holding only candidate.
    • accepts_every_strict_version0.1.5, 1.0.0, 12.0.255, 0.1.10.
    • refuses_without_a_candidate, refuses_an_empty_candidate,
      refuses_to_overwrite_a_frozen_snapshot (asserts the pre-existing frozen
      bytes are still there afterwards), refuses_without_a_package_version.
    • freezes_candidate_into_the_numbered_dir, freezes_nested_files_too,
      write_tree_refuses_an_existing_dir, read_tree_is_relative_and_recursive.
  • Mutations applied: nine, one at a time against the committed baseline, whole
    suite each time, tree restored and re-verified green (53 passed) at the end.
    Seven killed, one killed after adding a test, one survivor kept deliberately.

    mutation result
    freeze first, regenerate second (the inverted order) killed by 5 tests: regenerates_before_freezing, nothing_is_frozen_while_the_generator_runs, refuses_when_the_generator_freezes_the_numbered_dir_itself, refuses_an_empty_candidate, a_failing_generator_freezes_nothing
    drop the strict X.Y.Z guard killed by rejects_versions_the_append_only_gate_would_ignore
    create_dir -> create_dir_all in the copy killed by write_tree_refuses_an_existing_dir
    drop the pre-flight existing-frozen-dir guard killed by refuses_to_overwrite_a_frozen_snapshot
    drop the candidate-exists guard killed by refuses_without_a_candidate
    drop the post-generate frozen-dir guard killed by refuses_when_the_generator_freezes_the_numbered_dir_itself
    drop the empty-candidate guard killed by refuses_an_empty_candidate
    drop bash -euo pipefail survived the first pass — a_command_that_fails_anywhere_fails_the_cut was written for it and now kills it
    drop the post-copy byte-identity assertion survives, deliberately. It can only fire if the filesystem lies about a write it just accepted, so nothing short of fault injection reaches it. It is the diff -r the reference script ended with, kept as defence in depth and reported rather than papered over with a test that cannot fail
  • Oracle: rainlanguage/rain.factory.deploy@2026-07-25-rolling-candidate-model's
    script/cut-release.sh read as the behaviour being generalised;
    rainix-static/src/frozen_snapshots.rs is_tag() read directly for what the
    append-only gate does and does not recognise (and then called, not restated);
    rainix-autopublish.yaml's soldeer-generate-cmd read for the deprecation
    wording; both callers' package-release.yaml and src/generated/ layouts read
    live from their default branches, not assumed. All independent of this diff.

  • Category check: rainix-tag-release delegates cut-release to consumers; the freeze ordering is a silent-corruption hazard #301's six boxes. Freeze implemented in rainix as a
    rainix-static subcommand on the soldeer-gate precedent, invoked by
    rainix-tag-release — yes. Regenerate -> fmt -> freeze enforced in the tool —
    yes, and unrepresentable otherwise. Guards kept (strict X.Y.Z, candidate/
    must exist, refuse overwriting a frozen dir) — yes, plus empty-candidate and
    generator-created-the-dir, which are the same properties widened.
    Consumer input reduced to the pointer-generation command, defaulted by
    convention — yes. snapshot-generate-cmd deprecated the autopublish way — yes.
    rain.factory.deploy drops script/cut-release.sh — enumerated above, left for
    its own PR as instructed.

CI: cargo fmt --all -- --check and
cargo clippy --all-targets --all-features -- -D warnings -D clippy::all (CI's
exact invocation, after touching the changed files so a warm target cannot mask a
lint) both clean; pre-commit run --all-files matches main — same single
pre-existing rustfmt-conditional failure, nothing new.

🤖 Generated with Claude Code

@thedavidmeister thedavidmeister self-assigned this Aug 13, 2026
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@thedavidmeister, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

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

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 041e17b5-305a-4b33-9465-ad85352e7475

📥 Commits

Reviewing files that changed from the base of the PR and between 8af258f and 5bb2cb3.

📒 Files selected for processing (5)
  • .github/actions/cut-release/action.yml
  • .github/workflows/rainix-tag-release.yaml
  • rainix-static/src/cut_release.rs
  • rainix-static/src/main.rs
  • rainix-static/src/soldeer_gate.rs

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

@coderabbitai review

The earlier run on this PR was rate limited and reported pass without reviewing anything. Please review the branch as it now stands — notably rainix-static/src/cut_release.rs and the deletion of snapshot-generate-cmd from rainix-tag-release.yaml.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== changed files ==='
git diff --name-status HEAD^..HEAD

echo '=== diff summary ==='
git diff --stat HEAD^..HEAD

echo '=== relevant changed diff ==='
git diff --find-renames -- \
  rainix-static/src/cut_release.rs \
  rainix-static/src/main.rs \
  rainix-tag-release.yaml \
  .github/actions/cut-release \
  '*cut-release*' \
  '*rainix-tag-release*' || true

echo '=== candidate file locations ==='
fd -a 'cut_release\.rs|main\.rs|rainix-tag-release\.yaml|cut-release' . \
  -E .git -E target -E node_modules

echo '=== Rust module outlines ==='
if [ -f rainix-static/src/cut_release.rs ]; then
  ast-grep outline rainix-static/src/cut_release.rs --items all --view expanded
fi
if [ -f rainix-static/src/main.rs ]; then
  ast-grep outline rainix-static/src/main.rs --items all --view expanded
fi

echo '=== workflow references and input declarations ==='
rg -n -C 4 --glob '*.yaml' --glob '*.yml' \
  'snapshot-generate-cmd|pointers-generate-cmd|cut-release|rainix-static' .

Length of output: 3817


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== pull request metadata ==='
gh pr view 302 --repo rainlanguage/rainix --json baseRefName,headRefName,commits \
  --jq '{baseRefName, headRefName, commits: [.commits[].oid]}'

base_ref="$(gh pr view 302 --repo rainlanguage/rainix --json baseRefName --jq .baseRefName)"
echo "=== merge base with origin/${base_ref} ==="
merge_base="$(git merge-base "origin/${base_ref}" HEAD)"
printf '%s\n' "$merge_base"

echo '=== complete PR changed files ==='
git diff --name-status "$merge_base"...HEAD

echo '=== complete PR diff ==='
git diff --find-renames "$merge_base"...HEAD -- \
  rainix-static/src/cut_release.rs \
  rainix-static/src/main.rs \
  rainix-static/src/frozen_snapshots.rs \
  rainix-static/src/soldeer_gate.rs \
  .github/workflows/rainix-tag-release.yaml \
  .github/actions/cut-release/action.yaml

echo '=== cut_release implementation ==='
sed -n '1,540p' rainix-static/src/cut_release.rs

echo '=== command dispatch ==='
sed -n '1,180p' rainix-static/src/main.rs

echo '=== helper contracts ==='
sed -n '1,260p' rainix-static/src/frozen_snapshots.rs
sed -n '1,260p' rainix-static/src/soldeer_gate.rs

echo '=== release workflow ==='
sed -n '1,280p' .github/workflows/rainix-tag-release.yaml

echo '=== cut-release composite action ==='
sed -n '1,240p' .github/actions/cut-release/action.yaml

echo '=== action and workflow files in full PR ==='
git diff --name-only "$merge_base"...HEAD | grep -E '(^|/)(action|[^/]+)\.ya?ml$' || true

Length of output: 90614


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== cut-release composite action ==='
cat -n .github/actions/cut-release/action.yml

echo '=== comparable composite actions ==='
for action in \
  .github/actions/frozen-snapshots-append-only/action.yml \
  .github/actions/rpc-preflight/action.yml \
  .github/actions/no-submodules/action.yml
do
  if [ -f "$action" ]; then
    echo "--- $action ---"
    cat -n "$action"
  fi
done

echo '=== composite action references ==='
rg -n -C 3 --glob 'action.y*ml' \
  'path:|rainix-static|nix develop|generate-cmd|GITHUB_ACTION_PATH' .github/actions

echo '=== Cargo package and test configuration ==='
if [ -f rainix-static/Cargo.toml ]; then
  cat -n rainix-static/Cargo.toml
fi
rg -n -C 3 --glob '*.nix' --glob 'Cargo.toml' --glob 'flake.nix' \
  'rainix-static|cargo test|doCheck|cut_release' .

echo '=== numbered source lines ==='
nl -ba rainix-static/src/cut_release.rs | sed -n '45,235p'
nl -ba .github/workflows/rainix-tag-release.yaml | sed -n '45,225p'

Length of output: 22555


@thedavidmeister Reviewed the current branch.

I found no review findings.

The release workflow removes snapshot-generate-cmd and passes only pointers-generate-cmd to the new composite action. The composite action runs the new rainix-static cut-release command from its own checkout through the path: flake reference.

cut_release.rs enforces the required sequence:

  1. Validate the release plan before generation.
  2. Run the generate command.
  3. Run forge fmt.
  4. Refuse a generator-created numbered snapshot.
  5. Copy the non-empty candidate tree into the new numbered directory.
  6. Re-read both trees and verify byte equality.

The command also reuses soldeer_gate::read_local_version and frozen_snapshots::is_tag, so the release-version parsing and append-only tag acceptance stay aligned.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

HELD — do not merge. The design this PR sits in is unresolved, and merging now ships something we would immediately undo.

Specifically: this PR deletes snapshot-generate-cmd and adds pointers-generate-cmd, defaulting to forge script ./script/BuildPointers.sol. Two open questions decide whether that input should exist at all.

  • BuildPointers should not exist: pointer generation is part of build #304 — pointer generation is part of build, not a separate script. Eight repos already call theirs Build.sol; four call it BuildPointers.sol, and the split tracks the deploy side rather than a different job. If that holds, cut-release runs the repo's build and rainix asks for nothing — so this PR would be removing one knob and adding another in the same change, while enshrining the minority spelling as the default.
  • Release-time verify runs the whole suite and asserts nothing about the snapshot it publishes #303 — release-time verify runs the whole suite and asserts nothing about the snapshot it publishes. The frozen dir this workflow creates is imported by no test at the moment it is created, while the tagged-constants tests re-verify directories an append-only gate already makes immutable. That reshapes what cut-release should hand to the verify step.

What stands regardless of both: the Rust cut-release, the guards reusing frozen_snapshots::is_tag and soldeer_gate::read_local_version, and the regenerate-then-freeze ordering being unrepresentable rather than documented. The ordering hazard is real and live — S01-Issuer/st0x.deploy's script/cut-release.sh copies candidate at line 47 and only then runs forge script at line 51.

The two caller PRs are held with it, since they were sequenced behind this one: rainlanguage/rain.factory.deploy#9 and S01-Issuer/st0x.deploy#305. Neither is a no-op — snapshot-generate-cmd is required: true on main today, so dropping it before this lands fails at workflow_call startup.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

Caller count correction: there are three, not two.

The sweep behind this PR found two callers passing snapshot-generate-cmd — rain.factory.deploy and st0x.deploy. A third has appeared since: rainlanguage/rain.deploy#26 switches that repo from rainix-autopublish to rainix-tag-release and declares snapshot-generate-cmd: forge script ./script/Build.sol && forge fmt. It was not counted because it had not landed when the sweep ran.

That PR is correct as it stands — the input is required: true on main today, so it must pass one. But it means the deletion here has three callers to sequence, not two, and the caller PRs currently open (rainlanguage/rain.factory.deploy#9, S01-Issuer/st0x.deploy#305) do not cover it.

Note also that #26 uses script/Build.sol, not BuildPointers.sol — so the default this PR would have introduced (forge script ./script/BuildPointers.sol) is already the minority spelling among live callers, which is the substance of #304.

This PR remains HELD pending #304 and #303.

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.

rainix-tag-release delegates cut-release to consumers; the freeze ordering is a silent-corruption hazard

1 participant