rlsbl v0.113.0 /Check system
On this page

Reference for rlsbl checks across 6 tags: check metadata, severity, purity (which checks a preview runs), target applicability, and the path-capable lint, format and type-check built-ins.

#Check system

rlsbl includes 76 checks across 9 tags.

Run checks via the rlsbl check command. Checks are organized across 6 primary tags (project, release, changelog, workspace, quality, prepush) and validate project metadata, release state, changelog structure, workspace integrity, code quality, and pre-push enforcement. Four additional untagged checks run only with --all or --name. Three further tags exist for pipeline and target-specific grouping: preflight and preflight-changelog are run internally by rlsbl release run, and maven groups the Maven-specific check.

#Running checks

$_ bash
# Run all checks
rlsbl check --all

# Run all checks in a tag
rlsbl check --tag changelog

# Run a single check by name
rlsbl check --name version-consistency

#Check results

Each check returns one of four statuses that determine how the result is displayed and whether it blocks the release pipeline. The severity level (error or warn) is declared per-check in the check metadata stored in checks.toml and controls which status is reported on failure versus advisory findings:

Check results
StatusMeaningEffect
passCheck passedNo action needed
failCheck failedBlocking error -- must be fixed before release
warnAdvisory findingInformational -- does not block release
skipNot applicableCheck cannot run for this project (e.g., workspace check in a standalone project)

Severity is declared per-check in metadata. A check with severity = "error" reports fail on failure; one with severity = "warn" reports warn.

#Tags

Tags
TagPurposeCheck count
projectProject-level metadata, config schema, version consistency21
releaseGit tag and GitHub Release validation5
changelogJSONL changelog validation and structure11
workspaceMonorepo workspace integrity and dependency rules17
qualityCode quality, dependency analysis, scaffold hygiene16
prepushPre-push enforcement: changelog coverage, gitignore guard, manual-push warning, tests6

Some checks carry multiple tags, so they appear in multiple tag counts: test-suite is tagged prepush and quality, test-suite-workspace is tagged prepush and workspace, and scaffold-conflicts is tagged project, prepush, and release. Four checks (layers-violations, deps-unused, deps-undeclared, deps-stale) have no tag and only run with --all or --name. Internal tags used by the release pipeline: preflight (14 checks: library-lint, test-suite, dev-overlay-drift, maven-central-metadata, wrapper-producer, strictspec-certificate-gate, stricttest-floor, dep-floors, and the six path-capable tool checks lint, lint-scope-guard, format, format-scope-guard, type-check, type-check-scope-guard) and preflight-changelog (9 checks: the structural changelog checks, i.e. all changelog checks except changelog-entry and changelog-format-version). The maven tag groups maven-central-metadata.

#Project checks

Project checks
CheckSeverityDescription
lockwarnDetects stale lock state in .rlsbl/
version-consistencyerrorProject version matches across all target files (e.g., pyproject.toml, package.json, .rlsbl/version)
name-consistencywarnPackage name is consistent across manifest files
description-consistencywarnPackage description is consistent across manifest files
license-fileerrorA LICENSE file exists in the project root
license-consistencywarnLicense identifier matches across manifest files
config-schemaerror.rlsbl/config.json conforms to the expected schema (no unknown keys, correct types)
private-hook-staleerrorDetects leftover private repo hook files that should be deleted
publish-mode-workflowerrorpublish_mode: "none" repos must not have a publish workflow that pushes to public registries
npm-private-mismatcherrorpackage.json private field matches .rlsbl/config.json private flag (npm targets only)
target-version-readableerrorVersion can be read from all declared target files
dunder-version-missingerrorPyPI targets that keep a version constant in source must use __version__
selfdoc-version-drifterrorselfdoc-generated version references match the actual project version
scaffold-conflictserrorUnresolved git merge conflict markers in scaffold files (managed-files registry, .github/workflows/, all of .rlsbl/); also tagged prepush and release
cross-repo-path-sourceserror[tool.uv.sources] path entries in the committed pyproject.toml must resolve inside the repository (in-repo paths and workspace = true are legal; local overrides belong in dev-sources.toml.local-only). Also enforced unconditionally by rlsbl release run
dev-overlay-drifterrorPackages recorded in the rlsbl dev sync sentinel are still editable installs of their declared checkouts (a bare uv sync silently replaces an overlay with the released wheel)
requires-serviceserrorCI service containers declared under services/test_env are actually provisioned in the rendered CI workflow
wrapper-producererrorEvery launcher pipeline's wraps reference names a real binary-artifact pipeline, and the wrapped target's manifest still carries the shim-critical fields
strictspec-certificate-gateerrorA configured strictspec diff certificate reports no violated (or unsupported-and-unadjudicated) claim. Skips when the project has no strictspec_gate section
stricttest-floorerrorAn adopted sandboxed test runner works: the test_sandbox runner script exists and is executable, the config family is complete, and every CI workflow the family names actually invokes the runner. Skips when the project has adopted neither the test_sandbox family nor the stricttest plugin
dep-floorserrorEcosystem-internal dependencies declare a >= floor at the version the lock resolves. Compares pyproject.toml against uv.lock and package.json against package-lock.json; Go is structurally satisfied (require lines are the minimums). Skips when the project has no internal_dep_floors config key

