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:
| Surface | Use it when | Returns |
|---|---|---|
tr.test(...) | You want to inspect a run without blocking a release | Report |
tr.gate(...) | A release must clear fixed rules | GateReceipt or persisted Gate |
tr.suites.* | You need to create, version, or reuse the scenarios | Suite 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.