Concepts
Set rules once, group work by goal, then connect the AI your team uses.
Status: Beta Companion docs: Get started · Scopes and assets · Result
Three layers keep setup clear
Trinitite separates rules, goals, and connected systems. This keeps one setting from doing three jobs.
The three-layer model, three nouns, one rule
Scope
A goal
Declare the goal once. Every asset bound to this scope shares the goal and its stacked policies.
tr.scope("refund-flow", goal="Resolve refunds within policy")- A baseline holds the common rules for governed work.
- A scope names a goal and groups work that shares it.
- An asset is the model client, MCP server, CLI, skill, connector, sandbox, or RAG system that does the work.
An asset at the root uses the baseline. An asset created from a scope also gets that scope's goal and settings.
from trinitite import Trinitite
tr = Trinitite()
tr.govern(
frameworks=["soc2"],
policies=["refund-policy"],
mode="enforce",
)
refunds = tr.scope(
"refunds",
goal="Resolve valid refunds without exposing customer data.",
)
client = refunds.client("openai", credential="cred_openai_prod")
TypeScript has the same idea, with promises and camel case:
const tr = new Trinitite();
await tr.govern({
frameworks: ["soc2"],
policies: ["refund-policy"],
mode: "enforce",
});
const refunds = await tr.scope("refunds", {
goal: "Resolve valid refunds without exposing customer data.",
});
const client = refunds.client("openai", {
credential: "cred_openai_prod",
});
Asset constellation, one binding model
Selected asset
Client
- Binding
- Bind at the baseline or from a scope.
- Acquisition
- Inline (wrap), or split-govern for a direct provider call.
An asset with no scope inherits the baseline house rules. A scoped asset also inherits the scope goal and policies.
Start small, add detail when needed
The common path is short. Add a baseline or scope when the work needs its own rules or goal.
The ladder, one line, then composable, then fully customizable
Platform-default Guardian and policy bundle
Your governed call
Drop deeper only when you need to customize.
Use the wrapped client
For a model call made by your code, use the wrapped client and normalize the response:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Refund order 4821."}],
)
result = tr.result(response)
The provider response and the governance result are different objects. Keep the response for model output. Use the result for the verdict and evidence.
A result can arrive three ways
The right path depends on who owns the model call.
Three ways a result reaches you, one shape on every path
- 01Your codeThen
- 02tr.clientThen
- 03GuardianThen
- 04ProviderProduces
- One Result shapeverdict + diff + controls+ compliance + risk + receipt
tr.result(resp)Only acquisition differs. The result is the same, whichever path it came in on.
Inline
Your code makes a wrapped call. Use tr.result(response) right away.
Observed
Another system makes the call. Query stored decisions with tr.results.get,
tr.results.list, or correlation polling.
result = tr.results.wait_for_correlation(
"refund-4821",
poll_interval_seconds=0.5,
max_wait_seconds=15,
)
TypeScript uses await tr.results.waitForCorrelation(...).
Split governance in Python
Python can check input and output around a direct provider call:
input_result = tr.govern.input(
instructions="Do not expose customer data.",
input=messages,
masking=["pii"],
)
provider_messages = input_result.masked_body or messages
provider_response = provider.chat.completions.create(
model="gpt-4o",
messages=provider_messages,
)
output_result = tr.govern.output(
flow_id=input_result.flow_id,
instructions="Do not expose customer data.",
input=provider_messages + [{
"role": "assistant",
"content": provider_response.choices[0].message.content,
}],
)
Send masked_body to the provider when it is present. Output governance checks
the reply. It does not unmask it for you. Call output_result.unmask(...)
inside the approved trust boundary.
Split governance is Python-only. TypeScript supports wrapped calls and stored
result lookup, but it does not expose tr.govern.input(...) or
tr.govern.output(...).
One result shape
Each path gives you a GovernanceResult. Common fields include verdict,
diff, violations, compliance, and risk. Other fields appear only when
that call produced them.
Governance result
One object, clear next steps
- Value
- Structured trace
- Use
- Call the method when a person needs the decision steps.
Follow-up work uses methods and optional handles:
trace_response = result.explain()
if result.receipt and result.receipt.get("id"):
verification = result.receipt.verify()
In TypeScript, use await result.explain() and
await result.receipt.verify().
Where to go next
- Verdicts and modes maps every wire verdict to an app action.
- Integration routes starts with the wrapped call path.
- The closed loop shows how results become repeatable tests.