Skip to content
docs
Arxo ↗

Architecture

For LLMs6 sections

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.

Output
docs/build/examples/deadline-app

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.

  1. 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.
  2. Validate. The raw fields are checked and turned into a typed form input, or the request is rejected before any engine call runs.
  3. 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.
  4. 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/Berlin time zone, and the deadline policy urn:de:corpus:clir:bgb-fristen#BGB_FRISTEN_TAG. (Time zone, calendar data, and counting policy are three separate context elements — see the facts chapter.)
  5. Engine. The questions run against the open model, which returns the raw answer documents.
  6. Read. Each answer is reduced to one of four kinds: a value, a claim, not computed, or unknown.
  7. View. The reduced answer becomes the shape the page and the API response render, including the inconclusive shapes.
  8. 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.

StepModuleExportsRole
1public/index.htmlthe formcollect the four fields: eventDate, durationDays, candidateEnd?, legalTime
2src/input-schema.tsvalidateFormInput, validateDraftInput, FormInput, DraftInputreject bad forms and drafts before compute
3src/to-case.tsADAPTER_VERSION, toFacts, toCaseInput, toDraftCaseInputmap fields to facts and case input
4src/query.tsbuildCollect, buildTruthbuild the collect and truth questions
5src/runtime.tsopenModel, evaluateCollect, evaluateTruth, explainWhy, explainDrafthold the model; run questions
6src/read-result.tsreadCollect, readTruth, AppResultreduce answers to value, claim, not-computed, unknown
7src/to-view.tstoViewModelrender any AppResult for display
8src/capture.tsCAPTURE_FORMAT, captureEvaluationarchive captures as deadline-app.capture/1 with documentBase64 and sha256
8src/replay.tsreplayCapture, verifyCaptureIntegrityreproduce answers; check document bytes
8src/missing.tsASKABLE, missingFactsturn whyNot blockers into questions
—src/batch.tsrunBatchstream many cases to a line report
—src/check-contract.tsAPP_PREDICATES, checkContracttest whether a model serves this app
—src/server.tshandleRequest, createServerserve the form and the three API calls (evaluate, replay, capture download)
—src/cli.tsevaluate, explain, batch, check-contract, replayrun the flow without the server
—python/deadline.pythe same flowmirror the flow in Python

openModel opens the model once and hands the same handle to every caller:

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

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.

Output
browser --(4 fields)--> server --(facts + questions)--> engine
browser <--(view + capture id)-- server <--(answer documents)-- engine
browser <--(stored capture file)-- server

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.