Skip to main content

The closed loop

Keep runtime decisions, test evidence, and release checks connected. Each step leaves an object you can inspect.

Status: Beta Companion docs: Evals onboarding · Testing and CI · Receipts

The loop has a clear handoff

A useful loop is not a promise that every change ships itself. It is a set of steps your team can inspect and control:

  1. Bind a baseline, scope, and Guardian.
  2. Govern calls and keep their results.
  3. Turn selected traffic or fixed transcripts into eval cases.
  4. Compare a candidate against a known run.
  5. Use a gate before deployment.

Technical workshop

Inspect the closed loop

Nothing advances until you do.

Step 1 of 5

Pin the baseline, goal, and scope.

The same scope ID keeps the goal and evidence tied together. A result may carry violations, corrections, routing context, and a receipt. Optional fields stay optional, so the loop must not depend on every result having every field.

Scope each Guardian

Different jobs can use different Guardian versions while one control plane keeps the assignments visible.

Warden fleet

One control plane, independently scoped Guardians

Warden coordinating three scoped Guardians

Choose a Guardian to inspect its assignment.

Bound scope
refunds/*
Active version
guardian-refund-v3

The Warden coordinates policy and promotion. Each Guardian keeps its own scope, version, and receipts.

Use a scope for the job:

refunds = tr.scope(
"refunds",
goal="Resolve valid refunds without exposing customer data.",
)

response = refunds.client("openai").chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Refund order 4821."}],
)
result = tr.result(response)

The result records scope_id when the service includes it. That gives later queries and evals a stable way to group the work.

Keep versions addressable

A candidate and an active Guardian are different release objects. Keeping their versions separate makes rollback and comparison possible.

Version vault

Every version stays addressable

Active: guardian-refund-v3

guardian-refund-v3

Serving production calls

guardian://refund/v3

tr.distill creates v4 beside v3. Promotion and rollback change the active pointer; they do not overwrite the vault.

Pin the Guardian on a scope when a run must use a known version:

refunds = tr.scope(
"refunds",
goal="Resolve valid refunds without exposing customer data.",
guardian="guardian-refund-v3",
)

Do not treat a candidate as active just because it exists. Compare it, review the result, then choose whether to promote it.

Compare completed eval runs

Run the same approved scenarios against the baseline and candidate. Then compare the completed run IDs:

comparison = tr.eval.compare(
a="evr_release_12",
b="evr_release_13",
)

print(comparison.body["deltas"]["regressed_scenarios"])
print(comparison.body["statistical"]["regression"])

Receipt comparison

Compare sealed runs, not floating summaries

Focused receipt
eh_01J7CAND9D3E
Observed delta
2 verdicts differ

Both sides retain their own contract, inputs, verdict leaves, and signature.

Comparison pairs cases by scenario_id. Both runs must be complete. Receipt fields in the comparison can be empty when one of the runs has no receipt.

Put a gate in front of release

A gate runs a bound suite against thresholds. It keeps its run history and returns typed gate receipts.

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.

Python can create a gate by calling tr.gate(...):

gate = tr.gate(
name="refund-release-gate",
suite_id="suite_refunds",
guardian_id="refund-guardian",
thresholds={"accuracy": 0.95},
schedule="nightly",
)

for receipt in gate.runs(limit=10):
if receipt.passed is False:
print(receipt.failures)

TypeScript uses tr.gate.create(...):

const gate = await tr.gate.create({
name: "refund-release-gate",
suiteId: "suite_refunds",
guardianId: "refund-guardian",
thresholds: { accuracy: 0.95 },
schedule: "nightly",
});

Gate receipts use the gr_ family. A gate can be created, listed, read, updated, paused, resumed, deleted, and inspected through its run history. Permissions and account capabilities are enforced by the server.

Keep CI simple

Your CI job should run the test script and fail on the gate result:

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

Where to go next

  • Training turns reviewed calls into a new Guardian or model candidate.
  • Release gates checks a candidate before promotion or deployment.
  • Guardian covers version pinning, promotion, and rollback.
  • Evals onboarding creates the first fixed test.
  • Eval receipts explains verification and run comparison.
  • Schedule adds a readable cadence to recurring checks.