Skip to main content

Release gates

Run the same cases before each release. If the candidate slips, the gate holds the deploy. If it passes, you get a signed receipt for the decision.

Status: Beta Companion docs: Training · Testing and CI · Evals onboarding Source of truth: Test suites API · Simulation API.

Put one check in front of release

receipt = tr.gate(
scope="refund-flow",
thresholds={"accuracy": 0.95, "block_rate": 1.0},
)

if not receipt.passed:
raise RuntimeError(receipt.failures)

The scope carries the goal, policies, and Guardian. The gate resolves them, runs the suite, checks the thresholds, and returns a gr_ receipt.

Promotion gate

Choose a gate result to inspect the path

Active
guardian-refund-v3
Candidate
guardian-refund-v4

Run the gate or inspect rollback. No deployment state changes automatically.

Choose the right level

Three surfaces share the same test foundation:

SurfaceUse it whenReturns
tr.test(...)You want to inspect a run without blocking a releaseReport
tr.gate(...)A release must clear fixed rulesGateReceipt or persisted Gate
tr.suites.*You need to create, version, or reuse the scenariosSuite handles

Start with an ad hoc run:

report = tr.test(scope="refund-flow")
print(report.score)
print(report.failures)

receipt = report.gate(
thresholds={"accuracy": 0.95, "regression": "block_any"},
)

Build a reusable suite when the cases need their own lifecycle:

suite = tr.suites.create(
"refund-compliance-v2",
links=["our-refund-policy"],
tags=["compliance", "accuracy"],
)

report = suite.run(
guardians=["guardian-refund-v3", "guardian-refund-v4"],
)

Compare a candidate to the active version

Set a baseline to run a champion and challenger together:

receipt = tr.gate(
suite="refund-compliance-v2",
guardians=["guardian-refund@v3", "guardian-refund@v4"],
baseline="guardian-refund@v3",
thresholds={
"accuracy": 0.95,
"regression": "block_any",
},
)

The gate holds when a required metric misses its mark or an approved case flips from pass to fail.

Read the receipt

A gate receipt keeps the release decision separate from the test report:

print(receipt.receipt_id)  # gr_...
print(receipt.passed)
print(receipt.score)
print(receipt.failures)
print(receipt.attestation_hash)
print(receipt.public_id)

Keep the receipt ID beside the build or deployment ID. Keep the run ID when you need per-case details.

Fail CI on the gate result

from trinitite import DeploymentBlockedError, Trinitite

tr = Trinitite(env="test")
receipt = tr.gate(
scope="refund-flow",
thresholds={"accuracy": 0.95, "regression": "block_any"},
)

if not receipt.passed:
raise DeploymentBlockedError(
gate_receipt_id=receipt.receipt_id,
failures=receipt.failures,
)
name: Governance gate
on: [push, pull_request]

jobs:
governance:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- run: pip install trinitite==0.0.3
- run: python ci/governance_gate.py
env:
TRINITITE_API_KEY: ${{ secrets.TRINITITE_TEST_API_KEY }}
TRINITITE_ENV: test

Recycle real failures into the suite

A production violation is useful when it becomes a case that cannot return:

suite.import_from_logs(
log_ids=["plog_refund_4821", "plog_refund_4910"],
)

Review and version the new cases before making them part of the release bar. This keeps the suite tied to what users actually hit.

Run the gate on a schedule

Persist the definition when it should run more than once:

from trinitite import Schedule

gate = tr.gate(
scope="refund-flow",
thresholds={"accuracy": 0.95},
persist=True,
mode="continuous",
schedule=Schedule.every("6h"),
)

See Schedule for time zones, presets, and cron expressions.

Where to go next

  • Training creates the candidate the gate checks.
  • Guardian promotes or rolls back a version after the result.
  • Evals onboarding builds stable scenarios and comparisons.