docs← Back to article

Markdown for LLMs

Calculate and check: collect the end date, test the candidate

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

Download this articlePlain text ↗
# Calculate and check: collect the end date, test the candidate

The application asks two questions: when does the period end, and does
it end on the date the user proposes? This chapter asks both on the
example case and reads what comes back.

## At a glance

- **Goal:** compute the end date of the period, and test whether a
  candidate end date is established.
- **You need:** [Bind input](/build/bind-input/): `toCaseInput` supplies
  the case this chapter evaluates. The runtime singleton from
  [Start](/build/start/) opens the model once for both calls.
- **Run:**

  ```bash
  node --import tsx --test test/evaluate.test.ts
  ```

- **Files:**

  ```text
  src/query.ts        buildCollect, buildTruth
  src/runtime.ts      evaluateCollect, evaluateTruth
  ```

## Compute the end date

The first question leaves the end date open and lets the engine fill it
in. This is a collect query. For the example case it computes the value
— note the shape: the SDK returns an array of typed elements, not a
bare string:

```json
{
  "evaluationStatus": "COMPUTED",
  "value": [{ "kind": "value", "type": { "name": "urn:law:std#Date" }, "value": "2026-03-20" }]
}
```

The application's projection of that answer is the plain list
`["2026-03-20"]`, produced by the `readCollect` reader in the next
chapter. When this guide shows a bare value, it is always the
projection — handlers must be written against the SDK shape above.

## Check a candidate date

The second question fills the end date with the user's candidate and
asks whether that claim holds. This is a truth query. On the computed
date it establishes the claim:

```json
{ "evaluationStatus": "COMPUTED", "truthStatus": "TRUE_ONLY" }
```

On a wrong date — say `2026-03-21` — it answers `NEITHER`:

```json
{ "evaluationStatus": "COMPUTED", "truthStatus": "NEITHER" }
```

Each answer carries two statuses. `evaluationStatus` says how the
computation finished: `COMPUTED` means the engine finished. `truthStatus`
says what the finished computation supports: `TRUE_ONLY` means the
claim and not its negation; `NEITHER` means neither of them.

## Ask the two questions in code

The query builders name the question and its arguments. Both ask about
`frist_ende`, the end of the period with id `frist`. The collect query
leaves the end date open with the variable `?end`; the truth query
fills it with the candidate:

```ts
// src/query.ts
// "?end" marks the free position the engine must fill in.
export function buildCollect(id: string = CASE_ID): CollectQuery {
  return { kind: 'collect', predicate: 'frist_ende', args: [id, '?end'] };
}

export function buildTruth(candidate: string, id: string = CASE_ID): TruthQuery {
  return { kind: 'truth', predicate: 'frist_ende', args: [id, candidate] };
}
```

The runtime evaluates through the shared model. One open model serves
the whole process; every call passes the case input the adapter built:

```ts
// src/runtime.ts
// Calculate mode: which date(s) end the period?
export async function evaluateCollect(input: FormInput): Promise<Answer> {
  const model = await openModel();
  const query = buildCollect();
  return model.collect(query.predicate, query.args, toCaseInput(input));
}

// Verify mode: does the period end on the proposed date?
export async function evaluateTruth(input: FormInput, candidate: string): Promise<Answer> {
  const model = await openModel();
  const query = buildTruth(candidate);
  return model.truth(query.predicate, query.args, toCaseInput(input));
}
```

The same file also defines the explain calls for an answer that is not
established; [Collect missing facts](/build/collect-missing-facts/)
introduces them where they are first used, and `src/runtime.ts` in the
archive holds the full runtime.

## Why a wrong date answers NEITHER

The `NEITHER` on a wrong date surprises every newcomer, so read it
carefully. `NEITHER` says the computation supports neither the claim
nor its negation. The canon derives end dates; it does not derive "this
date is not the end". Nothing in the rules concludes the negation, so a
wrong candidate is not established — and its negation is not
established either. `NEITHER` is the honest status for "not shown",
and it is not `FALSE_ONLY`. `FALSE_ONLY` would mean the negation was
derived, which takes a rule that concludes it.

The same `NEITHER` appears when facts are missing: remove
`frist_dauer_tage` and truth on the right date still answers
`NEITHER`. "Wrong date" and "missing facts" share a status and differ
in their grounds — which is why the application must read the grounds
of the answer, not the status alone. Chapters 5 and 6 show how.

## Limits and errors

- `evaluationStatus` and `truthStatus` are separate axes. `COMPUTED`
  says the engine finished; `TRUE_ONLY` / `NEITHER` say what the
  finished computation supports. A non-`COMPUTED` status means the
  question was not computed at all — there is no truth status then.
- The candidate in `buildTruth` must be a validated date from the
  form schema. An unvalidated string fails inside the call instead of
  at the form, with a worse message.

## Check your understanding

Run the calculate tests and confirm all three shapes:

```bash
node --import tsx --test test/evaluate.test.ts
```

```text
ok 1 - collect computes 2026-03-20 for 14 days from 2026-03-06
ok 2 - truth on the correct date is TRUE_ONLY
ok 4 - truth on a wrong date is NEITHER, not FALSE_ONLY
# tests 13
# pass 13
# fail 0
```

Before reading each answer, predict it.

<details>
<summary>The user has not proposed a date yet. Which query does the application need?</summary>

Collect. `buildCollect` leaves the end date open with `?end`, and the
engine fills it in: `2026-03-20` for the example case. Truth needs a
candidate to check.

</details>

<details>
<summary>Why does the wrong date 2026-03-21 answer NEITHER and not FALSE_ONLY?</summary>

The canon has rules that derive end dates, but no rule that concludes
"this date is not the end". The wrong date is not established, and its
negation is not derived either, so the status is `NEITHER`.
`FALSE_ONLY` would require a derived negation.

</details>

<details>
<summary>You remove the duration fact and check the correct date 2026-03-20. What status comes back?</summary>

`NEITHER` again — the same status as a wrong date. Without the duration
the end date cannot be derived. The two cases differ in their grounds,
not in their status, which is why the application reads the grounds.

</details>

## Next

- [Handle results: every shape gets a rendering](/build/handle-results/)
- [How to read an answer](/guide/reading-an-answer/)
- [Query types](/build/sdk/query-types/) for when to use `collect`, `truth`, and the other operations