All checks were successful
PR Checks / rust-gates (pull_request) Successful in 5s
servuo-plugins hit both of these on its first real release run; this repo runs the same engine, so it has the same two defects latent. REGISTRY_USER / REGISTRY_TOKEN were empty there, yet the tag push SUCCEEDED: actions/checkout leaves an `http.<host>.extraheader` credential in the local git config, so `git remote set-url` to a URL with empty credentials still authenticated through that leftover header. The release API call had no such fallback and returned 401. Net result was the worst available outcome — the repo tagged, no release, and a failed job. Two fixes: A credential preflight, before anything is built or pushed, gated on the run actually intending to publish so a docs:/chore:-only merge (or this repo's pre-crate no-op) still passes on a repo with no secrets. It names the missing secrets and the scope they need instead of failing wherever they happen to be used first. Orphan-tag recovery. A tag with no release behind it means an earlier run died after tagging, and the old code treated any existing tag as "nothing to release" — so that state could never clear itself: every later run would see the tag and stand down, forever. The plan step now asks the API whether a release exists for the tag, and on 404 reuses the tag and publishes the release it is missing. This deliberately overrides the RELEASE=false the bump logic just decided, which is the whole point — with the tag in place there are no releasable commits after it. Anything other than 200/404 (network failure, bad token) is refused rather than guessed, since assuming "no release" would republish over a good one. The tag step now reuses an existing tag instead of failing on `git tag`, and the changelog for a recovery run summarizes what the tag contains (previous-tag..this-tag) rather than the empty range after it. sync-project-tree gets the same preflight: its first run on main failed with an opaque `git clone` error against `https://:@host/...` that said nothing about a missing secret. Verified by extracting every run block and exercising the paths: empty secrets fail the preflight with a legible message and populated ones pass; the no-Cargo.toml guard still short-circuits to release=false; a crate with no tag still takes the seed path; and against real repo state, a tag with a release stands down while an orphan tag recovers. Co-Authored-By: Claude <noreply@anthropic.com>