Skip to content
docs
Arxo ↗

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

For LLMs8 sections

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.

  • Goal: compute the end date of the period, and test whether a candidate end date is established.

  • You need: Bind input: toCaseInput supplies the case this chapter evaluates. The runtime singleton from Start opens the model once for both calls.

  • Run:

    Terminal
    node --import tsx --test test/evaluate.test.ts
  • Files:

    Output
    src/query.ts buildCollect, buildTruth
    src/runtime.ts evaluateCollect, evaluateTruth

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.

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.

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:

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:

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 introduces them where they are first used, and src/runtime.ts in the archive holds the full runtime.

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.

  • 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.

Run the calculate tests and confirm all three shapes:

Terminal
node --import tsx --test test/evaluate.test.ts
Output
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.

Documentation for Arxo. Writings — blog.arxo.io.

Anonymous visit counts on stats.arxo.io, no cookies.