rainix-autopublish.yaml publishes a version and then pushes the version bump that makes the next run see a new version. Nothing serialises its runs, and no consumer declares a concurrency: group either — all eleven package-release.yaml callers have zero concurrency:
rain.solmem, rain.interpreter, rain.math.float, rain.erc4626.words, rain.flare,
rain.orderbook, rain.strings, rain.datacontract, rain.metadata, rain.erc20,
rain.lib.typecast
Two merges landing close together start two runs. Both check out their own pre-bump sha, both read the same [package].version, both compute changed=true, and both reach Publish to Soldeer with the same package~version. One wins; the loser fails.
This is live, not theoretical. rain.solmem, 2026-08-17:
The worse outcome has not landed yet but is unguarded. Tag and push fetches, replays onto origin/<ref> and runs git push origin HEAD with no retry. If that push loses to a concurrent push after Publish to Soldeer succeeded, main is left declaring a version that is already on the registry, and soldeer_gate's ahead-invariant (rainix-static/src/soldeer_gate.rs) then hard-errors on every subsequent run — that repo's releases are blocked until a human hand-bumps foundry.toml.
Proposed fix
A workflow-level group in the reusable itself, so all eleven consumers get it without eleven edits:
name: rainix-autopublish
concurrency:
group: rainix-autopublish-${{ github.ref }}
cancel-in-progress: false
on:
workflow_call:
cancel-in-progress: false is the half that prevents the wedge — cancelling a run between Publish to Soldeer and Tag and push produces exactly the wedged state above. A literal group name, not ${{ github.workflow }} (which a called workflow inherits from its caller), avoids colliding with any group a caller declares.
Worth confirming on a real run that a workflow-level concurrency in a called workflow is honoured. GitHub's docs only imply it, via the warning against reusing the same group name in caller and called workflows.
Two things a concurrency group does not fix:
- A queued run still checks out its own event sha, so once the leader has bumped
[package].version the follower's tree is stale and trips the ahead-invariant. That is a red run rather than a wedge, but it is red on the second of every burst.
Tag and push still has no push-retry loop, which is the actual wedge mechanism. Serialising makes it far less likely to fire; it does not remove it.
rain.solmem has a caller-level group landed as a stopgap in rainlanguage/rain.solmem#138. Whatever PR lands the reusable-level fix here should delete that consumer-local block.
rainix-autopublish.yamlpublishes a version and then pushes the version bump that makes the next run see a new version. Nothing serialises its runs, and no consumer declares aconcurrency:group either — all elevenpackage-release.yamlcallers have zeroconcurrency:Two merges landing close together start two runs. Both check out their own pre-bump sha, both read the same
[package].version, both computechanged=true, and both reachPublish to Soldeerwith the samepackage~version. One wins; the loser fails.This is live, not theoretical. rain.solmem, 2026-08-17:
0.1.10; https://github.com/rainlanguage/rain.solmem/actions/runs/31998658964 failed withFailed to run soldeer: error during publishing: dependency already exists; https://github.com/rainlanguage/rain.solmem/actions/runs/31998666592 failed the gate withfoundry.toml [package].version (0.1.10) is not ahead of the published revision (0.1.10).0.1.11; https://github.com/rainlanguage/rain.solmem/actions/runs/31999962131 failed withdependency already exists.The worse outcome has not landed yet but is unguarded.
Tag and pushfetches, replays ontoorigin/<ref>and runsgit push origin HEADwith no retry. If that push loses to a concurrent push afterPublish to Soldeersucceeded,mainis left declaring a version that is already on the registry, andsoldeer_gate's ahead-invariant (rainix-static/src/soldeer_gate.rs) then hard-errors on every subsequent run — that repo's releases are blocked until a human hand-bumpsfoundry.toml.Proposed fix
A workflow-level group in the reusable itself, so all eleven consumers get it without eleven edits:
cancel-in-progress: falseis the half that prevents the wedge — cancelling a run betweenPublish to SoldeerandTag and pushproduces exactly the wedged state above. A literal group name, not${{ github.workflow }}(which a called workflow inherits from its caller), avoids colliding with any group a caller declares.Worth confirming on a real run that a workflow-level
concurrencyin a called workflow is honoured. GitHub's docs only imply it, via the warning against reusing the same group name in caller and called workflows.Two things a concurrency group does not fix:
[package].versionthe follower's tree is stale and trips the ahead-invariant. That is a red run rather than a wedge, but it is red on the second of every burst.Tag and pushstill has no push-retry loop, which is the actual wedge mechanism. Serialising makes it far less likely to fire; it does not remove it.rain.solmem has a caller-level group landed as a stopgap in rainlanguage/rain.solmem#138. Whatever PR lands the reusable-level fix here should delete that consumer-local block.