Facts and context
A question carries two things: the facts of the case and the context they are evaluated in. Facts arrive as plain predicates with plain values; the facade wraps each value by the type the canon declares for that parameter. Nothing is guessed from the shape of the value.
The object this example needs
Section titled “The object this example needs”The frist scenario needs three context fields and two facts — nothing else on this page is required to ask it:
const caseInput = { legalTime: '2026-09-17', timezone: 'Europe/Berlin', deadlinePolicy: 'urn:de:corpus:clir:bgb-fristen#BGB_FRISTEN_TAG', answers: [ { predicate: 'frist_ereignis', args: ['frist', '2026-03-06'] }, { predicate: 'frist_dauer_tage', args: ['frist', '14 calendar_day'] }, ],};const r = await fristen.truth('frist_ende', ['frist', '2026-03-20'], caseInput);// r.evaluationStatus === 'COMPUTED', r.truthStatus === 'TRUE_ONLY'legalTimeis the date on which the law is read: required.timezonesays how dates are read.deadlinePolicynames the canon’s rule for counting days.answersholds the facts: the event date and the duration.
Drop frist_dauer_tage from answers and the same call answers
COMPUTED with NEITHER: the engine supports neither the claim nor
its negation, because one premise is simply absent.
The full declarations follow. Their fields fall into three groups: the facts and the main context, which every question uses; extra parameters that only some tasks need; and data provenance and low-level integration, which matter when you record where facts came from or build the engine’s input yourself.
Facts and the main context
Section titled “Facts and the main context”export type BareValue = string | number | boolean;export type FactArg = Arg | BareValue;
export interface Fact { predicate: string; args: FactArg[]; /** `yes` (default when omitted), `no` (an explicit negative), `unknown` (no assertion). */ state?: Tristate; id?: string; package?: string; origin?: string; adjudicated?: boolean; evidence?: string[];}Python sends the same shape as a dict: predicate, args, and the
optional state ("yes", "no", "unknown"), id, package,
origin, adjudicated, and evidence keys.
| Field | Meaning |
|---|---|
predicate | The relation name: a local name (frist_ereignis), a qualified name (de.bgb.core::anspruch_entstanden_am), or a full StableId (urn:...#local) |
args | One value per parameter, in order; a bare value or a pre-built Arg, which passes through unchanged |
state | yes when omitted; no asserts the negation; unknown asserts nothing |
The remaining fields (id, package, origin, adjudicated,
evidence) describe provenance; they are listed in the last group
below.
Bare values are wrapped by the declared type
Section titled “Bare values are wrapped by the declared type”/** * A bare value in an argument position is wrapped by the DECLARED type of * the parameter: `'2026-03-06'` for a Date, `'14 calendar_day'` for a * Quantity, `'730000 KZT'` for Money, `'Abschlagszahlung'` for an enum * variant, `'frist'` (short) or `'urn:…'` (full) for an entity. An `Arg` * object is passed through unchanged. Nothing is defaulted. */The canon declares each parameter’s type, and the manifest
(questions() in TypeScript) exposes it with a widget — entity,
date, quantity, and the rest — so a form knows what to collect.
Your code passes the plain string and the facade does the wrapping:
{ predicate: 'frist_ereignis', args: ['frist', '2026-03-06'] },{ predicate: 'frist_dauer_tage', args: ['frist', '14 calendar_day'] },The first fact’s second argument is a date; the second fact’s is a
quantity of fourteen calendar days. A value that does not fit the
declared type is a FactError, not a silent coercion, and the error
names the path and the nearest valid names.
Entity ids are short names (frist) or full URNs; dates are calendar
strings (2026-03-06); quantities carry their unit exactly as the
canon declares it (14 calendar_day); money carries amount and
currency (730000 KZT); enum variants use the canon’s own spelling.
CaseInput
Section titled “CaseInput”export interface CaseInput { /** The date the question is asked on the legal axis. Required, never "today". */ legalTime: string; timezone?: string; decisionTime?: string; knowledgeTime?: string; deadlinePolicy?: string; /** Calendar snapshot the case is evaluated with; sent as `options.calendarSnapshot`. */ calendar?: string; namespace?: string; caseName?: string; queryId?: string; answers?: Fact[]; /** A case already lowered (assertions with `kind: 'assertion'`) is passed through. */ assertions?: unknown[]; constants?: Record<string, unknown>; options?: Record<string, unknown>; context?: Record<string, unknown>; free?: unknown;}| Field | Meaning |
|---|---|
legalTime | The date the law is projected on. Required; there is no default and “today” is never implied |
timezone | The time zone dates are read in, e.g. Europe/Berlin — not a holiday calendar |
deadlinePolicy | How days are counted: a StableId declared by the canon, e.g. urn:de:corpus:clir:bgb-fristen#BGB_FRISTEN_TAG |
calendar | A calendar-data snapshot the case is evaluated with (weekends, holidays) |
answers | The facts, as Fact above |
legalTime deserves emphasis: every question is asked on the legal
axis at an explicit date, because the canon in force on one date may
differ from the canon in force on another. The application always
supplies it.
The periods canon declares two counting policies: BGB_FRISTEN_TAG
counts from the next day and moves an end that falls on a weekend or
holiday to the next working day, while BGB_FRISTEN_KALENDER steps by
the calendar with the last-day-of-month rule and no roll. A question
about a period names one of them; without the policy the engine
cannot count, and the answer says the policy is missing.
Three independent context elements, three different jobs — do not merge them:
- time zone (
timezone, e.g.Europe/Berlin): how dates are read. It never supplies holidays. - calendar data (
calendar): the weekend/holiday snapshot the computation evaluates with. - counting policy (
deadlinePolicy): the rule for beginnings, endings, and rolls (BGB_FRISTEN_TAGvsBGB_FRISTEN_KALENDER).
Choosing Europe/Berlin alone does not give the computation a
holiday calendar; that comes only from the calendar data the case
carries.
An explicit no is not a missing fact
Section titled “An explicit no is not a missing fact”state: 'no' asserts the negation of the fact — the event did not
happen on that date — and the engine can conclude from it. Omitting
the fact asserts nothing: rules that need it stay undetermined, and a
truth question over the gap answers NEITHER. The query chapter
shows how why_not names exactly which premises were missing, so the
application can ask the user for them instead of guessing.
Extra parameters for particular tasks
Section titled “Extra parameters for particular tasks”These CaseInput fields matter only when the canon’s rules or the
question kind read them:
| Field | Meaning |
|---|---|
decisionTime | When the deciding act happened, for rules that read it |
knowledgeTime | What was known when, for rules that read it |
constants | Named constants the query may refer to |
free | The free position of a collect, when not named inline with ?name |
namespace | The namespace bare names resolve in; defaults to the canon’s |
caseName | A name for the case, used in query ids |
queryId | An explicit id for the question |
options | Engine options for this evaluation |
context | Further context the rules may read |
Data provenance and low-level integration
Section titled “Data provenance and low-level integration”Provenance fields on a Fact say where the fact came from; they are
carried into the proof:
| Field | Meaning |
|---|---|
id | A stable handle for the fact, used when provenance is attached |
package | Disambiguates a short name shared by several packages in the world |
origin | Where the fact came from, carried into the proof |
adjudicated | Whether a court established this fact |
evidence | Document handles the provenance refers to |
Two inputs bypass the facade’s own assembly:
assertionsonCaseInputtakes a case your code has already converted into the engine’s input form — a list of assertions markedkind: 'assertion'— instead ofanswers. The facade passes it through untouched: what you built is what the engine sees. Most applications never need it;answersis the normal route.- A pre-built
Argobject inargspasses through unchanged — the facade never re-wraps it — so a hand-builtArgmust already be well formed.
Limits
Section titled “Limits”- Argument order follows the canon’s parameter order; the manifest lists it per relation.
- A pre-built
Argobject or anassertionslist skips the facade’s checks and wrapping; the engine receives it as given.
Documentation for Arxo. Writings — blog.arxo.io.
Anonymous visit counts on stats.arxo.io, no cookies.