Skip to content

Releases and Safeguards

Allen Byrd edited this page Jun 2, 2026 · 1 revision

Releases & Safeguards — how RegRails ships, and what guards the ship

RegRails publishes the way a supply-chain-conscious project should: tag-driven, with no long-lived PyPI token, and with PEP 740 attestations + SLSA build provenance on every artifact. This page documents the release flow that exists today, the CI gates that run on every change, the post-publish verification step, and a set of planned safeguards (clearly marked as not-yet-shipped) borrowed from the discipline of the author's larger project, Evidentia.

Sources: .github/workflows/release.yml, .github/workflows/test.yml.


The tag-driven release flow (what ships today)

Publishing is gated on pushing a version tag — pushing to main does not publish anything. From release.yml, which triggers only on: push: tags: ["v*"]:

flowchart LR
  T["git tag -s vX.Y.Z<br/>(signed) + push"] --> W[release.yml fires on v* tag]
  W --> B["uv build<br/>(sdist + wheel)"]
  B --> A["actions/attest-build-provenance<br/>(SLSA provenance over dist/*)"]
  A --> P["pypa/gh-action-pypi-publish<br/>Trusted Publisher (OIDC) + PEP 740"]
  P --> PyPI[(PyPI: regrails)]
Loading

Step by step, exactly as the workflow runs:

  1. Trigger. A v* tag push — and only that — starts the release job. The workflow header says so explicitly: "Push-to-main does NOT publish; only a v tag does."*
  2. Build. uv build produces the sdist and wheel into dist/.
  3. Attest (SLSA). actions/attest-build-provenance@v1 attaches a SLSA build-provenance attestation over dist/*, verifiable later with gh attestation verify.
  4. Publish. pypa/gh-action-pypi-publish uploads to PyPI via a Trusted Publisher (OIDC)id-token: write — so there is no long-lived API token stored anywhere, and PyPI records PEP 740 attestations for the artifacts.

The job requests only the minimal permissions it needs (id-token: write for the OIDC Trusted Publisher + PEP 740, attestations: write for SLSA, contents: read).

Honesty note on the signed tag. The release flow is designed around a signed tag (git tag -s), matching the author's standing practice on Evidentia. The enforcement of "tags must be signed" is a repository ruleset concern, listed under Planned safeguards below — the workflow itself reacts to any v* tag; it does not currently reject an unsigned one.


What CI runs today (on every push / PR to main)

The test.yml workflow runs on every push and pull request to main (and workflow_dispatch), with a 10-minute timeout and contents: read only. Its gates, in order:

Gate Command What it proves
Lint uv run ruff check src tests style / lint clean
Types uv run mypy mypy-strict clean
Tests uv run pytest -ra -q the unit + golden + tamper-detection suite passes
Faithfulness gate uv run regrails check faithfulness --verbose every encoded rule's source_quote is verbatim CFR (see Methodology)
Coverage matrix uv run regrails coverage report the rule→scenario matrix regenerates (31/37, 6 gaps)
Provenance chain uv run regrails audit verify demo/recorded-runs/decisions.chain.jsonl the committed decision chain is intact (see Provenance & Audit)

The notable point: the faithfulness gate, the coverage matrix, and the hash-chain verification are CI gates, not just docs. A change that broke a verbatim quote, dropped coverage silently, or corrupted the committed decision chain would fail the build. There is also a separate action-smoke workflow exercising the reusable GitHub Action.


The reusable GitHub Action (how downstream pipelines gate on a decision)

RegRails ships a composite GitHub Action so another project can put the guardrail in its own CI: it installs regrails from PyPI (engine-only, no API key), runs regrails decide on a supplied query, and fails the job on a configurable set of outcomes — defaulting to block,escalate_human_review. It exposes outcome + risk-tier outputs and can write SARIF. This is the same deny-by-default stance as the engine, applied to a build pipeline.


Step-7 post-publish verification

After a release, the artifacts are verified from the published source, not from the local build — the same "Step 7" discipline used on Evidentia. The post-publish checks include:

  • PEP 740 attestations verified against the published wheel (e.g. pypi-attestations verify pypi --repository <github-repo>).
  • SLSA build provenance verified with gh attestation verify.
  • Fresh-install smokepip install regrails in a clean environment, then run the offline commands (regrails check faithfulness, regrails decide ..., regrails coverage report) to confirm the published package behaves as documented.

The principle: a green build is necessary but not sufficient — the claim "the signed, attested package on PyPI does what the README says" is only proven by verifying the published artifact end to end.


Planned safeguards (NOT yet shipped)

The following are planned hardening items, carried over from the discipline of the author's Evidentia release pipeline (where most already run). They are listed here for transparency about the intended trajectory — they are aspirational for RegRails and are not asserted to exist in the repository today. Each maps to a concrete failure mode it would close:

Planned safeguard What it would do Failure mode it closes
Version-consistency check A script asserting the version string agrees across pyproject.toml, the built wheels, the OSCAL export's info.version, the Action's pinned default, and the web copy. A release where the package version and a doc/Action/web version literal silently disagree.
Tag-time gate (block publish on a red tree) A pre-publish job that refuses to publish to PyPI if the test/lint/type/faithfulness gates are not green at the tagged commit. Tagging and publishing a commit whose tree is failing CI.
CI staleness mirrors CI re-runs the generators (coverage matrix, web data, exports) and fails on drift from the committed copies. Committed COVERAGE.md / eval.json / exports drifting out of sync with the code that produces them.
Secret-scan A CI scan that fails the build if a credential pattern appears in the tree. Accidentally committing an API key or token.
CLI ↔ web parity manifest + gate A manifest of the commands/claims the web UI presents, gated against the actual CLI surface so the site cannot overstate or drift from the tool. The web console claiming a command/behavior the CLI no longer matches.
Doc-consistency check A docs-health gate asserting headline numbers and cross-links in the README/docs match the tooling's real output. Stale headline figures in the docs (e.g. a repeated test-count number going out of date).
Signed-commit / required-signatures ruleset A repository ruleset requiring signed commits and/or signed tags before they land or trigger a release. An unsigned tag or commit entering the release path.

Why call these out as planned rather than quietly imply them? The same reason the coverage gaps and the benchmark disagreements are surfaced: claiming a safeguard that isn't wired yet would be exactly the soft validation this project is built to avoid. What ships today is the tag-driven, Trusted-Publisher, attested release plus the CI gates above; the table is the roadmap, not the present.


See also

  • Provenance & Audit — the keyless decision-log chain that the PEP 740 / SLSA artifact attestations complement at the package level.
  • Methodology & Limitations — the "OSCAL-shaped, not validated" honesty and the reproducibility commands CI runs.
  • Benchmark — the published evaluation whose raw outputs ship in the package.

Clone this wiki locally