Architecture
This page maps the deadline example as built: the layers, the data path from a form submission to a stored capture, the runtime singleton, and the boundaries between browser, example server, and engine.
docs/build/examples/deadline-appThe data path
Section titled “The data path”One request travels eight steps. Each step is one module with its own exports, listed in the table below; nothing is shared between requests except the model handle described after it.
- Form. The page shows four fields — the event date, the period length in days, an optional candidate end date, and the legal time — and the browser posts them to the example call.
- Validate. The raw fields are checked and turned into a typed form input, or the request is rejected before any engine call runs.
- Facts. The adapter turns the validated form into canon facts and
a case input. It stamps its own version (
1.0.0) so a capture records which mapping produced it. - Query. Two questions are built: a collect query that computes the
end date and a truth query that checks the candidate. Both carry the
legal time, the
Europe/Berlintime zone, and the deadline policyurn:de:corpus:clir:bgb-fristen#BGB_FRISTEN_TAG. (Time zone, calendar data, and counting policy are three separate context elements — see the facts chapter.) - Engine. The questions run against the open model, which returns the raw answer documents.
- Read. Each answer is reduced to one of four kinds: a value, a claim, not computed, or unknown.
- View. The reduced answer becomes the shape the page and the API response render, including the inconclusive shapes.
- Capture. The case, the pins, and the answer are stored with the canonical document bytes and their checksum. Replay later reproduces the answer and detects edited bytes; a separate step maps a report of missing premises to follow-up questions.
The command line runs the same steps without the server: evaluate
computes and stores, replay reproduces from a capture.
Module table
Section titled “Module table”| Step | Module | Exports | Role |
|---|---|---|---|
| 1 | public/index.html | the form | collect the four fields: eventDate, durationDays, candidateEnd?, legalTime |
| 2 | src/input-schema.ts | validateFormInput, validateDraftInput, FormInput, DraftInput | reject bad forms and drafts before compute |
| 3 | src/to-case.ts | ADAPTER_VERSION, toFacts, toCaseInput, toDraftCaseInput | map fields to facts and case input |
| 4 | src/query.ts | buildCollect, buildTruth | build the collect and truth questions |
| 5 | src/runtime.ts | openModel, evaluateCollect, evaluateTruth, explainWhy, explainDraft | hold the model; run questions |
| 6 | src/read-result.ts | readCollect, readTruth, AppResult | reduce answers to value, claim, not-computed, unknown |
| 7 | src/to-view.ts | toViewModel | render any AppResult for display |
| 8 | src/capture.ts | CAPTURE_FORMAT, captureEvaluation | archive captures as deadline-app.capture/1 with documentBase64 and sha256 |
| 8 | src/replay.ts | replayCapture, verifyCaptureIntegrity | reproduce answers; check document bytes |
| 8 | src/missing.ts | ASKABLE, missingFacts | turn whyNot blockers into questions |
| — | src/batch.ts | runBatch | stream many cases to a line report |
| — | src/check-contract.ts | APP_PREDICATES, checkContract | test whether a model serves this app |
| — | src/server.ts | handleRequest, createServer | serve the form and the three API calls (evaluate, replay, capture download) |
| — | src/cli.ts | evaluate, explain, batch, check-contract, replay | run the flow without the server |
| — | python/deadline.py | the same flow | mirror the flow in Python |
The runtime singleton
Section titled “The runtime singleton”openModel opens the model once and hands the same handle to every
caller:
open('de.bgb.fristen@0.1.0', { offline: true })The version pin makes answers reproducible; offline: true keeps
execution local (via is local) with no network in the call path.
Each evaluation builds a fresh case input per request through
toCaseInput, so one request cannot see another request’s facts: state
isolation comes from fresh inputs, not from locks.
Execution is sequential per request: each handler awaits its engine
calls in turn. The server makes no concurrency claim beyond what
node:http with await gives — requests interleave only where the
runtime yields, and no request mutates state another request reads.
Trust boundaries in words
Section titled “Trust boundaries in words”Three zones, two crossings:
- Browser. Holds the form and the rendered answer. It is untrusted input: values may be missing, mistyped, or hostile. Nothing the browser sends reaches the engine unchecked.
- Example server. Holds validation, the adapter, query building, the
model handle, reading, view shaping, and captures. It checks the form
first (
validateFormInput), maps only the four fixed fields, and pins the model version, the policy, the time zone, and the calendar data itself — none of those cross over from the browser. - Engine. Holds the canon and the evaluation. It sees only typed facts and questions; it answers with statuses, proof, and hashes.
The first crossing (browser to server) carries the four-field JSON
request in and a view plus a capture id out; a separate download
carries the full stored capture out for one id. The second crossing
(server to engine) carries facts and questions in and answer documents
out. Captures never cross back into the engine as trusted input:
replay re-runs the pinned case and compares, and
verifyCaptureIntegrity fails closed on edited bytes.
browser --(4 fields)--> server --(facts + questions)--> enginebrowser <--(view + capture id)-- server <--(answer documents)-- enginebrowser <--(stored capture file)-- serverThe Python mirror
Section titled “The Python mirror”python/deadline.py follows the same eight steps against the same
pinned model. Statuses and values match the TypeScript path; the result
hash and document bytes do not, so a capture is replayed by the SDK
that saved it. Where a step needs questions, unfold, or
focused_truth, the Python path reads the manifest the TypeScript side
produced at author time.
Documentation for Arxo. Writings — blog.arxo.io.
Anonymous visit counts on stats.arxo.io, no cookies.