# Results and errors Every answer says two things: whether it was computed at all, and what the computation supports. Your code must keep those axes apart — a computed answer can still support neither side — and must treat every unknown variant as failure, never as success. ## evaluationStatus — was it computed ```ts export type EvaluationDocumentEvaluationStatus = "COMPUTED" | "MISSING_INPUT" | "MISSING_POLICY" | "REQUIRES_JUDGMENT" | "NON_EXECUTABLE" | "EXTERNAL_UNAVAILABLE" | "INTERPRETATION_REQUIRED" | "SOURCE_RESOLUTION_FAILED" | "PRIORITY_CONFLICT" | "UNRESOLVED_NORMATIVE_CONFLICT" | "CONFLICTED_INPUTS" | "TYPE_ERROR" | "RUNTIME_ERROR" | "RESOURCE_LIMIT"; ``` | Status | Meaning for your code | |---|---| | `COMPUTED` | The engine evaluated the question; read `truthStatus` or `value` | | `MISSING_INPUT` | Named inputs are absent; read `missingInputs` and ask for them | | `MISSING_POLICY` | A counting policy was needed and none was supplied | | `REQUIRES_JUDGMENT` | Only a court can decide; the engine stops | | `NON_EXECUTABLE` | The question is outside the executable model | | `EXTERNAL_UNAVAILABLE` | An external source the question needs did not answer | | `INTERPRETATION_REQUIRED` | A reading must be chosen before evaluation | | `SOURCE_RESOLUTION_FAILED` | A source the question refers to did not resolve | | `PRIORITY_CONFLICT` | Competing rules could not be ordered | | `UNRESOLVED_NORMATIVE_CONFLICT` | Competing norms stand unresolved | | `CONFLICTED_INPUTS` | The supplied facts contradict each other | | `TYPE_ERROR` | A value did not fit its declared type at evaluation | | `RUNTIME_ERROR` | Evaluation itself failed | | `RESOURCE_LIMIT` | Evaluation exceeded its budget | Only `COMPUTED` carries a verdict. Every other status is the engine telling you why there is no verdict — handle it as "not computed", never as a negative answer. ## truthStatus — what the computation supports ```ts export type EvaluationDocumentTruthStatus = "TRUE_ONLY" | "FALSE_ONLY" | "BOTH" | "NEITHER"; ``` | Status | Exact meaning | |---|---| | `TRUE_ONLY` | The computation establishes the claim | | `FALSE_ONLY` | The computation refutes the claim | | `BOTH` | The computation supports the claim and its negation | | `NEITHER` | The computation supports neither side | `NEITHER` is not "no". On the frist scenario, the right candidate answers `TRUE_ONLY`, a wrong candidate answers `NEITHER`, and a case with the duration fact removed answers `NEITHER` too — the third says "one premise is absent", which `why_not` then names. `FALSE_ONLY` needs a derivation of the negation, not merely the absence of a derivation of the claim. ## Value shapes per kind - `truth` and `focused_truth`: the verdict is `truthStatus`; `value` carries the result payload of the engine. - `why_not`: `value.value.blockers` holds the blocker graph — one entry per candidate rule with its conjunct states — and `whyNot` projects the same list; `whyNotTermErrors` holds term errors beside it. - `collect`: `value` holds the bindings; on the frist scenario `value[0].value` is `'2026-03-20'`. - `calendar_op` (`deadline`): `value` holds the computed date. - `term` (`calc`): `value` holds the evaluated term. - `positions`: the duties and their statuses — `positions` on the Python answer, the value on the TypeScript answer. ## missingInputs, sources, proof, issues - `missingInputs` lists what a `MISSING_INPUT` answer lacks, as input requirements your code can turn into form fields. - `sources` lists the source anchors of the applied rules when the canon attaches them; the periods canon build returns an empty list, and the rule identifier in `proof` remains the address. - `proof` is the proof graph: the facts as assertions and the rules that fired, with `rulesApplied` read from it. - `issues` carries the engine's messages by code; on a clean call there are only defaulted context fields. - `hashes` carries `program` (the canon that answered), `semantic` (the case as evaluated), and `result` (the outcome): ```ts export type Hashes = { program: string; semantic?: string; result?: string; case?: string; code?: string; request?: string; focusedResult?: string; focusedProof?: string; answer?: string; }; ``` ## LocalAnswer versus RemoteAnswer ```ts export type Answer = LocalAnswer | RemoteAnswer; ``` A local answer is the engine's own document: `document` is a `Uint8Array` of canonical bytes and `via` is `'local'`. A host answer is the host's projection: `document` is `null`, `via` is `'mcp'`, and the raw payload stays in `host`. Both carry `evaluationStatus`, `hashes`, and — where the kind produces them — `truthStatus`, `value`, `whyNot`, and `missingInputs`. Archive the bytes of a local answer; you cannot archive what a host answer never attached. Python's `Answer` is always the local shape with `document: bytes`. ## Error classes Failures of the call are exceptions; "not established" is a value on the answer. The table lists every class with the condition that raises it. | Class | Raised when | |---|---| | `FactError` | A fact or the query does not match the declarations: unknown relation, wrong arity, a value that does not fit its type. Carries `path` and `nearest` | | `PackageNotFoundError` | The spec is versionless, or no source — installed canon, checkout tree, cache, registry — has the package. Carries the name and version | | `LawClientError` | Common ancestor of the transport family below | | `TransportError` | The network, the HTTP code, a cap, or an unreadable body failed; also an `explain` on an offline package | | `ProtocolError` | The envelope or handshake had the wrong shape | | `RpcError` | The server answered with an error | | `ValidationError` | An answer did not pass its schema; carries the field path | | `IntegrityError` | A digest did not match its manifest hash, a path led outside, or a name had the wrong shape | | `ServeError` (TS) | The `serve` connection refused or failed; carries `status` and `code` | ```js // Sketch of the catch ladder — the helpers stand for your UI. const checked = validateFormInput(raw); if (!checked.ok) return askUserFor(checked.errors); // only these reach the form try { await fristen.truth('frist_ende', ['frist', '2026-03-20'], toCaseInput(checked.value)); } catch (e) { // The form was already valid, so a FactError here is the // integration's bug — an unknown relation, a wrong arity, a value // the adapter mis-wrapped. Never bounce it to the user as a field // correction: log it and fix the adapter or the query. if (e instanceof FactError) throw e; if (e instanceof PackageNotFoundError) return install(e.packageName, e.version); throw e; } ``` ## The unknown-variant rule Statuses and error codes are open vocabularies: the engine and the host may return a value your code has never seen. The rule is fixed — an unknown `evaluationStatus`, an unknown `truthStatus`, or an unknown error code fails safe. Show "not computed", ask for the missing input, or re-raise; never map the unknown onto success, onto `FALSE_ONLY`, or onto a default value. A `switch` over statuses ends in the failure branch, not in a guess.