# 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 { 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 { 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.
The user has not proposed a date yet. Which query does the application need? 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.
Why does the wrong date 2026-03-21 answer NEITHER and not FALSE_ONLY? 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.
You remove the duration fact and check the correct date 2026-03-20. What status comes back? `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.
## 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