Skip to content
rlsbl.commands.release.release_state
On this page

The release's JSON state file: the canonical ordered step list, the success and failure markers each step records, and the resume logic they drive.

#rlsbl.commands.release.release_state

#rlsbl.commands.release.release_state

Release state file: JSON persistence for idempotent release flow, tracking which steps completed so failed releases can resume from the last successful step.

Tracks which release steps have completed so that a failed release can be resumed without re-executing already-done work.

Canonical step list ------------------- RELEASE_STEPS is the single ordered source of truth for every step a release records. The run guard message, save_step validation, resume skip logic, and auto-clear completeness checks all derive from it — never hardcode step counts or step-name lists elsewhere.

Two marker kinds are recorded in the state file:

  • Success markers (completed_steps list): the step finished (or was

not applicable and is provably done). These gate resume-skip.

  • Failure markers (failed_steps dict of step -> message): the step

ran and failed. They do NOT gate resume-skip (a resume re-attempts the step) — they feed the completion summary. Steps in FATAL_STEPS abort the release when they fail (state preserved, resumable); non-fatal steps record the failure and let the release complete.

A state file is provably complete when every canonical step has a success or failure marker and no fatal step failed (is_state_complete). Only then may the success path clear it.

State file location -------------------

  • Standalone projects: <project_dir>/.rlsbl/releases/in-progress.json
  • Releasable releases: the state belongs to the releasable, not the

representative member package, and is at <workspace_root>/.rlsbl-monorepo/releasables/<name>/releases/in-progress.json.

ALL derivations of the state path must go through :func:get_state_path (with the releasable dir from :func:resolve_releasable_dir when in a monorepo) so the run guard, resume CLI, executor, unexpected-files whitelist, and scrub agree on a single location.

The file is written at the start of the mutating phase, deleted on success, and left in place on failure.

#get_state_dir

python
def get_state_dir(project_dir: str, *, releasable_dir: str | None=None) -> str

Return the directory holding release state files.

releasable_dir is the releasable's state directory (.rlsbl-monorepo/releasables/<name>/); when given, state lives in its releases/ subdirectory instead of the project's .rlsbl/releases/. Delegates to the single releases-dir derivation in :mod:rlsbl.release_file (shared with the release-file family: unreleased.toml, v{x}.toml, unreleased.md, v{x}.md).

#get_state_path

python
def get_state_path(project_dir: str, *, releasable_dir: str | None=None) -> str

Return the path to the release state file (in-progress.json).

This is the ONLY function that may derive the state file location. Pass releasable_dir (from :func:resolve_releasable_dir or an already-resolved releasable config dir) for releasable releases.

#get_scrub_result_path

python
def get_scrub_result_path(project_dir: str, *, releasable_dir: str | None=None) -> str

Return the path to the scrub-result.json file (same home as the release state file).

#resolve_releasable_dir

python
def resolve_releasable_dir(project_dir, workspace_root) -> str | None

Resolve the releasable state dir for a project, or None.

Returns <workspace_root>/.rlsbl-monorepo/releasables/<name>/ when project_dir is a member of a releasable; None for standalone projects, non-member projects, or when workspace_root is None.

#StateResolutionError

Raised when the resume source cannot be resolved unambiguously.

#resolve_resume_source

python
def resolve_resume_source(workspace_root, cwd='.') -> tuple[str, str]

Resolve (project_dir, state_path) for rlsbl release resume.

  • Standalone (workspace_root is None): state under

<cwd>/.rlsbl/releases/.

  • Inside a member package dir: the releasable-aware state path. If

in-flight state exists only at the legacy per-project location, a :class:StateResolutionError with a migration hint is raised (never silently ignore pre-existing in-flight state).

  • At the workspace root: finds the single releasable whose state file

exists and resolves the representative member (from the saved monorepo_name, falling back to the first member). Errors on zero or multiple in-flight releasables.

#find_releasable_state_files

python
def find_releasable_state_files(workspace_root) -> list[tuple[str, str]]

Scan all releasables in a workspace for in-progress release state.

Returns a sorted list of (releasable_name, state_path) tuples for every releasable whose releases/in-progress.json exists. Used by the resume CLI when invoked from the workspace root.

#save_release_state

python
def save_release_state(state_path: str, state_dict: dict) -> None

Atomically write the release state dict to disk (tmp + os.replace).

#load_release_state

python
def load_release_state(state_path: str) -> dict | None

Read and parse the release state file. Returns None if missing.

#save_step

python
def save_step(state_path: str, step_name: str) -> None

Record a successful step: load, append to completed_steps, save.

Also clears any failure marker for the step (a resume that re-attempts a previously-failed step replaces the failure with success). Raises ValueError for step names not in :data:RELEASE_STEPS.

#save_step_failure

python
def save_step_failure(state_path: str, step_name: str, message: str) -> None

Record a step failure marker with a human-readable message.

Failure markers do NOT gate resume-skip; they feed the completion summary. A failure replaces any prior success marker for the step. Raises ValueError for step names not in :data:RELEASE_STEPS.

#clear_release_state

python
def clear_release_state(state_path: str) -> None

Delete the state file and its parent dir if empty (no-op if already absent).

This is an unconditional removal — used by the success epilogue (after :func:is_state_complete verification), rollback paths (state is useless after a local rollback), and PR-mode handoff (only the local mutating phase is tracked; publishing happens in CI).

#get_failed_steps

python
def get_failed_steps(state: dict) -> dict[str, str]

Return the failure markers dict (step -> message) from a state dict.

#get_missing_steps

python
def get_missing_steps(state: dict) -> list[str]

Return canonical steps that have neither a success nor a failure marker, in canonical order.

#has_fatal_failure

python
def has_fatal_failure(state: dict) -> bool

Return True if any fatal step has a failure marker.

#is_state_complete

python
def is_state_complete(state: dict) -> bool

Return True if the state is provably complete: every canonical step has a success-or-failure marker AND no fatal step failed.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search