#Release checks

Release checks
CheckSeverityDescription
local-tagwarnA git tag exists locally for the current version
remote-tagwarnThe version tag has been pushed to the remote (requires network)
github-releasewarnA GitHub Release exists for the current version tag (requires network)
branch-syncerrorLocal branch is not behind the remote tracking branch (requires network)

scaffold-conflicts (see project checks) is also tagged release. Release checks form a dependency chain: version-consistency -> local-tag -> remote-tag -> github-release. If an upstream check fails, downstream checks are skipped.

#Changelog checks

Changelog checks
CheckSeverityDescription
changelog-hasheserrorEvery commit hash in JSONL entries resolves via git rev-parse
changelog-rangeerrorEvery resolved hash falls within the unreleased range (after the last version tag)
changelog-coverageerrorEvery unreleased commit appears in at least one JSONL entry
changelog-orphanserrorNo entries where ALL hashes are unresolvable (stale from rebased/amended commits)
changelog-schemaerrorUser-facing entries have description and type; type is one of feature/fix/breaking
changelog-user-facingwarnAt least one entry is user-facing (hard error during release, warning in check mode)
changelog-batch-commitserrorNo single entry references more commits than max_commits_per_entry (default 5)
changelog-batch-entrieserrorNo single commit appears in more entries than max_entries_per_commit (default 5)
changelog-entrywarnCHANGELOG.md contains an entry for the current project version
changelog-format-versionwarnThe repo has recorded a changelog_format_version_enforced decision (enabling the gate below, or staying in legacy mode deliberately)
changelog-format-version-gateerrorWhen enforcement is on, every line in unreleased.jsonl and every finalized x.y.z.jsonl carries a supported format_version. Skipped while enforcement is off

Dependencies: changelog-range and changelog-coverage depend on changelog-hashes (hash resolution must succeed first).

#Workspace checks

