Skip to content
rlsbl.evidence_gate
On this page

Layered evidence gate that checks multiple sources to determine whether a release was published, producing CLEARED or BLOCKED verdicts.

#rlsbl.evidence_gate

#rlsbl.evidence_gate

Layered evidence gate that checks multiple sources to determine whether a release was published, producing CLEARED or BLOCKED verdicts.

The gate checks multiple evidence sources in order and produces a verdict: CLEARED (safe to undo) or BLOCKED (may have been published).

Evidence sources are extensible -- new sources can be added by implementing the EvidenceSource protocol and registering them in the sources list.

Current sources:

  • RegistryProbeSource: uses publication_probe() from target implementations

(npm, pypi, go)

  • CachedRegistrySource: asks the registry itself, for targets whose primary

probe answers from somewhere else (Go's asks the git remote; see the class docstring)

Future sources (not yet implemented):

  • CIPublishRunSource: checks GitHub Actions workflow conclusions
  • LocalStateSource: checks in-progress.json completed_steps
  • GitHubReleaseAssetSource: checks for uploaded assets

#Verdict

Result of the evidence gate.

#EvidenceKind

What kind of evidence a source provides.

#Evidence

A single piece of evidence from one source about one target.

Attributes:

  • source: Name of the evidence source (e.g. "registry_probe").
  • target: Name of the target (e.g. "npm", "pypi").
  • kind: Whether this evidence says PUBLISHED, UNPUBLISHED, or INCONCLUSIVE.
  • message: Human-readable detail.

#to_dict

python
def to_dict(self)

#EvidenceSource

Protocol for evidence sources.

#name

python
def name(self) -> str

Unique name for this evidence source.

#gather

python
def gather(self, targets, project_dir, version, ctx=None) -> list[Evidence]

Gather evidence for the given targets and version.

Args:

  • targets: list of target objects from TARGETS registry.
  • project_dir: path to the project directory.
  • version: the version string (without 'v' prefix).
  • ctx: optional project context.

Returns:

  • list of Evidence objects, one per target checked.

#RegistryProbeSource

Evidence source using target.publication_probe().

#name

python
def name(self)

#gather

python
def gather(self, targets, project_dir, version, ctx=None)

#CachedRegistrySource

Evidence source asking the registry itself, for targets that have a second probe.

A target's primary publication_probe does not have to ask the registry. Go's asks the GIT REMOTE whether the version's tag exists -- the right question for "did we tag this?", and the wrong one for "is this out in the world?": proxy.golang.org caches a module version PERMANENTLY the first time anyone resolves it, so a tag deleted after someone fetched it is gone from the remote and still served by the proxy forever. Reading the tag's absence as "never published" is how a destructive operation gets cleared for a version consumers can still download today.

Which targets have such a second probe is the target's own answer (supports_cached_registry_probe), not a name this module knows.

The source contributes only two kinds, never three:

  • PUBLISHED -- the registry serves it. Under the gate's rule that alone

blocks, which is the whole point.

  • INCONCLUSIVE -- it does not, or could not be asked.

Absence is deliberately never reported as UNPUBLISHED: a lazily-indexed registry is absent-by-default for a version nobody has fetched, so registry lag alone must never be the evidence that clears a deletion. Something that really did observe the version's absence has to say so.

#name

python
def name(self)

#gather

python
def gather(self, targets, project_dir, version, ctx=None)

#GateResult

Result of running the evidence gate.

Attributes:

  • verdict: CLEARED or BLOCKED.
  • evidence: all evidence gathered from all sources.
  • reason: human-readable explanation of the verdict.

#to_dict

python
def to_dict(self)

#run_evidence_gate

python
def run_evidence_gate(targets, project_dir, version, ctx=None, sources=None)

Run the layered evidence gate to determine if a release is safe to undo.

Decision rule:

  • CLEARED: at least one authoritative source says UNPUBLISHED and none says PUBLISHED.
  • BLOCKED: any source says PUBLISHED.
  • BLOCKED (hard error): no authoritative evidence at all (all INCONCLUSIVE).

Args:

  • targets: list of target objects from TARGETS registry.
  • project_dir: path to the project directory.
  • version: the version string (without 'v' prefix).
  • ctx: optional project context.
  • sources: list of EvidenceSource objects; defaults to DEFAULT_SOURCES.

Returns:

  • GateResult with verdict and evidence.

#write_undo_audit

python
def write_undo_audit(audit_dir, version, tag, gate_result, operator_context=None)

Write an audit record for a non-latest undo operation.

Creates undo-audit.json in the given directory with per-target evidence, verdict, and operator context.

Args:

  • audit_dir: directory to write the audit file to (e.g. .rlsbl/ or

the releasable dir).

  • version: the version that was undone.
  • tag: the git tag that was deleted.
  • gate_result: the GateResult from run_evidence_gate().
  • operator_context: optional dict with additional context.

Returns:

  • path to the written audit file.

Raises:

  • RlsblError: an existing undo-audit.json that cannot be parsed. The

append is a whole-file rewrite, so continuing would replace the records already in it.

More tools from this site

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