On this page
#Changelog
#0.113.0
Release-path unblocks: the CI gates stop refusing commits whose jobs actually passed, the monorepo router gains a run_all dispatch that runs every member's CI on one commit, and advisory warnings no longer abort a release.
Context
Every change here was reproduced against a real release that could not proceed.
A matrix job GitHub skipped emits one unsuffixed check run, while the same job run emits one per leg -- so a re-run that executed every leg could never supersede the earlier skip, and both the CI wait and the publish gate refused a commit whose jobs had all passed. Supersession is now decided by a strictly later check run for the same job; a failing leg still blocks, and nothing else can cover a skip.
The router's run_all dispatch input is the sanctioned way out of the adjacent trap: a resumed candidate that touches only some members leaves the rest filtered out by paths, and the previous remedy was to invent churn commits to widen the window. Nothing is waived -- the jobs still have to pass.
The candidate-window guard was always told a push was about to happen, so a resume with nothing left to push compared the candidate against itself and refused, naming a push that was not happening. It now reads the window the plan actually derived.
The pgdesign gate now matches pgdesign's own severity model, so a project with zero errors and some advisory warnings is releasable; and monorepo extract / extract-releasable are no longer consequential, since their history rewrite runs on a throwaway clone and touches nothing published or already pulled.
#Features
- Monorepo CI router: run every member's jobs on one commit. The generated
ci-router.ymlnow declares arun_allworkflow_dispatchinput. Dispatching with-f run_all=trueshort-circuits the paths filter so every member's CI jobs run on that exact commit -- the sanctioned way to unblock a release whose resumed candidate touches only some members, without inventing churn commits to widen the window. Nothing is waived: the jobs still have to pass. Runrlsbl monorepo syncto regenerate the router.
#Fixes
- Advisory pgdesign warnings no longer abort releases. The pgdesign release gate now runs
pgdesign check --tag validation --ignore-warnings, matching pgdesign's own severity model: warn-severity results are advisory and only errors block a release. Previously a project with zero errors but any warnings could not be released at all. - A skipped matrix job no longer blocks a release its legs passed. GitHub emits one unsuffixed check run for a job it skipped, but one per leg for a job it ran, so a re-run that executed every leg could not supersede the earlier skip and both CI gates refused a commit whose jobs had all passed. A skipped check is now superseded by a strictly later check run for the same job. A failing leg still blocks, and nothing else can cover a skip.
- A resume with nothing new to push is no longer refused as an "empty window". The pre-push candidate-window guard was always told a push was about to happen, so when the release branch was already at the candidate it compared that commit against itself and refused, naming a push that was not happening. It now uses the window the plan actually derived.
rlsbl monorepo extractandmonorepo extract-releasableno longer interrupt with a confirmation prompt. Their history rewrite runs on a throwaway clone, so nothing published or already-pulled is touched.
#0.112.0
A release that previews itself and verifies its own outcome: release run --dry-run now records the real mutating first half through the candidate push, and a live release probes every target's registry before it calls itself done.
Context
This release is the rlsbl half of an ecosystem-wide fix campaign, and it closes one theme: the release engine used to report success on the basis of what it DID, not what actually HAPPENED, and its preview used to describe a release rather than perform one with the writes withheld.
Two flagship changes carry that theme.
The --dry-run preview of release run was the weakest preview in the tool -- a summary followed by an empty would-do body -- because the mutating phase interleaved reads and writes, and under the effects regime a read issued after a recorded write cannot be answered. That phase is now split on the CI seam: Phase A (version bump through candidate push) is a plan-builder plus an executor, where the builder reads and derives and the executor only issues effects, so a preview records every mutation for real and prints the actual argv and byte counts. Below an explicit boundary line, Phase B -- everything that depends on CI's verdict -- is DECLARED as a table rather than guessed at. A preview cannot push by construction rather than by a flag check.
Outcome verification is the other half. After the CI wait, each publishable target's registry is probed on a bounded retry budget, and a version the registry is not serving is a hard error naming the registry and the remedy. This catches the green-but-empty publish -- a workflow that concluded successfully having uploaded nothing -- which a workflow-status check structurally cannot. Under --no-watch nothing is probed and the run says out loud that the outcome is unverified.
Around those sit the defects the same campaign surfaced by hitting them live. The exit-code family: a release ending on a failed deploy, hook, subtree push or mirror release exited 0 with its state cleared; a batch could announce completion over an empty released list; a stale completed plan could swallow a freshly written batch file. The batch flow: stranded members are resumable inside the batch, the empty-candidate-window guard runs per member before the push instead of after a full CI wait, and the finalize commit the batch itself creates is now pushed rather than left behind. CI classification: an infrastructure-killed run is recognized ahead of the code-failure signatures it incidentally matches and its failed jobs are rerun once, because a resumed release pushes nothing and could never produce a fresh run.
The rest are smaller and mostly mechanical: dry_run_supported declarations on the five commands that cannot honestly preview themselves (they now refuse at parse time and say so in --help), artifact-scoped secret scans in every publish template (a whole-tree scan blocked a release on a fixture and left a scarred version), the pgdesign @latest -> @v0 and pgdesign validate -> pgdesign check --tag validation fixes in both the CI template and the release path, a new dep-floors check so a release requiring new framework behavior must declare the >= floor a consumer will actually resolve, the display trio (unreleased, status, monorepo status agreeing on real coverage), and the infra-release CHANGELOG assembly bug that silently omitted every infra version's heading.
Internally, rlsbl now dogfoods the stricttest pytest plugin instead of carrying its own copy of the test-isolation floor, and its lock moves to the released strictcli that ships dry_run_supported.
#Features
- **New
dep-floorscheck.** A release that requires new behavior from a sibling framework package now has to declare a>=floor at that version, or the check blocks the release. Your dev lock resolves the new framework, so your suite passes -- but a consumer installing the published artifact resolves whatever the declared floor allows and gets an older framework without the behavior. The check comparespyproject.tomlagainstuv.lockandpackage.jsonagainstpackage-lock.json(Go needs nothing:requirelines are already the minimums), and names the dependency, the locked version, and the exact constraint to write. Opt in with theinternal_dep_floorsconfig key; monorepo siblings are enforced automatically. - The releasable CI run-everything hook is documented. The monorepo and release-workflow docs now explain why every member of a releasable carries the releasable's
CHANGELOG.mdin its CI paths filter: it makes a release commit verifiable for members whose own directory the release did not touch (a first release, where the version write is a no-op), at the cost of running every member's CI jobs on any release of that releasable. The publish gate's refusal to accept askippedcheck is stated as deliberate. - A release now verifies that the registry actually served the version. Until now a release verified only its process -- CI green, tag pushed, publish workflow dispatched -- and then announced success. A publish job that concluded without producing an artifact (a skipped matrix leg, a gate that refused, an upload that 4xx'd into a retry that never happened) ended as a green release with nothing published, and nothing said so. After the CI wait, every publishable target's registry is now probed for the new version on a bounded retry budget, and a version still not being served is a hard error naming the registry and the remedy. In a batch, every member is probed for its own version and all failures are reported together. This runs on the
--watchpath only: under--no-watchthe publish workflow is still in flight, so nothing is probed and the run says out loud that the outcome is unverified. - **
rlsbl release run --dry-runnow previews the release by running its first half with every mutation recorded.** It used to print a summary and an empty would-do body -- the deepest and most consequential command in rlsbl shipping its weakest preview. The mutating phase interleaved reads and writes, and under the effects regime a read issued after a recorded write cannot be answered, so no arrangement of those calls could survive its own preview. The version bump, keyword tagging, lockfile syncs, build, release commit and candidate push are now built as a plan and then issued: the preview records all of it, prints the real argv and byte counts, and reaches the candidate push -- which renders carrying«step N output», the framework's name for the commit the recorded commit step would have created. Below that a boundary line readseverything below depends on CI's verdict, and Phase B (the CI gate, changelog finalization, the tag, the GitHub Release, publishing, deploys, post-release hooks) is declared as a table rather than guessed at. Exit 0, nothing written, nothing pushed -- and a preview cannot push by construction, not by a flag check. A batch preview is the members' plans in order.
#Fixes
- **
rlsbl release resumeno longer refuses over rlsbl's own state file.** A failed release leaves.rlsbl/releases/in-progress.json(and, after a scrub,scrub-result.json) in the tree untracked, and the clean-tree gate counted them as uncommitted work — so the resume you were told to run refused to start. Those two files are now exempt by path, in both the standalone and releasable homes; a genuinely dirty tree still blocks, and the refusal now names the files that are actually blocking it. The scaffold's.gitignoretemplate ignores them too. - Publish workflows scan built artifacts, not the source tree. Every scaffolded publish workflow ran
gitleaks dir .over the whole checkout, so a secret-shaped string in a file that never ships (a fixture, a doc, a deliberately public client ID) could block a release mid-flight and leave a scarred version with no assets. Each ecosystem now scans what it is about to publish: npm/yarn/pnpm pack the tarball and scan it, go scans goreleaser'sdist/, maven scans the assembled jars, and deno/docker/hex/zig -- which have no pre-publish artifact -- drop the scan instead of scanning the tree. Scans now pass the project's.gitleaks.tomlexplicitly, so allowlisting a false positive works in CI. Re-runrlsbl scaffoldto pick this up. - A release that ends with a failed step now exits nonzero. Deploy failures, post-release hook failures, a failed subtree push, a failed mirror GitHub Release and a failed post-hoc snapshot were reported as warnings and the release still exited 0 with its state file cleared -- so CI and scripts read a partially-failed release as success. The completion summary is now the single decision point: any failed step exits 1 and keeps the in-progress state, so
rlsbl release resumere-attempts exactly those steps. The subtree push and mirror release are tracked steps now (SUBTREE_PUBLISHED,MIRROR_RELEASED) instead of bare warnings. - **
rlsbl monorepo release runno longer announces a completion it cannot evidence.** A member whose release call exited 0 unwound past the batch's own bookkeeping and was silently dropped from the batch, and the run still printedBatch release complete:over an empty list and exited 0. A zero exit is now only accepted when the member left in-progress state behind (it is genuinely mid-flight and finishes on the verified candidate); otherwise the batch hard-errors. A run that released nothing exits nonzero instead of announcing completion. - **A completed batch plan no longer swallows a fresh
unreleased.toml.** When every item in the resolved plan sidecar was already released,rlsbl monorepo release runarchived the batch file along with the plan and exited 0 -- and because plans are matched by item set and bump intent, a brand-new batch file written for the NEXT release validated against the stale plan and got renamed into the archive without releasing anything. The batch file is now never touched on that path; only the stale plan is archived, and the command exits nonzero naming the plan and the remedy. - **
rlsbl statusfinds CI workflows named per target.** A repo whose CI lives inci-go.ymlorci-pypi.ymlreportedCI: missing, because status probed only.github/workflows/ci.yml. It now lists every CI source it discovers -- the same set the monorepo router and the release CI gate are built from. - Scaffolded pgdesign CI no longer fails on both of its steps. The generated workflow installed pgdesign with
@latest, which resolves a phantomv1.0.0permanently cached on the Go module proxy, and then ranpgdesign validate ., a command deleted in pgdesign 0.12.0. The install now pins@v0(with an in-place comment explaining the phantom version) and the schema step runspgdesign check --tag validation. Re-runrlsbl scaffoldto pick this up. - **
rlsbl unreleasedandrlsbl statusno longer disagree about coverage.**unreleasedcounted autogenerated and changelog-only commits as MISSING work whilestatusexempted them, so the two commands reported different coverage for the same repo.unreleasednow reportscovered/tracked (N exempted)and marks exempt commits[EXEMPT]. Both also read a releasable member's entries from the releasable's changes directory instead of reporting "JSONL changelog not set up", and scope commits across every member of the releasable. - **
rlsbl monorepo statusreports real changelog coverage.** TheUnreleasedcolumn counted CHANGELOG.md bullet lines, which describe the previous release -- a package whose unreleased commits had no changelog entry at all still looked documented. The column is nowCoverageand reportscovered/tracked (N exempted)per row. - Infra releases now get a CHANGELOG section. Generated changelogs silently omitted every
infrarelease's## X.Y.Zheading, because an infra release'sunreleased.jsonlis empty by definition and the generator skipped empty sections. The### Infrastructureblock now renders as intended. - Releases of pgdesign-schema projects work again.
rlsbl release runinvokedpgdesign validate, a command deleted in pgdesign 0.12.0, so the build step aborted with a misleading "pgdesign validate failed" message on every pgdesign-target release. It now runspgdesign check --tag validationfrom the schema directory. - **
--dry-runno longer crashes on the project lock.** Every preview that took rlsbl's advisory lock --monorepo release run --dry-run,scaffold --dry-run,release scrub --dry-run-- died withio.UnsupportedOperation: filenobefore it could preview anything, because the lock file was routed through the recording seam andfcntl.flockneeds a real file descriptor. The lock is process infrastructure, not a previewable effect: it is now taken (and cleaned up) for real in both modes. - A preview no longer creates real temporary files or directories.
claim-name --dry-runleft a real staging directory behind on every run, and a previewed changelog append left a stray.tmpin.rlsbl/changes/:tempfilecreates its entry in every mode while the matching cleanup was only recorded. Temp files and directories now go through the effect seam, so a preview records what it would create and creates nothing. - **
rlsbl monorepo status --helpno longer describes a column that does not exist.** The help text promised "the number of unreleased commits for every project" and "projects with zero unreleased commits are shown as up-to-date"; the column has been real JSONL changelog coverage for some time --covered/trackedwith an(N exempted)suffix, orno changelog. The help now says so. - Monorepo CI router documentation corrected. Both the release-workflow and monorepo pages still described the reusable-workflow era: that
monorepo syncrewrites each CI workflow's trigger toworkflow_call:and generates a router that dispatches to per-project workflows, and that the<router job key> / <ci job name>check-run naming follows from calling member CI as a reusable workflow. The router inlines every job instead (GitHub rejects a workflow referencing 20+ reusable workflows), writes no root copy, removes stale ones, and sets that check-run name explicitly so branch protection rules and the publish gate's regexes keep matching. - A release candidate that cannot trigger its own CI is now refused in a second, not after a full CI wait. The generated monorepo router gates each project's CI job on the paths a push touched, computed against that push's own before-SHA. When the window matched none of the releasing project's filters its job concluded
skipped, the publish gate refused the skipped check, and the release deadlocked on a tag that could never publish -- discovered only after the whole CI cycle. The window is computable from the diff, so it is now checked before the push: an empty one is a hard error naming the project's filters, what the window actually changed, and how to widen it. Nothing is pushed, tagged or finalized, and the version number is not burnt. - A batch release resumes its own stranded members instead of dying on them. A batch that died between its pass-1 commits and its pass-2 completion left every member holding an
in-progress.json. Re-running the batch handed each of those members back torelease run, whose contract is that an in-progress release must be resumed rather than restarted -- so the batch refused on state it had created itself, with no way to finish the group as a group. Members left mid-flight are now seeded straight into pass 2 and covered by this run's single candidate push and CI gate. - Every release entry point loads the shared env file, and a missing one is a hard error.
release run,release resume,deployandmonorepo release rundid not all load it, so a deploy or post-release hook that needed a secret got a different environment depending on which command reached it. The load is now one shared helper on all four paths, and a configured env file that does not exist stops the release with the path instead of continuing into a hook that will fail obscurely. - External checks are no longer told a shallow clone has never been released.
RLSBL_LAST_TAG=""is a signal meaning 'this project has no releases yet', and a check reading it takes its first-release branch. Any failure to resolve the last tag was flattened into that same empty string, so a clone with truncated history looked brand-new to every external check and silently widened each one's commit range. An unresolvable tag is now a hard error naming the remedy (git fetch --unshallow); only a genuinely untagged project produces the empty value. - A CI-SHA marker that cannot be reconciled aborts the release instead of warning. The marker is the publish gate's only precise statement of which commit CI proved green; without it the gate falls back to whatever commit the workflow happens to observe. A failure to read or write it onto an existing GitHub Release used to print a warning and continue, so the release proceeded to publish under a verdict nobody had established. It now aborts before tagging, preserving the state for
rlsbl release resume. - Release output redirected to a file or a CI log now appears in the right order and survives a killed run. Python block-buffers stdout as soon as it is not a terminal, so a release emitted its progress in 8KB gulps that landed after the stderr warnings they belonged next to, and a run killed mid-block lost its final lines -- exactly the output an operator needs when a release dies. Both streams are now line-buffered for the whole CLI process.
- **A release no longer aborts on its own
scrub-result.json.** The unexpected-files guard exemptedin-progress.jsonbut not the scrub result written alongside it, so a release following arelease scrubstopped on a file rlsbl had created itself. - A batch release refuses a candidate push that cannot trigger every member's CI. The per-member release path has checked its push window against the router's paths filter since the empty-window guard landed, but the batch orchestrator's own single candidate push went unguarded. The guard now runs once per member before that push -- per member, not against the union of the members' filters, because a union passes as soon as one member's paths are touched, which is precisely the half-skipped batch the two-pass design exists to prevent.
- **
rlsbl deploy --dry-runno longer previews a deploy the branch forbids.** The preview gate sat above the branch restriction, so running it on a branch outside the target'sonly_onlist printed a full deploy plan and exited 0 -- a confident description of a deploy that could never run. The restriction is evaluated first now, so a preview refuses exactly where the live run refuses. - **
rlsbl release run --dry-runno longer narrates work it only recorded.** The preview issues the first half of a real release with every mutation recorded rather than performed, but two steps spoke through channels the preview does not silence: the ecosystem-keyword tagger printedTagged package.json with "rlsbl" keyword(it narrates through its ownquietflag, not the release log), and the stale-artifact cleaner reported the files it had cleared. Both claimed completed work in the past tense for writes that never happened. Both are silent under--dry-runnow, and unchanged in a live run. - **
rlsbl release run --dry-runno longer crashes when the candidate is already CI-verified.** Previewing a resume past the CI gate -- or a batch member the orchestrator had already gated -- skips Phase A entirely, then reached the Phase-A/Phase-B boundary with no plan to render and died withUnboundLocalError. The preview now states plainly that Phase A was already done, and renders the boundary and the declared Phase-B table as it does on every other path. - **A malformed
pipelinesvalue is a config error now, not a crash.**pipelinesis a map of pipeline name to pipeline config; a scalar there --"pipelines": "none"being the tempting one -- is truthy, so it slipped past every check on the release path and killedrlsbl release runmid-preflight withAttributeError: 'str' object has no attribute 'items', naming no config key at all. Any non-map value is now refused with aConfigErrornaming the two real shapes:"pipelines": {}to publish nowhere,"publish_mode": "none"to suppress publishing entirely. The identical crash reached throughgo_introspect's pipeline scan is fixed with it. - **The five commands that refuse
--dry-runnow say so before they parse anything, and in--help.**commit,release init,monorepo init,monorepo removeandmonorepo release initcannot honestly preview themselves, and used to refuse from inside the handler -- after argument validation, sorlsbl monorepo remove --dry-runanswered with a missing-argument error instead of the refusal, and nothing in--helpor the dumped schema said the command would refuse at all. The refusal is a framework declaration now: it fires at parse time, reads--dry-run is not supported by command '<path>': <reason>, and every one of the five renders a "Dry run:" section in its help. The reasons themselves are unchanged. - A batch release no longer leaves its last commit unpushed.
rlsbl monorepo release runcommitted the archived batch release file after the final member's push and never published it, so every repository that completed a batch release stayed permanently one commit ahead of its remote and the next release's preflight met a diverged branch. The batch now pushes that commit as its own final step, refusing to carry any commit it did not create. - A CI run killed by infrastructure no longer strands a release. When a provider outage killed every job on runner acquisition or action download, rlsbl read the run as a deterministic code failure and refused to retry -- and a resumed release pushes nothing, so no fresh run could ever appear and the release became unrunnable. Such runs are now recognized as infrastructure failures ahead of the code-failure signatures they incidentally match, and rlsbl reruns their failed jobs once by itself. When it genuinely does not retry, the abort message now names the manual
gh run rerun <id> --failedandrlsbl release resumeremedy.
#0.111.0
A release can no longer burn a version number: main is the candidate, CI gates the tag, and a drift guard refuses to ship commits the release did not create -- alongside the effects regime, release reconcile, and the removal of dev-branch releases, rlsbl push, the watch daemon, cargo publishing, changeset-file mode and every timeout environment variable
Context
The centrepiece is a reordering. rlsbl used to bump the version, commit, tag, push and create the GitHub Release, and only then find out whether CI agreed. When it did not, the version number was spent: a tag and a Release existed for a commit that could not publish, and the only way out was to skip a number and try again. The order is now candidate-first. The version-bump commit goes to the release branch untagged, rlsbl waits for CI on exactly that commit, and only a green verdict produces the tag, the Release and the changelog finalization. A red CI leaves you with an ordinary branch you can fix forward on, at the same version, as many times as it takes. Batch releases work the same way with one wait for the whole batch -- and one push, because the CI router filters paths per push, so a push per member would leave every other member's job skipped on the commit its tag was about to land on.
Two gates that were supposed to answer the same question, and did not, are now one predicate. The release gate judged the workflow RUN; the publish gate judged the releasing project's own CHECK RUNS. In a monorepo those come apart whenever the paths filter finds no change under a project: its job concludes skipped, the run still concludes success, so the release tagged and the publish gate then refused the same commit. Both sides now ask "did this project's CI pass here?", only success passes, and a skipped or absent check is a hard error that names the filter mismatch.
The forward drift guard is the other half of the rollback guard that already existed. A release pins HEAD when it starts and re-checks the branch at four points; any commit in that window it did not create -- a concurrent session sharing the worktree, most likely -- aborts the release by name instead of silently shipping under the tag.
A lot was deleted, and the deletions share a rationale: every one of them was a second way to do something rlsbl already did once. Dev-branch releases and rlsbl push are gone, so a release starts from a release branch or it does not start. RLSBL_RELEASE_PUSH is gone, replaced by an internal --no-verify on release pushes and a pre-push hook that knows which ref namespaces it owns. The timeout environment variables are gone in favour of config keys and explicit flags -- an env var that silently changes how long a release waits is exactly the kind of invisible state this tool exists to remove. The detached watch daemon is gone; the CI wait is in-process now, which is what the new ordering needed anyway. Cargo publishing and the crates.io launcher are gone because nothing in the fleet ships a crate. Changeset-file coverage mode is gone because no configuration ever selected it.
rlsbl also moved onto strictcli's effects regime as its reference consumer. Every subprocess launch, filesystem mutation and network call now routes through one internal module with a test that fails if a new one appears outside it, and every command is classified, so --dry-run records instead of acting. Confirmation is declared per command rather than inferred from "this mutates something", and the flag that skips it is --approve-consequential; --yes is gone. rlsbl monorepo snapshot --check split into snapshot-check for the same reason: one command cannot be both a pure read and a write.
Finally, rlsbl release reconcile closes the loop on history rewrites. safegit stopped requiring a handshake before rewriting a release-managed repository, so rlsbl detects the damage afterwards instead -- the changelog's hash resolution fails loudly, changelog remap --from-journal repairs the entries from safegit's journal, and release reconcile re-pushes the moved tags and recreates their Releases. It is fail-closed: a tag whose divergence the journal cannot explain is an error, never a force-push. This is why the release now requires safegit 0.25.0 or newer.
#Breaking
- **Release files now require a
format_versionfield.**.rlsbl/releases/unreleased.tomlmust carryformat_version = 1;rlsbl release initstamps it automatically. An in-flight release file created before this change is rejected until you re-runrlsbl release initto re-scaffold it. - Removed the detached watch daemon.
--watch-async(onrelease run,release resume,release retry, andmonorepo release run) andrlsbl watch --stopare gone, along with the internal--as-daemon-childmode and the per-commit pidfile/log files under.rlsbl/. CI watching is now always in-process: pass--watchto block, or--no-watchand runrlsbl watch <sha>yourself later. - **Dev-branch releases and
rlsbl pushremoved.** A release can only start from a release branch —rlsbl release runandrlsbl monorepo release runnow hard-error anywhere else instead of fast-forward merging a dev branch into main. Therlsbl pushcommand is gone (pre-push-checkstays). Pushing a release branch by hand is now a hard error rather than a warning, and theRLSBL_RELEASE_PUSHbypass variable is deleted: release-internal pushes usegit push --no-verify. The generated pre-push hook is namespace-aware — it enforces onrefs/heads/*and exits 0 forrefs/tags/*andrefs/backups/*; re-runrlsbl scaffoldto pick it up. - Timeout environment variables removed.
RLSBL_PUSH_TIMEOUT,RLSBL_CHECK_TIMEOUT,RLSBL_HOOK_TIMEOUT,RLSBL_BUILD_TIMEOUTand per-targetRLSBL_BUILD_TIMEOUT_<TARGET>no longer do anything. Declare timeouts with thepush_timeout,check_timeout,hook_timeout, andbuild_timeoutkeys in.rlsbl/config.json, or pass the new--push-timeoutflag torelease run,release resume, andmonorepo release run. Shipped defaults are raised: push 120s to 300s, check 120s to 900s. - Cargo publishing target and crates.io launcher removed. The
cargorelease target andcargopublish pipeline no longer exist, along with the crates-wrapper launcher for Go binaries, the crates.io branches ofcheck-name,claim-name, andrelease yank, and the cargo CI/publish templates. Rust projects are no longer an rlsbl release target; aCargo.tomlin the repo is still recognized by theplaintarget's manifest detection. - Changeset-file coverage mode removed. The
coverage_unitconfig key is deleted along with thechangeset-filemode it selected: there are no pending changelog files,changelog addalways appends tounreleased.jsonl, and release finalization, undo, the pre-push coverage check, and the changelog checks are single-mode. Removecoverage_unitfrom.rlsbl/config.json-- it is now a dead key. - Releases now gate on CI before anything irreversible happens.
rlsbl release runpushes the version-bump commit to the release branch untagged, waits for the repository's own CI to go green on exactly that commit, and only then finalizes the changelog, tags the verified commit, pushes the tag and creates the GitHub Release. A red CI leaves no tag, no GitHub Release and no finalized changelog behind: the version number is not burnt, and the fix lands forward on the same version withrlsbl release resume. Repositories with no push-triggered workflow proceed without a gate; a repository that declares push-triggered CI but produces no runs for the candidate is a hard error. - Batch releases gate on one CI wait for the whole batch.
rlsbl monorepo release runpushes every member's version-bump commit untagged first, waits once for CI on the resulting branch tip, and only then finalizes, tags and releases each member — so every member tag points at the same CI-verified commit. A red batch leaves no tag, no GitHub Release and no finalized changelog for any member, so no version number is burnt anywhere in the batch. - **
rlsbl release scrubnow requires safegit >= 0.25.0.** It no longer sets theRLSBL_SCRUB_ORCHESTRATEDhandshake (safegit 0.25.0 stopped requiring it) and passes--approve-consequentialexplicitly to safegit's three scrub modes, whose destructive confirmation is no longer answered by--json. A history rewrite performed outsiderelease scrubis now caught by the changelog hash checks and repaired withrlsbl changelog remap --from-journalandrlsbl release reconcileinstead of being blocked up front. - Breaking: rlsbl runs on strictcli's effects regime.
--dry-run,--approve-consequential,--quietand the new--verboseare framework-owned flags now — rlsbl no longer declares them, and none of them has a short form, so-yis gone (there is no--yeson a strictcli app at all). Every command declares whether it isread_onlyormutating, and that classification decides one thing only: whether--dry-runrecords its effects instead of performing them. Confirmation is a separate, per-command declaration — a command prompts before it runs only if it declares itselfconsequential, and--approve-consequentialskips that prompt. A consequential command exits 1 on a non-interactive stdin unless the flag is passed, so a script, hook or CI step that drives one of rlsbl's consequential commands must pass--approve-consequential; everything else runs uninterrupted. Under--dry-runmutations are recorded rather than performed and printed as a numbered would-do log — including subprocesses: a preview no longer runs your release hooks, your tests, or any child command. - **Breaking:
rlsbl monorepo snapshot --checkis nowrlsbl monorepo snapshot-check.** One command could not be both: verifying the artifact is a pure read, regenerating it writes and commits. The writer previews completely under--dry-run— it decides staleness before writing, so the preview names both the write and the commit — and the checker never writes anything at all. CI steps using--checkmust switch to the new command. - Breaking: three commands stopped asking their own confirmation questions.
claim-name,release deprecateandrelease undoeach prompted on top of the framework's confirmation, in different words, with their own non-interactive error text. There is one confirmation per consequential command now, and the flag that skips it is--approve-consequential.release undoalso asked a second question halfway through a rollback — "Push changes to remote?" — whose default left the remote holding the release it had just undone locally; it now pushes as part of the rollback it was already authorized to perform. - Only genuinely dangerous commands ask for confirmation now. strictcli replaced the inferred "every mutating command prompts" rule with a declared
consequential, so 15 rlsbl commands ask before running --claim-name,deploy,release run/resume/retry/undo/deprecate/yank/scrub/reconcile,monorepo release run/mirror/absorb/extract/extract-releasable-- and everything else (commit,scaffold,changelog add,monorepo sync,dev install, ...) runs without interruption. The skip flag is--approve-consequential;--yesno longer exists.claim-namegains--force-publishfor the separate decision of publishing despite a taken-name check.
#Features
- npm launcher first-run download mode. Launcher pipelines now take a required
downloadkey ("first-run"or"postinstall"). Infirst-runmode the npm wrapper does zero network I/O at install time and lazily downloads its checksum-verified binary on the first CLI invocation, caching it under a platform cache dir;postinstallkeeps the install-time download. PyPI launchers may only usefirst-run. - strictspec certificate deploy gate. A new opt-in preflight check,
strictspec-certificate-gate, consumes a strictspecstrictspec diffcertificate as aformat_versiondeploy gate. Enable it by adding astrictspec_gatesection (acertificatepath, and optionally anadjudicationfile) to.rlsbl/config.json; projects without the section are unaffected. Aviolatedclaim blocks the release,corpus-supported/provenclaims pass, and any otherwise-unsupported claim must be discharged by a committed adjudication file. A configured-but-missing or malformed certificate or adjudication file is a hard error. - Release files are validated with strictspec.
.rlsbl/releases/unreleased.tomlis now checked against a strictspec schema before parsing, so a malformed file, an invalidbump/modeenum value, an unknown key, or overlappinginclude/excludetargets is rejected with precise, field-level diagnostics. - External checks now receive the release context. Both freeform and structured
external_checkssubprocesses run withRLSBL_PROJECT_ROOT,RLSBL_LAST_TAG(empty string on a first release) andRLSBL_UNRELEASED_RANGEin their environment, resolved through the project's own monorepo tag glob and computed once per check run. Release-diff gates no longer need their owngit describe. See the availability matrix in the configuration docs. - The pre-release channel is documented. The release-workflow docs now cover
preidend to end: entering a channel with a normal bump, advancing and promoting withbump = "prerelease", stabilizing withpreid = "stable", the alpha < beta < rc < stable ordering rules, and what changes downstream (GitHub pre-release flag, npm dist-tag, changelog file naming).rlsbl release initscaffolds the key with an explanation. - **Changelog lines carry a
format_version.** Every linerlsbl changelog addwrites now records aformat_versionand is validated against a strictspec schema. Enforcement is opt-in per repo: whilechangelog_format_version_enforcedis absent from.rlsbl/config.jsonthechangelog-format-versioncheck warns; set it totrueandchangelog-format-version-gatehard-fails any changelog line missing the field, or tofalseto stay in legacy (mixed-format-tolerant) mode deliberately. - The documentation site is complete. Every public module in the package now carries a docstring, so the generated API reference documents 100% of symbols instead of leaving pages blank, and the scaffold, release-workflow, and other guide pages gained worked end-to-end usage examples.
- **
--ci-timeout,--check-timeoutand--hook-timeoutare now flags.**rlsbl release run,rlsbl release resumeandrlsbl monorepo release runaccept all four release timeouts on the command line, matching the existing--push-timeoutpattern: pass the flag to override for one invocation, or set the matching config key in.rlsbl/config.json. Previously only--push-timeouthad a flag, so a slow preflight suite or release hook could only be accommodated by editing the config file. - A release refuses to ship commits it did not create.
rlsbl release runpins HEAD when it starts and checks the branch against its own commit trail at four points — entry, candidate push, right after the CI gate, and the final push. A commit that lands on the release branch mid-flight (a concurrent session, an editor auto-commit) aborts the release by name, showing each foreign SHA and its subject, instead of silently joining the release under its changelog. Nothing is rolled back: the foreign work is preserved, and the version survives for a clean re-run. - **New command:
rlsbl release reconcile.** Repairs the release metadata a history rewrite invalidates but leaves behind — the remote's tags and the GitHub Releases attached to them. It reads safegit's rewrite journal to work out exactly which tags moved, force-pushes them with explicit leases, and recreates their Releases from the changelog. Usable after ANY out-of-band rewrite (a rawsafegit scrub,git filter-repo, someone else's rewrite), not just one driven byrlsbl release scrub. Fail-closed: a tag whose divergence the journal cannot explain is a hard error rather than a force-push.--dry-runprints the plan. - The changelog docs explain the auto-commit's HEAD move.
rlsbl changelog addcommitsunreleased.jsonl, which moves HEAD onto the changelog commit — so a followinggit commit --amendamends the wrong thing. The changelog reference now names the hazard, shows the exact sequence that produces it, and gives both ways out: record the entry last, or defer the commit with--no-commit. - Sandboxed test runners are now scaffolded. Declare a
test_sandboxsection in.rlsbl/config.jsonandrlsbl scaffoldemits an executable bubblewrap runner script: the repo bound read-only, the suite in a throwaway writable copy, no network, and only the toolchain caches you list. It exportsSTRICTTEST_SANDBOX=1for the stricttest floor. - **New check:
stricttest-floor.** Once a repo adopts the sandboxed test runner (or the stricttest plugin),rlsbl checkand the release preflight fail on a missing or non-executable runner, an incompletetest_sandboxsection, or a CI workflow that claims to use the runner but does not. Repos that have not adopted it see a visible skip. - Docs: the sandboxed test runner and the checks page.
docs/configuration.mddocuments the newtest_sandboxsection field by field, and the checks reference now lists the five project checks it had been missing. - Documentation corrections: the release workflow page now describes main-as-candidate ordering (candidate push, CI gate, fix-forward recovery with
rlsbl release resume) instead of the old commit-tag-push flow;ci_timeoutand the--ci-timeout/--check-timeout/--hook-timeoutoverrides are documented; the changelog and scaffold pages name the real--no-auto-commitflag; and the checks reference lists every check with correct per-tag counts. - Five commands say why they cannot preview.
rlsbl commit,release init,monorepo init,monorepo removeandmonorepo release initreject--dry-runwith a reason instead of accepting it and doing nothing informative: each is a single small mutation whose preview could only restate what you typed, or scaffolds a file whose whole point is that you edit it. Every other mutating command records a real preview.
#Fixes
- **
workspace-unbuildableno longer false-fails polyglot monorepos.** When the repo root is not a uv workspace (no rootpyproject.toml), the check now runsuv syncin each pypi project's own directory instead of a root-leveluv sync --all-packagesthat died with "No pyproject.toml found". Repos that do have a root uv workspace are unchanged. - npm publish workflows now install dependencies first. All four npm publish templates (npm, launcher, pnpm, yarn) run a lockfile-strict install before publishing, so a package whose
prepackscript builds no longer fails at publish time with a missing dev toolchain. Re-runrlsbl scaffold(orrlsbl monorepo sync) to pick it up; the package must have a committed lockfile. - First monorepo releases no longer deadlock the publish gate. Each releasable member's CI router paths filter now includes the releasable's
CHANGELOG.md, so a release commit that touches only releasable metadata still triggers the project's CI and the publish gate sees a real conclusion instead of a skipped check. Re-runrlsbl monorepo syncto regenerate the router. - Go releases and launcher shims now target the prefixed monorepo tag. The generated Go publish job builds with goreleaser and uploads the archives and
checksums.txtto the real release tag itself, instead of delegating to goreleaser's publisher (which cannot address a<name>@vX.Y.Ztag and attached assets to a bare-vX.Y.ZRelease nobody reads). Launcher shims gain atagPrefixtemplate var, so their download URLs resolve in prefixed-tag repos. Re-runrlsbl scaffoldto regenerate. - The publish gate's CI-SHA marker is always present. The
rlsbl-ci-shamarker is now written into the GitHub Release notes even when the Release already exists (a resumed release, or one created out of band): it is added, or an outdated one replaced, instead of being skipped. The gate no longer falls back to gating on an arbitrary commit. - Renaming a releasable no longer trips the pre-push hook. The boundary alias tag pushed by
rlsbl monorepo rename-releasablenow uses--no-verify(like every other tool-driven push) and honours the project's configuredpush_timeoutinstead of a hardcoded 120 seconds. - The launcher publish probe checks the URL users actually download. The generated npm/PyPI launcher publish workflow built its asset-verification URL from the repo basename and a bare
vstrip, so in a prefixed-tag monorepo (<name>@v1.2.3) it probed a URL nobody fetches — passing while every install 404s. It now bakes in the wrapped producer'sassetProjectandtagPrefix, the same values the shim downloads with. Re-runrlsbl scaffoldto regenerate. - **A fresh
rlsbl scaffoldnow passes its own checks.** The auto-generated.rlsbl/config.jsonpipeline entry was missing the mandatorytargetlink, sorlsbl check --name config-schemafailed on the tool's own output — and, because publish-template resolution matches on that link, a Go library repo was handed the binary/goreleaser publish workflow (the second scaffold pass then reported "unchanged", so the documented scaffold-review-scaffold flow never healed it). Scaffold now emits the link, library repos get the library publish workflow on the first pass, and their next-steps no longer advertise a goreleaser build that never runs. - A repo's first commit is no longer invisible to changelog scope matching.
git diff-treeprints nothing for a parentless commit without--root, so the initial commit — the one that creates an entire project — looked like it touched no files:rlsbl changelog addrefused it as out of scope for its own project or releasable, and the coverage check silently skipped it. It is now matched like every other commit. - The docs no longer describe features that were removed. Generated and hand-written pages still advertised the deleted cargo/crates.io target across the README, the CLI and API reference, and the pipelines page (which counted 10 pipeline types instead of 9 and never documented the mandatory pipeline
targetkey). The dev-workflow page's editable-install table,--venvsupport list, CI-retry description, pre-push severities, andpre-push-checkstatus were all out of date, the last of these claiming a removed command "still works with a deprecation warning" when it hard-errors and blocks the push. - The publish gate's failure guidance is followable again. When CI is red on a released commit the gate no longer tells you to "re-run CI to green on this exact commit" (impossible when the failure is in the code there). It explains that rlsbl verifies CI before tagging, that re-dispatching the publish cannot change the answer, and points at the real remedy: fix forward, cut a new release, then
rlsbl release deprecatethe old one.rlsbl release resumealso regenerates CHANGELOG.md before finalization, so a fix-forward commit's changelog entry reaches readers. - Generated docs are no longer one release stale. The release now passes the version it is about to publish to
selfdoc genandselfdoc checkas--version-override. Previously selfdoc ran before the version bump was written to disk, so every version-bearing generated line (the CLI index's Version line, root-file version directives) was written for the previous version — and that churn tripped the doc-staleness check on the next release, aborting it until the operator committed the release's own regeneration artifacts and baseline-accepted. Requires a selfdoc that supports--version-override. - Two command help strings now tell the truth.
rlsbl pre-push-check --helpsaid it verified CHANGELOG.md; it is a retired stub that always exits 1 and points atrlsbl scaffold.rlsbl dev install --helpclaimed a system-wide install across seven targets; it now names which targets support--global,--venv, and--uninstallseparately. --check-timeoutnow governs config-declared external checks; previously they always ran on thecheck_timeoutconfig key or the shipped default regardless of the flag.- A release CI wait that runs out of time is now reported as an unresolved timeout with its own remediation (check the runs, resume when green, raise
--ci-timeout) instead of being misreported as a CI failure. The run-discovery grace is also budgeted inside--ci-timeout, so a short budget no longer collapses the completion wait to one second. rlsbl monorepo release runnow refuses to gate, tag and release a batch whose branch tip carries commits the batch did not create (a concurrent session's work riding in between the last candidate push and the CI gate). The abort names every foreign commit and rolls nothing back.rlsbl release --helpnow lists all 10 release subcommands (it omittedreconcileand claimed 9), andrlsbl monorepo --helplists all 18 monorepo subcommands (it claimed 16).- An invalid
--check-timeoutor--hook-timeoutvalue is now reported against the flag instead of as "Invalid check_timeout in .rlsbl/config.json". - Sandbox test runner reclaims its own leaked scratch. A killed
scripts/test.shused to leave a full copy of the repo underTMPDIRand a clone of the uv cache behind; enough of them filled a tmpfs/tmp. Every run now sweeps orphaned scratch dirs it owns before allocating new ones, skipping any whose owner process is still alive.scripts/test.sh --sweep-onlyruns just the sweep. - Scrub documentation corrected. The release-workflow page said safegit blocks direct destructive scrubs in rlsbl-managed repositories; that guard no longer exists. It now describes what a direct scrub leaves behind and points at
rlsbl release reconcile, and states the real safegit floor (0.25.0). - Sandbox test runner no longer leaks a repo copy on every run.
scripts/test.shended inexec bwrap, which replaced the shell and discarded the EXIT trap that removes the throwaway tree copy and the uv cache clone -- so successful runs leaked too, not just killed ones. bwrap now runs as a child and its exit status is forwarded after cleanup. - Project tests no longer break on a stdlib-shadowing module.
rlsblruns a project's pytest aspython -m pytest, which puts the project directory first onsys.path; a flat-layout module there with a stdlib name (anhtml.py, say) then shadowed the real one and broke pytest's own plugin loading. The invocation now passes-P(PYTHONSAFEPATH), which suppresses that injection without changing anything else. - Batch releases no longer half-publish.
rlsbl monorepo release runpushed one candidate per member, so the CI router — which filters paths per push — skipped every other member's CI job on the commit all the tags landed on, and those members' publish gates then refused to publish. The batch now publishes every member in a single push and gates that one commit, so each member's own CI runs on the commit its tag points at. - **
rlsbl release resumeno longer breaks three ways.** (1)--dry-runperformed the entire release for real — commits, tag, push to the release branch, GitHub Release, publish dispatches — because the dry-run gate existed only on the fresh-release path; it now previews the remaining steps and changes nothing. (2) A resume tagged whatever the branch tip happened to be and labelled it(CI-verified), because re-entering the release erased the recorded CI-verified commit; the tag now always lands on that commit, and a candidate that cannot be established is a hard error rather than a silent fall back to HEAD. (3) A concurrent session's commit could ride into a resumed release and reach the release branch under its version; once CI has passed, the release is sealed and any commit outside its own trail aborts by name, while a deliberate fix-forward before the CI gate is still adopted and re-gated. - **
rlsbl monorepo snapshot --dry-runno longer writes and commits.** The global--dry-runflag was accepted and dropped, so the command regeneratedsnapshot.jsonand committed it anyway. It now reports whether the artifact would change and writes nothing. - Batch-released versions keep their description and context. A member released via
rlsbl monorepo release runtakes its release description from the batch file, which was never archived per member — so the next changelog regeneration silently stripped the description and context from that version's.mdand itsCHANGELOG.mdsection. The metadata is now archived alongside the version like a standalone release's, and survives every regeneration. - **
rlsbl release run --dry-runno longer deletes a leftover release state file.** When a previous run finished but crashed before clearingin-progress.json, the auto-clear ran during previews too. A dry run now reports what it would clear and leaves the file in place. - The PyPI token no longer travels in a command line.
rlsbl claim-name --target pypipassed it asuv publish --token <secret>, where a process listing could read it — and, once previews started printing the commands they would run, a would-do log would have printed it too. It ridesUV_PUBLISH_TOKENin the child's environment now. - **
rlsbl <command> --dry-runworks without an argv rewrite.** rlsbl carried a shim that moved--dry-run,--yes,--quietand--verbosein front of the command name because strictcli only recognized them there. strictcli 0.35.3 recognizes them anywhere in argv, so the shim is gone and the flags are handled by the framework exactly where you type them. rlsbl now requires strictcli >= 0.35.3. - Releases of selfdoc-using projects work again.
selfdoc'sgen,checkanddeploybecamemutatingcommands under strictcli's effects regime, so the release flow's subprocess calls hit the confirm protocol: they hard-errored on non-interactive stdin and prompted mid-release from a terminal. All three now pass--yes. - Releases of selfdoc-using projects work again. strictcli's confirm protocol now keys on a declared
consequentialrather than onmutating, and the--yesflag no longer exists on a strictcli app. The release flow'sselfdoc gen/selfdoc check/selfdoc deploysubprocesses no longer pass it; none of those commands is consequential, so none of them prompts. - **
rlsbl commitandrlsbl changelog addwork again.** strictcli replaced the inferred "mutating means confirm" rule with a declaredconsequential, and--yesno longer exists on a strictcli app. rlsbl was passing--yestosafegit commit, sorlsbl commitfailed outright andrlsbl changelog add's auto-commit left the entry uncommitted.release scrubnow passes--approve-consequentialto safegit's three scrub modes. - **The
cloudflare-pagespipeline can deploy again.**selfdoc deploynow declares itselfconsequential-- it makes a Cloudflare Pages deployment live, or force-pushesgh-pages-- so the bare invocation the pipeline used would hard-error on the release runner's non-interactive stdin, aborting the publish step. The pipeline passes--approve-consequential; the approval was already taken byrlsbl release runone level up.selfdoc genandselfdoc checkare not consequential and stay bare. - A private monorepo can no longer publish to a public registry.
rlsbl monorepo syncinlined every member's publish workflow into the root publish router purely on that file's existence, ignoring the member'spublish_mode. A member declaringpublish_mode: "none"that still carried a stale publish workflow got its jobs inlined, so the next<name>@v*release tag would have run a real registry publish. Suppressed members are now excluded, their stale publish workflows are deleted, and a generated router left with nothing to publish is removed. - **
rlsbl scaffold --target <t>no longer deletes the other targets' files.** On a multi-target repo the flag routed to the single-target scaffold path, so every file belonging to an unlisted target --ci-<other>.yml,.goreleaser.yml,VERSION,.npmignore-- fell out of the plan and was orphan-deleted along with its stored merge base, and the mergedpublish.ymlwas rewritten with only one target's jobs.--targetnow DECLARES a target (for ones auto-detection cannot find, such asplain): it is added to the project's target set and the scaffold always covers every target. - Re-scaffolding a project with CI service containers or a subdirectory target no longer crashes or invents merge conflicts. Scaffold applied its CI-workflow YAML rewrites (service-container injection, subdirectory
working-directoryinjection) after the three-way merge and stored the rewritten text as the merge base while the template side stayed raw -- so base and template differed by the whole rewrite on every run, any local edit in that region conflicted, and scaffold then parsed the conflict-marked output as YAML and died with a barewhile scanning a simple keyerror naming no file. The rewrites now run before the merge, and conflict-marked text is reported with the file name and the conflicting line ranges. - **A Go project declaring
artifact: "library"no longer gets goreleaser files.** Scaffold classified Go projects by scanning forpackage mainanywhere in the tree, so a declared library that ships a dev helper underscripts/got a.goreleaser.ymlaimed at that helper plus aversion.gowritten into it -- regenerated on every scaffold -- even though its publish workflow correctly ran no goreleaser job. The pipeline's declaredartifactis now authoritative; detection only applies when nothing is declared yet. - Dev nodes no longer get a publish workflow. A monorepo project marked
dev_node = true(orreleasable = false) cannot be released --rlsbl release runhard-errors on it -- yet scaffold still wrote apublish.ymlfor it whenever itspublish_modewas the ordinary default"ci". Publish scaffolding is now derived from the project's position in the workspace graph, and an existingpublish.ymlon such a project is swept on the next scaffold. - **A Go target living in a subdirectory gets the right
artifactin its generated pipeline config.** Scaffold auto-detected the artifact kind at the project root, where a subdirectory Go target has nogo.mod; detection failed and fell back to"binary", so a Go library undergo/was declared a binary. Detection now runs in the target's own directory. - A release can no longer tag a version that could never publish. The release CI gate judged the workflow RUN, while the publish gate judged the releasing project's own CHECK RUNS. In a monorepo those disagree whenever the CI router's paths filter finds no change under a project: its job concludes
skipped, the router's run still concludessuccess, so the release tagged and created a GitHub Release -- and the publish gate then correctly refused the same skipped check, leaving a tag and a Release for a version with no path to any registry and no re-run that could fix it. Both gates now decide "did this project's CI pass on this commit?" with one predicate built from the publish gate's own check-name matcher, in which onlysuccesspasses. A skipped or absent check is a hard error that names the filter mismatch and the remedy, and no version number is burnt. - Monorepo publishes are no longer blocked by a publish gate that could not find the release commit. The gate resolves the commit CI ran on from the
rlsbl-ci-shamarker in the GitHub Release body, but the gate job had neithercontents:readnor a repository context forgh release view, so the marker read failed silently and the gate fell back to the tag commit -- whose path-filtered CI is skipped in a monorepo, blocking every publish and every retry. Separately, the gate's check-name pattern dropped the per-target suffix for workspace projects, so a project whose CI comes fromci-<target>.ymlproduced a regex that never matched its real check run. All three are fixed, so a monorepo release publishes on the first attempt.
#0.110.2
CI-portable hermetic test sandbox; ships the full 0.110 cycle to registries
Context
v0.110.0/v0.110.1 were gate-blocked by red CI (the new sandbox needed runner portability work: bwrap 0.11 source build + AppArmor sysctl, pinned Go with GOTOOLCHAIN=local, pinned uv, git-filter-repo on the runner, fixture dependency + PEP-517 build-backend cache warming, checkout credential persistence disabled); this patch completes it -- the full suite now runs inside the read-only no-network sandbox on CI, and the publish gate certifies it.
#Infrastructure
- CI-portable hermetic test sandbox; ships the full 0.110 cycle to registries
#0.110.1
Portable test sandbox: CI-compatible bwrap flags (fix-forward for the unpublished v0.110.0)
Context
v0.110.0 was tagged and GitHub-released but never published to npm/PyPI: the publish workflow gates on CI, and CI was red because scripts/test.sh used the bubblewrap overlayfs options (--overlay-src/--tmp-overlay), which the stock apt bubblewrap on the CI runner is built without. This patch replaces the overlay with universally-supported bwrap flags (read-only download-cache + file:// GOPROXY with a tmpfs GOMODCACHE for Go; a copy-on-write reflink clone of the uv cache), turning CI green so this release cycle ships to the registries.
#Fixes
- Test sandbox works on bubblewrap builds without overlayfs. The sandboxed test runner (
scripts/test.sh) no longer relies on--overlay-src/--tmp-overlay, so it runs on the stock apt bubblewrap shipped on CI runners.
#0.110.0
Absorb/extract round-trip overhaul, mirror reconciler, commit-aware tag guards, infra bump type, CI service containers, dual-registry formalization, handler safety hardening
Context
This cycle hardens the release machinery along several fronts. The dry-run incident prompted prompt EOF handling, a push rehearsal step, and elimination of **_kwargs in handlers in favor of registry-enforced signatures. The bump-type rename (hotfix -> infra) came with a fleet archive migration; legacy bump values now hard-error rather than silently coerce. Absorb now rewrites history to the package prefix with tag import and changelog remap, eliminating the double-append class of bug. Extract gained tag translation, commit-map remap, and a self-commit so absorb/extract round-trips stay coherent. Monorepo mirror became a tool-owned plan/apply reconciler (force-with-lease with an exact tripwire). Tag push guards are now commit-aware (the silent-skip path is removed). CI service containers are declarative with an enforce check. Dual-registry support formalizes scan-depth pins and a mixed-scheme guard. record-gif was removed. The CI suite now runs inside a hermetic bwrap test sandbox: repo read-only, a throwaway writable copy, private tmpfs, no network, plus an always-on env-poisoning floor and a bare-run refusal guard.
#Breaking
- **
rlsbl record-gifremoved.** The vhs-based demo-GIF recorder command has been removed. monorepo mirroris now an idempotent plan/apply reconciler: the mirror is a tool-owned derived artifact,--dry-runprints a plan (converged/behind/scaffold-missing/contract-violated/virgin), convergence force-pushes with lease, a tripwire refuses mirrors carrying foreign hand-authored commits, and scaffold failures are hard errors.- **Bump type
hotfixrenamed toinfra.** Usebump = "infra"for infrastructure-only releases; the legacyhotfixvalue is rejected, and encountering it in an archived release file is a hard error during changelog regeneration. - **
monorepo absorbreshaped:absorb <source_repo> <dest_path>.** The second positional is now the destination path (with--namedefaulting to its basename), plus new--registry-nameand--releasableflags. Replaces the oldabsorb <source_path> <package_name>signature.
#Features
- **
rlsbl push --dry-run.** Runs all preflight checks and shows what would be pushed without pushing. - **
rlsbl push --dry-runnow rehearses the push.** After the preflight checks it runs a real dry-run rehearsal, surfacing remote-side rejections (protected branch, stale ref) without performing a real push. - **
monorepo check-nameshonorsregistry_name.** When a workspace project declaresregistry_name, it is checked verbatim on the registry (prefix/suffix are ignored), since that field is the package's true registry identity. - CI service containers. Declare
services(image, ports, env, health checks, setup commands with SQL/command verification) andtest_envin.rlsbl/config.json;rlsbl scaffoldrenders them into the per-target test CI workflow, and the newrequires-servicescheck hard-errors when a declared service is not provisioned on disk. - **
monorepo absorbnow rewrites history instead of a verbatim subtree add.** It relocates the source's full commit history under the destination prefix, imports version tags under the monorepo tag scheme, remaps JSONL changelog hashes to the new commits, routes changelogs to the releasable dir under--releasable, and self-commits the result. - **
monorepo extractnow produces a coherent standalone repo.** It translates the package's monorepo-scheme tags to standalonev{version}(multi-member releasable extracts keep the releasable scheme and recreate the[[releasables]]grouping), prunes foreign packages' tags, remaps changelog hashes to the rewritten commits (dropping entries whose commits were pruned), and self-commits the migrated state. - Sandboxed test runner.
scripts/test.shruns the suite inside a bwrap sandbox with the repo read-only, a throwaway writable copy, private tmpfs, and no network.
#Fixes
- Fix.
claim-namenow honors--dry-run(previously it published the placeholder package even in a dry run) and asks for confirmation before publishing;--yesskips the prompt. - Confirmation prompts fail cleanly on non-interactive stdin.
claim-name,push,release deprecate, andrelease undonow emit a clear error naming the consequence and suggesting--yes, instead of an EOFError traceback, when stdin is closed. - Mixed-scheme monorepo guard. A monorepo member directory that declares both a Go target (path-based
{path}/v*tags) and an @-style target ({name}@v*) now fails with a clear error instead of silently picking an ordering-dependent tag prefix. A newmixed-tag-schemesworkspace check reports the same condition. - Tag push safety. Releases now verify that a matching remote tag points at the same commit before skipping the tag push; a divergent or unverifiable remote tag is a hard error instead of a silent skip.
- Pre-flight tag collision check. A release now aborts before any local mutation if its computed tag already exists on the remote (or the remote cannot be reached to verify), instead of failing only at push time after the version bump and commit.
- **
monorepo addgains--registry-nameand an honest--dry-run.**--registry-namerecords a project's registry identity in workspace.toml.--dry-runnow fully validates and reports what would happen without adding the project, scaffolding, or syncing -- previously it silently scaffolded and synced for real. - absorb/extract hard-error up front on broken target declarations.
monorepo absorbandmonorepo extractnow abort before any history rewrite when a package's.rlsbl/config.jsonexists but declares notargetskey, instead of silently importing wrongly-schemed version tags that break version resolution later. - Hardened release push guard. The pre-push hook now honors the
RLSBL_RELEASE_PUSHbypass only during an actual in-progress release (anin-progress.jsonstate file must exist), so a stray environment variable can no longer silently disable the manual-push guard. - Git operations are anchored to their project root. Release, undo, deploy, status, and scrub now pass an explicit working directory to every branch/push/commit git call instead of relying on the process working directory, and the commit helpers refuse to write into a different repository than the one intended. This prevents release actions from accidentally operating on the wrong repository.
#0.109.1
Scaffold truthful status labels, crates wrapper checksum verification, corrected monorepo positional bindings, and Go dead-modules testdata exclusion
Context
Found via the new CLI wiring coverage (which itself now covers all commands via the deterministic strictcli 0.32.x coverage check) plus fleet verification.
#Fixes
- Scaffold truthfulness. Byte-identical files are reported as
unchangedinstead ofupdatedduring re-scaffold. - Crates wrapper integrity. The generated
build.rsnow sha256-verifies the downloaded release archive againstchecksums.txtand hard-fails on any mismatch. - Fixed swapped arguments in monorepo extract/absorb/extract-releasable. The two positional arguments now bind in the documented order shown in each command's help; previously the first argument you passed was silently bound to the second slot (and vice versa).
- **Dead-package check no longer flags Go
testdata/fixtures or test-only packages.** The Go dead-code detector now applies the same test-context exclusion as other languages, so package directories undertestdata/(at any depth) and packages consisting solely of*_test.gofiles are never reported as dead.
#0.109.0
Structured external checks, declarative dead-module exclusions, check-name --json, launcher shim generation, and deterministic check verdicts
Context
Check verdicts no longer depend on which tools a machine has installed: missing tools now hard-fail instead of silently skipping, both in checks and in release test runners. external_checks gained an explicit structured/freeform schema with rlsbl-composed argv and competing-scope guards (breaking: the kind key is now required). dead-modules gained a reason-gated committed exclusion list. check-name gained --json plus a fix making multi-target checks actually check every target. The launcher pipeline kind is now end-to-end functional with checksum-verified download shims. rename-releasable crash recovery and batch-archival guards were hardened, and the release preflight now prints a positive success summary.
#Breaking
- Structured external checks.
external_checksentries now require an explicitkindmarker (structuredorfreeform); missing/unknownkindand unknown keys are hard errors. Structured entries (mypy,ruff-check,ruff-format) run via rlsbl-composed, shell-freeuv runargv, honor the configured check timeout, and emit a competing-scope guard that rejects tool config which would silently override or narrow the declaredpaths.
#Features
- Releases now confirm which preflight checks passed. The release log prints a positive summary ("Preflight: N checks passed (...)") for every check that ran, including config-declared external checks, so a green preflight is visible rather than silent.
- Launcher packages now ship working download logic. npm and PyPI
artifact: "launcher"pipelines generate platform-aware shims that download the wrapped binary from its GitHub Release, SHA-256-verify it against the release's checksums.txt before installing, and fail loudly on tampering or a missing asset. Scaffold fills the launcher manifest's non-name fields once (never inventing the package name), and a publish workflow verify step probes both the binary asset and checksums.txt so goreleaser naming drift becomes a red CI job instead of broken installs. - Declarative dead-module exclusions. Files that are legitimately unreachable by design (demo apps, tool configs) can be listed in
.rlsbl/dead-modules.tomlwith a mandatory reason; entries never act as entry points (no laundering), and stale entries hard-fail a newdead-modules-stalecheck. - **
check-name --json.** New flag emits machine-readable results: one JSON object for a single name+target, an array otherwise. Each object carries a unifiedstructured_conflictslist of {name, rule} entries keyed by stable per-mechanism tokens (npm-moniker, pypi-separator, pypi-ultranorm, crates-separator, stdlib).
#Fixes
- rename-releasable crash recovery. A crash mid-rename no longer lets a re-run push the alias tag over an uncommitted rename with a stale publish gate; re-runs now heal completely. The command also asks for confirmation before pushing (bypass with --yes) and no longer demands gh auth for renames that create no tag.
- Batch release safety. A failed push no longer lets the batch repair pass archive the batch as complete on local-only tag evidence; a batch with an unfinished (in-progress) release is left intact for resume or rollback.
- Checks no longer lie on misconfigured machines. github-release, workspace-unbuildable, and ruff-lint now hard-fail when their tool is missing (or gh is unauthenticated) instead of silently skipping; ruff-lint reports exact violation counts (was ~10x inflated line counts) and enforces a minimum ruff version.
- Releases no longer silently skip tests. When a required test tool (uv, pytest, npm, mvn) is missing, or a package.json/pom.xml manifest is corrupt or absent for a declared target, the test step now hard-fails instead of passing silently.
rlsbl check-name --target npm --target pypinow checks every target, not just the first. Multi-target name checks previously stopped after the first registry.
#0.108.0
Launcher artifact kind for wrapper-package publishing, handler migration to ctx-first signatures, and rename-releasable argument order fix.
Context
Adds artifact: "launcher" pipeline kind that produces npm/PyPI shim packages downloading pre-built binaries from GitHub Releases. All 52 command handlers migrated to strictcli v0.29.0 ctx-first + Outcome signatures. The monorepo rename-releasable positional argument order now matches mv convention (<old> <new>).
#Features
- New feature. Launcher artifact kind (
artifact: "launcher") for wrapper-package publishing. Launcher pipelines produce npm/PyPI shim packages that download a pre-built binary from a GitHub Release at install or first-run time. Includes config validation (wraps,binary_sourcerequired),wrapper-producercheck,needsdependency chain in CI workflows, and URL verify-before-publish step.
#Fixes
- Fix. Corrected positional argument order for
monorepo rename-releasable: now reads<old> <new>matchingmv/git mvconvention.
#0.107.0
State-model completion with primary-target designation, legacy field deletion, rename-releasable command, save_workspace in-place editing, claim-name conflict enumeration, and fleet sweep script.
#Features
- New
rlsbl monorepo rename-releasable <old> <new>command: renames a releasable group across workspace.toml and its state directory, regenerates publish gate prefixes, and pushes a boundary alias tag for the current version when the tag format contains {name}. Idempotent and crash-safe.
#Fixes
- Publish gate marker read is now retried. The gate retries reading the
rlsbl-ci-shamarker from the GitHub Release body (5 attempts, 5s apart) before falling back to $GITHUB_SHA, so GitHub API read-replica lag on a freshly created release no longer causes the gate to poll the wrong commit and time out. - check-name / claim-name conflict enumeration. PyPI and crates.io normalization collisions now list every conflicting package and the normalization rule, matching npm's existing behavior. Previously only the first collision was shown.
#0.106.0
Publish-gate reliability (check-run dedup + recorded CI SHA), in-place release retry, snapshot-before-tag, post-push failure-state fixes, per-target publish paths, unscoped npm wrapper, required Go artifact key, and claim-name conflict enumeration.
Context
This release hardens the publish pipeline against retry-poisoning and post-tag commit drift: the CI gate now resolves the exact commit CI ran on from a recorded marker and collapses retried check-runs to the latest, so a superseded failure can no longer block a legitimate publish, and retries rerun in place rather than spawning divergent state. It also completes the path-aware publishing model: each target publishes from its own subdirectory, the npm binary wrapper uses bare unscoped names, and Go pipelines must declare an explicit binary/library artifact key.
#Breaking
- npm binary wrapper config changed. The npm binary wrapper (Go/Zig targets) now activates via
{"npm_wrapper": {"enabled": true}}instead of the removedscopekey, and publishes bare per-platform package names (<bin>-linux-x64) instead of scoped@scope/namenames. A stalescope/npm_scopekey is now a hard error. - **Go pipelines now require an explicit
artifactkey.** Everytype: "go"pipeline must declareartifactas"binary"or"library"-- there is no default. The validation error names both values and includes an auto-detected suggestion.
#Features
- **Documented
check_timeout/RLSBL_CHECK_TIMEOUTand thetestconfig block.** The configuration reference now covers the check subprocess timeout budget and per-target test-selection filters (e.g. pytest markers). - **Pipeline docs cover the required go
artifactkey.**docs/pipelines.mddocuments that go pipelines must declareartifact(binary/library, no default), how the library tag/version handling works, and that private modules usepublish_mode"none".
#Fixes
- Publish gate resilience. The CI gate now collapses retried check-runs to the latest per name (a stale failure from a superseded CI run no longer blocks publishing forever) and resolves the exact release commit from an rlsbl-ci-sha marker in the GitHub Release body, falling back to $GITHUB_SHA for older releases.
- Publish retry from main, and router regeneration on upgrade. A
workflow_dispatchretry of the monorepo publish router fired atref=mainwithinputs.tagset now matches the releasing project's jobs (and docker/zig publish steps) instead of silently skipping them. The publish cache also records the rlsbl version, so an upgrade that changes the router generator forcesmonorepo syncto regeneratepublish.ymleven when no member workflow changed. - Auto-retry reruns CI in place. When
rlsbl watchauto-retries a failed CI run, it now reruns the same run in place (gh run rerun) instead of dispatching a fresh workflow run. This avoids creating a duplicate check-run (which poisoned the publish gate) and retries on the failed commit rather than branch HEAD. If the rerun also fails, its failing-step log tail is printed alongside the run URL. - Monorepo release tag is now the pushed tip. The monorepo snapshot commit is created before the release tag instead of after the push, so the tag points at the exact commit CI runs on.
- Docker/Zig publish skip-detection. Publish workflows dispatched at a branch with an explicit tag input no longer mis-detect the already-published state; the skip check now uses the resolved release tag.
- Cleaner release push failures. A push that fails after the release is tagged now prints a concise error with resume guidance instead of a Python traceback, and a tag push that only stalled transiently is retried and, on success, continues straight into GitHub Release creation instead of stopping.
- Safer batch-release completion. A monorepo batch item is only treated as released (and skipped/archived) when its tag is confirmed on the remote, not merely tagged locally; the batch file is never archived while any item still has an in-progress release. This prevents a failed-to-push release from being silently dropped.
- check-name / claim-name now list all conflicting packages. When an npm name collides with multiple existing packages after punctuation stripping, every colliding package is shown (not just the first), with the normalization rule stated explicitly.
- **Go library pipeline fails loudly on a missing
artifactkey.** Selecting the publish workflow with noartifactconfigured now raises a clear error instead of silently assuming a binary. - Go library publish workflow verifies the right module and version. The module path is baked from
go.modat scaffold time (correct for monorepo subdirectory modules) and the version is derived correctly from plain (v1.2.3), releasable (<name>@v1.2.3), and subdir (<subdir>/v1.2.3) release tags. Private modules should usepublish_mode"none". - Retry-safe publish tag resolution. Go and Maven Central publish workflows now derive the release tag from the workflow_dispatch
taginput (falling back to the ref), so retrying a publish at ref=main no longer mis-resolves the version tomain. - Subdirectory publish workflows for single-target projects. Scaffolding a lone target that lives in a subdirectory now produces a publish workflow that runs in that subdirectory (correct working-directory and rewritten package paths), instead of a root-anchored one.
- Correct working-directory for monorepo root publishers with subdirectory targets. Inlined publish jobs for a root package whose target lives in a subdirectory now run in that subdirectory.
- Multi-target local publish. In a standalone release, each pipeline now publishes from its own linked target's subdirectory instead of the primary target's path, so multi-target projects with targets in distinct subdirectories publish the correct artifacts.
- Per-target stale-artifact cleaning. The pre-build artifact clean now clears each subdirectory target's own dist/ (not just the project root's), so a multi-target release's secret scan is scoped to freshly built artifacts across all targets.
- Monorepo subdir publish paths. A monorepo root publisher with a target in a subdirectory now renders composed packages-dir/version-file inputs in the generated publish workflow instead of root-anchored ones, so the correct artifacts and version files are used.
#0.105.1
Fix CI regression in dry-run preflight + monorepo recovery dispatch.
#Fixes
- Fix. Monorepo publish router now defines
workflow_dispatch.inputs.tagso retry dispatch resolves the correct project. Gate resolver falls back to the tag input. Also,rlsbl release retryauto-scaffold now generates correct monorepo/releasable tag format instead of standalonev1.2.3.
#0.105.0
Check outcome model adoption, undo plan-phase rework, ResolvedTarget unification, idempotent publish, publish_mode enum, scaffold base-healing, dev-sync sentinel, per-target test config.
Context
Breaking changes: publish_mode replaces private config key; --force-overwrite removed from scaffold; migrate command removed; pipeline target field now required; check implementations use reporter API.
New features: idempotent publish with registry probes; recovery dispatch across all publish templates; scaffold base-healing from history; dev-sync overlay sentinel and drift check; per-target test markers config; external-check name validation; malformed config hard-errors (test blocks, lint TOML, hook timeout, batch_limits); changelog entry type validation; smarter CI watch with failure classification; configurable-budget hints in timeout messages; npm name-collision errors name the conflicting package; robust per-version changelog regeneration; first-release guard against destroyed tags; undo completeness guard for multi-commit releases.
#Breaking
- **Breaking:
privateconfig key replaced bypublish_mode;rlsbl migrateremoved.** .rlsbl/config.json now requires"publish_mode": "ci"(publish via CI pipelines) or"publish_mode": "none"(suppress publishing). A config still carryingprivateis a hard error naming the exact edit.scaffold --privateis replaced by--publish-mode <ci|none>; private repos must choose explicitly, public repos default toci. Theprivate-publish-workflowcheck is renamedpublish-mode-workflow. Runscripts/sweep_publish_mode.py --fixto migrate configs. Therlsbl migratecommand is removed -- it delegated to migrable (TOML) while rlsbl configs are JSON, so every invocation errored since it shipped. - **
scaffold --force-overwriteremoved.** The flag wholesale-overwrote every managed file and destroyed local edits; it is deleted. Re-scaffolding a legacy repo now heals missing bases and three-way merges instead, and a file with no reconstructable base hard-errors with remediations rather than silently overwriting. - **Pipelines now require an explicit
targetlink.** Each pipeline entry must declare atarget(a target name it publishes for, ornullfor a targetless deploy). Missing fields and references to non-existent targets are hard errors. - Breaking. Migrated check system from CheckResult to strictcli's reporter-based check API (ErrorReporter/WarnReporter + CheckOutcome).
#Features
- Per-target test config.
.rlsbl/config.jsongains an optionaltestsection: settest.pypi.markers(e.g. "not integration") to append-m <markers>to the built-in pytest run, so slow or credentialed integration tests can be excluded from release/check test runs. - Dev-sync overlay sentinel.
rlsbl dev syncwrites a sentinel recording the path-source overlay it applied, a newdev-overlay-driftcheck flags when the working copy's overlay has drifted from that record, and a malformed sentinel now hard-errors instead of being silently treated as empty. - Smarter watch on CI failure.
rlsbl watchnow classifies CI failures before retrying and always prints the tail of the failure log, so you see why a run failed instead of a bare retry. - Configurable-budget hints in timeout messages. Timeout errors now point to the environment variable that controls the budget, and the built-in pytest run is invoked as
python -m pytestso it always uses the project interpreter rather than a system-installed pytest. - Scaffold heals missing merge bases from history. Re-scaffolding a project that predates merge-base tracking now reconstructs each managed file's base from its most recent
rlsbl scaffoldcommit and runs a normal three-way merge (conflicts surface as markers, never silent overwrites) instead of skipping the merge and advising a destructive overwrite. Reconstruction is read-only during analysis, so--dry-runreportsWould heal:and writes nothing; healed bases are persisted so future runs need not re-reconstruct. - New feature. Go pipelines distinguish library vs binary via the artifact config key. Library projects get a lightweight module-availability verification publish template instead of goreleaser. Auto-detected during scaffold.
- New feature. npm wrapper publish jobs support provenance attestation. When provenance is enabled, --provenance flag and id-token: write permission are added to the generated workflow.
- Idempotent publish. Pipeline publish() methods probe the linked target's registry before publishing; already-published versions are skipped. Per-pipeline resume via published_targets in release state. PyPI publish passes --check-url. Already-exists errors (npm E403, PyPI 400, crates.io) treated as success.
- Go probe rework. Go target publication_probe now uses git ls-remote --tags instead of Go module proxy HTTP GET. Avoids proxy cache lag and works for private modules.
- CI template probes. Go, Docker, Maven Central, and Zig publish templates now check if already published before running build/publish steps.
- npm wrapper per-package probes. Each platform package probes npm registry before publishing; wrapper meta-package gated on its own probe step.
- Recovery dispatch. All 13 publish templates gain a tag workflow_dispatch input for retry dispatch. Checkout uses inputs.tag with release.tag_name fallback. Concurrency keyed on tag. retry.toml gains tag field; dispatch passes -f tag=
. - New feature. Added pure/impure partition for dry-run preflight checks, showing which checks would run without executing impure ones.
- New feature. External checks now use strictcli's check provider pattern for proper registration before filtering.
#Fixes
- **Invalid
batch_limitsconfig now hard-errors.** Wrong-typed keys, a non-dictbatch_limitsblock, and unresolvable exclusion commit hashes are rejected loudly instead of being silently ignored. - Malformed lint and hook config now hard-errors. Malformed lint TOML, an invalid parser value, wrong-typed lint config fields, and an invalid
RLSBL_HOOK_TIMEOUTvalue are rejected with a clear error instead of being used as-is or silently ignored. - Changelog entry type is validated.
rlsbl changelog add --typenow rejects any value that is not one offeature,fix, orbreakingfor user-facing entries. - Safer release rollback. Rollback now preserves tracked finalize files, drops the misleading force-push advice, checks for residual state, and the pre-publish secret scan is scoped to freshly built artifacts.
- First-release path guarded against destroyed tags. The first-release flow no longer breaks when tags have been destroyed, and the destroyed-tag guard message now diagnoses a changed
tag_formatas a likely cause. - Robust per-version changelog regeneration. Regeneration now handles a read-only per-version
.mdfile and writes it atomically, and duplicate-commit messaging is stabilized. - Clearer npm name-collision errors. When an npm package name collides with an existing moniker, the error now names the concrete conflicting package.
- External checks run even with customized hooks. Release preflight now runs external checks regardless of hook customization, and the dry-run preflight reports honestly what it would do.
- External-check names are validated. External-check names must match
[a-z][a-z0-9-]*, which excludes glob metacharacters that could pattern-match a built-in check, and a name colliding with a built-in check is now a hard error. - **Malformed
testconfig blocks are gated during release.** A malformedtestsection (unknown targets/options, bad marker types) is now rejected before any release mutation instead of surfacing later. - **
release undoreworked for safety.** It now fully unwinds multi-commit releases (previously only one commit was reverted, stranding version files at the undone version);--dry-runpreviews the complete plan and changes nothing (the flag was previously ignored, so a dry run still deleted tags and the Release and pushed a revert); and undoing the latest release now requires registry evidence -- a published release is refused and routed torelease yank/release deprecateinstead of being destroyed. - Undo hardening. The release commit walk now refuses partial undo when a foreign commit blocks the version-bump commit. INCONCLUSIVE evidence gate prints remediation guidance. The audit commit exception is narrowed to CalledProcessError.
- Fix. npm, cargo, and deno pipelines now publish from the target's directory (cwd=dir_path) so subdirectory targets build and publish correctly. Secret scan covers per-target dist/ directories.
- Fix. Merged publish workflow generation now uses pipeline template_mappings for template resolution instead of hardcoded target-name paths, so pipeline config drives template selection.
- Bug fix. The undo completeness guard error message no longer references undefined variables.
#0.104.1
Root-publisher publish gate, pytest rootdir pin, batch release idempotency, push-timeout resume resilience
Context
Four bug fixes addressing release-flow reliability:
- Monorepo sync now generates a gated publish router for root-only publishers (was silently skipped)
- Member CI pytest invocations pin --rootdir . to prevent workspace-root escape
- Batch releases persist a resolved plan sidecar for idempotent resume
- Post-TAGGED push failures are now resumable instead of triggering destructive rollback
#Fixes
- Fix. Monorepo sync now generates a gated publish router for workspaces whose sole publisher is the root package. New required
publish_gate_check_regexconfig key specifies which CI check the gate matches. Previously, root-only publishers silently skipped gate generation and publishes ran ungated. - Fix. Member CI jobs now pin
--rootdir .on pytest invocations, preventing rootdir from escaping to the workspace root. Newmember-pytest-configworkspace check hard-errors when a member lacks its own pytest config while a workspace-root conftest exists. - Fix. Batch releases now persist a resolved plan sidecar; on resume, already-released items are skipped and a stale-but-complete batch file is auto-archived. Previously, a mid-batch resume re-released completed items and a stale batch file persisted across releases.
- Fix. A push failure after the TAGGED step is now classified as resumable -- the local tag, release state, and finalized files are preserved, and the exact resume command is printed. Previously, push timeouts triggered a full rollback that deleted the tag, state file, and committed changelog files, making
release resumeunusable.
#0.104.0
Mandatory npm provenance declaration with a preflight visibility guard, plus monorepo release-correctness fixes: structured-target scaffold config, loud scaffold-commit failures, virtual-workspace-root check skipping, releasable-aware orphan detection, mode-aware CI-sync check, two-level lint config, and per-releasable status rows.
Context
This release bundles Phases 4, 5, and 6.
Phase 6 (npm provenance) makes the npm publish pipeline's provenance behaviour explicit and safe. npm build-provenance attestations require a PUBLIC GitHub source repository and GitHub Actions OIDC, so:
- npm-type pipelines now require an explicit boolean
provenancekey in
.rlsbl/config.json (no implicit default -- the correct value depends on repository visibility). This is a breaking config change for existing npm pipelines; add "provenance": true for public repos or false for private.
- The scaffolded publish workflow (npm/pnpm/yarn variants) emits
--provenance
only when the pipeline declares it, via a namespaced {{#if npm.provenance}} template conditional. --access public stays hardcoded on purpose (scoped packages are forbidden in this ecosystem; unscoped public packages require it). The yarn variant, which was previously missing provenance entirely, is now consistent with the others.
- A pre-mutation preflight guard probes repository visibility via
gh repo view --json isPrivate when (and only when) an npm pipeline requests provenance, and aborts on a private or non-GitHub repo with the three ways out spelled out. No network call happens when provenance is not requested.
- Local (non-CI) npm publishes never pass
--provenance, since OIDC
build-provenance is impossible outside GitHub Actions.
Phases 4 and 5 are the monorepo release-correctness fixes already landed on main: scaffold no longer downgrades structured publish targets, scaffold-commit failures are now loud, virtual uv workspace roots skip inapplicable package/publish checks, changelog orphan detection respects a releasable's custom tag format, the workspace-ci-synced check validates the inlined ci-router.yml, releasable members can share a single releasable-level lint config, and rlsbl monorepo status shows one row per releasable with its real tag, version, coverage, and members.
#Breaking
- **npm pipelines now require an explicit
provenanceboolean.** The npm publish workflow emits--provenanceonly whenprovenance: true, a pre-release guard blocksprovenance: trueon private or non-GitHub repositories, and local publishes never attest provenance. Add"provenance": true(public repos) orfalse(private) to each npm pipeline in.rlsbl/config.json.
#Features
- Releasable member packages can now share a single lint config at the releasable level.
library-lintreads.rlsbl-monorepo/releasables/<name>/lint/<language>.tomlwhen a member has no lint override of its own, andrlsbl monorepo cleanupremoves a member's.rlsbl/lint/only when it is byte-identical to that shared config -- a genuine per-member override is preserved.
#Fixes
- Release init now fails loudly when it cannot commit the scaffolded release file. A failed auto-commit is no longer silently swallowed, which previously left an uncommitted release file that could block later releases.
- **
rlsbl monorepo release initnow reports an actionable error for a releasable whose config has an emptytargetslist.** The banned empty-targets config surfaces a clear remedy (remove the key or setprivate: true) instead of a confusing no-eligible-releasables error. - **
rlsbl initno longer downgrades structured target entries to plain strings.** Registering a target preserves existing entries such as{"name": "go", "path": "go/"}, keeping subdirectory paths intact. - **
rlsbl changelog edit --idnow prints a clean error when no entry matches** instead of crashing with an internal error. - Virtual uv workspace roots no longer report spurious check failures. A
pyproject.tomlwith[tool.uv.workspace]but no[project]table is recognized as a non-package root: version, name, config-schema, and publish-workflow checks now skip with a clear reason instead of failing on a missing version orprivatekey. - Changelog orphan detection no longer raises false positives for releasables with a custom tag format. A changelog entry whose commit is within the releasable's own tag range is no longer flagged as a stale orphan because of an unrelated
v*tag elsewhere in the repo. - **The
workspace-ci-syncedcheck now validates the inlinedci-router.ymlinstead of looking for per-project{name}-ci.ymlfiles.** Monorepos synced with the current inline router no longer report spurious missing-workflow failures; a project is verified present by its inlined router jobs. - **
rlsbl monorepo statusnow shows one row per releasable with the releasable's real tag, version, changelog coverage, and member list.** Releasable members previously showed Tag(none)because the tag glob was derived per-member instead of from the releasable's tag format.
#0.103.2
Fix release-edit CHANGELOG.md lookup for explicit-mode releasables and carry the package prefix on amended releasable changelog entries.
Context
GitHub Release notes sync (rlsbl release edit) now works for explicit-mode releasables instead of failing with 'CHANGELOG.md not found', and amended releasable changelog entries now render with their [name] prefix like added entries.
#Fixes
rlsbl release editnow finds CHANGELOG.md for explicit-mode releasables (previously failed with "CHANGELOG.md not found").rlsbl changelog amendnow sets thepackagesprefix on amended releasable entries, so they render with the[name]prefix like added entries.
#0.103.1
release init can no longer clobber a filled release file (refuse-unless-pristine + atomic create).
#Fixes
release initandmonorepo release initcan no longer clobber a filled-in release file; they refuse unless the file is a pristine scaffold and no-op idempotently when it is
#0.103.0
Schema dump correctness, CI working-directory injection, import name auto-detection, and configuration validation in checks.
#Breaking
- Breaking. Failed or timed-out strictcli schema dumps now abort the release instead of printing a warning.
#Features
- New feature. Schema dump version field now matches the release being cut, not the previous version.
- New feature. Scaffolded CI workflows for subdirectory targets now include the correct working-directory.
- New feature. monorepo sync auto-detects Python import names that differ from package names, populating import_name in workspace.toml.
- New feature. rlsbl check now validates targets and release.mode configuration, catching errors previously only found during release.
#0.102.0
Rollback safety guard and documentation directive extras.
#Features
- New feature. Release rollback now detects concurrent commits from other sessions and refuses to proceed, preventing data loss from blanket git reset.
- New feature. Documentation directives require selfdoc-core, installable via
pip install rlsbl[docs].
#0.101.1
CI fixes: gitleaks install in CI workflows, inline monorepo CI router (GitHub rejects routers with >20 reusable workflow calls), reusable-call guard. Release blog posts now generate via the selfblog CLI.
Context
A monorepo's CI router had been broken since June 24 because GitHub Actions rejects workflows with more than 20 reusable workflow calls. The router is now inlined per-package, a guard check prevents regenerating routers that exceed the limit, and gitleaks is installed in CI so the secret-scan gate works in workflows.
#Fixes
- Bug fix. Monorepo CI router now inlines jobs instead of reusable workflow calls -- GitHub rejects routers with more than 20
uses:calls, which silently broke CI for large monorepos. - Release blog posts now generate via the selfblog CLI (
selfblog post generate --from-release); selfdoc gen/check are unchanged
#0.101.0
Dev-branch workflow, changeset-file coverage mode, crates.io target, yank/deprecate split, secret scan gate, PR-mode removal, external check providers, entry id field, rlsbl push, remap tooling + post-rewrite hook.
Context
Large feature release spanning multiple areas:
- Dev-branch workflow: support for releasing from non-main branches with branch-aware version management.
- Changeset-file coverage mode: alternative to commit-based coverage that tracks changes via explicit changeset files.
- Crates.io target: new pipeline target for Rust crate publishing.
- Yank/deprecate split: separated yank (registry removal) from deprecate (soft deprecation with messaging).
- Secret scan gate: pre-push and pre-release checks for accidentally committed secrets.
- PR-mode removal: removed the PR-based release workflow in favor of direct branch releases.
- External check providers: pluggable check system allowing third-party validation providers.
- Entry id field: unique identifiers on changelog entries for stable cross-referencing.
- rlsbl push: dedicated push command with safety checks replacing raw git push.
- Remap tooling + post-rewrite hook: SHA remapping infrastructure for commit rewriting workflows.
#Breaking
- Yank/deprecate split.
release yankis now registry-aware removal (npm deprecate, cargo yank, Go retract, PyPI checklist).release deprecateis the soft GitHub-only flag.release undosupports non-latest stillborn releases with layered evidence gate. - PR-mode removal. Removed the PR-based release flow (zero adopters, 5 latent bugs).
release.modeconfig key is now a hard error. Consolidated config validation banstargets: []and stale PR-mode state.
#Features
- External check providers. Projects can declare subprocess checks in
.rlsbl/config.jsonunderexternal_checks. Each entry specifies a name, shell command, check tag, and optional dependencies. Checks run duringrlsbl check --tagand the release preflight. Non-zero exit is a hard fail with no bypass. - crates.io target. Full release pipeline for Rust crates:
check-namewith hyphen/underscore normalization,claim-namewith permanence confirmation, CI publish workflow with OIDC Trusted Publishing, and version bump/publish sequencing. - Changeset-file coverage mode. New
coverage_unitconfig key supportschangeset-fileas an alternative to per-commit coverage. Changelog add writes pending files, prepush uses diff-based coverage, release finalization concatenates pending files, and all commands are mode-aware. - Entry id field. Changelog entries now include a stable ULID-style
idfield, auto-generated bychangelog add. Used bychangelog amendandchangelog editfor unambiguous entry selection. - Dev-branch workflow.
rlsbl release runfrom a non-release branch fast-forward merges the release branch to HEAD before releasing. Newrlsbl pushcommand for dev branches with branch guard, changelog coverage preflight, and behind-remote detection. - Secret scan gate. Pre-publish gitleaks-based artifact scanning detects secrets in build artifacts (
.whl,.tar.gz,.tgz,.zip). Hard fail when secrets are found, configurable via.gitleaks.tomlallowlists. - Changelog remap command. New
rlsbl changelog remapcommand remaps stale commit hashes in JSONL files from multiple sources (--map-file,--from-journal,--stdin). Auto-installed post-rewrite hook triggers remap on amend/rebase.
#0.100.1
Fix CI_CHECK_REGEX in publish router to match actual GitHub check-run names
Context
The monorepo CI router generates job keys from CI workflow filenames (e.g. core-ci from core-ci.yml), but the publish router gate was using project names (e.g. core) in the regex. This caused the gate to never find matching check runs, timing out after the grace window.
#Fixes
- Fixed CI_CHECK_REGEX in publish router. The gate job regex now matches actual GitHub check-run names (CI filename-based, e.g.
name-ci / test) instead of project-name-based names that never matched.
#0.100.0
Remove vestigial hashes.json from scaffold metadata
Context
hashes.json was never read for any scaffold decision -- it was write-only dead state. This release removes all production code that maintained it, adds automatic cleanup of existing hashes.json files on next scaffold run, and deletes the associated tests.
#Breaking
- Removed hashes.json from scaffold metadata. The file was vestigial (never read for any decision). Existing hashes.json files are automatically deleted on next scaffold run.
#0.99.0
Versioned JSONL merge during migrate-releasable, packages prefix in CHANGELOG rendering, absorb changelog routing fix
Context
migrate-releasable previously orphaned versioned changelog files, leaving only unreleased.jsonl in the releasable directory. This release adds full changelog consolidation: versioned JSONL files and release metadata are merged during migration. CHANGELOG rendering for releasable members now includes the package name prefix for clarity in multi-package projects.
#Features
- Versioned JSONL merge during migrate-releasable. Released changelog files are now consolidated into the releasable directory alongside unreleased entries, preserving full version history after migration.
- Packages prefix in CHANGELOG rendering. Version sections for releasable members now include the package name prefix (e.g.,
core/0.5.0) so multi-package changelogs are unambiguous.
#Fixes
- Absorb routes changelogs to releasable directory. When absorbing a project into a monorepo with a releasable name, changelog files are placed in the releasable's changes directory instead of the package-level directory.
#0.98.2
Fix CI test failure: preid test no longer depends on local git tag history.
#Fixes
- Fix. CI test reliability: preid test now mocks tag_exists_locally directly instead of depending on local git tag history.
#0.98.1
Fix scaffold hash pruning that broke CI by over-aggressively deleting hashes.json entries.
#Fixes
- Scaffold hash pruning no longer removes entries for files not actively deleted during the scaffold run.
#0.98.0
JVM monorepo analysis, maven-central pipeline, CI templates for 5 targets, build hardening
Context
Major additions for JVM/Android monorepo support: dead-module detection, circular dependency analysis, Gradle version catalog parsing, GPG signing validation, and maven-central pipeline scoping with preflight checks. CI template coverage expanded to dart, flutter, pgdesign, native-android, and native-ios targets, with a capability filter so the workspace-ci-synced check skips targets that lack template support.
Build checks are now pre-push (not post-push) and fatal -- build failures abort the release instead of being warnings. Version catalog resolution uses fallthrough instead of hard errors for missing keys. Scaffold now cleans up orphaned bases, stale hashes, and empty directories.
#Breaking
- Build failures abort releases. Build checks are now pre-push (not post-push) and failures are fatal. Build timeout is configurable via
build_timeoutin config. - Version catalog fallthrough. Missing keys in Gradle version catalogs now fall through to raw string resolution instead of raising errors.
#Features
- New helper. Extracted
tag_exists_locallyandtag_exists_on_remotehelpers from duplicated tag-probe logic across release flows. - Scaffold cleanup. Orphan base sweep, stale hash pruning, and empty directory cleanup during scaffold operations.
- Maven-central pipeline. Preflight validation tag, pipeline-scoping gate, publishSetup hints with maven-central requirements, and pipeline documentation.
- Gradle catalog parsing. Resolve
libs.<alias>references from Gradle version catalogs when scanning Maven dependencies. - Dead JVM module detection. Build class-to-file index from JVM sources and detect modules with no inbound references.
- Circular JVM dependency detection. Detect circular dependencies between JVM modules in the workspace dependency graph.
- CI templates. Added CI template support for dart, flutter, pgdesign, native-android, and native-ios targets. The workspace-ci-synced check now filters by target capability so projects with unsupported targets are skipped.
- Signing check. GPG signing configuration validation for maven-central metadata.
#Fixes
- Fix false-pass. Maven-publish source/javadoc detection no longer matches bare
maven-publishplugin references, preventing false positives. - Fix warn-cascade. Self-gating logic no longer suppresses its own warnings. Monorepo init now rejects directories inside an existing rlsbl-managed repo.
- Fix test-suite-workspace. Maven is now a recognized target in workspace test validation.
#0.97.1
Internal quality pass: circular-dep cycle resolution, CLI help expansion, selfdoc-check zero warnings, dev-group UX fixes.
Context
Completes the quality cleanup phase: resolved all three import cycles (cycle 2 extracted to resolve.py, cycles 1+3 structural via workspace_types.py, parameter injection, prepush_utils.py/constraints.py extraction), expanded CLI help text to meet documentation checks, polished 42 docstrings for SEO compliance, and parameterized 14 callers for dev-group workspace-root UX.
#Fixes
- Fix.
release initandmonorepo release initnow auto-commit the scaffoldedunreleased.toml, eliminating the manual commit step that blocked--no-allow-dirtyreleases. - Fix. All per-sub-project commands now show a workspace-root-specific error message instead of the misleading 'Run rlsbl monorepo add' when invoked from the monorepo workspace root.
- Expanded CLI help text. All command, flag, and argument help strings now meet the 50-character minimum for documentation quality checks.
- Improved CLI help text for documentation quality. Shortened overlong command descriptions and added numeric data points to meet SEO007 and SEO008 checks.
#0.97.0
Multi-artifact releasables (per-member publishing at the shared version, collision-proof asset naming), version-consistency enforcement for published members, monorepo-init bootstrap fix
Context
Completes the six-todo scope of the Phase 10 multi-artifact releasable effort.
#Features
- Per-member publish loop. In releasable mode, each non-private member with publish pipelines is published independently at the shared version. Private members are skipped, pipeline-less members are logged, and resume tracks per-member progress.
- Releasable version-consistency checks. Published members' manifests must match the releasable version. Dead-workspace-packages exempts published releasable members.
- Per-member asset upload. In releasable mode, each publishing member uploads assets with member-name-prefixed filenames for deterministic naming.
#Fixes
- Monorepo init bootstrap fix.
rlsbl monorepo initno longer requires a pre-existing.rlsbl/directory, enabling fresh workspace initialization. Refuses nested workspaces.
#0.96.0
Publish workflows now gate on CI success for the release commit (no more publish-races-CI), with a ref-based retry contract and a fix for dispatch runs skipping all router jobs.
Context
Closes the wesktop-v0.7.0-class failure mode where a package was published while CI was failing. A gate job (single source of truth in rlsbl/publish_gate.py) now runs first in every publish workflow: it polls check runs for the release SHA (ref-based, so workflow_dispatch retries at the tag ref work), uses explicit conclusion semantics, and hard-errors on failed, cancelled, or skipped CI. The gate is wired across templates, the merged-workflow generator, the npm wrapper, and the monorepo router; router conditions are ref-based (fixing dispatch runs skipping all jobs), publish concurrency is per-ref, and the gate job requests checks:read. Verified live on a staging sandbox repo across 6 scenarios.
#Features
- Publish gating. Publish workflows now wait for CI to pass on the release commit before publishing: a
gatejob polls the commit's CI check runs (ref-based, so dispatch retries at the tag ref work) and hard-errors on failed, cancelled, or skipped CI. Monorepo publish routers use ref-based tag matching, fixing workflow_dispatch runs skipping all jobs. - Docs: publish gating. New release-workflow.md section covering the publish gate's conclusion semantics, the retry-dispatch-at-tag-ref contract, and monorepo router gating.
#0.95.0
Local dev overlays via rlsbl dev sync, the cross-repo path-source ban, a release-time version-skew guard, detached CI watching with --watch-async/rlsbl watch --stop, and history-consistent scrubs on safegit 0.22.0.
Context
This release is a hard-constraints sweep: committed lockfiles must be registry-pure (the new cross-repo-path-sources check bans out-of-repo [tool.uv.sources] paths, and scaffolded pypi CI now runs uv sync --locked), and nothing degrades silently -- the version-skew guard hard-errors when a dev-overlaid dependency's local checkout is ahead of its PyPI release, and unpublished overlays or registry failures are hard errors, never skips. Local editable development moves to rlsbl dev sync overlays instead of committed path sources. Scrubs are now orchestrated with safegit 0.22.0: persisted SHA maps, in-history JSONL remapping via --remap-shas-in, journal-backed recovery for no-match validation scrubs, and a cleanup_ok gate, so history rewrites keep changelog metadata consistent end to end.
#Features
- **New
rlsbl dev synccommand: local editable overlays.** Overlay local editable checkouts of sibling projects onto the project's locked environment, driven by a git-invisibledev-sources.toml.local-onlyfile at the project root -- develop against sibling checkouts without committing machine-local[tool.uv.sources]path dependencies. Runs oneuv sync --inexactexcluding every overlaid package, thenuv pip install -e <path>per entry, so locked registry wheels never clobber the local checkouts. RequiresUV_NO_SYNC=1in the environment (hard error otherwise, since a bareuv runwould silently wipe the overlays); every overlay-file problem is a hard error, never a silent no-op. - **
rlsbl release scrubnow produces history-consistent changelogs.** The scrub hands safegit (>= 0.22.0 required) repeatable--remap-shas-inglobs covering every changelog directory, so commit hashes inside the JSONL files are remapped at EVERY commit of the rewritten history -- historical versions stay self-consistent, not just HEAD. rlsbl's own worktree remap step became a validation gate; dangling hashes (e.g. abbreviated hashes, or scrubs run outside orchestration) are repaired from safegit's persisted rewrite journal, and CHANGELOG regeneration is now asserted byte-identical instead of rewritten. - Scrub aborts safely when safegit's post-rewrite cleanup failed.
cleanup_ok: falsefrom safegit is now a hard error before anything is committed or pushed, with the cleanup errors and prune remediation printed; a resumed run re-checks whether the prune has since completed and continues once no pre-rewrite object resolves. - **
rlsbl watch --stop [sha]stops a detached background watcher.** With a SHA it stops that watcher; without one it stops the single live watcher or lists candidates. Sends SIGTERM, escalates to SIGKILL after 5s, and cleans up stale pidfiles left by dead watchers. - **
--watch-asyncreleases without blocking on CI.**release run,release resume,release retry, andmonorepo release runnow accept--watch-async(mutually exclusive with--watch; exactly one of the pair is required) to spawn a detached background watcher and return immediately. The watcher logs to.rlsbl/watch-<sha>.log.local-only, keeps auto-retry, publish audit, and desktop notifications working, and refuses to double-spawn for the same commit.--no-watch-asyncis equivalent to--no-watch. - Docs: detached watching documented. The release workflow page now covers
--watch-async, the per-commit log/pidfile locations, andrlsbl watch --stop. - **New check:
cross-repo-path-sources.** Committedpyproject.tomlfiles must not declare[tool.uv.sources]path entries that resolve outside the repository. In-repo paths andworkspace = truesources stay legal. Runs under theprojecttag and unconditionally duringrlsbl release run. - Scaffolded pypi CI is locked and tests.
ci.ymlnow runsuv sync --locked(registry-pure lockfiles are guaranteed by the new ban check) and adds auv run pyteststep when the project declares pytest. TheUV_NO_SOURCESenv escape hatch is gone. - New release guard: version skew.
rlsbl release runnow readsdev-sources.toml.local-onlyand hard-errors when an overlaid dependency's local checkout is ahead of its latest PyPI release ("release the dependency first"). Unpublished overlay packages and registry failures are hard errors too -- never a silent skip.
#Fixes
- **Fixed
rlsbl dev installfor path-scoped targets.** Each target's install command now runs in that target entry's declared directory instead of the project root, so multi-target projects (e.g. a Go binary withnpm/andpypi/wrapper subdirectories) install correctly: npm finds its package.json and uv finds its pyproject.toml. Uninstall likewise resolves the package name from the target's own manifest. - **Clearer
rlsbl dev installskip messages.** When a target has nothing to install (e.g. a Go library with no main packages), the skip line now shows the target's actual reason instead of the generic "install not yet supported" message. - Fix.
rlsbl dev installandrlsbl dev syncinvoked at a monorepo workspace root now explain to cd into a sub-project instead of misleadingly suggestingrlsbl monorepo add. - Fix.
rlsbl --helpnow reports accurate command counts (49 commands, 15 top-level, 4 groups), derived from the live command registry instead of stale hand-maintained literals. - **
rlsbl watchpublish-workflow audit works from subdirectories.** The audit now looks for.github/workflowsat the git repo root instead of the current directory, so watching from a monorepo package directory no longer falsely warns that a publish workflow is missing.
#0.94.0
Overhauls the releasable release flow with provable post-release step tracking and incremental resume, moves releasable state and release files into the releasable directory, and hardens the Go pipeline and release-scrub with hard errors in place of silent skips. Includes breaking changes: Go pipelines now require explicit install_paths, and pipeline publish failures are fatal.
Context
This release is a correctness push for the releasable model plus a philosophy sweep replacing silent skips with hard errors.
The releasable release flow previously wrote per-package artifacts to an arbitrary "representative" member package and had no record of which post-release steps had completed, making partial failures unrecoverable and unverifiable. Post-release steps are now tracked with provable completeness and incremental resume, releasable state and release files live under the releasable's own directory, representative writes are eliminated, changelogs are generated canonically per releasable plus a combined root changelog, and a new releasable-residue check with a monorepo cleanup command guards against stray artifacts.
The Go pipeline moved from filename-based main.go globbing to toolchain-based entry-point detection with mandatory install_paths, because silent install-skips left stale binaries. The same hard-errors-over-silent-skips principle now applies to pipeline publish failures (fatal) and release-scrub (file/recipe modes actually working, real-binary e2e harness, validation gate, explicit-lease pushes, committed no-content audit archive, orchestration handshake, min safegit 0.21.1). Also: watch reliability fixes, per-SHA CI concurrency groups, member-context resolution unification, changelog --dry-run fix, and an unversioned-boundary check.
#Breaking
- **Go pipelines with
local: truemust declareinstall_paths.** The new mandatory config key lists the main-package dirs togo install(e.g.["./cmd/x"]); each path is validated againstgo list. The oldgo install ./...auto-detection is gone -- it silently skipped cmd-layout projects with non-main.go entry files forever. Publish failures (missing go toolchain, unreadable go.mod, proxy notification failure, install failure) are now raised instead of warn-and-continue. - strictcli schema dumps can no longer be silently skipped. Go entry-point detection for strictcli projects uses
go list(handlescmd/x/cli.goentry files, whose schema dump was previously skipped on every release). If go.mod requires strictcli but the entry point cannot be determined, release validation aborts with a hard error instead of silently shipping a stale schema. - Pipeline publish failures now abort the release. A failed
publish(previously downgraded to a warning) preserves the release state and exits with an error; runrlsbl release resumeto re-attempt it. Forlocal: truepipelines the publish step IS the release — warn-and-continue silently shipped unpublished releases.
#Features
- New check.
unversioned-boundary(tag: workspace) errors when a releasable project has a runtime dependency on a project withreleasable = falsethat is not dev-only -- such dependencies ship changes inside consumer releases with no changelog coverage. Sibling ofdev-only-boundary. - Release state tracking overhauled with a canonical step list. The in-progress guard message now derives its step count from the real list (fixing the hardcoded
n/6count), names the missing steps, and auto-clears state files from releases that provably completed (all steps marked, no fatal failures) instead of hard-blocking. - Post-release steps are now tracked and resumable. Asset upload, pipeline publish, deploy, post-release hooks, and snapshot regeneration each record success or failure in the release state file.
rlsbl release resumeskips already-completed post-release steps instead of re-running the whole phase, and non-fatal failures (deploy, hooks, snapshot) are loudly named in a completion summary. - Releasable release state no longer pollutes the representative member package. In-progress release state (and scrub-result.json) for releasable releases now lives under
.rlsbl-monorepo/releasables/<name>/releases/instead of the representative member's.rlsbl/releases/.rlsbl release resumealso works from the workspace root (it finds the one releasable with in-flight state and errors clearly when two are mid-release), and legacy state at the old location is detected with a migration hint instead of being silently ignored. - Release workflow docs now cover state tracking and resume. The pipeline-order table documents the post-release steps (assets, publish, deploy, hooks, snapshot) with their fatality classes, and a new section explains the in-progress state file, its releasable location, and
rlsbl release resume. - New.
rlsbl release scrubsupports recipe mode: pass asafegit scrub runTOML recipe to apply multi-operation scrubs with the same metadata repair, push, and release-recreation flow. - New.
rlsbl release scrubsetsRLSBL_SCRUB_ORCHESTRATED=1in the safegit scrub subprocess environment, so future safegit releases can require orchestrated scrubs in rlsbl-managed repos. - New. A
releasable-residueworkspace check (hard error) flags per-package release-state residue in releasable member packages, and a newrlsbl monorepo cleanupcommand removes it via saferm and commits the deletions. Per-package.rlsbl/hooks/directories are now preserved by cleanup (per-package script hooks are a live feature), and members whose path is the workspace root are exempt (protects workspace-level.rlsbl/and the combined root CHANGELOG.md). - New. The scrub metadata commit now carries a machine-greppable
Scrub-remap: <old_head>..<new_head>trailer for audit trails. - New. Successful scrubs now leave a committed audit archive (
.rlsbl/scrubs/scrub-<newhead>.json, or under the releasable directory in explicit monorepo mode) instead of deleting the scrub state. The archive schema is hard-whitelisted -- commit SHAs, tag refnames, reason, mode, and step list only -- so a committed scrub artifact can never re-introduce scrubbed content. - Recipe-mode scrubbing is now CLI-reachable.
rlsbl release scrub --recipe <recipe.toml>runs a safegit scrub recipe; the mode was already implemented in the scrub flow but never registered on the CLI. File mode no longer demands the match-only--replace/--mangleflags that made it unusable from the command line.
#Fixes
rlsbl watchno longer re-watches or re-retries a retry run that reappears in the late re-poll -- previously a failed workflow's retry could be dispatched twice on the same commitrlsbl watchretry deduplication now also applies when only a single CI run is being watched -- previously single-run watches could double-retry a failed workflowrlsbl watchauto-retry now waits for the actual dispatched retry run to appear instead of attaching to the just-failed original run (or another known run) and reporting a bogus retry result- Scaffolded CI workflows now use a per-SHA
concurrencygroup (cancel-in-progressdedupes re-runs of the same commit only), so back-to-back pushes can no longer cancel an earlier commit's CI run; the monorepo CI router gets the same group and called project workflows have workflow-level concurrency stripped - Fix. Releasable-level
private: falseandtargetsconfig is now consistently inherited everywhere a member package's publish status is resolved: Go companion tag creation, thego-companion-tagscheck, and primary target path resolution now agree with version sync instead of silently treating such members as private. - Fix.
rlsbl changelog add/amend/edit --dry-runno longer writes anything: previously the global--dry-runflag was silently dropped, so entries were appended, auto-committed, and--allow-batcheven modified config.json. Dry-run now runs all validation and prints the entry JSON that would be written. - Go project detection now uses the go toolchain. cmd-layout projects whose entry file is not named
main.go(e.g.cmd/x/cli.go) are correctly classified as binaries: goreleaser and version.go get scaffolded, npm wrapper jobs are included, and.goreleaser.ymlgets the rightmain:instead of a silent.fallback.version.gois scaffolded into the detected main-package dir (a rootpackage mainfile withoutfunc mainused to breakgo build ./...in cmd-layout projects), and goreleaser ldflags now injectmain.Versionmatchingvar Version(the lowercasemain.versioninjection was a no-op). - **
release retry,release yank, andrelease editnow work for releasable members whose targets are configured only at the releasable level.** Previously retry failed with a config error (or "no package.json/pyproject.toml/go.mod found") and yank/edit could mis-resolve the release tag, because these commands ignored releasable-level config inheritance. - **
rlsbl checknow fails loudly on broken releasable member configs.** The go-companion-tags check reports a failure naming the member when its config.json is corrupt or resolves no targets, instead of silently skipping the member (which let the check disagree with the release flow about which members are published). - Check reference docs corrected. The docs claimed 50 checks, used the obsolete
dev-node-boundaryname, and omitted nine existing checks; the check reference, monorepo workspace-check table, and README now match the actual 57-check inventory, including the internalpreflight/preflight-changelog/maventags. - Fix. A hung
go installduring local Go pipeline publishing no longer stalls the release forever: the call is bounded by a 300s timeout and expiry is a hard error. - Fix. Releasing a monorepo releasable with a corrupt member
config.jsonnow aborts during Go companion-tag collection instead of silently releasing without that member's Go module proxy tag. - Fix. The
go-companion-tagsworkspace check now fails loudly when a releasable's version file cannot be read, instead of silently skipping that releasable. - Fix. Releasable releases now write the canonical CHANGELOG.md into the releasable directory (where
rlsbl changelog generateand thechangelog-entrycheck already expected it) instead of the representative member's directory, and additionally generate a combined root CHANGELOG.md covering all releasables of the workspace. The GitHub-Release-notes recovery hint now points at the releasable changes directory. - Fix.
rlsbl release scrub --filenow works: it invokessafegit scrub filewith the correct CLI (positional path,--from), instead of passing flags safegit rejects. File mode had never worked. A scrub that finds nothing to rewrite now reports "No matches found" instead of crashing on empty safegit output. - Fix. Releasable releases no longer pollute the representative member: the
.rlsbl/versionmarker is only refreshed for actually-scaffolded standalone projects (never in releasable mode), a private representative's manifests are no longer version-bumped or keyword-tagged (the releasable version file remains the source of truth), and stale batch-limit exclusions are cleaned from the releasable-level config.json wherechangelog add --allow-batchwrites them. - Fix.
rlsbl release scrub --dry-runnow shows real match and commit counts. It previously read keys that don't exist in safegit's dry-run JSON and always reported "0 commits would be rewritten, 0 tags affected". - Fix. PR-mode releases of releasables now write
pending.jsoninto the releasable's releases directory (via the release state-dir resolver), and the release-finalize workflow locates it by globbing both the standalone and releasable locations (hard error on ambiguity) -- previously the workflow hardcoded the repo-root path and was broken for non-root representatives. Dispatch workflows are now also collected from the workspace root's.github/workflows/. - Fixed strictcli self-detection false positive.
_go_mod_has_strictclimatched the strictcli module path anywhere in go.mod, so the strictcli Go library's ownmoduledeclaration counted as a dependency and rlsbl hard-errored demanding an entry point, blocking releases of any repo whose module path matches a consumed-tool prefix. Detection now only considersrequiredirectives (single-line and block forms). - Fix.
rlsbl release scrubnow requires safegit >= 0.21.1 (the version its file/recipe/dry-run contracts are built against) and no longer crashes on safegit versions with build suffixes like0.21.1+dirty. - Fix.
rlsbl release undois now releasable-aware: it unfinalizes the version JSONL and regenerates CHANGELOG.md at the releasable level (canonical file plus combined root file) instead of the representative member's.rlsbl/changes/, and it clears the preserved in-progress release state after a successful rollback so a previously failed release no longer hard-blocks the nextrlsbl release run. Failed-release rollback also cleans orphaned finalized JSONL files from the releasable changes dir. - Fix. Post-scrub changelog remapping now maps abbreviated commit hashes (unique-prefix match against the rewrite map) and reports every hash it could not map. Previously only exact full-SHA matches were remapped and everything else was silently skipped, leaving dangling references after a history rewrite.
- Fix.
rlsbl release scrubnow remaps commit hashes in releasable-level changelog directories (.rlsbl-monorepo/releasables/*/changes/), not just per-project.rlsbl/changes/. Explicit-mode monorepo changelogs were previously left with dangling pre-rewrite hashes. - Fix.
rlsbl release resumenow works when invoked from a monorepo workspace root: it finds the single releasable with an in-flight release instead of exiting with a 'not inside any registered project' error. - Fix.
rlsbl release scrubvalidates after remapping that every commit hash in every changelog file resolves against the rewritten history, and aborts loudly BEFORE committing or force-pushing if any hash is dangling (resume state is kept). - Fix. The release-workflow documentation no longer claims releases are atomic: it documents the preserved-state/resume model, corrects the post-release hook's pipeline step number, and documents the releasable location of scrub-result.json.
- Fix.
rlsbl changelog generate --auto-commitno longer crashes with an UnboundLocalError in releasable mode. - Fix.
rlsbl release scrubnow commits the deletion of tracked.validatedcaches (previously left as a dirty working tree after the scrub), persists its remap/delete file lists so resumed runs commit them too, and aborts before force-pushing if the metadata commit fails. - Fix.
rlsbl changelog amendandrlsbl changelog editnow regenerate the canonical releasable CHANGELOG.md and the combined root CHANGELOG.md in releasable mode, instead of writing a CHANGELOG.md into the member package's directory. - Fix. Post-scrub force-pushes now use
--force-with-leasewith explicit expected values captured from the remote before the rewrite -- for the branch AND every tag. Bare--force-with-leaseis useless after a scrub (the rewrite updates remote-tracking refs) and tags were previously pushed with plain--forceand failures merely warned; a rejected push (remote changed mid-scrub) is now a hard error that preserves resume state. - Fix. During a post-scrub force-push, the pre-push changelog coverage check now prints "history rewrite detected: old remote head unresolvable — coverage check skipped" instead of passing silently and vacuously when the pre-rewrite remote head no longer resolves.
- Fix. Post-scrub CHANGELOG regeneration is skipped for projects without a
.rlsbl/changes/directory, so releasable members no longer get a fabricated stub CHANGELOG.md. The scrub flow is now covered end-to-end against the real pinned safegit binary in CI. - Fix. Releasable single releases now store and archive their release file (
unreleased.toml→v{x}.toml, plus the blog body) under the releasable's own directory (.rlsbl-monorepo/releasables/<name>/releases/) instead of the representative member's.rlsbl/releases/— so file-based releasable releases no longer recreate per-package residue that fails thereleasable-residuecheck.release init,release run,release undo, rollback cleanup, and the changelog metadata backfill all use the new location; a release file left at the old member location is a hard error with a migration hint. - Fix.
rlsbl release scrubnow loudly reports changelog hashes the JSONL remap could not handle (unmapped hashes and ambiguous abbreviated hashes, per file) instead of silently leaving them unchanged. - Fix.
rlsbl release scrubno longer force-pushes a stray non-tag refname from the rewrite's tag list — non-refs/tags/refnames are skipped with a warning. Post-rewrite changelog hash validation now always resolves against the scrubbed repository, not the process working directory. - Fix.
rlsbl monorepo statusno longer shows "no changelog" for releasable member packages — the Unreleased column now reads the releasable-level CHANGELOG.md. - Fix.
rlsbl release retryfor releasable members now keepsretry.tomlunder the releasable's releases directory instead of the member package's.rlsbl/releases/. - Fix. Successful releases no longer leave a stale
in-progress.jsonbehind when--watchis used, which previously blocked the next release with 'a previous release is in progress'.
#0.93.0
Migrated all boolean CLI flags to explicit-default style via strictcli.
Context
All bool flags now use --flag/--no-flag syntax with explicit defaults. Multiple renames: --no-commit to --auto-commit, --no-tag to --auto-tag, --no-resolve to --validate-hashes, --force to --force-overwrite on scaffold. --watch and --allow-dirty are now required flags with no default.
#Breaking
- Breaking: Multiple flag renames:
--no-committo--auto-commit,--no-tagto--auto-tag,--no-resolveto--validate-hashes,--forceto--force-overwriteon scaffold.--watchis now required (must pass--watchor--no-watch).--allow-dirtyand--hardare now required.--forceon deploy deleted. All bool flags require explicit defaults.
#0.92.0
Dispatch preid inputs, PR-mode resume fix, end-to-end pre-release integration test
#Features
- Dispatch workflow. Release dispatch now supports all bump types (prerelease, hotfix) and pre-release identifiers (alpha, beta, rc, stable) as workflow_dispatch inputs.
#Fixes
- PR-mode resume. PR-mode release resume now correctly preserves release_mode in the state file, preventing PR-mode releases from resuming as imperative.
#0.91.1
Fix critical pre-release wiring bugs and CI template pre-release awareness
#Fixes
- Fix.
preidnow correctly wired throughcompute_release_versiontobump_version. Pre-release versions (e.g.,0.43.0-alpha.0) are now produced correctly during releases. - Fix. CI publish templates now handle pre-release versions. npm templates add
--tag {preid}for pre-releases. Docker template skips:latesttag for pre-releases. - Fix. Release group help text updated to list all 8 subcommands including
resumeandscrub.
#0.91.0
Pre-release channels, PR-based release flow, idempotent release resume, registry version detection, and remote release dispatch.
Context
This release adds three major capabilities: (1) pre-release channel support with PEP 440 translation and npm dist-tags, (2) PR-based release flow as an alternative to direct push, and (3) idempotent release resume for recovering from mid-release failures. Also adds registry version drift detection and a workflow_dispatch template for remote release triggers.
#Features
- Idempotent release flow. New
rlsbl release resumecommand. In-progress state file tracks completed steps; per-step guards enable safe resumption after mid-release failures. - Pre-release channel support.
preidfield in release TOML,bump = "prerelease"type, PEP 440 translation for PyPI, npm--tagdist-tags, Docker:latestskip, GitHub--prereleaseflag, and changelog consolidation for stable releases. - PR-based release flow.
release.mode = "pr"in config creates arelease/vX.Y.Zbranch and PR instead of pushing directly. Finalize workflow dispatches publish after merge. PR-mode undo closes the PR and deletes the release branch. - Registry version detection.
rlsbl status --registryqueries npm, PyPI, Go, and crates.io for published versions and shows drift between local and registry versions. - Remote release trigger.
release-dispatch.ymlscaffold template withworkflow_dispatchinputs for--bumpand--description.
#Fixes
- Partial push rollback fix. Tag push failure no longer rolls back already-pushed commits.
#0.90.2
Fix test mocks for Python 3.14 compatibility
Context
ensure_github_topic and watch run_cmd migrated from run() to run_gh() but test mocks still targeted run(), causing failures on Python 3.14 CI
#Fixes
- Bug fix. Fixed watch test mocks for run_gh migration in TestRePoll.
#0.90.1
Fix tagging test for Python 3.14 compatibility
Context
ensure_github_topic migrated from run() to run_gh() but the test mocks still targeted run()
#Fixes
- Bug fix. Fixed test mock for run_gh migration in tagging module.
#0.90.0
Quick bump shortcut, run_gh centralization, changelog preflight unification
Context
Three major improvements in this release:
- Quick bump shortcut:
rlsbl release run --bump patch --description "Fix X"allows simple releases without creating a release file first. The release file ceremony remains available for complex releases.
- run_gh centralization: All 33 repo-scoped
ghCLI invocations now go through a singlerun_gh()function, replacing the oldgh_env()pattern. This provides a cleaner API with consistent error handling and environment setup.
- Changelog preflight unification: Changelog validation is now unified into the preflight check system via
app.run_checks(tag_expr="preflight-changelog"), eliminating duplicate validation logic between the release flow and the check system.
Also includes fixes for get_push_timeout crashing with config=None, uv.lock tracking, and a load_workspace scoping bug.
#Features
- **New:
run_gh()centralized GitHub CLI wrapper.** All 33 repo-scopedghinvocations now go throughrun_gh(), replacing the oldgh_env()pattern with a cleaner API. - New: Changelog validation unified into preflight check system.
app.run_checks(tag_expr="preflight-changelog")eliminates duplicate validation between release flow and check system. - New: Quick bump shortcut.
rlsbl release run --bump patch --description "Fix X"bypasses the release file ceremony for simple releases.
#Fixes
- Fix:
get_push_timeoutno longer crashes whenconfig=None. - Fix:
uv.lockis now tracked in git (was incorrectly gitignored). - Fix:
load_workspacescoping bug in release flow — local imports no longer shadow module-level import.
#0.89.0
Preflight check integration and unreachable-code false positive fix
Context
Release flow now delegates test and lint validation to the check system via app.run_checks(tag_expr="preflight"), eliminating duplicate wrapper functions that historically diverged and caused release failures. Unreachable-code lint rule no longer treats inline comments as statements.
#Features
- Release preflight checks via check system. The release flow now runs test-suite and library-lint checks via
app.run_checks(tag_expr="preflight")instead of duplicate wrapper functions, with per-member iteration for releasable releases.
#Fixes
- Unreachable-code false positive fix. Tree-sitter comment nodes are no longer treated as statements, fixing false positives and false negatives in the unreachable-code lint rule.
#0.88.1
Fix release lint to respect lint_allow from workspace.toml
#Fixes
- Bug fix. Release lint now respects lint_allow from workspace.toml, matching the behavior of rlsbl check --tag quality.
#0.88.0
Configurable check timeout, centralized GitHub repo resolution with SSH alias support, push-access verification, library lint exceptions, and stderr surfacing for failed commands
Context
This release consolidates several reliability and usability improvements. The run_gh() migration replaces scattered remote-URL parsing across all commands with a single utility that handles SSH aliases and the github_repo config key. Push-access verification catches GH_TOKEN mismatches before the release flow runs. Library lint exceptions (lint_allow) support per-package overrides for forbidden-import checks. Configurable check timeout lets projects with slow test suites raise the default 120s limit.
#Features
- Configurable check timeout.
RLSBL_CHECK_TIMEOUTenv var andcheck_timeoutconfig key let you control test and lint timeouts (default remains 120s). - Centralized GitHub repo resolution. New
github_repoconfig key and automatic SSH alias handling for allghcommands, replacing scattered remote-URL parsing with a singlerun_gh()utility. - Centralized GitHub repo resolution. Migrated yank, undo, release-retry, watch, PR, and check commands to
run_gh(). - Centralized GitHub repo resolution. Migrated checks, tagging, and private-repo detection to
run_gh(). - Push access verification.
validate_gh_push_accesschecks the authenticated user has push access before releasing, with a hard error and actionable message on mismatch. - Library lint exceptions.
lint_allowfield inworkspace.tomlfor per-package forbidden-import exceptions (e.g., allowing a library to import its own test utilities).
#Fixes
- Stderr surfacing. Push and GitHub Release creation failures now show the actual error message from
gh/git pushinstead of a generic failure.
#0.87.0
Ruff integration, unreachable-code detection, non-workspace test runner, and validate_release_targets union fix
Context
Added ruff as a dev dependency with ruff-lint quality check. Built custom unreachable-code detection in the Python AST linter using tree-sitter. Restructured the pypi test runner to probe where pytest is declared and build the correct uv run invocation per project (workspace member vs standalone). validate_release_targets now validates against the union of all releasable member targets. Removed silent error swallowing in member version sync and dead-code convention fallback.
#Features
- New feature. Unreachable code detection in the Python AST linter -- flags code after unconditional return/raise and after exhaustive if/elif/else blocks.
- New feature.
detect_uv_workspace_rootutility for detecting whether a project is a uv workspace member. - New feature.
ruff-lintquality check runs ruff on the project duringrlsbl check --tag quality. Skips gracefully when ruff is not installed. - New feature. Pypi test runner detects where pytest is declared per project (dependency groups, optional extras, uv dev deps) and builds the correct
uv runinvocation. Hard error when pytest is not declared. Workspace members preserve existing behavior.
#Fixes
- Bug fix. Config read errors in member version sync now propagate to the release rollback handler instead of being silently skipped.
- Bug fix.
detect_python_package_rootno longer returns a nonexistent convention path -- returnsNonewhen no package directory exists on disk. - Maintenance. Removed unused imports and variables across the codebase (ruff F401/F841 cleanup).
- Bug fix.
validate_release_targetsnow validates against the union of all releasable member targets, not just the representative member.
#0.86.0
AST-based __version__ bumping, dunder-version-missing check, and releasable test runner fix
Context
The __version__ bumping system was rewritten from regex to AST parsing, eliminating an entire class of missed version bumps (typed annotations, pre-release strings). A new dunder-version-missing check catches the pgdesign bug class where a version constant exists under the wrong name. detect_python_package_root gained uv build-backend and src/ layout support. The releasable test runner now dispatches the correct test command per member target type.
#Features
- New feature.
detect_python_package_rootnow supports uv build-backendmodule-rootconfig,src/layout filesystem detection, and raises a hard error on ambiguous package layouts. - New feature.
__version__bumping rewritten with AST parsing — now correctly handles typed annotations (__version__: str = "1.0.0") and pre-release version strings. - New feature. New
dunder-version-missingproject check detects pypi targets where__init__.pyhas a version-like constant (e.g.,VERSION = "1.0.0") but no__version__.
#Fixes
- Bug fix. Releasable test runner now detects the correct target type per member package instead of using the releasable-level registry for all members.
- Bug fix. Member package version sync errors now propagate to the release rollback handler instead of being silently swallowed as warnings.
#0.85.0
Hotfix release type, TYPE_CHECKING import detection, ci-synced dev_node skip, root .rlsbl coexistence guard.
#Features
- New feature.
if TYPE_CHECKING:imports are now excluded from bothdeps-undeclaredanddeps-unusedchecks, eliminating false positives for typing-only imports. - New feature.
bump = "hotfix"release type for infrastructure-only releases. Bypasses the user-facing entry requirement, generates a### Hotfixchangelog section from the release description, and errors if user-facing entries are present. - New feature.
root-rlsbl-conflictcheck errors when both.rlsbl/and.rlsbl-monorepo/exist at the workspace root. Scaffold now skips entirely at workspace roots.
#Fixes
- Fix.
workspace-ci-syncedcheck no longer fails for dev_node projects missing CI workflows.
#0.84.0
Go companion tags, workspace root CI fix, uvNoSources lint, BaseTarget fix.
#Features
- New feature. Automatic Go companion tags during releasable releases. When a releasable contains non-private Go packages,
{path}/v{version}tags are created alongside the primary releasable tag for Go module proxy compatibility. - New feature. Release rollback and
release undonow clean up companion Go tags alongside the primary release tag. - New feature.
go-companion-tagsworkspace check validates that Go releasable members have companion tags for the current version.
#Fixes
- Fix.
BaseTarget.template_vars()returnsTemplateVarsinstead of plain dict, ensuring auto-namespacing works for all targets including those inheriting the base implementation. - Fix.
{{#if uvNoSources}}conditional in pypi CI template now uses namespaced form{{#if pypi.uvNoSources}}for consistency with all other template conditionals. - Fix. Scaffold no longer generates CI workflows at monorepo workspace roots. The ci-router already handles per-package CI; root-level CI with import checks always failed.
#0.83.1
Fix release finalization leaving deleted unreleased.toml uncommitted.
#Fixes
- Fix. Release finalization now properly commits the deletion of
unreleased.tomlafter archiving, preventing a dirty working tree.
#0.83.0
Remove publish.json, releasable-owned targets, no empty release files, monorepo release subgroup.
Context
Breaking changes: publish.json config file removed (unused by all projects, 4-level inheritance simplified to 2-level). Release finalization no longer creates empty unreleased.toml. Monorepo release commands restructured into a subgroup (monorepo release -> monorepo release run, release-init -> release init, release-order -> release order). In explicit releasable mode, targets are releasable-owned and cannot be overridden by per-package config.
#Breaking
- Breaking. Removed
publish.jsonconfig file support. All config fields now live inconfig.jsononly. The 4-level inheritance chain is now 2-level (releasable config.json -> per-package config.json). Themigrate-publish-configcommand is removed. - Breaking. Release finalization no longer creates an empty
unreleased.toml. Runrelease initormonorepo release initto scaffold the next release file. - Breaking. Monorepo release commands restructured:
monorepo release->monorepo release run,monorepo release-init->monorepo release init,monorepo release-order->monorepo release order.
#Features
- New feature. In explicit releasable mode, targets are owned by the releasable — per-package
targetsconfig cannot override the releasable's targets. Fixesrelease initfailing to detect targets when members havetargets: [].
#0.82.1
TemplateVars auto-namespacing, check-name polish, template fixes.
#Features
- New feature.
TemplateVarsdict subclass auto-generates namespaced template variable keys, ensuring all scaffold modes (single-target, multi-target, monorepo sync) resolve variables consistently.
#Fixes
- Fix.
check-name --target githubno longer shows redundant 'GitHub repos' in the Checked summary. - Fix. Monorepo
check-names --targethelp text now includesgithub.
#0.82.0
Check system scope adapter, detect_targets config inheritance, template var hard errors, workspace-targets fix, check-name --target github, batch release bug fixes.
Context
Major architectural changes: declarative scope adapter for the check system (requires strictcli 0.21.0), detect_targets() now uses 4-level config inheritance with a two-tier rule (missing targets key is a hard error when config.json exists), and unresolved template variables are now hard errors instead of silent pass-through.
Breaking: projects with .rlsbl/config.json that lack an explicit "targets" key will now get a hard error with auto-detected hints. Template vars that previously survived as literal {{varName}} in output files will now error at scaffold/sync time.
#Breaking
- Unresolved template variables are now hard errors. Scaffold and sync operations raise
ConfigErrorinstead of emitting a warning when template variables cannot be resolved. Namespace mismatches in cargo/go/npm templates fixed; optional variables wrapped in{{#if}}blocks.
#Features
- Release init warning for explicit-mode monorepos.
release initnow warns when run in a monorepo with[[releasables]], directing users tomonorepo release-initinstead. - **
check-name --target githubsupport.** GitHub is now a first-class target for name availability checks, showing repo count and org-scoped availability. - **
detect_targetsconfig inheritance with two-tier rule.** Target detection now supports 4-level config precedence viaread_project_config(releasable config dir for monorepo inheritance). Two-tier rule: no config.json means auto-detect from manifests; config exists withouttargetskey raisesConfigErrorwith hints. - Scope adapter for check system. Checks now declare scope tokens (e.g.
workspace,non_dev_only,library) inchecks.toml, and a scope adapter pre-filters context before check functions run. Removesisinstanceboilerplate and inline filtering from all check implementations. Also adds a newscaffold-gitignore-stalecheck that warns when project.gitignorefiles are missing rlsbl-specific entries.
#Fixes
- Batch release fix. Skip redundant environment validation (gh auth, clean tree, branch/remote checks) per sub-project in batch release mode -- the batch orchestrator already validates upfront.
- **
check-nameno longer leaks GitHub API calls.** Previously, every npm/pypi/go name check also ran an unconditional GitHub lookup. GitHub checks now only run when--target githubis specified. - **
workspace-targetscheck improvements.** Skipdev_onlyandreleasable=falseprojects in per-project target validation. Add union-per-releasable verification: each releasable must have at least one target across all its member packages. - Fix.
sync_workspaceskipsuv syncwhen nopyproject.tomlexists at workspace root, fixing pre-push failures in non-Python monorepos.
#0.81.7
Add 120s subprocess timeouts to Maven lint and all test runners
#Fixes
- Add subprocess timeouts. Maven lint and all test runners now have 120-second timeouts, preventing the release pipeline from hanging indefinitely on slow or stuck subprocesses.
#0.81.6
Fix PyPI scanner false positives for prefixed package names
#Fixes
- Fix PyPI scanner false positives.
deps-undeclaredno longer reports false positives when a workspace project's PyPI name differs from its workspace name (e.g.,orxtra-transportvstransport).
#0.81.5
Fix CI: autouse saferm mock for scaffold tests
#Fixes
- Fix CI test failures. Autouse saferm mock fixture applied to all scaffold tests.
#0.81.4
Fix CI: mock saferm in all scaffold tests
#Fixes
- Fix CI test failures. All scaffold tests now mock saferm for CI environments.
#0.81.3
Fix CI: mock saferm in scaffold orphan tests
#Fixes
- Fix CI test failure. Scaffold orphan detection tests now mock saferm for CI environments.
#0.81.2
Fix coverage check for releasable monorepos, Maven lint support, gradle lockfile sync, batch_limits inheritance
#Features
- Maven lint support.
lint_library()now runs detekt, checkstyle, or./gradlew checkfor maven library projects via the newMavenLinterclass.
#Fixes
- Fix coverage check ignoring releasable JSONL entries.
rlsbl check --tag changelogfrom the workspace root now correctly reads releasable-level changelogs instead of falling back to per-package paths. Also prevents crash on implicit-mode workspaces. - Fix gradle lockfile sync. Lockfile sync now checks for
./gradlewin the project directory instead of requiringgradleon PATH. - Batch limits inherit from releasable config.
rlsbl changelog addnow respects releasable-levelbatch_limitssettings, and auto-created exclusions are written to releasable-level config when in releasable mode. - Fix phantom hashes.json entries. Scaffold no longer leaves stale entries in hashes.json for config files removed by the releasable config skip.
#0.81.1
Fix CI test failure: mock saferm in migration tests
#Fixes
- Fix CI test failure. Migration tests now mock saferm for CI environments where it is not installed.
#0.81.0
Config-driven hooks, releasable config inheritance, Maven Central publishing, JVM monorepo support, Go strictcli detection, and migration CLI
Context
This release completes 9 phases of work spanning the full plan: release-blocking bug fixes (lockfile sync, changelog scope, selfdoc dirty tree), Go strictcli detection for 9 projects, releasable-level config inheritance with 4-source precedence, config-driven hooks replacing bash scripts (breaking), migration CLI for releasable model adoption, Maven Central publishing via the vanniktech plugin, JVM dependency graph and import scanning, Gradle version catalog support, and extended artifact cleanup.
Hook scripts are deprecated in favor of config entries. Existing customized scripts still work with a migration warning. New projects will not have hook scripts scaffolded.
#Breaking
- Config-driven hooks replace bash scripts. Hook commands are now defined in config.json with dual syntax (string shorthand or structured objects with
cmd/dir/env). Hook template scaffolding removed. Existing customized scripts still work with a migration warning.
#Features
- Go strictcli detection.
detect_strictcli()now finds Go projects using the strictcli library, scanninggo.modand entry points. Schema dump runsgo runinstead ofuv runfor Go projects. - Releasable-level config inheritance. Per-package config is now merged on top of releasable-level defaults at load time. Scaffold skips writing per-package config.json/publish.json when identical to the releasable config.
- **
rlsbl monorepo migrate-releasablecommand.** One-command migration to the releasable model: consolidates changelogs and versions, creates migration tags, cleans up per-package artifacts (hooks, bases, lint, CHANGELOG.md, version, redundant config). - Maven Central publishing support. New
maven-centralpipeline type delegating to the vanniktech Gradle plugin. Publish workflow template with GPG signing. POM metadata validation check. Maven test execution (./gradlew test/mvn test). CI templates updated to Java 25, setup-java@v5, setup-gradle@v6. - JVM monorepo support. Gradle/Maven dependency graph parsing for
monorepo impactand release ordering. Java and Kotlin import scanning for dependency validation checks.read_metadataimplemented for maven targets. - Gradle version catalog support.
read_version/write_versionwork withlibs.versions.tomlvia explicitversion_catalog_keyconfig. Build step (./gradlew build/mvn package), lint detection (detekt/checkstyle), andgradle.lockfilesync added for maven targets.
#Fixes
- Workspace root lockfile sync. Release flow now syncs lockfiles at the workspace root for uv, npm, and Go workspaces after version bumps. Go workspace
go.work.sumis handled separately withgo work sync. - Fix multi-releasable changelog scope. Changelog validation in release flow now correctly scopes to the releasable's member projects instead of checking all commits in the repo.
- Commit selfdoc outputs during release. Selfdoc-generated files are now committed immediately after
selfdoc genwith anAutogenerated: truetrailer, preventing dirty-tree failures on release retry.
#0.80.0
Batch release improvements: monorepo-wide lock, upfront validation, --watch support, dry-run summary
#Features
- Batch monorepo-wide lock.
rlsbl monorepo releaseacquires a workspace-level lock before the batch loop, preventing concurrent batch releases from interleaving. - Upfront validation for batch release. gh auth, clean tree, and branch checks run once before releasing any package, failing fast on misconfiguration.
- **
--watch/--no-watchfor monorepo release.** Watches CI for the last release's commit after the entire batch completes. Per-package watch hints suppressed during batch. - Dry-run summary for batch release. Shows each releasable's bump type and description in release order.
#Fixes
- **Fix:
uv build --out-dir distin publish template.** Ensures build artifacts land in the expected directory for PyPI upload. - Fix: shallow clone test failure in CI.
#0.79.2
Fix migration consolidation bugs, CI template variable, and shallow clone test failure
#Fixes
- Fix: migration consolidation bugs.
consolidate_changelogs()now deduplicates cross-package entries, auto-creates batch exclusions for oversized entries, and creates a consolidation tag to prevent scope expansion failures. - **Fix: unreplaced
minRequiredPythonin CI template comment.** Template now uses un-namespaced variable name that works in both single and multi-target scaffold. - Fix: status command test failure in CI shallow clones.
#0.79.1
Raise CI coverage threshold to 90% with 4873 tests at 91.78% coverage
#Features
- CI coverage threshold raised to 90%. Test suite expanded to 4873 tests with 91.78% coverage.
#0.79.0
Fix publish workflow releasable tags, UV_NO_SOURCES for monorepos, namespace package scanning
Context
Three gaps discovered during first real-world use of the releasable model: (1) publish workflow ignored releasable tags, (2) UV_NO_SOURCES broke monorepo CI for workspace-source projects, (3) deps-unused produced false positives for namespace packages. Also adds template engine conditionals, extract/absorb CLI commands, migrate-publish-config command, and removes implicit releasable mode (monorepos must define [[releasables]]).
#Breaking
- Remove implicit releasable mode. Monorepo workspaces must define
[[releasables]]in workspace.toml. Hard error if missing.
#Features
- Template engine conditionals.
{{#if varName}}...{{/if}}blocks for conditional template content. - **New command:
rlsbl migrate-publish-config.** Splits publishing fields from.rlsbl/config.jsoninto.rlsbl/publish.json. - **New commands:
rlsbl monorepo extract,absorb,extract-releasable.** First-class operations for moving packages in and out of monorepos.
#Fixes
- Fix: publish workflow releasable tags. Monorepo publish router uses releasable tag prefixes instead of per-project tags in explicit mode.
- Fix: UV_NO_SOURCES omitted for monorepo members. CI template conditionally includes
UV_NO_SOURCESonly for projects with path-based sources. Monorepo sync strips it as a safety net. - Fix: namespace package import scanning.
deps-unusedcorrectly detects workspace imports through namespace packages (e.g.,from orxt.protocols import X). Auto-discovers namespace-to-project mapping from source layout.
#0.78.0
Releasable model: groups of packages sharing a version, changelog, and release lifecycle
Context
Introduces the releasable model — a ground-up redesign of monorepo versioning. Instead of per-package versioning (N versions, N changelogs, N tags), packages can be grouped into releasables that share a single version, changelog, and release lifecycle. The model covers the full stack: data model, version management, changelog scoping, release flow, hooks, scaffold, migration tooling, extract/absorb operations, and single-project unification.
Breaking: dev_node is split into dev_only (boundary guardrail) and releasable = false (not versioned). The --dev-node CLI flag is renamed to --dev-only. The dev-node-boundary check is renamed to dev-only-boundary.
#Breaking
- dev_node split.
dev_nodeis now two independent concepts:dev_only(boundary guardrail preventing runtime dependencies) andreleasable = false(not versioned). The--dev-nodeCLI flag is renamed to--dev-only. Thedev-node-boundarycheck is renamed todev-only-boundary.
#Features
- Releasable model. Packages can be grouped into releasables sharing a version, changelog, and release lifecycle. Define
[[releasables]]in workspace.toml and assign packages withreleasable = "name"orreleasable = false. - Per-releasable version files. In explicit mode, each releasable stores its version at
.rlsbl-monorepo/releasables/{name}/version. Version consistency checks treat this as the source of truth. - Per-releasable changelog. In explicit mode, changelogs live at
.rlsbl-monorepo/releasables/{name}/changes/. Commit coverage, validation, and CHANGELOG.md generation are scoped to releasables. Entries gain an optionalpackagesfield for per-package attribution. - Publishing config separation. Publishing-related fields (
targets,private,pipelines,push_timeout,tag) can now live in.rlsbl/publish.jsonseparate from.rlsbl/config.json. Hard error if both files have publishing fields. - Release flow on releasables.
rlsbl release runcreates one commit and one tag per releasable. Batch release iterates releasables in dependency order. Release file supports[releasables.name]sections. Undo, retry, yank, and edit commands adapted. - Per-releasable hooks. Two-level hook system: per-releasable hooks at
.rlsbl-monorepo/releasables/{name}/hooks/plus per-package hooks. Tests and lint aggregate across all member packages. - Per-package cleanup utilities.
cleanup_per_package_release_state()removes dead.rlsbl/changes/and.rlsbl/releases/for releasable members.verify_minimal_rlsbl()checks for unexpected files. - Scaffold releasable directories. In explicit mode,
rlsbl scaffoldandrlsbl monorepo synccreate per-releasable directory structure. Per-package changelog infrastructure is skipped for releasable members.monorepo addgains--releasableflag. - Migration tooling.
detect_migration_state()analyzes workspace readiness.consolidate_changelogs()merges per-package changelogs into per-releasable.consolidate_versions()unifies versions.create_migration_tag()creates releasable-format tags. - Standalone releasable model. Single-project repos internally use the releasable abstraction. Optional
.rlsbl/releasable.tomlfor explicit configuration. - Extract and absorb.
cmd_extract()extracts a package or releasable from a monorepo into a new repo viagit filter-repo.cmd_absorb()brings an external repo into a monorepo viagit subtree add. Changelog migration included.
#Fixes
- Audit fixes. Release checks use releasable tag format. Prepush checks are releasable-aware. Post-release hooks use multi-level system. Publishing config conflict detection handles edge cases.
#0.77.1
Fix stale commit-level batch exclusion cleanup
Context
clean_stale_exclusions now removes commit-level batch exclusions when all referenced commits have been finalized, matching the existing entry-level cleanup behavior.
#Fixes
- Bug fix. Stale commit-level batch exclusions are now cleaned from
config.jsonduring release finalization, matching the existing cleanup for entry-level exclusions.
#0.77.0
GitError/PostReleaseError exceptions, detached HEAD/shallow clone/push timeout handling, variant delay, dep_validation realpath
Context
Final cleanup round: completed the exception hierarchy (GitError for git infrastructure, PostReleaseError for post-mutation failures), fixed three unhandled edge cases (detached HEAD, shallow clone, push timeout), added rate-limiting delay to variant checks, standardized dep_validation.py on realpath, and triaged the hardening roadmap into focused todos.
#Breaking
- Breaking. Added
GitErrorandPostReleaseErrorexception types. Last 2sys.exitcalls inexecute.pyconverted toPostReleaseError.
#Features
- New feature.
check-namevariant checks now respect the--delayflag for rate-limiting, using sequential checking instead of concurrent threads when a delay is set.
#Fixes
- Bug fix. Detached HEAD now raises a clear
GitErrorinstead of silently returning"HEAD"as the branch name, which caused dangerous behavior inpush_if_neededandrelease undo. - Bug fix. Shallow clones now produce a clear
GitErrorinstead of silently falling back to all-history range. Consolidated duplicate tag discovery functions into a singleget_last_version_tag()inutils.py. - Bug fix. Push timeout now raises
GitErrorwith an actionable message instead of a raw Python traceback. - Bug fix. Standardized
dep_validation.pypath normalization onrealpath, consistent with the rest of the codebase. Fixes potential mismatches with symlinked project directories.
#0.76.0
Split release.py into a package, ExemptionRegistry, path normalization, orphan messaging
Context
The 1985-line release.py monolith was split into 6 modules with clean boundaries. 27 sys.exit calls converted to a proper exception hierarchy. The 24-parameter _run_release_mutating signature replaced with a ReleaseState dataclass. Changelog exemptions centralized into an ExemptionRegistry. Path normalization standardized on realpath. Orphan check messaging improved with entry context.
#Breaking
- Breaking. Split
release.py(1985 lines) into a 6-modulerelease/package:__init__.py(orchestrator),execute.py,validate.py,hooks.py,publish.py,rollback.py. Replaced the 24-parameter_run_release_mutatingsignature with aReleaseStatedataclass. Converted 27sys.exitcalls toReleaseValidationError/HookErrorexceptions.
#Features
- New feature. Centralized changelog exemption logic into
ExemptionRegistrywith ordered predicates and per-rule unit tests. Fixed batch exclusion hash matching (abbreviated hashes now resolve to full SHAs).
#Fixes
- Bug fix. Changelog orphan check now reports entry context (file, line, how many commits are valid) instead of bare hash errors. Detects partially stale entries where all commits are either unresolvable or out of range.
- Bug fix. Standardized path normalization on
realpathacross root discovery, containment checks, and lint exclusion. Fixes potential mismatches when project directories are accessed through symlinks.
#0.75.1
Fix workspace test runner uv sync
Context
The workspace test runner was running uv sync per sub-project instead of once at the workspace root, breaking monorepos with cross-project dependencies.
#Fixes
- Bug fix. Workspace test runner now runs
uv sync --all-packagesat the workspace root instead of per sub-project, fixing monorepo test suites with cross-project dependencies.
#0.75.0
Normalization collision detection, ultranorm by default, claim-name command, exit codes
Context
check-name was reporting names as available when registries would reject them due to normalization collisions (e.g., llmloop vs llm-loop on PyPI, toolstream vs tool-stream on npm). This release fixes the false positives, adds the claim-name command for definitive name reservation, and introduces proper exit codes.
#Breaking
- Breaking. Removed
_normalize_npm_monikerinternal function (replaced bynormalize_npmfromtargets/utils). Removed the--ultranormalized-variantsflag (ultranorm is now always-on for PyPI).
#Features
- New feature.
check-namenow detects normalization collisions as hard errors. Names likellmloopthat collide withllm-loopon PyPI, ortoolstreamthat collides withtool-streamon npm, are correctly reported as taken instead of a soft warning. - New feature. Ultranormalization (visual-ambiguity detection like
c1i/cli) now runs by default for PyPI. The dead--ultranormalized-variantsflag and its misleading tip have been removed. - New feature.
check-nameexit codes now distinguish results: 0 for available, 1 for taken/collision, 2 for error. - New command.
rlsbl claim-name <name> --target npm|pypipublishes a minimal placeholder package to definitively reserve a name on a registry.
#Fixes
- Bug fix.
check-namenpm moniker search failure is now a hard error instead of silently reporting the name as available.
#0.74.1
Fix first release on empty remote
Context
When a project had a configured remote but no commits had been pushed yet, the remote-ahead check would abort because origin/{branch} did not exist. The check now detects this and skips gracefully.
#Fixes
- Bug fix. Release no longer aborts on first push to an empty remote. When
origin/{branch}does not exist after a successful fetch, the remote-ahead check is skipped with an informational message instead of hard-aborting.
#0.74.0
Blog post integration for releases, richer hook environment variables, and assembly support in post-release hooks.
Context
Adds blog = true support in release TOML to auto-generate blog posts via selfdoc during releases. Enriches hook env vars with bump type, previous version, and description. Adds conditional selfdoc assembly push to the post-release hook template for unified documentation deployment.
#Features
- Blog post integration. New
blog = truefield in release TOML generates a blog post during release viaselfdoc post generate --from-release. Supports user-authored body file, changelog integration, and archival. - Richer hook environment. Pre-checks, pre-release, and post-release hooks now receive
RLSBL_BUMP_TYPE,RLSBL_PREV_VERSION, andRLSBL_DESCRIPTIONenvironment variables. - Assembly support in post-release hooks. Post-release hook template now conditionally runs
selfdoc assembly pushwhen assembly config is present.
#0.73.0
Changelog edit command, dev_node release prohibition, config hardening, and coverage reporting
#Breaking
- Breaking. Dev node projects can no longer be released.
rlsbl release runandrlsbl release editnow error with "dev_node projects cannot be released". Removedev_node = truefrom workspace.toml to make a project releasable.
#Features
- New command.
rlsbl changelog editamends entries in released (read-only) JSONL files -- temporarily unlocks the file, appends the entry, re-locks, regenerates CHANGELOG.md, and syncs GitHub Release notes. - New. CI now reports test coverage via pytest-cov.
#Fixes
- Fix. The
privateconfig key is now required -- missing or corrupted config files produce a hard error instead of silently continuing. - Fix. Retry
gh release createwith race-condition detection when the release already exists.
#0.72.0
Silent exception audit, release robustness fixes, dev-node batch guard
Context
Systematic audit replaced silent exception swallowing with explicit warnings or hard errors across all commands. Release undo and create now handle GitHub API failures gracefully. Dev-node projects in batch releases now hard-error instead of silently skipping.
#Breaking
- Dev-node batch release guard.
rlsbl monorepo releasenow hard-errors if a dev_node project is included in a batch release, instead of silently skipping it.
#Fixes
- Silent exception audit. Replaced silent exception swallowing across all commands with explicit warnings or hard errors. Safety guardrails now fail loudly instead of silently bypassing checks.
- Fix CheckResult status. Use 'fail' instead of 'error' to comply with strictcli constraints.
- Graceful release failure handling.
rlsbl release runnow handlesgh release createfailures gracefully instead of crashing. - Undo pre-check.
rlsbl release undonow checks that the GitHub Release exists before attempting to delete it.
#0.71.1
Release undo traceback logging, per-version .md canonical regeneration, test isolation fixes, and CI JUnit XML reporting.
#Fixes
- Fix.
release undoerror handlers now log tracebacks instead of silently swallowing exceptions. - Fix. Per-version
.mdfiles are now regenerated from the archived release.tomlafter finalization, ensuring metadata is sourced from the canonical file.
#0.71.0
Scaffold-conflict check consolidation, scope-aware deps-unused, release finalization fixes, and dev_node layer exemption.
Context
Post-0.70.0 cleanup wave driven by loose ends from the previous session plus cross-session review. The two overlapping scaffold-conflict checks merged into one (paired-marker detection, recursive .rlsbl/ scan, path:line reporting). deps-unused became scope-aware: guarded imports satisfy only optional deps, so a hard dependency imported only inside try/except ImportError is now flagged. Release finalization gained two fixes: backfilled per-version .md files are committed (no more dirty tree after release) and the new version's .md gets description/context from day one. Layers auto-exempt dev_node projects. All 14 selfdoc SEO warnings eliminated. test_check.py migrated off unittest.TestCase.
#Breaking
- Check consolidation. The
scaffold-conflict-markerscheck is removed;scaffold-conflictsabsorbs it: it now scans all of.rlsbl/recursively, reports each conflicted file aspath:line, and no longer false-positives on bare=======lines (e.g. markdown setext underlines).
#Features
- Custom asset build commands are now echoed to stdout before execution, providing an audit trail of the exact shell command run during release asset builds.
- New check.
scaffold-conflictserrors when scaffold-managed files or CI workflows contain unresolved merge conflict markers, blocking releases until resolved.
#Fixes
- Fix.
release runno longer errors whenpush_timeoutis unset; it defaults to 120 seconds as documented. - Fix.
release runre-run after a partial failure could silently overwrite an already-finalized changelog file; it now aborts with a clear error. - Fix. Advisory lock files (.rlsbl/lock) are now gitignored by scaffold, so a crashed release can no longer dirty the working tree.
- Fix.
release undoleft the release file finalized: the bump type, description, and context vanished fromunreleased.tomland a stale read-onlyvX.Y.Z.tomlremained; undo now restores it (preserving any new user content with a warning). - Fix. Releases no longer leave a dirty working tree: backfilled older per-version changelog
.mdfiles are now included in the finalization commit, and the new version's.mdnow contains the release description and context. - deps-unused is now scope-aware. Imports inside try/except ImportError only satisfy optional deps (scope dev/peer); a hard dependency (runtime/explicit) imported only inside a guard is flagged with a dedicated error suggesting to declare it optional or import it unconditionally.
- Fix.
get_push_timeoutnow raisesConfigErrorinstead ofValueErrorfor invalidRLSBL_PUSH_TIMEOUTorpush_timeoutconfig values. - Fix. The
layers-violationscheck now auto-exempts dev_node projects; they no longer need manual layer assignments. - Fix.
rlsbl deploy --dry-runnow displayslocal_stepsbefore remote steps, mirroring execution order. - Docs. Fixed stale references to the old monolithic
checks.pyafter the split into thechecks/package.
#0.70.0
Custom exception hierarchy, checks.py modularization, import scanning fixes, deploy local_steps, and 9 truncation removals.
Context
Codebase investigation surfaced 50 curiosities; 13 were elevated to decisions and executed across 11 phases. The biggest structural changes: 105 bare ValueErrors replaced with 5 typed exception classes, and the 1,978-line checks.py split into a 6-module package. Import scanning got two correctness fixes (guarded imports for deps-unused, root-relative test context detection). The deploy module gained local_steps for pre-SSH commands like cross-compilation. All silent truncation was purged (9 instances). Test suite gained xdist (96s -> 27s), parametrization, and real CI runs.
#Breaking
- Custom exception hierarchy replaces ValueError. rlsbl now raises ConfigError, WorkspaceError, ChangelogError, VersionError, and ReleaseFileError instead of bare ValueError. Code that catches ValueError from rlsbl APIs must update to catch the new types.
- Ultranorm variant cap is now a hard error. check-name with highly-ambiguous names now errors instead of silently warning and continuing with incomplete results.
#Features
- npm CI templates handle missing lockfiles. Templates now use npm ci when a lockfile exists and fall back to npm install when absent. Zero-dependency wrapper packages are auto-detected and skip CI generation.
- npm wrapper improvements. bin/cli.js now verifies Python >= 3.11, checks rlsbl is installed (with clear error messages), and supports Windows via python3/python/py fallback.
- Deploy: local_steps field. Deploy targets can now run local commands (cross-compilation, file transfers) before SSH-based remote steps.
- target-count selfdoc directive. New directive dynamically generates the release target count with a link to the targets documentation page.
#Fixes
- Remove silent data truncation. Changelog commit messages and batch exclusion reasons are no longer silently truncated at 72/60 characters.
- Remove display truncation caps. Unreleased subjects, pre-push missing commits, OTA native files, orphan hashes, and discover descriptions now show full content instead of capping at arbitrary limits.
- Changelog regeneration preserves release metadata. Descriptions and context from archived release .toml files are now read during CHANGELOG.md regeneration, fixing data loss on subsequent regenerations.
- deps-unused no longer flags guarded imports. Imports inside try/except ImportError blocks now count as used, fixing false positives for optional dependencies.
- Test context detection uses root-relative matching. src/test/ is no longer falsely classified as test code. Added testdata/ and integration_test/ patterns.
- BFS and topological sort use correct data structures. Workspace graph BFS uses deque (O(1) dequeue) and topological sort uses heapq instead of re-sorting.
- Spec docs corrected. Fixed incorrect claim that spec target has no ci_templates.
- Deploy docs corrected. Fixed config key from deploy_targets to deploy.
#0.69.2
Deno publish idempotency, orphan sweep script
#Features
- Feature. Deno publish template is now idempotent with JSR version-exists pre-check.
#0.69.1
Scrub tag matching, cmd_amend fix, strictcli public API, todo cleanup
#Fixes
- Fix. Release scrub correctly identifies which project a monorepo tag belongs to using prefix matching instead of version-only lookup. Also caches workspace loading (5 calls reduced to 1).
- Fix.
changelog amenduseswritable_jsonlcontext manager, preventing file permission leak if duplicate commit check exits early.
#0.69.0
Managed-files registry, lint config filtering, publish idempotency, monorepo path exemption
#Features
- Feature. Scaffold now tracks generated files in
.rlsbl/managed-files.jsonand automatically deletes orphans on re-scaffold. Hash comparison protects user-modified files. Separate fromhashes.json(change detection). - Feature. Scaffold only generates lint configs for declared targets (pypi→python.toml, npm→npm.toml, go→go.toml). Unused configs are orphan-detected on next scaffold.
- Feature. All publish workflow templates are now idempotent (safe to retry). PyPI uses
skip-existing, npm/cargo/hex use version-exists pre-checks.
#Fixes
- Fix.
.rlsbl-monorepo/paths are now auto-exempted from changelog coverage requirements.
#0.68.4
scaffold --force preserves gitignore entries
#Fixes
- Fix.
scaffold --forceno longer destroys user-added .gitignore entries. The additive merge (only add, never remove) now applies regardless of--force.
#0.68.3
CI template UV_NO_SOURCES fix
#Fixes
- Fix. CI template sets
UV_NO_SOURCES=1env var at workflow level, covering bothuv syncanduv runcommands.
#0.68.2
CI uses --no-sources for portable uv sync
#Fixes
- Fix. CI template uses
uv sync --no-sourcesto ignore local path overrides and resolve from PyPI in CI. Works correctly in both standalone projects and monorepo workspaces. - Fix.
test-suitecheck skips gracefully at workspace root instead of hard-erroring.
#0.68.1
Per-target CI files, YAML indent fix, monorepo sync update
#Features
- Feature. Multi-target projects now generate per-target CI workflow files (
ci-pypi.yml,ci-go.yml, etc.) instead of a singleci.ymlfrom the primary target. npm wrapper packages (no test script) skip CI generation entirely. Oldci.ymlis automatically cleaned up on re-scaffold. - Feature. Monorepo sync handles per-target CI files: globs
ci-*.ymlfrom sub-projects, syncs each with project-name prefix, cleans up stale files.
#0.68.0
release scrub command, V4 hook removal
#Breaking
- Breaking. Removed
pre-push-checkcommand. Runrlsbl scaffoldto upgrade your hook to the current format (rlsbl check --tag prepush).
#Features
- Feature. New
rlsbl release scrubcommand wraps safegit scrub with automatic release metadata cleanup: remaps JSONL changelog hashes, regenerates CHANGELOG.md, force-pushes rewritten history, and recreates GitHub Releases for affected tags. Includes error recovery via scrub-result.json.
#Fixes
- Fix. Merged publish.yml now uses correct 6-space indentation for GitHub Actions steps, matching the standard 2-space-per-level convention. Previously, multi-pipeline projects got 4-space indentation due to missing ruamel.yaml indent configuration.
#0.67.1
V4 shim workspace fix, check-name multi-target, zero SEO warnings
#Features
- Feature. Comprehensive documentation update: rewrote pre-push hook section, added test-suite-workspace docs, monorepo pre-push behavior, deps-unused note, and eliminated all SEO warnings (was 104, now 0).
#Fixes
- Fix. Deprecated pre-push hook (old format) now creates proper workspace context at monorepo root, enabling all prepush checks including
test-suite-workspace. - Fix.
check-name --targetnow supports multiple values. Passing--target npm --target pypichecks both registries instead of silently ignoring the first. Invalid target names produce a hard error.
#0.67.0
Monorepo pre-push support, test-suite-workspace check, release rollback cleanup
#Features
- Feature. New
test-suite-workspacecheck runs tests for affected workspace projects during pre-push. Detects changed files from push refs, maps to projects via watch patterns, skips dev_node projects.
#Fixes
- Fix. Pre-push hook now works from monorepo root directory.
test-suitecheck hard-errors in workspace context with a clear message directing totest-suite-workspace. - Fix. Release rollback now cleans up orphaned generated files (versioned JSONL, markdown, release TOML) that previously blocked retry with 'working tree not clean'.
#0.66.4
Dynamic check-count directive, quality count fix, exclusion cleanup integration test
#Features
- Feature. New
check-countselfdoc directive auto-generates the check count from checks.toml, eliminating manual count maintenance in docs.
#0.66.3
deps-undeclared respects try/except ImportError for optional imports
#Fixes
- Fix.
deps-undeclaredcheck no longer flags imports insidetry/except ImportErrorortry/except ModuleNotFoundErrorblocks. These are standard Python optional-dependency patterns and are now correctly recognized as intentionally unguarded.
#0.66.2
Auto-cleanup stale exclusions, DRY refactor manual-warning, hook V5 e2e test
#Features
- Feature. Stale batch_limits exclusions (referencing
version: unreleased) are automatically removed from config.json during release finalization.
#0.66.1
Fix uv sync stripping pytest in workspaces, add workspace-unbuildable check
#Features
- Feature. New
workspace-unbuildablecheck detects workspace members with broken build configs beforeuv sync --all-packagesfails at release time.
#Fixes
- Fix.
uv syncnow uses--all-packagesto prevent stripping pytest from workspace member venvs. Fixes test runner falling through to system Python in uv workspaces.
#0.66.0
Prepush check system: test-suite check, version-tag skip removal, hook V5
Context
Major overhaul of the pre-push hook system. All pre-push logic migrated into the rlsbl check system under a new 'prepush' tag. The hook now runs 'rlsbl check --tag prepush' with 4 checks (changelog coverage, gitignore guard, manual-push warning, test-suite). Version-tag skip removed -- checks always run. Shared testing.py module extracted from release flow.
#Features
- Feature. New
prepushcheck tag with 4 checks:prepush-changelog-coverage,prepush-gitignore-guard,prepush-manual-warning, andtest-suite. Pre-push hook now runsrlsbl check --tag prepushinstead of the deprecatedrlsbl pre-push-check. Version-tag skip removed -- all checks always run. Fast-fail ordering viadepends_on: test-suite skips if changelog coverage fails. - Feature. Extracted shared test-running logic to
rlsbl/testing.py. Release flow and test-suite check both use the same function, eliminating duplication. - Feature. Updated documentation for prepush check system, fixed stale check counts.
#0.65.4
Fix CI test failure from --yes flag in safegit commit calls
#Fixes
- Fix. Updated commit command tests to expect the new
--yesflag in safegit calls.
#0.65.3
safegit --yes, doc gaps, SEO fixes, retry dedup test
#Features
- Feature. Passes
--yesto safegit commit calls, future-proofing against interactive prompts in safegit. - Feature. Documentation expanded: glob pattern reference for layers, user-level config docs, flag-to-config mapping table, enriched module docstrings for SEO, and expanded section intros across 5 pages.
- Feature. Added concurrent retry deduplication test verifying only one retry fires per workflow name when multiple runs fail in parallel.
#0.65.2
Watch retry dedup, no-runs exit 0, docs polish
#Features
- Feature. Documentation expanded: new utility commands guide, fixed pipeline per-type inaccuracies, added Flutter release config and deploy env var expansion docs, improved section intros for AI citation.
#Fixes
- Fix. Watch auto-retry now deduplicates by workflow name, preventing multiple retries when parallel runs from the same workflow fail. Watch exits 0 (not 1) when no CI runs are found, since the release already succeeded.
#0.65.1
Fix CI test failure from batch hint message
#Fixes
- Fix. Batch size check test updated to account for hint message in failure details.
#0.65.0
Documentation overhaul and notification UX fix
Context
Comprehensive docs rewrite (13 new guide pages, 2600+ lines), notification click-to-open fix, pipeline env var validation ordering fix, and new --allow-batch flag for changelog add.
#Features
- Feature. Comprehensive documentation overhaul: 13 new guide pages covering release workflow, changelog, scaffold, checks, deploy, dev workflow, import scanning, dependency validation, pipelines, layers, and native targets. Expanded targets, monorepo, and configuration references. Fixed stale check counts.
- Feature.
changelog add --allow-batchflag auto-creates a batch_limits exclusion when an entry exceeds the commit limit, preventing agents from splitting cohesive entries. Batch size check now runs at add-time (not just during release validation).
#Fixes
- Fix. Desktop notification no longer auto-opens browser unconditionally; URL opens only when the user clicks the notification action button.
- Fix. Local pipeline env var validation now runs before mutating operations, preventing partial state on missing credentials.
#0.64.2
Fix Docker metadata false positive in scaffold check, add SSH host validation for subtree remotes.
#Features
- New feature. Validate that subtree remote SSH hosts match the origin remote host, catching misconfigured mirror URLs early.
#Fixes
- Fix. Exclude Docker metadata-action
type=semver,pattern=lines from thescaffold-unreplaced-varscheck. These lines use{{version}},{{major}},{{minor}}, and{{patch}}as Docker's own template syntax, not unreplaced rlsbl variables.
#0.64.1
Fix dead-modules false positives and watch test notification leak.
#Features
- New. Auto-retry failed CI workflows once before reporting failure.
- New. Open GitHub Actions page on CI failure and Release page on success when watch notifications fire.
#Fixes
- Fix. Exclude
.selfdoc,_build, and browser asset directories from dead-modules scan to prevent false positives. - Fix. Mock notifications and URL opens in watch tests to prevent desktop notification spam during test runs.
#0.64.0
Monorepo mirror command, native-android and native-ios targets.
#Features
- New command.
monorepo mirrorcopies monorepo sub-project state (version, changelog, docs) into a standalone mirror repository for independent distribution. - New target.
native-androidrelease target for Android application projects using Gradle, with automatic mutual exclusion against themaventarget to prevent conflicts. - New target.
native-iosrelease target for iOS application projects with Xcode and Tuist support.
#0.63.2
Fix CI test failure when selfdoc not installed.
#Fixes
- Fix. CI test no longer fails when selfdoc is not installed in the test environment.
#0.63.1
Pass RLSBL_DIST_DIR env var to custom_assets build commands.
#Features
- New feature. Custom asset build commands now receive the dist directory via the
RLSBL_DIST_DIRenvironment variable, eliminating the need for hardcoded paths.
#0.63.0
Pipeline separation: versioning and publishing are now independent concerns.
Context
Breaking: publish() and build_assets() removed from targets, replaced by pipeline types. DocsTarget removed (use cloudflare-pages pipeline). flutter-ios/flutter-android merged into single flutter target. The publish config key is no longer recognized -- use pipelines. New: 9 built-in pipeline types, custom_assets, hooks override built-ins, 5 new checks, pipeline introspect table. Fix: multi-target array bugs in snapshot/graph/status.
#Breaking
- Pipeline system replaces target.publish(). The
publishconfig key is no longer recognized; usepipelinesinstead. BaseTarget no longer has publish() or build_assets() methods. - DocsTarget removed. Docs deployment is now a cloudflare-pages pipeline, not a target.
- Flutter targets merged. flutter-ios and flutter-android merged into a single flutter target.
#Features
- Pipeline infrastructure. 9 built-in pipeline types: npm, pypi, go, cargo, deno, hex, maven, docker, cloudflare-pages.
- custom_assets support. Arbitrary artifact builds via pipeline config.
- Hooks override built-ins. Customized pre-release hooks skip built-in tests/lint.
- 5 new checks. private-publish-workflow, scaffold-conflict-markers, npm-private-mismatch, target-version-readable, selfdoc-version-drift.
- Pipeline introspect table. Shows registered pipeline types in selfdoc output.
#Fixes
- Multi-target bug fixes. Fixed snapshot, graph, and status commands to handle multi-target projects instead of taking only the first target.
#0.62.0
Capability-gated publish/build_assets, Swift inheritance cleanup, version_file dir_path, and bug fixes.
Context
Breaking: BaseTarget.build_assets() returns [] instead of raising NotImplementedError. publish() and build_assets() calls are now gated on target.capabilities. SwiftAppleTarget inherits from SwiftTarget. version_file() accepts optional dir_path for dynamic filename resolution. Plain exclusion list fixed. Flutter table cells corrected.
#Breaking
- Breaking.
BaseTarget.build_assets()returns an empty list instead of raisingNotImplementedError. Custom targets that relied on the exception should declarebuild_assetsin their capabilities frozenset.
#Features
- Refactor. SwiftAppleTarget now inherits from SwiftTarget, eliminating 9 duplicated methods.
- New feature.
publish()andbuild_assets()are now gated on target capabilities. Targets that don't declare the capability are skipped instead of calling no-op methods. - New feature.
version_file()now accepts an optionaldir_pathparameter. DenoTarget uses it to resolvedeno.jsonvsdeno.jsoncdynamically.
#Fixes
- Fix. Release confirmation prompt now shows the correct version filename for Deno projects using
deno.jsonc. - Fix. PlainTarget no longer auto-detects alongside the docs target when both
VERSIONandselfdoc.jsonare present. - Fix. Flutter targets now show
pubspec.yaml (flutter)in the target table instead of---for detection files.
#0.61.1
Fix feature matrix column order and add directive tests.
#Fixes
- Fix. Restored original display order for feature matrix columns (was inadvertently alphabetized).
#0.61.0
Target introspection: capabilities, ecosystem, and auto-generated target table.
Context
All 18 release targets now declare capabilities (publish, build_assets, read_name, read_metadata, ci_templates, dev_install), ecosystem labels, and auto-detectability. A new table-targets selfdoc directive auto-generates a comprehensive target details table, replacing the stale manual table. MATRIX_COLUMNS is now derived from TARGETS.keys() for DRY consistency.
#Features
- Refactor. Feature matrix directive now uses
selfdoc.tables.render_markdown_tableandgenerate_feature_matrix_data()returns raw data instead of markdown. - New feature. All 18 release targets now declare
capabilities,ecosystem, andauto_detectableattributes. Capabilities are a frozenset of strings (publish,build_assets,read_name,read_metadata,ci_templates,dev_install) describing what each target supports. Ecosystem is a human-readable label. Auto-detectable indicates whether the target can be auto-detected from project files. - New feature. Auto-generated target details table via
:-: table-targetsselfdoc directive, showing all 18 targets with ecosystem, detection files, version file, tag formats, capabilities, and dev install commands. - Refactor.
MATRIX_COLUMNSin checks is now derived fromTARGETS.keys()instead of a hardcoded tuple, ensuring the feature matrix stays in sync when targets are added.
#Fixes
- Docs. Replaced stale manual target table in docs with auto-generated directive. Fixed all target count references across docs and CLI help strings (was 12/14/17, now 18).
#0.60.1
Feature matrix n/a support for toolchain-native checks
#Features
- Feature matrix. Support "n/a" cells for checks where the toolchain already handles the concern natively (e.g., circular-deps for Go modules).
#0.60.0
Go workspace dependency validation, circular dependency detection, Dart dead-module detection, and feature matrix selfdoc directive.
#Features
- Go workspace dependency validation. GoImportScanner enables deps-unused, deps-undeclared, deps-runtime-test-only, and deps-dev-in-lib checks for Go projects.
- Circular dependency detection. New
circular-depsquality check detects intra-package circular import chains using Tarjan's SCC algorithm. Per-language severity: Python=warn, npm=error. Go is skipped (compiler catches cycles). - Dart dead-module detection. The
dead-modulescheck now supports Dart projects, finding unreachable files via entry-point reachability from pubspec.yaml barrel files and bin/ scripts. - Feature matrix directive. New
table-feature-matrixselfdoc directive auto-generates a language support table in docs.
#0.59.1
Fix false-positive deps-undeclared findings in root-project dep scan when sibling project source files are present.
#Fixes
- Fix. Root-project dep scan no longer produces false-positive deps-undeclared findings from sibling project source files.
#0.59.0
Unified manifest detection, PlainTarget auto-detection, monorepo sync template resolution, required template variables, and fixes for sub-directory scaffold operations and release-file commit exemption.
#Features
- PlainTarget auto-detection. VERSION-only projects are now auto-detected without opt-in configuration.
- Unified manifest detection. Target detection files are now derived from the target registry as a single source of truth, eliminating hardcoded manifest lists.
- Required template variables.
process_templatenow errors on missing critical variables at scaffold time instead of silently leaving placeholders. - Monorepo sync template resolution.
monorepo syncnow resolves template variables in per-project CI workflows instead of copying them verbatim.
#Fixes
- Scaffold auto-commit and hook install from sub-directories. Scaffold auto-commit and pre-push hook installation now work correctly when run from monorepo sub-project directories.
- Release file commits auto-exempted. Commits that only touch
.rlsbl/releases/files are now automatically exempted from changelog coverage checks.
#0.58.0
New dead-workspace-packages check for detecting unused library packages in monorepo workspaces
#Features
- New check.
dead-workspace-packagesdetects workspace library packages never imported by any sibling, with specific messaging for test-only imports.
#Fixes
- Improved. Expanded monorepo
release initcommand description for better selfdoc SEO.
#0.57.1
Fix Python 3.14 compatibility in monorepo release-init
#Fixes
- Fix.
monorepo release-initno longer crashes on Python 3.14 when rendering commented-out package sections. Thetomlkit.dumps()call was replaced withtomlkit.item().as_string()to avoid aValueErrorwhen serializing arrays.
#0.57.0
Batch release-init filtering, retry.toml cleanup, and feature support matrix
#Features
- New.
monorepo release-initnow accepts a--packagesflag to scaffold only specific packages. Packages with zero unreleased commits are automatically commented out with an explanation. - New. Feature support matrix:
get_feature_matrix()andgenerate_feature_matrix_markdown()functions showing which checks support which targets.
#Fixes
- Fix.
retry.tomlis now cleaned up when validation fails during release retry, in both the init and retry handlers. Previously, an invalidretry.tomlcould be left behind, blocking subsequent retries.
#0.56.0
New checks (license-file, scaffold-unreplaced-vars, dead-modules-npm), batch release-init improvements, and scaffold/release fixes
Context
LICENSE is no longer scaffold-managed -- projects own their LICENSE file, enforced by the new license-file check. Batch release-init now supports Flutter targets and includes TOML comments. Several fixes for retry.toml cleanup, bare scaffold on plain targets, workspace-unregistered false positives, and shared release config validation.
#Breaking
- LICENSE removed from scaffold. Projects must now manage their own LICENSE file. The
license-filecheck enforces its presence.
#Features
- npm dead module detection. New
dead-modules-npmcheck detects unreachable modules in npm packages by tracing entry-point reachability through the dependency graph. - New checks.
license-fileerrors if LICENSE is missing, empty, or contains template variables.scaffold-unreplaced-varsdetects leftover template variables in scaffold-generated files. - Batch release-init improvements. Flutter targets now get per-target config sections. Release files include TOML comments and the
contextfield.
#Fixes
- Fix retry.toml blocking release.
release retrynow cleans up the scaffolded retry.toml when validation fails, preventing it from blocking subsequentrelease runattempts. - Fix bare scaffold for plain targets.
rlsbl scaffoldnow reads targets from config.json for already-scaffolded projects that use plain targets, instead of erroring. - Fix workspace-unregistered false positives. The check now skips private
package.jsonfiles and parent directories of registered projects. - Shared release config validation. Single and batch release paths now use a shared validator, fixing missing Flutter validation in the batch path.
#0.55.0
Monorepo release-init command, Go dead-modules detection, scaffold and workspace check fixes
#Features
- New command.
monorepo release-initscaffolds a batch release file with per-package sections for all workspace projects. - New feature.
dead-modulescheck now supports Go projects. Detects unreferencedinternal/packages via tree-sitter import scanning.
#Fixes
- Fix. Template sheltering now handles dotted placeholders like
{{zig.projectName}}, preventing YAML corruption in multi-target scaffold. - Fix. Scaffold with explicit
--targetnow uses the current directory as project root, fixing dev_node detection for plain-target projects in monorepos. - Fix. Workspace stale-entries and unregistered checks now recognize all 16 target manifest types plus
.rlsbl/config.jsonas a universal project indicator.
#0.54.2
Batch release description enforcement, scan cache optimization, lint exclusion todo cleanup
#Fixes
- Fix. Batch release files now enforce mandatory description per package, matching single-release behavior.
- Fix. Dep quality checks now share a cached import scan instead of scanning 4x per workspace project.
#0.54.1
Make release description mandatory
#Breaking
- Breaking. Release description is now mandatory in unreleased.toml. Every release must have a short summary.
#0.54.0
Dev node architecture, release descriptions, quality checks, and graph enhancements
Context
The changelog_exempt flag (v0.53) was renamed to dev_node because agents misinterpreted exempt as a desired behavior rather than a graph position. Dev nodes now fully opt out of the changelog system instead of being exempted from enforcement.
#Breaking
- Breaking. Renamed
changelog_exempttodev_node. Dev node projects no longer participate in the changelog system — no JSONL entries, no CHANGELOG.md, no pre-push enforcement. Release does version bump + tag + GitHub Release (body from release description). Boundary guardrail prevents non-dev-nodes from depending on dev_nodes at runtime.
#Features
- Internal. Added
WorkspaceProjecttyped wrapper andprojectfield onProjectContextfor structured workspace project access. - New feature. Release description and context fields in
unreleased.toml. Description appears in CHANGELOG.md under version heading. Context appears as collapsible details block. Description mandatory for dev_node releases. - New feature. Three quality checks:
deps-runtime-test-only(warning: runtime dep only used in tests),deps-dev-in-lib(error: dev dep imported in lib code),dead-modules(warning: Python modules with zero intra-project references).
#Fixes
- Fix. Library lint now excludes test and example files by default. Go
*_test.go, Pythontest_*.py/tests/, npm*.test.ts/__tests__/, andexamples/dirs are no longer flagged for stdout usage.
#0.53.0
#Breaking
- Breaking. Renamed
internalworkspace flag tochangelog_exempt. The new name describes the behavior (changelog enforcement exemption), preventing misuse.
#Features
- Internal. Added
scopefield to Dependency namedtuple (runtime/dev/peer/explicit). Reverse dependency graph now supports scope-filtered traversal.
#0.52.0
#Breaking
- Breaking. All target methods (
template_mappings,read_name,write_version) now requirectxparameter (no default). Fixed_is_libraryCWD bug intemplate_mappings.
#Features
- New feature.
internal = trueworkspace flag. Internal projects are fully exempt from changelog enforcement: no JSONL entries needed, pre-push check skips them, no user-facing requirement. Version bumps and GitHub Releases still work. - New feature.
deps-unusedcheck now scans JS/TS imports via tree-sitter. npm workspace dependencies (workspace:*) are no longer false-positived as unused.
#Fixes
- Fix. Go target no longer misdetects cmd-layout projects (
cmd/*/main.go) as libraries. Scaffold now correctly includespublish.ymland.goreleaser.ymlfor these projects. - Fix. Standalone multi-target publish workflows now inject
working-directory,packages-dir, and version-file paths for targets in subdirectories. Refactored to structured YAML.
#0.51.0
#Breaking
- Breaking. Renamed
monorepo_roottoworkspace_rootonProjectContext. DeletedProjectCheckContext— all check functions now receiveProjectContextwith pre-loaded config. - Breaking. All target methods (
template_vars,shared_template_mappings,read_name,write_version,publish) now acceptctx: ProjectContextinstead ofproject_root. Fixed latent bugs where dartwrite_versionand dockerread_namedidn't receive config.
#Features
- Internal. Added
[tool.uv.sources]for editable strictcli install.
#0.50.0
#Breaking
- Breaking. Config utility functions (
should_tag,get_push_timeout,push_if_needed,read_deploy_config,get_publish_config) now accept a config dict instead ofproject_root.write_project_configreturns the updated dict. - Breaking. All target
publish()methods now acceptctx: ProjectContextinstead ofproject_root. Config is read fromctx.config— no redundant disk reads during publish.
#Features
- Internal. Extracted shared
_rel_to_git_rootutility, eliminating duplicate path normalization in release flow. - Internal. Added regression test for release abort cleanup (unexpected-files scenario).
#0.49.2
#Features
- Internal. Commands that don't read config no longer load it at startup. Scaffold commands use
ProjectContextfor direct config access. - Internal. Added release integration tests exercising the full release flow against real git repos (no mocked subprocess calls).
#Fixes
- Fix. Increased npm check-name timeout from 10s to 30s to reduce CI flakiness on cold-cache runners.
- Fix. Changelog finalization now uses git-relative paths consistently, matching the version-bump commit's path format.
- Fix. Monorepo release lock file now uses the correct root path.
#0.49.1
#Fixes
- Fix. Monorepo release commits now use correct relative paths for version-bumped files.
#0.49.0
#Breaking
- Breaking. Release, pre-push check, deploy, and undo commands now receive
ProjectContextinstead of bareproject_root. Config is read once at startup viactx.configinstead of re-reading from disk at each call site.
#Fixes
- Fix. Configuration docs no longer trigger stale-description warnings on every release. The
versionfield is excluded from the selfdoc.json schema table. - Fix. Adapted check system bootstrap for strictcli v0.11 API (required
appfield in checks.toml).
#0.48.2
#Fixes
- Fix. Lockfile modifications from version bump no longer falsely flagged as concurrent changes during release.
#0.48.1
#Fixes
- Fix.
rlsbl scaffoldno longer crashes for Go, Zig, and Docker projects (missingproject_rootargument intemplate_vars/shared_template_mappings).
#0.48.0
#Breaking
- Breaking. All internal APIs now require explicit
project_rootparameter (no silent CWD fallback). Eliminates the last footgun from the os.chdir removal.
#Fixes
- Fix. Release aborts (e.g., unexpected dirty files) now revert version-bumped files instead of leaving the working tree dirty. Prevents selfdoc hash oscillation on subsequent releases.
- Fix. Corrupt
.rlsbl/config.jsonnow raises a clear error with the file path instead of silently returning empty config. - Fix. Release lock file is now created relative to the project root, not CWD.
#0.47.0
#Breaking
- Breaking.
release initscaffoldsbump = ""(was"patch") andrelease retryscaffoldsref = ""(was the tag). Both fields must now be set explicitly. Scaffolded files include explanatory TOML comments.
#Features
- New flag.
watch --run-idmonitors CI runs by ID instead of commit SHA. Accepts multiple values (--run-id 123 --run-id 456). Mutually exclusive with the SHA positional arg. - Retry + watch integration.
release retrynow captures dispatched workflow run IDs and passes them towatch --run-id, so monitoring works correctly even when the dispatch ref differs from the release tag. - CI sync rewrite. Monorepo CI sync transforms rewritten from text-based to structured YAML (ruamel.yaml round-trip mode).
working-directoryis now injected per-job (not workflow-level), correctly handling cross-project CI workflows. - Monorepo scoping.
statusandunreleasednow filter commits by project directory in monorepo mode, showing only commits that touch the current project's files.
#Fixes
- Fix.
changelog addin monorepo sub-projects no longer writes to the root JSONL file. Path resolution uses explicit project root instead of CWD. - Fix. Config reads (
push_timeout, publish config, deploy config, etc.) no longer fail silently after CWD shifts during monorepo releases. All config functions accept explicitproject_root. - Fix. Release no longer falsely aborts with 'unexpected modified files' when project_root is an absolute path.
#0.46.0
#Breaking
- Breaking.
--updateflag removed fromscaffold. Barerlsbl scaffoldis now idempotent — creates what's missing, three-way merges what exists. Run it anytime.
#0.45.1
#Fixes
- Bug fix. Scaffold now creates non-workflow files (like
.npmignore) from all targets, not just the primary. - Bug fix. Docker publish template no longer has
{{version}}replaced by the literal version. Added escape syntax (\{{...}}) to the template engine. - Bug fix. Scaffold now uses the correct subdirectory path for npm targets instead of always looking at the repo root.
- Docs.
release retryhelp text updated to describe dispatch-only behavior.
#0.45.0
#Breaking
- Breaking.
push_timeoutmust now be explicitly set in.rlsbl/config.jsonor viaRLSBL_PUSH_TIMEOUTenv var. The implicit 120-second default is removed.
#Features
- Improvement. The
plainrelease target now bumpspyproject.tomlversion when present, keeping it in sync withVERSION.
#Fixes
- Bug fix.
release runnow correctly applies directory scoping for monorepo changelog validation, matchingrlsbl check --tag changelogbehavior. - Bug fix. Changelog checks now use the target-specific tag pattern in monorepos (e.g.,
go/v*for Go targets instead ofgo@v*).
#0.44.0
#Breaking
- Breaking.
release retryis now dispatch-only. It no longer deletes/re-creates the GitHub Release or re-uploads assets. It dispatches workflows fromretry.tomlviagh workflow run. Schema simplified to 3 fields:version,dispatch,ref.
#0.43.1
#Features
- Scaffold. npm targets now get a
.npmignorefile with sensible defaults, preventing non-npm content from being published.
#Fixes
- Bug fix.
release retrynow always dispatches all workflows listed inretry.toml, not just as a fallback when no runs are found. Fixes CI never being re-triggered when Publish triggers naturally.
#0.43.0
#Breaking
- Breaking.
edit-release,undo, andyankare now subcommands of thereleasegroup:release edit,release undo,release yank. - Breaking.
release retryis now file-driven viaretry.toml. Auto-scaffolds the file with defaults when not present. The version positional argument is removed.
#Features
- Improvement.
release yanknow shows a confirmation prompt before destructive operations, with--yesto bypass. - Improvement.
release runnow detects monorepo root and errors with guidance to usemonorepo release.
#Fixes
- Bug fix. Multi-target scaffold now correctly includes
workflow_dispatchin merged publish workflows. - Bug fix.
selfdoc.jsonversion is now auto-bumped during release even whendocsis not in the configured targets. The version-consistency check also detects drift independently of target config. - Bug fix. Monorepo changelog coverage now only requires entries for commits touching the package's own files, not all commits in the range.
- Bug fix.
release yanknow uses correct monorepo tag format instead of hardcodingv{version}.
#0.42.0
#Breaking
- Breaking.
rlsbl releaseis now a command group. Userlsbl release run(wasrlsbl release),rlsbl release init(wasrlsbl release-init), and the newrlsbl release retry. - Breaking.
rlsbl release runrequires--watchor--no-watch.--watchauto-monitors CI to completion;--no-watchprints the watch hint.
#Features
- New command.
rlsbl release retryre-creates a GitHub Release to re-trigger CI/CD workflows, with asset re-upload and automaticgh workflow runfallback. - Scaffold. All generated CI and Publish workflow templates now include
workflow_dispatchtrigger for manual re-triggering after GitHub Actions outages. - Watch.
rlsbl watchnow suggestsrlsbl release retrywhen no CI runs are found but a GitHub Release exists for the commit. - New target.
pgdesignrelease target for database schema projects.
#Fixes
- Bug fix.
rlsbl checkcommands no longer crash in monorepo context withWorkspaceGraph.__init__() missing required argument.
#0.41.7
#Features
- New feature. Releases are now blocked when there are no user-facing changelog entries. Shown as a warning during
rlsbl check, hard error duringrlsbl release. - New feature.
changelog addandchangelog amendnow detect duplicate commits -- hard error when the same commit appears in an entry with matching type, warning when types differ.
#Fixes
- Fix. DocsTarget version sync now also updates the last entry in selfdoc.json
versionsarray, preventing VER003 lint failures after release.
#0.41.6
#Features
- New feature. Run
selfdoc gen --no-commitbeforeselfdoc checkduring release, ensuring docs are regenerated from source before validation.
#Fixes
- Fix.
selfdoc checkfailures now print a clean error message instead of a raw Python traceback.
#0.41.5
#Fixes
- Fix. Detect npm moniker collisions for separator-free names. Previously,
check-name pgspec --target npmreported "available" even thoughpg-specexists and npm considers them identical. Now generates insertion variants (inserting-,.,_at every position) and checks each. Also fixed dot separator handling -- dots are now treated identically to dashes and underscores, matching npm's actual normalization.
#0.41.4
#Fixes
- Fix: selfdoc hashes no longer left dirty after release. Content hashes are refreshed after the version bump so the working tree stays clean post-release.
#0.41.3
#Features
- DocsTarget is now a versioned target. Reads and writes version from
selfdoc.json. Theversion-consistencycheck now includes docs targets instead of skipping them.
#0.41.2
#Features
- Go assets use goreleaser for cross-compilation. When goreleaser is installed,
build_assets()produces binaries for linux/darwin/windows x amd64/arm64. Falls back to host-onlygo buildwhen goreleaser is unavailable.
#0.41.1
#Breaking
- **Breaking:
.claude/settings.jsonremoved from scaffold templates.** Claude Code does not require this file. Projects with customized hooks keep their existing file.
#Features
- Config validation and migration. Release flow now validates
publish.<target>.assetsconfig.scaffold --updateauto-detects missingprivatekey. Newconfig-schemacheck (29 checks total). Removedget_max_asset_size_mb()implicit default.
#Fixes
- **Fix:
library-lintcheck skips non-library projects.** Standalone projects no longer produce 523 spurious lint errors inrlsbl check --all.
#0.41.0
#Breaking
- **Breaking:
privateconfig key is now required.** No implicit default. Set"private": truefor private repos or"private": falsefor public repos. Private repos are blocked from publishing to public registries. - Breaking: private hook template removed. The
post-release-private.sh.tplscaffold template is deleted. Asset upload is now a built-in release step configured viapublish.<target>.assets. A newprivate-hook-stalecheck detects legacy private hooks. - Breaking: CLAUDE.md removed from scaffold templates. Projects using selfdoc root file templates should manage CLAUDE.md via
docs/_CLAUDE.md. Scaffold no longer creates or updates CLAUDE.md. - **Breaking:
run_cmdlegacy signature removed.** The release command no longer acceptsrun_cmd(registry, args, flags). UseReleaseConfigfromrlsbl.release_fileinstead.
#Features
- **New command:
changelog amend.** Amend historical changelog entries in released JSONL files. Unlocks the file, appends the entry, re-locks, regenerates CHANGELOG.md, and syncs GitHub Release notes viaedit-release. - **Per-target
build_assets().** New target method for building distributable artifacts. Implemented for PyPI (uv build), npm (npm pack), Go (go build), and Cargo (cargo build --release). - Release asset upload. Configure
publish.<target>.assets: trueandmax_asset_size_mbto build and upload artifacts to GitHub Releases duringrlsbl release. Size guard aborts if artifacts exceed the limit. - Auto-dump strictcli schema during release. Projects with strictcli in their dependencies automatically run
--dump-schemaduring release. Changed.strictcli/schema.jsonis included in the release commit. - Selfdoc check runs earlier in release pipeline. Moved from after lint to after pre-checks hook, catching staleness errors before tests run.
#Fixes
- Fix: noop commit warnings eliminated. Monorepo sync, monorepo snapshot, and validation cache no longer warn when nothing changed. Prints a clear skip message instead.
- **Fix:
release-initno longer refuses empty files.** An empty or whitespace-onlyunreleased.tomlleft after finalization no longer blocksrelease-init. - Fix: version-consistency check no longer fails on DocsTarget. Targets with no version file (like docs) are skipped in the version comparison.
#0.40.1
#Fixes
- Fix. CI workflow updated for the
checktocheck-namerename.
#0.40.0
#Breaking
- **Breaking: removed
--skip-tests,--skip-lint,--skip-docs,--skip-remote-check, and--no-tagflags from the release command.** Tests, lint, and remote checks now always run. Selfdoc check is automatically skipped when docs target is excluded in the release file. Only--allow-dirtyremains as a release-specific flag.
#Features
- selfdoc root file templates. README.md and CLAUDE.md are now auto-generated by selfdoc from
docs/_README.mdanddocs/_CLAUDE.mdtemplates. Edit the templates, not the generated files.
#0.39.0
#Breaking
- Breaking: file-based releases.
rlsbl releasenow reads from.rlsbl/releases/unreleased.tomlinstead of CLI args. Runrlsbl release-initto scaffold the file. The file declares bump type, included/excluded targets, and per-target config. - **Breaking:
rlsbl doctor,monorepo lint, andchangelog validateremoved.** Replaced by the unifiedrlsbl checkcommand. Userlsbl check --all,rlsbl check --tag workspace, orrlsbl check --tag changelog. - **Breaking:
--dry-run,--yes,--quietare now global flags.** They apply to all commands. Bothrlsbl --dry-run releaseandrlsbl release --dry-runwork. - Breaking: PyYAML replaced with ruamel.yaml. Projects extending rlsbl's YAML handling must use the ruamel.yaml API.
- Breaking:
rlsbl check(name availability) renamed torlsbl check-nameto make room for the unified check system.
#Features
- Unified check system. 27 checks migrated to strictcli's
@app.check()framework. Runrlsbl check --all, filter by tag (--tag project,--tag changelog,--tag workspace,--tag release), or by name (--name lock). Supports dependency ordering, JSON output, dry-run, and verbose modes. - Dart target and workspace scanner. New
darttarget type: reads/writespubspec.yamlversion field with round-trip formatting preservation. DartScanner detects intra-workspace dependencies in Dart monorepos. - Flutter iOS/Android targets with Shorebird OTA. Separate
flutter-iosandflutter-androidtargets with platform-specific tags. Release filemodefield (ota/build) controls release type. Native file detection blocks OTA when platform code changes. Build number (+N) management opt-in via config. - **
monorepo graphcommand.** Export the workspace dependency graph as JSON, DOT (Graphviz), or indented text. Filter with--root(transitive deps),--reverse(transitive rdeps), and--depth. - **
monorepo snapshotcommand.** Generates.rlsbl-monorepo/snapshot.jsonsummarizing all packages, versions, deps, and graph structure.--checkmode for CI staleness detection. Auto-regenerates during monorepo releases. - Architectural layer rules. Define
[layers]inworkspace.tomlwith ordered layers and glob-based package assignments. Thelayers-violationscheck enforces dependency direction. Supports unrestricted packages, forbidden targets, and explicit cross-layer allowances. - Dependency-import validation. New
deps-unusedanddeps-undeclaredchecks scan source files (Python via tree-sitter, Dart via regex) to verify declared dependencies match actual imports. Whitelist via.rlsbl-monorepo/dep-overrides.toml. - **
monorepo impactcommand.** Analyze blast radius of changes: input a package name, file paths, or git range (--since). Outputs direct/transitive dependents, test scope, and release candidates in JSON or text. - Coordinated multi-package releases.
monorepo releasereads a batch release file (.rlsbl-monorepo/releases/unreleased.toml) and releases packages in topological order. Newdeps-stalecheck detects outdated intra-workspace constraints. Advisory constraint propagation after each release.
#0.38.2
#Fixes
- Fix. Publish router no longer fails for plain-target releases. Added a no-op job so GitHub Actions workflows succeed even when all conditional publish jobs are skipped.
#0.38.1
#Features
- Hook-generated files included in release commit. Files modified or created by pre-checks and pre-release hooks are now automatically detected and included in the release commit, instead of being left dirty.
#Fixes
- Fix PyPI build for subdirectory targets.
uv build --out-dirno longer produces a doubled path (e.g.pypi/pypi/dist) when the pypi target lives in a subdirectory.
#0.38.0
#Breaking
- Monorepo publish router inlines jobs.
monorepo syncnow inlines each sub-project's publish steps directly into the rootpublish.ymlinstead of calling per-project reusable workflows viaworkflow_call. This fixes PyPI Trusted Publishing (OIDC), which rejects tokens from reusable workflows. Per-project source workflows stayon: release(standalone-ready). Old{name}-publish.ymlwrappers are auto-deleted on sync.
#Features
- Publish router hash cache.
monorepo syncnow caches SHA256 hashes of per-project publish workflows in.rlsbl-monorepo/publish-cache.jsonand skips router regeneration when nothing changed.
#Fixes
- **Fix Zig npm-wrapper
needs:dependency.**build_npm_publish_jobs()no longer hardcodesneeds: [goreleaser]-- Zig projects now correctly useneeds: [build-and-upload]. - **Fix monorepo sync self-reference with
path=".".** Projects withpath="."no longer wrap the generated router as their own publish workflow.
#0.37.0
#Features
- New feature. Auto-sync lockfiles during release. After version bump, rlsbl detects uv.lock, package-lock.json, and go.sum, runs the appropriate sync command, and includes modified lockfiles in the release commit.
#Fixes
- Fix. Restore path-based changelog-only commit exemption alongside the Autogenerated trailer check. Manual commits touching only .rlsbl/changes/ or CHANGELOG.md are now correctly skipped during coverage validation.
- Fix. Skip gitignored lockfiles during release lockfile sync. Previously, syncing a lockfile that was in .gitignore caused git add to fail and aborted the release.
#0.36.1
#Fixes
- Fix. Monorepo target detection now resolves project paths relative to the monorepo root, not the current directory. Fixes changelog validation and status reporting when running from inside a sub-project.
- Fix.
uv buildanduv publishnow run from the sub-project directory in monorepo mode, fixing build failures for PyPI targets.
#0.36.0
#Features
- New feature. Asset size guard in private post-release hook -- checks files in
dist/againstmax_asset_size_mb(default 2MB) before uploading to GitHub Releases.
#0.35.1
#Features
- Cleanup. Changelog commit exemption now uses only the
Autogenerated: truetrailer; old path-pattern fallback removed.
#Fixes
- Bug fix. Release finalization now generates the per-version
.mdfile alongside the JSONL. - Bug fix. Release flow no longer false-positives on files written by hooks, lint, or changelog generation.
#0.35.0
#Features
- **
rlsbl releasewarns about stale changelog entries in monorepo mode.** During finalization, entries referencing commits outside the unreleased range are flagged to stderr (warn-only; no entries stripped) so out-of-range entries don't silently carry into the next release cycle. - **Template action versions now use
{{action "name"}}placeholders.** GitHub Action versions in scaffold templates are resolved at scaffold time fromrlsbl/data/action_versions.toml, eliminating duplication between the table and 60+ template references. Updates to action versions now require editing only the table.
#Fixes
- Release flow no longer crashes when extracting the changelog entry. Fixed a regression introduced in v0.34.0 where the in-memory preview of
generate_changelogused## Unreleasedas the heading but entry extraction looked for## X.Y.Z, returning None and crashing later in GitHub Release notes generation. The preview call now passesversion_override=new_versionso the heading matches. - **
max_entries_per_commitdefault raised from 2 to 5.** The previous default 2 was too strict and contradicted the documented practice that one commit can appear in multiple JSONL entries (e.g., fix + feature). Default now matches the existingmax_commits_per_entrydefault of 5.
#0.34.0
#Features
- Configurable changelog batch-limits validation.
rlsbl changelog validatenow checks that no entry has more thanmax_commits_per_entry(default 5) commits and that no commit appears in more thanmax_entries_per_commit(default 2) entries. Both limits configurable per project via thebatch_limitssection in.rlsbl/config.json. Known violations can be silenced via anexclusionslist with a requiredreasonfield for audit purposes.
#0.33.0
#Features
- **
rlsbl dev installexpanded.** New--global(default) and--venvflags. Added support for hex, deno, zig, and swift targets in addition to pypi/npm/go/cargo. For pypi/npm,--venvinstalls into the project's local environment (uv sync/npm install).
#Fixes
- PyPI publish error names both env vars. When the PyPI publish credential is missing and no
token_varoverride is configured, the error message now reads 'no PYPI_TOKEN or TWINE_PASSWORD' instead of hardcoding only PYPI_TOKEN. - Em-dash in status warning.
rlsbl status's 'commits ahead' warning now uses an em-dash separator (— run) instead of two hyphens (-- run). - **Empty
release_branchesconfig is now an error.** Previously an explicit empty list silently fell back to['main','master']and disabled the manual-push warning. To opt out of the warning, remove therelease_brancheskey entirely; to limit to specific branches, list them. - **
rlsbl releaseno longer pollutes the working tree on abort.** Previously, when a pre-mutation check (selfdoc, tests, lint, hooks) failed, the regeneratedCHANGELOG.mdwas left in the working tree and had to be manually reverted. The write is now deferred until after all pre-checks pass.
#0.32.0
#Features
- Status warning for unreleased commits.
rlsbl statusnow prints a prominent! N commits ahead of <tag>warning when the current project has unreleased commits since its last tag. - Centralized GitHub Actions versions. All scaffolded workflows now read action versions from a single table (
rlsbl/data/action_versions.toml), andactions/checkouthas been bumped to v6 across every template ahead of the June 2026 Node 20 deprecation. - **
scaffold --dry-run.** Preview which files would be created, updated, conflicted, or unchanged without writing anything. - **
scaffold --updateregenerates stale pre-push hooks.** Known-old hook contents are recognized by content hash and overwritten with the current template; user-customized hooks are preserved and a unified diff is printed for manual review. - Pre-push hook warns on manual release-branch pushes. Pushing to
main/masteroutsiderlsbl releasenow prints a prominent warning (configurable viarelease_branchesin.rlsbl/config.json). The push is not blocked. - Config-driven per-target publish gating. A new
publishsection in.rlsbl/config.jsondeclareslocal: true|falseandtoken_varoverrides per target, replacing ad-hoc env-var-presence checks across all 7 publishable targets. - **New
rlsbl dev installcommand.** Locally installs the current project in editable mode based on its target (pypi →uv tool install -e ., npm →npm link, go →go install ./..., cargo →cargo install --path .). Supports--uninstall, and in monorepos:--all,--include,--exclude. - **User-owned
ci-custom.ymlandpublish-custom.yml.** Customize CI without three-way merge conflicts by adding your own jobs in a separate workflow file thatrlsbl scaffoldnever touches. - **
--no-commitflag onrlsbl monorepo init/add/sync.** Consistent withscaffoldandchangelog; enables batched migrations into a single commit.
#Fixes
- Go monorepo tag bug fix. Release in a Go monorepo no longer produces malformed tags like
auth-gatewayv0.1.0when the workspace.toml path lacks a trailing slash — the separator is now inserted defensively, and workspace paths are normalized at load time. - Monorepo publish workflows now run on release. The generated publish router declares per-target
permissions:andsecrets: inherit, fixing thestartup_failurethat affected every monorepo publish run on GitHub Actions. - **
scaffold --updateno longer commits files with merge conflict markers.** Conflicted files are excluded from the auto-commit and listed so they can be resolved manually. - Docs target no longer fails when Cloudflare credentials are missing.
DocsTarget.publish()gracefully skipsselfdoc deploywhenCF_ACCOUNT_IDorCF_PAGES_API_TOKENis absent, instead of producing a hard subprocess error on every release. - **
Autogenerated:trailer uses git's--trailerflag.** rlsbl-created commits now record the trailer viagit commit --trailerinstead of embedding it in the commit message body. This makes the trailer correctly parseable bygit interpret-trailersand other git tooling, which previously couldn't read the embedded form.
#0.31.0
#Breaking
- Breaking. Go targets in monorepos now use path-based tags (
go/v0.1.1) instead of name-based ([email protected]), matching Go module proxy requirements. All tag discovery and matching updated.
#Features
- Feature. Go CLI projects automatically run
go installafter release to update the local binary. - Feature.
selfdoc checkruns automatically during release whenselfdoc.jsonexists. Skip with--skip-docs.
#Fixes
- Fix. Commits touching only changelog infrastructure files (
.rlsbl/changes/,CHANGELOG.md) are again exempt from coverage, even without theAutogeneratedtrailer. - Fix. Release flow now rolls back locally on push failure: deletes the tag, resets commits, and prints recovery instructions. Branch and tag are pushed in a single
git pushcommand. - Fix.
rlsbl undonow handles the two-commit release pattern (version bump + changelog finalize), reverting both and restoring JSONL state.
#0.30.1
#Features
- Feature. All
commit_files()calls now default toautogenerated=True, ensuring every rlsbl-generated commit carries theAutogenerated: truetrailer. - Feature. Pre-push hook now blocks pushes when
unreleased.jsonl,.validated, orCHANGELOG.mdare gitignored.
#Fixes
- Fix.
.validatedcache is no longer gitignored; tracked per project rules. - Fix. Pre-push hook now reads all JSONL files (unreleased + versioned) for coverage, fixing release pushes being blocked after changelog finalization.
#0.30.0
#Breaking
- Breaking. Changelog coverage exemption now uses the
Autogenerated: truegit trailer instead of file-path pattern matching and commit-message regexes. Auto-generated commits must carry this trailer to be exempt.
#Features
- New command.
rlsbl commitcommits files with anAutogenerated: truegit trailer, marking them as auto-generated for changelog coverage exemption. - Feature. Changelog finalization,
rlsbl changelog add, andrlsbl changelog generateauto-commits now carry theAutogenerated: truetrailer. - Feature. Pre-push hook skips JSONL coverage check when pushing a version tag (standalone
v*or monoreponame@v*).
#0.29.1
#Fixes
- Fix. Release no longer aborts when the
.validatedchangelog cache is updated during validation.
#0.29.0
#Features
- New command.
rlsbl monorepo lintdetects unregistered projects and stale workspace entries.
#Fixes
- Fix. Pre-push hook now recognizes monorepo release commit messages and filters coverage checks to only commits affecting each sub-project.
- Fix. Release commit now includes all version files modified by targets (
__init__.pyfor PyPI,build.zig.zonfor Zig, dynamically-chosen file for Maven). - Fix. Built-in tests, lint, and hook scripts now run from the correct sub-project directory in monorepo mode.
- Fix. Changelog validation,
rlsbl status, andrlsbl changelog validatenow use project-scoped tags (<name>@v*) in monorepo mode. - Fix.
rlsbl undonow correctly finds project-scoped tags and matches monorepo release commit messages.
#0.28.1
#Fixes
- Fix: status applies changelog-only exemptions.
rlsbl statusnow excludes changelog-only commits from the JSONL coverage count, consistent withchangelog validate. Shows exemption count when applicable. - Fix: gitignore uses set-union merge on scaffold update.
scaffold --updatenow appends new entries to.gitignorewithout three-way merge, eliminating predictable conflicts. No lines are removed or reordered. - Fix: backfill script uses CWD as default project root.
scripts/backfill_changelog.pynow operates on the current directory instead of hardcoding rlsbl's own root. Accepts--project-rootfor explicit override.
#0.28.0
#Breaking
- Revert: strict type validation removed. Changelog entry types are freeform strings again. Non-standard types render under "Other" in generated CHANGELOG.md. Formalization deferred until real-world usage patterns emerge.
#Features
- **
changelog generateauto-commits.** Generated CHANGELOG.md and per-version .md files are now auto-committed after generation. Use--no-committo skip.
#Fixes
- **Fix: monorepo publish workflow renamed to
publish.yml.** The monorepo publish router was namedpublish-router.yml, which broke PyPI Trusted Publishing OIDC claims when projects moved between standalone and monorepo layouts. Now alwayspublish.ymlin both contexts. - Fix: monorepo sync handles trailing slashes, version-file paths, and packages-dir. Sync no longer produces double-slash globs from workspace.toml paths with trailing slashes. Action inputs like
go-version-fileare rewritten to include the sub-project path.pypa/gh-action-pypi-publishgetspackages-dirinjected whenworking-directoryis set. - **Fix: monorepo publish workflow renamed to
publish.yml.** The monorepo publish router was namedpublish-router.yml, which broke PyPI Trusted Publishing OIDC claims. Now alwayspublish.ymlin both standalone and monorepo contexts. - Fix: monorepo sync handles trailing slashes, version-file paths, and packages-dir. Sync no longer produces double-slash globs. Action inputs like
go-version-fileare rewritten for sub-project paths.pypa/gh-action-pypi-publishgetspackages-dirinjected.
#0.27.0
#Breaking
- Breaking: JSONL changelog required.
rlsbl releasenow requires.rlsbl/changes/to exist. The manual CHANGELOG.md heading check fallback has been removed. Runrlsbl scaffold --updateto set up JSONL changelogs. - Breaking: old lint.toml ignore format removed. The deprecated flat
ignorekey in.rlsbl/lint.tomlis no longer supported. Use per-language config in.rlsbl/lint/python.tomletc. - **Breaking:
get_*method aliases removed from BaseTarget.** Target implementations now use bare method names (version_file,template_dir, etc.) matching the ReleaseTarget protocol. - Breaking: lint returns empty results when no language detected. Previously defaulted to Python scanning; now requires a language marker file (pyproject.toml, go.mod, package.json).
#0.26.1
#Fixes
- Fix: Watch CI hint points to the correct commit. The release command now captures the pushed SHA before running post-release hooks, so
rlsbl watchtargets the release commit instead of a post-hook auto-commit.
#0.26.0
#Features
- **
rlsbl yankcommand.** Deprecate or delete past releases. Soft yank (rlsbl yank v1.2.3) marks the GitHub Release as a pre-release with a deprecation notice. Hard yank (rlsbl yank v1.2.3 --hard) deletes the GitHub Release and its git tag entirely. - **PyPI release bumps
__version__.** Python source files containing__version__are now updated alongsidepyproject.tomlduring release, keeping runtime version introspection in sync. - Strict type validation for JSONL entries. Changelog entries with
user_facing: truenow requiretypeto be one offeature,fix, orbreaking. Unrecognized types are rejected during validation andchangelog add.
#Fixes
- Fix: scaffold stays in sub-project directory. In monorepos,
rlsbl scaffoldno longer walks up to the repo root when project files already exist in the current directory. Previously this caused scaffold to operate on the wrong project.
#0.25.4
- No user-facing changes.
#0.25.3
#Fixes
- Fix: tag-based range for changelog validation. Unreleased commit detection now uses
<last_tag>..HEADinstead oforigin/main..HEAD. This correctly answers "what hasn't been released" rather than "what hasn't been pushed," and works regardless of branch naming convention or remote configuration.
#0.25.2
- No user-facing changes.
#0.25.1
#Fixes
- Fix: built-in lint only runs on library projects. The pre-release lint check no longer runs on CLI applications, avoiding false positives for
print()calls and entry points. Lint runs only for monorepo projects withlibrary = true. - Fix: CHANGELOG.md regenerated after JSONL finalization. The release command now regenerates CHANGELOG.md after renaming
unreleased.jsonlto the versioned file, so the changelog heading shows the version number instead of "Unreleased".
#0.25.0
#Features
- Structured JSONL changelog. Changelog entries are now backed by structured JSONL files in
.rlsbl/changes/. Each entry maps a description to commit hashes withuser_facingandtypefields.rlsbl changelog add/validate/generatecommands manage entries.rlsbl releaseauto-validates coverage and generates CHANGELOG.md. Pre-push blocks on missing coverage.rlsbl unreleaseduses exact hash matching.rlsbl statusshows coverage info. Scaffold creates the directory for new projects.changelog addauto-commits entries (with--no-commitopt-out). Coverage validation skips changelog-only commits. Internal commit logic centralized viacommit_fileshelper. Pre-push hook compatible with strictcli argument handling. - Changelog backfill script.
scripts/backfill_changelog.pymigrates an existing CHANGELOG.md into JSONL files by parsing entries, mapping them to commits via keyword matching, and writing per-version JSONL files.
#Fixes
- Fix: selfdoc CLI documentation. Replaced incompatible
code-helpdirectives with auto-generated CLI docs viaselfdoc gen, fixing the build warning after the strictcli migration. - Fix: monorepo commands commit without safegit.
monorepo init,add, andsyncnow properly fall back to plain git when safegit is not installed, instead of silently skipping the commit. - Fix: pre-push hook compatibility with strictcli. The pre-push hook no longer passes extra arguments that strictcli rejects. Release pushes (containing a version bump commit) skip the JSONL coverage check entirely since validation already ran during
rlsbl release.
#0.24.0
#Features
- **
rlsbl edit-release [version].** New command that updates GitHub Release notes from CHANGELOG.md. Auto-detects the current version if none is given. Supports--dry-runto preview without changes.
#0.23.1
- No user-facing changes.
#0.23.0
#Breaking
- Breaking: hooks are now scaffold-managed.
pre-release.shandpost-release.share no longer user-owned.scaffold --updatemerges template improvements into existing hooks via three-way merge. - **Breaking:
rlsbl initremoved.** Theinitalias forscaffoldhas been dropped. Userlsbl scaffolddirectly. - **Breaking:
--registryflag removed.** Use--targetinstead. The--registryflag was deprecated since 0.20.0.
#Features
- Per-command help. CLI dispatch migrated to strictcli. Every command and monorepo subcommand now has its own
--helpwith typed flags, arguments, and descriptions. - Multi-ecosystem library lint. Library boundary lint now supports Go and npm in addition to Python. Go lint detects forbidden imports (
net/http,cobra,urfave/cli),fmt.Println, andfunc main(). npm lint detects forbidden imports (express,koa,commander, etc.),console.log, andbinentry points. - AST and regex lint backends. Each language has two lint implementations: tree-sitter AST (accurate, default) and regex (lightweight fallback). Select via
parser = "regex"in.rlsbl/lint.toml. - Per-language lint config. Lint rules are now configured in
.rlsbl/lint/python.toml,go.toml, andnpm.tomlwith[forbidden-imports],[stdout], and[entry-point]sections. Scaffold generates these with sensible defaults. The oldlint.tomlignore list is deprecated; a warning is shown if detected. - Built-in pre-release checks.
rlsbl releasenow runs tests and lint automatically before releasing. No hook customization needed for standard checks. Skip with--skip-testsor--skip-lint. - Two-hook model. New
pre-checks.shhook runs before built-in checks (for setup tasks). The existingpre-release.shruns after (for custom validation). - Standalone library lint.
rlsbl doctor --check library-lintnow works on any project, not just monorepo libraries.
#Fixes
- Fix: lint false positives from vendored code. The lint file walker no longer descends into
.venv/,node_modules/,__pycache__/, and other non-source directories.
#0.22.1
#Features
- Name check short-circuiting. When a name is already taken, variant checking and GitHub repo lookups are now skipped, reducing API calls from 6 to 1-2 per taken name. Ultranormalization breaks after the first conflict instead of checking all 64 variants.
- Reason-specific explanations. Check results now explain WHY a name was rejected: stdlib module conflict, npm moniker collision, or visual similarity. Previously only the status ("taken") was shown.
- PyPI caveats for available names. Available PyPI names now always show the prohibited names list warning and suggest
--ultranormalized-variantswhen the flag wasn't used. - Steps-run summary. Verbose check output ends with a "Checked:" line listing which validations ran (registry, stdlib, variants, GitHub, ultranormalization, moniker similarity).
- Multi-name summary. Batch checks now print an aggregate summary ("N available, M taken") and the delay setting after the table.
- Rate limit retry visibility. HTTP 429 retries now print "Rate limited, retrying in Ns..." to stderr instead of sleeping silently.
#0.22.0
#Features
- Library boundary lint. Tag monorepo projects with
library = truein workspace.toml (ormonorepo add --library true) to enforce library discipline.rlsbl doctor --check library-lintdetects forbidden imports (argparse, flask, click, etc.),print()calls, and CLI entry points in library source files via Python AST analysis. Logging usage is flagged as a warning. Violations block releases via the pre-release hook. Supports.rlsbl/lint.tomlignore lists for false positives. - **Library column in
monorepo status.** Shows which projects are tagged as libraries. Dynamic column — hidden when no projects havelibrary = true. - PyPI check: Simple API. Switched from the JSON API to the Simple API for availability checks. Fixes false "available" results for package names that are registered but have no releases.
- PyPI check: stdlib collision detection. Names that conflict with Python standard library modules (like
queue,json,os) are now reported as taken without hitting the network. - **PyPI check:
--ultranormalized-variants.** New flag that generates visual-similarity variants (l/1/i and o/0 substitutions) and checks each against PyPI. Catches names likeclithat PyPI rejects due to collision withcl1. - npm check: moniker similarity detection. The npm check now queries the search API to detect moniker conflicts — names that npm considers identical after stripping punctuation. Catches cases like
selfdocconflicting withself-doc. - **
rlsbl doctor --check <name>.** Run a single diagnostic check by name instead of all checks. Useful in CI and pre-release hooks.
#Fixes
- Fix: scaffold template variable resolution for secondary targets. Multi-target projects (e.g., pypi+npm) no longer leave
{{registryUrl}}unresolved when npm is a secondary target.
#0.21.2
#Features
- Hook output is now visible. Pre-release and post-release hooks stream stdout/stderr to the terminal in real-time. On failure, the actual exit code is shown instead of a generic error message.
- **
RLSBL_HOOK_TIMEOUTenvironment variable.** Configure a timeout in seconds for release hooks. Default is no timeout (hooks run to completion), fixing the previous 120-second hard limit that silently killed long-running test suites.
#0.21.1
#Features
- **
rlsbl release --allow-dirty.** Release with uncommitted changes in the working tree. Pre-existing dirty files are tracked and excluded from the unexpected-files safety check, so genuinely new unexpected files are still caught. - **
monorepo addpasses--targetto scaffold.** When an explicit target is specified (e.g.,--target plain), it is forwarded to the scaffold subprocess instead of relying on auto-detection. Removes a special case wheremonorepo addpre-created a VERSION file for plain targets.
#0.21.0
#Features
- **Cross-registry
depends_onin workspace.toml.** Monorepo projects can now declare explicit dependencies on siblings in any ecosystem:depends_on = ["framework"]. These edges are respected byrelease-order, counted instatusDeps/Rdeps columns, and shown inoutdatedwith status "explicit". Usemonorepo add --depends-on name1,name2to set them during project registration. - **
plaintarget for no-build-system projects.** Register template directories, shared config, or other non-code projects withmonorepo add --target plain. Uses a plainVERSIONfile, creates GitHub Releases with changelog notes, but generates no CI or publish workflows. Standard tag format. - **
--targetflag onmonorepo add.** Explicitly specify any target type, bypassing auto-detection. Works for all registered targets, not justplain. - Reusable npm binary wrapper. The platform-specific npm package distribution pattern (esbuild/biome/turbo-style) is now a shared module used by both Go and Zig targets. Platform list is configurable via
npm_wrapper.platformsin project config. - Zig target. New release target for Zig projects. Detects
build.zig.zon, manages versions viaVERSIONfile with best-effort.zonsync, generates CI (viamlugg/setup-zig) and publish workflows that cross-compile for 6 platforms from a single runner. Supports optional npm binary wrapper distribution. - CI router fix for projects without workflows.
monorepo syncno longer includes projects without CI workflows in the CI router, preventing references to non-existent workflow files.
#0.20.0
#Breaking
- **Breaking:
rlsbl checknow requires--target.** The implicit default of checking both npm and PyPI has been removed. Specify--target npm,--target pypi, or--target goexplicitly.
#Features
- Multi-name checking.
rlsbl check foo bar baz --target pypichecks multiple names in one invocation and prints a compact table. Single-name invocations still show the verbose format with variants. - **Rate limiting for
rlsbl check.** New--delayflag (default 200ms) throttles between names. HTTP 429 responses from PyPI/Go/GitHub trigger automatic retry with exponential backoff. - **
rlsbl monorepo check-names.** Batch-check name availability for all workspace projects. Supports--prefixand--suffixto evaluate namespacing strategies (e.g.,--prefix www- --target pypi). - **
rlsbl monorepo outdated.** For each project, shows its intra-workspace dependency constraints against current sibling versions. Reports versioned deps as ok/outdated, and labels workspace/path references. - **Dependency columns in
rlsbl monorepo status.** New Deps and Rdeps columns show how many workspace siblings each project depends on and how many depend on it. Hidden when no intra-workspace dependencies exist. - Monorepo-aware pre-push changelog check. The pre-push hook now detects monorepo context and only checks changelogs for projects whose files appear in the pushed commits.
- **Per-subcommand help for
rlsbl monorepo.** Each subcommand now has its own--helpwith usage and description. - Path dependency rewriting for PyPI. When building a PyPI package in a monorepo, path dependencies (e.g.,
core @ {root:uri}/../core) are automatically rewritten to versioned constraints in a temp copy. The working tree is never modified. - **
rlsbl monorepo release-order.** Shows the topological order to release projects in, based on intra-workspace dependencies. Detects cycles.
#0.19.1
- No user-facing changes.
#0.19.0
#Features
- **
rlsbl deploy [name]command.** Deploy to configured targets via SSH with health checks (HTTP, TCP, script) and automatic rollback on failure. Configure targets in.rlsbl/config.jsonunder thedeploykey. Supports--dry-runto preview without executing and--forceto override branch restrictions. - Deploy CI workflow template.
rlsbl scaffoldgenerates a deploy workflow when deploy targets are configured. - Project root discovery. rlsbl now finds the project root automatically, so commands work from any subdirectory (like git, cargo, npm).
- Scaffold untracks gitignored files.
rlsbl scaffoldrunsgit rm --cachedon tracked files matching new.gitignoreentries. - **Monorepo: lock files moved to
.rlsbl-monorepo/.**rlsbl releaseno longer creates a spurious.rlsbl/directory at the repo root. - Deploy after publish.
rlsbl releaseruns deploy automatically after publish completes. Deploy failures do not undo the release — retry withrlsbl deploy <name>.
#0.18.1
#Features
- **
env_fileconfig key.** Set"env_file": "~/path/to/.env"in.rlsbl/config.jsonto load environment variables before release. Useful for secrets needed by target publish steps (e.g.,CLOUDFLARE_API_TOKENfor docs deploys).
#0.18.0
#Breaking
- **Breaking: removed
--includeand--excludeflags fromrlsbl release.** Target selection for releases is now config-only viarelease_targetsin.rlsbl/config.json.
#Features
- Go binary distribution: Homebrew tap. Go binary projects can now scaffold Homebrew tap support. Set
{"homebrew": {"tap": "homebrew-tap"}}in.rlsbl/config.jsonto add abrews:section to the goreleaser config and passHOMEBREW_TAP_TOKENto the publish workflow. Users install viabrew install user/tap/tool. - Go binary distribution: npm wrapper. Go binary projects can scaffold npm binary wrapper packages (the esbuild/biome/turbo pattern). Set
{"npm_wrapper": {"scope": "@user"}}to generate platform-specific packages for 6 platforms (linux/darwin/win32, x64/arm64), a wrapper package withoptionalDependencies, a bin script, and a publish workflow that packages goreleaser archives into npm packages. Users install vianpm install -g @user/tool.
#0.17.0
#Features
- Cross-target metadata in templates. Multi-target projects now merge template variables from all targets during scaffold, not just the primary. Variables are namespaced by target (
{{pypi.minRequiredPython}},{{npm.minRequiredNode}}). CI templates include runtime version references from project manifests (requires-python,engines.node,godirective,rust-version). - **
rlsbl doctor: metadata consistency checks.** Three new checks validate that package name, license, and description are consistent across targets. Names are normalized per registry conventions (npm scope stripping, PyPI PEP 503, Go module path). Mismatches produce warnings, not failures. - Per-target subdirectory paths. Targets can now live in subdirectories. Configure with
{"name": "npm", "path": "npm/"}in thetargetsarray (plain strings still default to project root). Version sync, build, publish, doctor, status, and scaffold all resolve per-target paths. Useful for projects that ship wrapper packages alongside the main artifact.
#0.16.1
#Features
- npm scaffold: lockfile warning.
rlsbl scaffoldnow warns when an npm project has no lockfile (package-lock.json,pnpm-lock.yaml, oryarn.lock) and adds a "run npm install" step to the next steps output. Prevents broken CI fromnpm cifailing on first push.
#0.16.0
#Features
- **
rlsbl doctorcommand.** Diagnoses release state with 7 checks: stale lock file, version consistency across targets, local/remote tag existence, GitHub Release existence, branch sync, changelog coverage.--fixauto-repairs safe issues (stale locks, missing remote tags, missing GitHub Releases). - Lock file hardening.
.rlsbl/lockadded to gitignore template.atexithandler ensures lock cleanup on process exit. Newis_stale()helper for doctor. - Go scaffold: goreleaser ldflags. Goreleaser template now includes
-X main.version={{.Version}}for version injection at build time. - Go scaffold: dynamic goreleaser main field.
main:field auto-detects root vscmd/<name>/project structure via{{goreleaserMain}}template variable. - Go scaffold: version.go generation. Binary Go projects get a scaffolded
version.gowithReadBuildInfofallback forgo installusers. Libraries are excluded. - npm: package manager detection.
NpmTargetdetects pnpm, yarn, or npm by searching for lock files from the project directory up to the git root. - npm: per-manager CI and publish templates. Separate CI and publish workflow templates for npm, pnpm, and yarn. pnpm templates include
pnpm/action-setup@v4. All CI templates now include an install step before testing.
#0.15.0
#Features
- Deleted built-in config migration engine. Removed
rlsbl/lib/(ConfigMigrator, schema_loader, ~440 LOC), therlsbl configsubcommand tree, and all associated tests (~1,500 LOC). Config migrations are now handled by the externalmigrabletool. - **
rlsbl migratecommand.** Shells out tomigrable migrate --config-dir .rlsbl. Supports--dry-runand--status. Gives install instructions if migrable is not found. - **
--jsonflag forrlsbl status.** Outputs structured JSON with name, version, target, branch, tag, clean, changelog, ci, publish. - Documented hidden flags.
--no-commitand--skip-sharednow appear inrlsbl scaffold --help. - Go root main detection.
rlsbl scaffoldwarns when a Go project has its main package incmd/<name>/instead of the project root, sincego install module@latestwon't work.
#0.14.0
#Features
- Router watch paths. Projects in a monorepo can declare extra file patterns to watch via
--watchonmonorepo addor thewatchkey inworkspace.toml. The CI router generates multi-line path filters including both the project directory and all watch entries. - Swift target split. New
swift-appletarget for Apple-platform Swift projects (macOS-only CI). The existingswifttarget keeps macOS+ubuntu for server-side Swift.swift-applerequires explicit declaration in.rlsbl/config.json(no auto-detection). - Subtree publishing. Monorepo projects with
subtree_remoteconfigured get automatic git subtree pushes after release. The project's subdirectory is split and pushed to a mirror repo with plain semver tags, enabling SPM consumption. A GitHub Release is also created on the mirror. Failures are non-fatal. - Explicit target enforcement.
rlsbl scaffoldnow warns when using auto-detected targets and always writes the detected target to.rlsbl/config.json, ensuring subsequent runs use explicit config. - **
monorepo statusenhanced.** Optional Watch column (path count) and Remote column (subtree URL) appear when projects use these features. - **
monorepo syncwarns for Swift projects** withoutsubtree_remote, since SPM can't resolve monorepo-style prefixed tags. - 12 registered targets. swift-apple joins npm, pypi, go, swift, cargo, deno, docker, hex, maven, spec, docs.
#0.13.1
#Features
- **Monorepo sync adds
working-directory.** Synced CI workflows now includedefaults: run: working-directory: {path}so steps run in the correct project subdirectory. - Monorepo add shows guidance. When no target is detected, the error now suggests which manifest files to create.
- Dynamic merged publish workflows. Multi-target projects now get dynamically generated publish workflows composed from each target's individual template. Supports all 11 targets, replaces the static npm+pypi+go-only merged template.
#0.13.0
#Features
- Monorepo support. New
rlsbl monorepocommand family for managing multi-project repos with independent versioning. Projects remain fully standalone — extracting to a solo repo requires zero config changes. -monorepo initcreates a.rlsbl-monorepo/workspace.tomlworkspace manifest. -monorepo add <path>registers a project (auto-detects target, auto-scaffolds, auto-syncs CI). -monorepo remove <path>unregisters a project. -monorepo listshows all registered projects. -monorepo statusshows version, latest tag, target, and unreleased changelog entries per project. -monorepo synccopies per-project CI workflows to root, rewrites triggers toworkflow_call, generates a CI router (paths-filter dispatch) and publish router (tag-prefix dispatch), sets copies read-only with source header. - Scoped releases in monorepos.
rlsbl releaseinside a monorepo project automatically prefixes tags ([email protected]), scopes version reads/writes and changelog to the project subdirectory, and uses a scoped commit message. - **Monorepo-aware
rlsbl status.** Shows monorepo tag format and project count hint when inside a monorepo project. - Scaffold triggers monorepo sync. Running
rlsbl scaffoldinside a monorepo project automatically syncs workflows to root.
#0.12.0
#Features
- 7 new release targets. swift (SPM), cargo (Rust/crates.io), deno (JSR), hex (Elixir/hex.pm), maven (Gradle/Maven), docker, spec (versioned specifications). Each with CI/publish templates and hybrid publish support.
- Opt-in target config. Projects declare targets in
.rlsbl/config.json"targets"array. Auto-detection remains as fallback for existing projects. - Hybrid publish.
rlsbl releasepublishes locally when ecosystem token is available (NPM_TOKEN, CARGO_REGISTRY_TOKEN, HEX_API_KEY, etc.). Falls back to CI otherwise. - Private registry workflow.
rlsbl scaffold --private(or auto-detected) skips publish.yml, generates a post-release hook that uploads artifacts to GitHub Releases. Prints consumer install instructions. - tomlkit replaces TOML regex.
pyproject.tomlediting (version bumps, keyword injection) now uses tomlkit for correct round-trip editing with comment preservation. Fixes edge cases with[project.urls]sub-tables. - detect_targets() covers all targets. Auto-detection now finds all 11 registered targets, not just npm/pypi/go.
#0.11.3
#Features
- **
--include/--excluderelease flags.** Control which targets run during release. Replaces--skip-docs. - **
release_targetsconfig.** Declare baseline targets in.rlsbl/config.jsonto avoid auto-detect surprises.
#0.11.2
#Features
- Watch re-polls for late-starting workflows. After initial runs complete, waits 5 seconds and re-polls for workflows that started late (e.g., Publish triggered by GitHub Release creation). Fixes missing Publish detection.
#0.11.1
#Features
- Release aborts if behind remote. Fetches origin before releasing and exits if local branch is behind. Use
--skip-remote-checkfor offline releases. - Watch reports which workflows ran. After CI completes, prints a summary table of workflow names and results. Warns if a publish workflow exists on disk but didn't trigger.
#0.11.0
#Features
- Docs system extracted to selfdoc.
rlsbl docscommands removed. DocsTarget now detectsselfdoc.jsonand delegates to theselfdocCLI. Install selfdoc separately for documentation generation. - **
rlsbl checkincludes GitHub repo search.** Shows repo count as informational context (not an availability check) after registry checks. - Codehome target removed. The plugin target and
rlsbl registercommand were premature and have been removed pending a more mature design.
#0.10.1
#Features
- Codehome target rewrite. Now root-scoped: each plugin (or plugin group) lives in its own repo with
plugin.json. Standardv1.2.3tags, no namespacing. Push is delivery. - **
rlsbl registercommand.** Prints the JSON registry entry for the current plugin repo (name, repo URL, description, plugins provided). - Scaffold template for codehome.
rlsbl scaffold --target codehomecreates CI workflow that validates plugin.json. - Plugin validation. Release validates
plugin.jsonhas required fields (name, version, description) and valid semver.
#0.10.0
#Features
- Codehome plugin target.
--target codehome --scope plugins/<name>releases individual plugins from a monorepo. Reads/writesplugin.toml, creates namespaced tags ([email protected]). - Docs target. Auto-generate documentation from Python docstrings and deploy to Cloudflare Pages or GitHub Pages.
rlsbl docs init/build/serve/deploycommands. Zero external dependencies (stdlibast+ built-in MD/HTML converter). - **
rlsbl targetscommand.** Lists all available targets with detection status, scope type, and version file. - Multi-target release. Secondary root-scoped targets (e.g., docs) auto-run build+deploy during release. Use
--skip-docsto opt out. - **
--targetand--scopeCLI flags.**--targetselects the release target explicitly.--scoperestricts operations to a subdirectory for subdir-scoped targets. - Scoped release safety. Validates scope path exists, includes pyproject.toml in commit for plugin targets, warns when
--scopeis used with root-scoped targets. - **
--registrydeprecated.** Use--targetinstead. Prints a deprecation warning when used.
#0.9.1
#Features
- **
rlsbl config showsubcommand.** Barerlsbl confignow prints help; useconfig showfor project info. - Race condition parsing fix. Porcelain parser handles stripped leading whitespace correctly.
#0.9.0
#Features
- **
rlsbl unreleasedcommand.** Lists commits since last tag, cross-references CHANGELOG entries, reports coverage status. Supports--jsonfor machine-readable output. - **
rlsbl prscommand.** Lists open GitHub pull requests for the current repo. - Config management system.
rlsbl config init/migrate/statussubcommands for managing project config with schema-driven migration (deep merge, flat merge, list-by-key merge strategies, versioned migrations, atomic writes). - Scaffold auto-commits. Created files are committed automatically (use
--no-committo opt out). Runs config migrations when.rlsbl/config-schema.jsonexists. - Parallel watch.
rlsbl watchpolls CI runs concurrently (total time = max of all runs, not sum). - Parallel variant checking.
rlsbl checkuses ThreadPoolExecutor for concurrent registry queries. - Advisory lockfile.
.rlsbl/lockprevents concurrent release/scaffold operations. - **
rlsbl undoimprovements.** Auto-pushes revert commit (with confirmation prompt, or automatic with--yes). Prints structured failure summary table with remediation commands on partial failure. - Pre-release suffix support.
bump_versionhandles versions like1.0.0-beta.1. - **
--forceno longer overwrites user-owned files.** CHANGELOG.md, LICENSE, and hooks are preserved even with--force. - **Pre-release hook receives
RLSBL_VERSION.** Matches the existing post-release hook behavior. - Release race condition fix. Aborts if unexpected files are modified before commit.
- Top-level error handler sanitized. No longer exposes sensitive CalledProcessError details.
- Discover hardened. Pagination capped at 20 pages; retries once on HTTP 403 with
Retry-Afterheader. - **
record-gifvalidates flags.** Clear error message on non-integer flag values. - Release prompt mentions ecosystem tagging when enabled.
- npm check timeout. Variant checking has 10-second subprocess timeout.
#0.8.3
#Features
- Pre-release hook runs Python checks before npm (faster failure)
- Go CI template uses
go-version-file: go.modinstead of hardcoded versions; adds-raceflag - Node.js 24 in all CI/publish templates (dropped Node 18 EOL)
#Fixes
- Fix watch: resolve short SHAs to full 40-char (
gh run list --commitrequires it)
#0.8.2
#Features
- Handle KeyboardInterrupt in watch command (clean exit, no stack trace)
- Escape AppleScript strings in watch notifications (prevents injection via git tags)
- Clear error when
--registryis missing a value - Resolve project config path at call time (not module import time)
- Add
--width,--height,--font-size,--durationflags to record-gif
#0.8.1
#Features
- Templates included in wheel. Moved
templates/into therlsbl/package so non-editable installs (pip, pipx) get them. Previouslyrlsbl scaffoldcrashed on PyPI installs. - **
undochecks prerequisites.** Now verifies gh CLI auth and clean working tree before proceeding. - TOML trailing comma fix. Adding the rlsbl keyword no longer produces a double comma.
- **Pagination URL validation in
discover.** Only followsLinkheader URLs pointing toapi.github.com. - **
.rlsbl/versionincluded in release commit.** No more orphaned version marker changes. - Non-ASCII preserved in package.json.
json.dumpsnow usesensure_ascii=False.
#0.8.0
#Features
- Universal three-way merge for scaffold updates. Replaced all format-specific merge strategies (YAML job-level, JSON deep-merge, line-based, section append) with
git merge-file. Bases are stored in.rlsbl/bases/at scaffold time. On--update, user customizations and template updates merge cleanly; conflicts get git-style conflict markers. - **Removed
ruamel-yamldependency.** No longer needed since YAML-aware merging is replaced by three-way text merge. - Detailed scaffold output. Every file now shows its action: created, updated, merged, unchanged, user-owned, or CONFLICTS.
#0.7.0
#Features
- **Removed
check-prscommand.** Was a useless wrapper aroundgh pr list. - **JSON deep-merge for
.claude/settings.json.** Scaffold now merges new template keys into existing user settings instead of skipping the file. User values are preserved. - YAML job-level merge for CI workflows.
ci.ymlandpublish.ymlare now merged at the job level: rlsbl-managed jobs are updated, user-added jobs are preserved. Usesruamel.yamlfor comment-preserving round-trip parsing. - Explicit USER_OWNED category.
CHANGELOG.md,LICENSE, and hooks are formally marked as user-owned and never overwritten. - LICENSE year update.
scaffold --updateextends the copyright year range to the current year.
#0.6.0
#Features
- Scripts moved to subcommands.
check-prs.sh,record-gif.sh, andpre-push-hook.share no longer scaffolded intoscripts/. They are now built-in subcommands:rlsbl record-gif,rlsbl pre-push-check. - **
rlsbl watchcommand.** Monitors all CI runs for a commit, prints results to stderr, sends desktop notification, exits 1 on failure.rlsbl releaseprintsWatch CI: rlsbl watch <sha>for easy invocation. - Removed built-in background CI watcher from
rlsbl release. Userlsbl watchexplicitly instead. - **Hooks moved to
.rlsbl/hooks/.**pre-release.shandpost-release.shmoved fromscripts/to.rlsbl/hooks/.rlsbl releaselooks for hooks there. - Pre-push hook is a one-liner.
.git/hooks/pre-pushnow callsexec rlsbl pre-push-check "$@"instead of being a full script copy. Updates happen viauv tool upgrade rlsbl, not re-scaffolding.
#0.5.2
#Features
- Version detection reads source tree first.
__version__now readspyproject.tomldirectly when running from source, fixing stale metadata from editable installs. - CLAUDE.md template is registry-specific. Publish setup instructions (NPM_TOKEN, Trusted Publishing, GoReleaser) now match the project's registry instead of always showing NPM_TOKEN.
- Gitignore merge normalizes trailing slashes.
*.egg-info/and*.egg-infoare now recognized as duplicates during scaffold merge. - **
record-gif.shno longer hardcodes/tmp/.** Uses baremktempfor portability. - **Go check hidden by default in
rlsbl check.** Only shown with--registry go. Labels changed from "available"/"taken" to "not found"/"exists" since Go modules use repository paths. - **Auth hint on 403 in
rlsbl discover.** Suggestsgh auth loginwhen rate-limited.
#0.5.1
#Features
- CI watcher prints to stderr. The background CI watcher now writes results to inherited stderr instead of attempting tty detection. AI agents and terminal users both see CI pass/fail in their output stream. On failure, the GitHub Actions run URL is printed.
- **
*.local-onlygitignore pattern.** Scaffolded.gitignorenow includes*.local-only. Use a.local-only/directory or*.local-onlysuffix to keep files out of version control without per-file gitignore entries.
#0.5.0
#Features
- Post-release hooks.
scripts/post-release.shruns after a successful release withRLSBL_VERSIONenv var set. Non-fatal (release is already complete). Scaffolded viarlsbl scaffold. - CI watcher. After pushing, rlsbl spawns a background process that watches CI via
gh run watchand sends a desktop notification (notify-send on Linux, osascript on macOS) when CI passes or fails. - Ecosystem discoverability:
rlsbl discovercommand lists all rlsbl-tagged projects via GitHub topics rlsbl configshows ecosystem tagging status and source--quietflag is respected by all tagging output- Auto-tagging:
scaffoldandreleaseinject"rlsbl"keyword into package.json/pyproject.toml and add therlsblGitHub topic - Opt-out via
--no-tagflag, project config (.rlsbl/config.json), or user config (~/.rlsbl/config.json) rlsbl discover --minefilters to the authenticated user's repos
#0.4.2
#Features
- Configurable push timeout via
RLSBL_PUSH_TIMEOUTenv var (default 120s), fixing timeouts on repos with slow pre-push hooks - Print a note when
RLSBL_PUSH_TIMEOUToverrides the default
#Fixes
- Fix own pre-push hook missing VERSION file detection for Go projects
#0.4.1
#Features
- Go adapter uses VERSION file as version source (not git tags)
- First release bootstraps from VERSION without bumping
- Pre-release.sh template auto-detects Go/npm/Python and runs appropriate checks
- Pre-push hook template supports Go VERSION file
- GoReleaser NEXT_STEPS clarified (CI handles it, no local install needed)
#0.4.0
#Features
- Go project support: scaffold with GoReleaser, CI, and publish workflows
- Version-file-less registries: release skips commit step when version is the git tag
- Go name availability check via pkg.go.dev
- Cross-compilation template (linux/darwin/windows x amd64/arm64)
#0.3.1
- No user-facing changes.
#0.3.0
#Features
- Confirmation prompt on release (skip with --yes)
configcommand: show detected registries, scaffolding state, workflows, hooksundocommand: revert a botched release (deletes tag, reverts commit, deletes GitHub Release)- Merged publish workflow for dual-registry projects (scaffold generates one file with both npm + pypi jobs)
#0.2.0
#Features
- CLI redesign:
--registryflag replaces positional registry argument - Rename
check-namecommand tocheck - All commands are top-level:
rlsbl release,rlsbl check,rlsbl scaffold,rlsbl status
#Fixes
- Fix astral-sh/setup-uv version (v7, not v8)
#0.1.1
#Fixes
- Fix astral-sh/setup-uv version (v8 tag doesn't exist, use v7)
#0.1.0
#Features
- 4 top-level commands:
release,status,scaffold,check-name - Initial release as
rlsbl(renamed from share-it-on) - Pure Python (stdlib only, Python 3.11+, tomllib for TOML parsing)
- Auto-detects registries from project files (package.json, pyproject.toml)
- Release syncs version across all detected version files
- Context-aware scaffold: appends CLAUDE.md, merges .gitignore, preserves custom CI
- Hash-based
--updatemode detects customized files - Pre-release hook, pre-push changelog enforcement
- Dual-publish CI: npm (token) + PyPI (OIDC Trusted Publishing)
- Also installable via npm (thin Node wrapper)