The ladder
Belora trusts what people said, doubts what agents guessed, and asks a person to decide. This is how a claim climbs from one to the other.
Signal or hypothesis
A signal is something a human said: support, email, chat or feedback relayed with the author who said it. It is filed and clustered into a theme at once. A hypothesis is something an agent noticed. It is evidence for a claim, never a signal. POST /v1/ingest decides by source, which defaults to agent:
| source | Becomes | Needs author |
|---|
| agent | a hypothesis (the default) | no |
| support | a signal | yes |
| email | a signal | yes |
| chat | a signal | yes |
| feedback | a signal | yes |
| meeting | a signal | no |
POST/v1/ingest
Submit an observation (a hypothesis by default), a human utterance (a signal) or a meeting transcript; the id is returned before any pipeline runs
ingestservice keysignal:ingestsuccess 202 or 200mcp ingest
Body
Variant 1 of 2
| source? | "agent" | "support" | "email" | "chat" | "feedback" | Defaults to agent: an agent observation, filed as a hypothesis that becomes a signal only once corroborated and promoted. The human-utterance sources (support, email, chat, feedback) file a signal; author names the human who said it and, on a person key, defaults to the person the key is bound to. |
| content | string | |
| externalRef? | string | |
| author? | string | |
| occurredAt? | integer | |
Variant 2 of 2
| source | "meeting" | A transcript of a meeting: filed as a receipt, processed by the meeting workflow, and turned into signals. The speakers are in the transcript, so author is not accepted. |
| title? | string | |
| externalRef? | string | |
| occurredAt? | integer | When the meeting started, in epoch milliseconds |
| participants | object[] | |
| participants[].name | string | |
| participants[].role | "engineer" | "pm" | "designer" | "manager" | "executive" | "system" | "external" | "unknown" | "customer" | |
| transcript | object | object | |
Response
Variant 1 of 3
| signalId | string | |
| status | "ingesting" | "clustering" | "parked" | "themed" | "archived" | |
Variant 2 of 3
| hypothesisId | string | |
| status | "hypothesis" | "corroborated" | "promoted" | "expired" | "rejected" | |
Variant 3 of 3
| meetingId | string | |
| status | "queued" | "normalizing" | "extracting" | "reducing" | "materializing" | "completed" | "failed" | |
Errors: 401 unauthorized403 forbidden400 validation_failed409 conflict429 rate_limited
Proved by ingest.signal ingest.hypothesis
Corroboration
A hypothesis moves through hypothesis, corroborated, promoted, expired, rejected. An agent's claim starts at 30% confidence with the submitting key as its first witness. It becomes corroborated at 2independent witnesses. A keyed witness is the person behind the key: a second key minted by the same person adds evidence and no witness. Evidence that arrives from a signal or the Detective, with no key, counts as a witness of its own, and Belora's own runner is never one. The same content from a key that already said it changes nothing.
A key may hold 5 items on one claim, and key-submitted findings fill at most 30 items of it; a finding past either is refused with 409 conflict. A claim with no new evidence for 30 days expires, and new evidence reopens it.
Stated, not run by the suite: hypothesis.corroborate
Promotion
Promotion is a proposal, never a side effect. When enough independent keyed witnesses agree, when signal-backed confidence passes the promotion threshold, or when a person holding decision:resolve promotes it by hand, Belora files one promote_hypothesis proposal. Its accepted verdict marks the hypothesis promoted and admits the claim to the signal pipeline as a signal authored by Belora. A hypothesis already promoted or rejected is not raised again.
Stated, not run by the suite: hypothesis.promote
Canon
Canon is what every later signal and job is read against: the keyword taxonomy, standing procedures and the vocabulary of departments, areas and roles. An agent's memory changes only through a proposal of one kind, and a verdict applies it. One kind has one decider:
| Proposal kind | Decided by | What it changes |
|---|
| accept_signal | the reviewer | Admit a claim into the signal pipeline. |
| merge_themes | a person | Fold one theme into another. |
| sop_change | a person | Create or rewrite a standing procedure. |
| link_identity | a person | Tie an external account to a person. |
| vocab_add | the reviewer | Coin a department, area or role. |
| promote_hypothesis | the reviewer | Turn a corroborated claim into a theme. |
| merge_people | a person | Fold person records into one. |
| session_commit | a person | A conversation's confirmed conclusion. |
| keyword_change | the reviewer | Add, merge or drop a keyword in the taxonomy that routes every signal. |
| fast_track_theme | the reviewer | Let a theme skip the decision wait and build. |
A procedure body an agent wrote is served into every later job, so only a person installs it. Belora's own pipeline also writes some memory without a proposal: a keyword its extraction step coins while it reads a signal or a technical document, a person entry the first time it sees a name, the taxonomy seed, and the merge of vocabulary entries a person asks for. That is the pipeline's own vocabulary and never goes through the reviewer.
Stated, not run by the suite: memory.promotion
Falsifiers
A claim that asks Belora to change something says what would prove it wrong. A finding attached to a hypothesis must carry a falsifier. A proposal may carry one. A proposal the reviewer decides, filed with no falsifier, is escalated to a person with the reason recorded when it is filed, even when the reviewer would have accepted it; only an accept_signal carries a stated confidence, so for that kind the rule applies only above 40%, and every other kind is held to it at any confidence. A vocabulary proposal from a recap names no falsifier, so a person decides it. The explorations list shows what would corroborate each open claim.
Stated, not run by the suite: memory.falsifier hypothesis.corroborate
Decisions and their 3 doors
A decision is a product question with labelled options, routed to an owner. Its status is one of pending, resolved, superseded, deferred. A verdict names an exact option label and a rationale, and may come through any of 3 doors. All of them call one action, so the same permission checks run and one decision.resolved event records which door was used.
| Door | How |
|---|
| api | POST /v1/decisions/{id}/verdict with a person key that holds decision:resolve. |
| console | The decision card in the Belora console, answered by a signed-in member. |
| slack | An option button in the Slack DM or thread, answered by someone whose Slack identity is verified. |
The first verdict wins. A repeat from a caller who may answer is 200 with the stored resolution and alreadyResolved: true, and writes nothing; a caller who may not answer is refused, repeat or not. A service key cannot answer: a verdict needs a person.
POST/v1/decisions/{id}/verdict
Record a person's verdict on a decision, or on the question a held job paused on (addressed by the job's id); an already resolved decision replays its stored outcome
decisions.verdictperson keydecision:resolvesuccess 200mcp submit_verdict
Body
| option | string | The exact label of one of the decision's options |
| rationale | string | Why this option; stored on the decision |
Response
| id | string | |
| question | string | |
| options | object[] | |
| options[].label | string | |
| options[].note | string | |
| options[].tradeoff | string | |
| theme | object | |
| theme.id | string | |
| theme.title | string | |
| owner | object | null | |
| ownerIsCaller | boolean | |
| createdAt | number | |
| nudgedAt? | number | |
| escalatedTo? | string | |
| escalatedAt? | number | |
| status | "pending" | "resolved" | "superseded" | "deferred" | |
| resolution | object | null | |
| alreadyResolved | boolean | |
Errors: 401 unauthorized404 not_found400 validation_failed409 conflict429 rate_limited403 key_cannot_commit_judgment403 key_lacks_capability403 answerer_not_a_person403 answerer_unlinked403 answerer_not_verified403 answerer_lacks_capability403 not_a_judge404 decision_not_found
Proved by decision.verdict
Proved by
Every claim on this page is one of these checks; those marked stated are documented behaviour the suite cannot drive. Run them against your org:
npx @belora/conformance --key $BELORA_CONFORMANCE_KEY --api $API --mcp https://mcp.belora.dev/mcp
- ingest.signal A human utterance is filed as a signal
POST /v1/ingest with source support and an author answers 202 with a signalId before any pipeline step runs.
- ingest.hypothesis An agent observation is a hypothesis, not a signal
POST /v1/ingest without a source answers 202 with a hypothesisId and status hypothesis; no signal exists until the hypothesis is promoted.
- hypothesis.corroborate A second person corroborates a hypothesisstated, not run by the suite
The same content ingested by a key of a second person turns the hypothesis to corroborated, while a second key of the same person adds evidence and no witness.
- hypothesis.promote A corroborated hypothesis promotes through a proposalstated, not run by the suite
A hypothesis with enough independent witnesses files one promote_hypothesis proposal; its accepted verdict marks the hypothesis promoted and admits the claim to the signal pipeline.
- decision.verdict A person key answers a decision exactly once
POST /v1/decisions/{id}/verdict from an eligible person key resolves the decision, a repeat answers 200 with alreadyResolved true and writes nothing, and decision.resolved carries door api.
- memory.promotion Each proposal kind has one published deciderstated, not run by the suite
A proposal is decided by the reviewer or by a person according to its kind, and a procedure body written by an agent is installed only by a person.
- memory.falsifier A confident claim with no falsifier is not applied automaticallystated, not run by the suite
A reviewer-decided proposal whose stated confidence is above the published floor and which names no falsifier is escalated to a person instead of applied.