Release workflow
Verso calculates one exact file plan, then records progress through explicit local stages and one remote transaction. The persisted plan makes an interrupted release inspectable and recoverable without recomputing its intended output.
Before any write
Verso loads one release-group config, discovers its members, verifies that their versions are consistent, resolves one target version, and calculates every file's actual before and after content. A full release also calculates the changelog, commit message, tag, hooks, and atomic push. A bump plan contains only version-file changes and version hooks.
verso --version 2.0.0 --dry-runverso bump minor --dry-runDry-run renders that exact plan, including full-file before/after diffs, without writing files, running hooks, creating a transaction journal, or executing mutating Git commands.
Add --group core when working with a named release group.
For execution, Verso checks the worktree policy and planned paths, rejects an existing target tag, and takes a repository transaction lock. Only one Verso transaction may be active per Git repository.
Persisted stages
After the first file-change confirmation, Verso stores the plan and progress under Git metadata and uses atomic file replacement. The release stages are:
planned -> before_version -> apply exact files -> files-applied -> after_version -> before_commit -> git add/commit -> committed -> after_commit -> before_tag -> annotated tag -> tagged -> after_tag -> before_push -> git push --atomic -> pushed -> after_push -> clear transactionverso bump follows only planned -> files-applied, runs before_version and after_version, then
clears the transaction. It does not change the changelog, stage files, create a commit or tag, or
push.
The release confirmations before files, commit, tag, and push default to yes. A target that is not
greater than the current version has an earlier confirmation that defaults to no. --yes accepts all
of them.
Status, resume, and abort
verso status [--json]verso resume [--retry-hook | --skip-hook]verso abort [--force]status is read-only. It reports the operation, release group, stage, version pair, changed-file
count, interrupted hook, and whether abort is still allowed.
resume first reconciles the journal with the exact file contents, HEAD, and tag. It skips completed
hooks and confirmations, then continues from the recorded stage. A hook that was active when the
process stopped has an unknown outcome: inspect its side effects, then explicitly use --retry-hook
or --skip-hook.
If another mutating release or bump is started while a transaction is active, Verso shows its group, version pair, and stage, then prompts to resume or abort it. Recovery completes that invocation; it does not continue into the newly requested release.
abort is conservative. It compare-and-deletes only the exact tag object Verso created, soft-resets
only the expected release commit, unstages only planned paths, and restores a file only when it still
matches the plan's after content. It refuses an unexpected HEAD, moved tag, or later file edit
rather than overwriting unrelated work. Once the release commit exists, abort also checks the pinned
remote and refuses an unowned target tag or a branch that already contains the release commit.
Resume keeps using the push URL, commit, and tag persisted when the transaction started, even if
local HEAD advances after push begins.
Failure and cancellation behavior
Expected file, hook, commit, and tag failures before push trigger a best-effort local abort. Release
hook failures are checked against the pinned remote first; if that remote cannot be inspected, Verso
preserves the journal instead of assuming the hook did not publish. A crash, user cancellation at a
later confirmation, or an ambiguous state preserves the journal so the user can choose resume or
abort while push has not started. A push failure preserves the journal for resume only.
| Stopping point | Persisted state and next action |
|---|---|
| Before initial file confirmation | No transaction and no file change |
| Files applied / before commit | Files remain unstaged; resume commits or abort restores them |
| Commit created / before tag | Expected release commit remains; resume tags or abort soft-resets and restores |
| Tag created / before push | Local commit and tag remain; resume pushes or abort removes the expected local work |
| Atomic push failed | Local commit, tag, and journal remain; fix or inspect the remote, then resume |
Push succeeded / after_push failed | Local and remote refs remain; only resume is allowed |
Cleanup never uses a hard reset. Restore, unstage, reset, tag-delete, journal, or remote-verification errors are reported instead of being hidden.
Hooks and CI handoff
Hooks are arbitrary project shell commands. Dry-run prints their names and commands but executes none.
Pass secrets through the environment rather than storing them in config, keep hooks retry-safe, and
prefer CI for destructive or credentialed publication. after_push is a follow-up boundary: it can be
resumed, but it cannot undo remote refs. After an interrupted hook, confirm that its old process has
stopped before retrying, skipping, or aborting the transaction.
Registry publication, GitHub Releases, binary builds, and deployment remain the responsibility of tag-triggered CI.
Before enabling automation, capture both checks in the target repository:
verso doctor --jsonverso --version <next-version> --dry-run --jsonAdd --group <NAME> to both commands when automating a named release group.
Atomic push details
Show the exact refs pushed by Verso
Verso resolves the current branch's configured remote and merge ref, then pushes only these two refs:
<release-commit-oid>:refs/heads/<upstream-branch><tag-object-oid>:refs/tags/<release-tag>The command uses git push --atomic; unrelated local tags are not included. The remote must support
atomic pushes, so it accepts both refs or neither.