Consolidate claims
A claim is a compact conclusion that names the observations behind it, created deterministically today with no model in the path.
An observation says what happened. A claim says what someone concluded, and points at the
evidence. Keeping the two apart is what lets a compiled pack carry conclusions that stay
checkable: every claim resolves back to observations through
GET /v1/claims/:id/evidence.
Create a claim
const result = await titen.consolidate('user_rama', [
{
kind: 'procedural',
statement: 'Deploy smoke must pass before release.',
confidence: 0.95,
sources: [{ observation_id: obs.observation_id, relation: 'supports' }],
},
]);
// result.claims[0].claim_id → "claim_f3963d7b876143f5bfc8230db2757067"
// result.model_used → false
curl -X POST http://127.0.0.1:8787/v1/consolidations \
-H "authorization: Bearer $TITEN_API_KEY" \
-H 'content-type: application/json' \
-d '{"subject_id":"user_rama","claims":[{
"kind":"procedural","statement":"Deploy smoke must pass before release.",
"confidence":0.95,
"sources":[{"observation_id":"obs_ed91a2d0f66143fbba66c932494d5e64","relation":"supports"}]}]}'
One request carries up to 50 claims, each with up to 20 sources and a statement up to 4000 characters.
Kinds
The kind is a typed field because pack diversity reads it: at most three items of one kind enter a compiled pack.
| Kind | What it holds |
|---|---|
semantic_fact |
A fact about the subject that is not tied to one moment. |
episodic_event |
Something that happened, as a conclusion rather than raw evidence. |
preference |
What the subject wants. |
procedural |
Guidance for how work should be done. |
decision |
A choice that was made, and therefore something a later decision can supersede. |
relationship |
A link between two subjects, backed by evidence. |
Sources carry the relation
Every source names an observation and how it relates to the claim: supports,
contradicts or qualifies. At least one supports is mandatory.
{ "error": { "code": "VALIDATION_ERROR",
"message": "A claim needs at least one supporting source." },
"meta": { "request_id": "req_e23da3bea5924b3f99cf84cf856e72ed" } }
That rule is what keeps the store from accumulating free-floating assertions.
An unknown source id, a foreign tenant’s observation and someone else’s private record
all return the same non-disclosing 404 NOT_FOUND.
Trust and visibility are inherited
Omit trust and the claim takes the highest trust among its supporting sources. Omit
visibility and it takes the narrowest visibility across all its sources, including
the ones that contradict or qualify. Supplying either field can only narrow the result:
{ "error": { "code": "VALIDATION_ERROR",
"message": "Claim trust may not exceed the trust of its supporting evidence." },
"meta": { "request_id": "req_baae3681b26d44fe9ebd10f1b264bb3d" } }
Consolidation therefore cannot launder trust: evidence enters with a ceiling from its credential, and the claim above it inherits that ceiling.
confidence is separate and defaults to 0.8: how sure the author is, greater than 0 and
at most 1. It never substitutes for trust. Both appear in a compiled pack, and
ranking uses them for different things.
Contradiction produces a dispute, not a winner
Attach a contradicts source and the claim commits with status: "disputed":
{ "data": { "subject_id": "user_docs_loop", "project_id": null, "model_used": false,
"claims": [{ "claim_id": "claim_d5ce4657d6b947aa8ade42271b4176f5",
"kind": "procedural", "status": "disputed", "trust": "verified",
"visibility": "team", "confidence": 0.9,
"valid_from": "2026-07-30T10:47:30.946Z", "valid_to": null,
"evidence_ids": ["obs_c98879c3adc44f6a9b2227b821a033bd",
"obs_bc4984ed9153467eb41b4753c5c96d6c"] }] },
"meta": { "request_id": "req_…", "replayed": false, "model": "disabled" } }
The claim is still retrievable. It arrives in a context pack with status: "disputed", a
conflict score component of 1 and an entry in the pack’s conflicts array. The
disagreement is the part an agent needs to ask a better question. Resolving it is an
explicit lifecycle transition that records who decided and why.
No model is involved
model_used: false in data and model: "disabled" in meta are literal. Consolidation
today is deterministic, and no model call happens on this route.
Proposing claims from eligible observations with a model is on the roadmap. It changes what Titen proposes, not what it accepts. Every proposed claim would still pass the same schema, scope, evidence, trust and temporal validation as a hand-written one, and a model could not invent a source identifier, raise trust above its evidence, delete evidence, or resolve a dispute. Deterministic rules run first, and a model returning invalid sources commits nothing.