How it works

Contradictions

Two claims that cannot both be true, stored as a pair with an actionable question — and pinned into every prompt until a human answers it.

On this page

What one is

A contradiction is two claims that cannot both be true, stored as a pair with an actionable question. Not a flag on either memory, and not a resolution — a third thing, with its own identity and its own lifecycle.

The question is the part that matters. “These memories disagree” is not actionable; “Which is it?” is. Every tension carries a question phrased so it can be answered in a sentence, because the answer usually arrives as an ordinary user message and the thing doing the asking is a prompt.

text
### Unresolved Contradictions- [CRITICAL] "We deploy on Fridays" conflicts with "We never deploy on Fridays". Ask: Which is it?

Why it is always injected

A tension is pinned into every context build until it is resolved, and it is the one thing always injected in full regardless of budget — because the whole point is that the agent sees both sides rather than picking one.

This is the concrete case for the exemption L0 gets from the budget. A tension that got truncated away is indistinguishable from a tension that was never detected, and both look exactly like a working system right up until the agent acts on the wrong premise.

The severity tag on a tension — CRITICAL above — is not severity of the disagreement but of what the agent might do while it is unresolved. It is what makes triage possible when there are more open tensions than anyone has time to read.

How one is detected

Detection is deliberately conservative, because a false tension is expensive in a way a missed one is not. A tension that does not exist pins two claims into every prompt forever and trains the user to ignore the section; a missed one is a single bad turn.

Two paths produce one. A merge candidate that cannot satisfy the merge rule while sharing enough structure to be about the same thing is a tension, not two memories. Separately, capture compares a new statement against what is held, and an explicit negation of a stored claim — “we never deploy on Fridays”, against a stored “we deploy on Fridays” — is the clearest signal there is.

And a human can always record one directly, which matters more than it sounds: the deterministic paths only see claims that were both stored. “These two things you told me cannot both be true” is something a person notices and the service cannot.

ts
await memory.tensions.create({  claimA: "We deploy on Fridays",  claimB: "We never deploy on Fridays",  actionableQuestion: "Which is it?",  impact: "critical"})

Resolving one

Resolving keeps the resolution, including the reusable pattern it revealed. Deleting it would mean rediscovering the same contradiction next month.

That pattern is the genuinely valuable output. A resolved tension is evidence about how this project actually works — that the Fridays rule was a deployment-window constraint rather than a general one, say — and it is stored as a fact that participates in tiering like any other.

OperationWhat it does
OpenPinned into every prompt, listed in the dashboard by status.
ResolvedMarks the answer, stores the pattern it revealed, unpins the pair. The resolution is kept.
ReopenedPins the pair again. Used when the answer turns out to have been situational.
DeletedRemoves it entirely. Discards the pattern too.

Reopen and delete are separate because the second is destructive and the first is not. A resolution that was right for one deploy cycle is common enough that conflating it with deletion loses the pattern.

Where to look

Tensions lists them by status with resolve and reopen, and the overview counts the open ones, because an unresolved tension is the single most likely thing to need a human.

They also show up in GET /v1/stats and in GET /v1/tensions?status=open, so a build can surface the count without reading prose. The API reference has both.