Workspace checks
CheckSeverityDescription
workspace-ci-routererrorThe generated ci-router.yml exists at the repo root (it holds every project's inlined jobs; per-project coverage is workspace-ci-synced)
workspace-ci-syncederrorEach in-scope project's CI jobs are inlined into the shared ci-router.yml
workspace-targetserrorEach project's declared target matches its actual manifest files
workspace-unregisterederrorNo project directories with manifest files exist outside of workspace.toml
workspace-stale-entrieserrorNo workspace.toml entries point to directories that no longer exist
dev-only-boundaryerrorNo non-dev-only project has a runtime dependency on a dev-only project
unversioned-boundaryerrorNo releasable project has a runtime dependency on an unversioned project (releasable = false, not dev-only)
dead-workspace-packageswarnDetects workspace packages with no commits since their last release
subtree-remote-reachableerrorConfigured subtree remote URLs are reachable (requires network)
workspace-unbuildableerrorWorkspace members build under uv sync --all-packages (pypi workspaces only)
scaffold-gitignore-stalewarnWorkspace project .gitignore files contain all rlsbl-managed entries
root-rlsbl-conflicterrorRoot .rlsbl/ does not coexist with .rlsbl-monorepo/
go-companion-tagswarnNon-private Go members of releasables have companion tags for the current version; a broken member config is a hard failure
releasable-residueerrorReleasable member packages carry no per-package release state (.rlsbl/changes/, .rlsbl/releases/, .rlsbl/version, etc.); hooks/ and root-path members are exempt
member-pytest-configerrorWhen the workspace root has a conftest.py, every member with a tests/ directory pins its own pytest rootdir, so a member run cannot escape into the root config
mixed-tag-schemeserrorNo member directory declares both Go's path-based {path}/v* tags and {name}@v* tags, which would make the publish-router prefix ordering-dependent

test-suite-workspace (see prepush checks) is also tagged workspace.

#Quality checks

Quality checks
CheckSeverityDescription
dead-moduleswarnDetects source modules with no inbound imports (unreachable code)
dead-modules-staleerrorEvery path declared in dead-modules.toml still exists, so an exclusion cannot silently outlive the file it excused
circular-depswarnDetects circular import dependencies between modules
library-linterrorRuns lint rules for library projects (API surface, exports)
ruff-linterrorProject passes ruff lint checks (skipped when ruff is not installed)
linterrorRuns ruff check over the paths declared in the checks.lint config block; also tagged preflight. Skips when the config block is absent
lint-scope-guarderrorruff config carries no include/extend-include competing with checks.lint.paths; also tagged preflight
formaterrorRuns ruff format --check over the paths declared in the checks.format config block; also tagged preflight. Skips when the config block is absent
format-scope-guarderrorruff config carries no include/extend-include competing with checks.format.paths; also tagged preflight
type-checkerrorRuns mypy over the paths declared in the checks.type-check config block; also tagged preflight. Skips when the config block is absent
type-check-scope-guarderrormypy config carries no files/packages/modules competing with checks.type-check.paths; also tagged preflight
deps-runtime-test-onlywarnRuntime dependencies that are only imported in test files
deps-dev-in-liberrorDev dependencies used in library source (should be runtime deps)
scaffold-unreplaced-varserrorLeftover {{...}} template placeholders in workflow files
maven-central-metadataerrorMaven Central publishing requirements are met (POM metadata, sources/javadoc jars); also tagged maven

test-suite (see prepush checks) is also tagged quality.

#Prepush checks

Prepush checks
CheckSeverityDescription
prepush-changelog-coverageerrorVerifies every pushed commit has a JSONL changelog entry
prepush-gitignore-guarderrorBlocks push if rlsbl-managed files are gitignored
prepush-manual-warningwarnWarns on manual push to release branch (non-blocking)
test-suiteerrorRuns project tests (pytest / go test / npm test)
test-suite-workspaceerrorRuns tests for affected workspace projects (monorepo only)

scaffold-conflicts (see project checks) is also tagged prepush. Dependencies: test-suite and test-suite-workspace both depend on prepush-changelog-coverage -- fast checks fail first, so the test suite is skipped if changelog coverage fails. test-suite is also tagged quality, so it runs under both rlsbl check --tag prepush and rlsbl check --tag quality.

#Untagged checks

These 4 checks have no tag assignment and run only when explicitly requested via --all or --name. They are excluded from tag-based runs because they require specific project configurations (layer rules, workspace manifests) or have longer execution times:

Untagged checks
CheckSeverityDescription
layers-violationserrorDependency direction violates architectural layer rules defined in workspace.toml
deps-unusederrorDeclared dependencies that are never imported
deps-undeclarederrorImported packages that are not declared as dependencies
deps-staleerrorWorkspace dependency versions that are outdated relative to available versions

#Target applicability

Not all checks apply to all 18 targets. Each check declares its applicability as one of three categories, which determines whether it runs for a given project based on the project's detected targets:

  • Universal (None): runs for any target -- most project, release, and changelog checks
  • Workspace-only ("workspace"): runs only in monorepo workspaces, target-agnostic
  • Target-specific (frozenset): requires specific language targets with import scanners or AST analysis
Target applicability
Checkpypigonpmdartmaven
circular-depsyesn/ayesyesyes
cross-repo-path-sourcesyesnononono
dead-modulesyesyesyesyesyes
dead-modules-staleyesyesyesyesyes
dep-floorsyesyesyesnono
deps-dev-in-libyesyesyesyesyes
deps-runtime-test-onlyyesyesyesyesyes
deps-undeclaredyesyesyesyesyes
deps-unusedyesyesyesyesyes
dev-overlay-driftyesnononono
dunder-version-missingyesnononono
formatyesnononono
format-scope-guardyesnononono
library-lintyesyesyesnoyes
lintyesnononono
lint-scope-guardyesnononono
maven-central-metadatanonononoyes
npm-private-mismatchnonoyesnono
ruff-lintyesnononono
test-suiteyesyesyesnoyes
type-checkyesnononono
type-check-scope-guardyesnononono

#Excluded targets

Some checks explicitly exclude specific targets where the compiler or language toolchain already enforces the same constraint natively, making rlsbl's check redundant. These exclusions prevent false positives and unnecessary warnings:

Excluded targets
CheckExcluded targetReason
circular-depsgoGo compiler rejects circular imports

#Check metadata

Checks are declared in rlsbl/data/checks.toml with metadata that controls execution order, dependency resolution, and result severity. Each check entry has the following fields that the check runner uses to determine when and how to execute the check:

Check metadata
FieldTypeDescription
tagsarray of stringsWhich tags include this check (empty = untagged, only runs with --all or --name)
severity"error" or "warn"Whether failure blocks (fail) or advises (warn)
fastboolWhether the check completes quickly (used for prioritization)
pureboolWhether the check starts only programs on the observe allowlist (see Purity below)
needs_networkboolWhether the check requires network access (e.g., GitHub API calls)
depends_onarray of stringsOther checks that must pass first (skipped if dependency fails)

Checks are implemented via the @app.error_check("<name>") and @app.warn_check("<name>") decorators in the rlsbl/checks/ package (one module per tag, e.g. project.py, release.py, workspace.py), which register the function with strictcli's check system. The name passed to the decorator must match the key in checks.toml, and the decorator chosen must match that entry's severity.

#Purity

A pure check starts only read-only programs on the observe allowlist. The allowlist is rlsbl/observe_allowlist.py, whose written standard is no user-visible mutation: ref updates, index writes and credential emission are refused there, so any program that reaches the list changes nothing a user would notice. A check that starts no program at all is trivially pure.

A check is impure when it starts a program that is not on that list. Every impure check today runs a tool that writes: ruff rewrites files, uv sync materializes an environment, the test suites and gradle build.

Purity decides what a preview does. Under rlsbl release run --dry-run the preflight runs its pure checks for real and lists the impure ones as would run: <name> (impure) -- so a preview reports real findings from everything that can be run without changing anything, and is honest about the rest.

This rule replaced an older one, "the check starts no program at all". That rule forced nine checks that spawn only read-only local git (the changelog validators, the two pre-push checks, workspace-unregistered, go-companion-tags) to be declared impure, and it quietly declared two checks pure that do spawn: local-tag runs git tag --list, and config-schema can reach go list on its error path. All eleven are pure under the current rule, and are now declared so deliberately rather than by accident.

The declaration is verified, not trusted: tests/test_check_purity.py executes every pure-declared check under an effects observer and fails on any spawn whose argv matches no allowlist prefix.

needs_network is orthogonal: it says whether a check needs the network to answer at all, never whether it may mutate. A pure check may be a network read.

#Examples

#Running all checks before a release

$_ bash
rlsbl check --all
#   lock .......................... pass
#   version-consistency ........... pass
#   config-schema ................. pass
#   license-file .................. pass
#   scaffold-conflicts ............ pass
#   cross-repo-path-sources ....... pass
#   changelog-hashes .............. pass
#   changelog-range ............... pass
#   changelog-coverage ............ FAIL
#     Uncovered commits:
#       a1b2c3d  Add retry logic
#       e4f5g6h  Fix timeout bug
#   changelog-schema .............. pass
#   changelog-user-facing ......... warn  No user-facing entries
#   local-tag .................... warn  No tag for v0.5.3
#   test-suite ................... pass
#
#   12 passed, 1 failed, 2 warnings

#Investigating a specific check failure

$_ bash
# Run just the failing check to see detailed output
rlsbl check --name changelog-coverage
#   changelog-coverage ............ FAIL
#     Uncovered commits:
#       a1b2c3d  Add retry logic
#       e4f5g6h  Fix timeout bug
#     Fix: run `rlsbl changelog add --commits <hash> ...` for each

# Fix it
rlsbl changelog add --commits a1b2c3d --description "Add retry logic to HTTP client" --type feature
rlsbl changelog add --commits e4f5g6h --description "Fix timeout crash on slow connections" --type fix

# Verify the fix
rlsbl check --name changelog-coverage
#   changelog-coverage ............ pass

#Checking workspace integrity in a monorepo

$_ bash
rlsbl check --tag workspace
#   workspace-ci-router ........... pass
#   workspace-ci-synced ........... pass
#   workspace-targets ............. pass
#   workspace-unregistered ........ FAIL
#     packages/new-lib/ has pyproject.toml but is not in workspace.toml
#   workspace-stale-entries ....... pass
#   dev-only-boundary ............. pass
#   dead-workspace-packages ....... warn  library 'old-utils' not imported by any workspace package
#
#   Fix: run `rlsbl monorepo add --path packages/new-lib --target pypi`

#Pre-push check output

$_ bash
# Triggered automatically by git push, or run manually:
rlsbl check --tag prepush
#   prepush-changelog-coverage .... pass
#   prepush-gitignore-guard ....... pass
#   prepush-manual-warning ........ skip  (not a release branch push)
#   test-suite .................... pass
#   scaffold-conflicts ............ pass
Search