docs← Back to article

Markdown for LLMs

Facts and context

The source Markdown for this article. Copy it into your assistant or download it as a text file.

Download this articlePlain text ↗
# 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

The frist scenario needs three context fields and two facts — nothing
else on this page is required to ask it:

```js
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'
```

- `legalTime` is the date on which the law is read: required.
- `timezone` says how dates are read.
- `deadlinePolicy` names the canon's rule for counting days.
- `answers` holds 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

### Fact

```ts
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

```ts
/**
 * 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:

```js
{ 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

```ts
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_TAG` vs
  `BGB_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

`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

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

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:

- `assertions` on `CaseInput` takes a case your code has already
  converted into the engine's input form — a list of assertions
  marked `kind: 'assertion'` — instead of `answers`. The facade passes
  it through untouched: what you built is what the engine sees. Most
  applications never need it; `answers` is the normal route.
- A pre-built `Arg` object in `args` passes through unchanged — the
  facade never re-wraps it — so a hand-built `Arg` must already be
  well formed.

## Limits

- Argument order follows the canon's parameter order; the manifest
  lists it per relation.
- A pre-built `Arg` object or an `assertions` list skips the facade's
  checks and wrapping; the engine receives it as given.