Skip to content
docs
Arxo ↗

Prepare and publish an answer

For LLMs8 sections

Answering happens in the conversation. Publishing moves an answer out of it: first into a frozen, checkable document, then optionally to a public address that can later be withdrawn. This page describes the mechanism, separates what was observed from what was not run, and distinguishes three states that are easy to confuse: holding an answer, holding a prepared document, and publishing.

Publication is a separate permission. Never call a publication tool as a side effect of answering; run it only when the user asked for a frozen or public document, and enforce that check in your application code (Safety and reliability).

Output
ask (law_ask) --> capture (law_capture_answer) --> prepare (law_prepare_answer)
|
v
frozen document (24 h, not public)
|
v
publish (law_publish_answer)
|
v
public address + separate revoke secret
|
v
revoke (law_revoke_answer)
closes page, API and download

Preparing re-executes rather than trusts: it repeats the earlier calls through the engine and compares every input, the law, the resources, the tool code and the result; any mismatch rejects the whole document. That behavior is contract-described. No prepare succeeded in this track, so the re-execution itself is NOT RUN. The step order and tool names are verified surfaces.

The four publication tools are a thin proxy to an external answers service. Without its address and token (LAW_ANSWERS_URL, LAW_ANSWERS_TOKEN) they refuse before reading any argument, and nothing leaves the host.

law_answer_capabilities reports which producers can be captured and prepared. Observed (schema law.answers.capabilities/0.2): 9 supported, 3 declared_only (a schema without an executable witness, so capture cannot succeed).

Producer idStatus
law_asksupported
law_case_asksupported
law_process_runsupported
law_editionssupported
law_arguesupported
law_amendsupported
law_draft_impactsupported
law_finitsupported
law_loopholesupported
compliancedeclared_only
law_querydeclared_only
rule_applicationdeclared_only

Producer support is about capture and preparation, not about the same-named tool running. In this track law_argue is a supported producer while the law_argue call itself refused on a world merge, and the structural law query command runs while law_query is a declared-only producer. Do not carry support in either direction.

The refusal observed on capture and prepare (server locale Russian):

Output
Ошибка: Arxo Answers не настроен: нужны LAW_ANSWERS_URL и LAW_ANSWERS_TOKEN;
обычный law_ask продолжает работать без них

with isError: true and structured code ANSWERS_UNAVAILABLE. The shipped English string reads: “Arxo Answers is not configured: LAW_ANSWERS_URL and LAW_ANSWERS_TOKEN are required; an ordinary law_ask keeps working without them”.

CodeMeaningStatus
ANSWERS_UNAVAILABLEService address or token not configuredObserved
ANSWERS_BAD_RESPONSEThe service answered with an invalid resultRead in the server source
ANSWERS_TOO_LARGEThe analysis exceeds the transfer limit; split it across publicationsRead in the server source

On ANSWERS_UNAVAILABLE, stop the publication plan and name the missing setting; retrying does not help.

The refusal reached step by step, each request carrying the control values really returned by the previous step. The case is the feeding-break question over kz-labour-code (Labour Code of the Republic of Kazakhstan): one child, a 25-minute break, legalTime 2026-09-01.

Step 0: ask. law_ask returns COMPUTED / TRUE_ONLY through the one-child rule. The response carries a replay control (schema law.answers.replay-control/0.1) that later steps echo back: the request as asked, the checks caseHash, codeHash, programHash, resultHash, rustCodeHash (equal to codeHash here), and responseHash:

Output
caseHash sha256:d6ae0115bc6d23dda0734a4be87d055d2925a2624f397580c0cf94ae54389251
codeHash sha256:5a64bffcce72767c5db83b46698ef11f3bac835c9e71455f82964e3993dd5038
programHash sha256:09de6428d383eeea903d37a1a1aba64c46d7c868089d57cd37df9ee7b02b8302
resultHash sha256:cf19c47a357f480896d7715f1583ed3bee4fa4c7bec389e96e4dc009478ec5d9
responseHash sha256:75fc31d73d336c0369b6b7e6fba1e9dfac14c85c5191226e9e863d35d7091b21

codeHash moves with the engine build; resultHash stays.

Step 1: capture. The request threads the producer id and the request echoed from step 0:

JSON
{"producer": "law_ask",
"request": {"kind": "truth", "package": "kz-labour-code",
"predicate": "feeding_break_too_short",
"args": ["urn:kz:tk:employee:1", "urn:kz:tk:employer:1"],
"facts": [
{"predicate": "children_under_eighteen_months",
"args": ["urn:kz:tk:employee:1", 1]},
{"predicate": "child_feeding_break_minutes",
"args": ["urn:kz:tk:employee:1", "urn:kz:tk:employer:1", 25]}],
"legalTime": "2026-09-01"}}

Observed: isError: true, ANSWERS_UNAVAILABLE. No producer control was issued.

Step 2: prepare. The request carries the human question and one calculation holding the same request and the full replayControl from step 0 (the wire call carries the request twice: beside the replay control and inside it). Shape, with the objects abbreviated:

JSON
{"question": "Is the feeding break too short?",
"calculations": [{"id": "c1", "label": "feeding break truth",
"request": {"kind": "truth", "package": "kz-labour-code", "…": "as in step 1"},
"replayControl": {"schemaVersion": "law.answers.replay-control/0.1",
"request": {"…": "as in step 1"},
"checks": {"caseHash": "…", "codeHash": "…", "programHash": "…",
"resultHash": "…", "rustCodeHash": "…"},
"responseHash": "…"}}]}

Status of this block: labeled shape; the sent call carried the full objects and the hash values printed in step 0. The runnable script below takes both objects from the step-0 response as objects, never retyped.

Observed: the identical refusal. No preparation id was issued.

Step 3: publish. NOT RUN. Publish takes a preparation id, and no prepare call issued one. Blocker: the answers service is not configured. Reproduction: set LAW_ANSWERS_URL and LAW_ANSWERS_TOKEN, rerun steps 1 and 2 to obtain a preparation id, then call law_publish_answer with it.

Step 4: revoke. NOT RUN. Revoke takes the public id and the separate secret from a publish that never ran. Reproduction: after a successful publish, call law_revoke_answer with the returned public id and revoke secret.

No prepare, publish or revoke call completed in this track. Capture and prepare ran and refused with real threaded values; publish and revoke never ran. What follows is the tool contract, not an observed run.

  • Prepare takes 1 to 12 earlier law_ask calls, or 1 to 12 producer runs (the two inputs exclude each other), each with its request and replay control, plus the human question and an optional assistant summary and explanations tied to calculation ids. It returns a preparation id for a frozen, non-public document held for 24 hours.
  • Publish takes a preparation id and returns a public address. Repeating it with the same id returns the same address and the same revoke secret instead of creating a second document.
  • Revoke takes the public id and the separate secret (the public id alone carries no control rights) and closes the page, the API and the download.

This track therefore makes no claim about preparation latency, link formats, access control on the preparation link, or how a published page renders.

StateHow it arisesWho can read itWhat it promises
Held answerA computed answer in the conversation, with proof and hashesConversation participantsThe bytes reproduce from the recorded inputs; nothing about access or permanence
Prepared documentlaw_prepare_answer succeeds; a preparation id existsWhoever holds the preparation link, for 24 hoursFrozen inputs and re-executed results; explicitly non-public
Published pagelaw_publish_answer succeeds; a public address existsAnyone with the address, until revokedA stable public address; withdrawal via the revoke secret

Confusing these states is the main publishing failure. A hash is not access: identical hashes say the computation reproduces, not who can read it. Engine determinism does not mean two agents write the same summary. And a prepared document is not published: it expires, and it was never public.

JSON
// 1. MCP: the support matrix. Only supported producers can be captured.
{"tool": "law_answer_capabilities", "arguments": {}}
JSON
// 2. MCP: ask first; the response carries the replay control.
{"tool": "law_ask",
"arguments": {"package": "kz-labour-code",
"predicate": "feeding_break_too_short",
"args": ["urn:kz:tk:employee:1", "urn:kz:tk:employer:1"],
"facts": [
{"predicate": "children_under_eighteen_months",
"args": ["urn:kz:tk:employee:1", 1]},
{"predicate": "child_feeding_break_minutes",
"args": ["urn:kz:tk:employee:1", "urn:kz:tk:employer:1", 25]}],
"legalTime": "2026-09-01"}}
  1. Runnable: examples/publishing/thread_sequence.py threads the step-0 objects into capture and prepare. It needs a source checkout with the prebuilt MCP server binary (standard library only; it finds the checkout root from its own location). From this section’s directory:
Terminal
LAW_MCP_PROFILE=kz python3 examples/publishing/thread_sequence.py

Expected without the answers service: step 0 COMPUTED / TRUE_ONLY; steps 1 and 2 isError: true with ANSWERS_UNAVAILABLE; steps 3 and 4 NOT RUN with the reason taken from the prepare response. On a configured service a successful prepare prints its preparation id, and the script still stops there.

JSON
// 4. NOT RUN: publish needs a preparation id no prepare call issued.
{"tool": "law_publish_answer",
"arguments": {"preparationId": "(from a successful prepare)"}}

For connection setup, see the MCP connection guide.

Show commands, versions and results

Transport: the session’s harnessed MCP tools were unusable (“MCP stdio connection is closed”), so the evidence below came from direct stdio to the prebuilt local MCP server with profile kz (server arxo-law-kz 0.1.0, canon kz.sources/2026.2.0). Commit not recorded.

StepDateResult
tools/list2026-10-03The four publication tools present
Step 0 ask2026-10-03COMPUTED / TRUE_ONLY, resultHash sha256:cf19c47a…
Capabilities matrix2026-10-039 supported, 3 declared_only, as tabled
Capture, full step-0 request2026-10-03isError: true, ANSWERS_UNAVAILABLE
Prepare, full request and replay control2026-10-03Identical refusal, no preparation id
Threaded rerun (thread_sequence.py)2026-10-04Step 0 same resultHash (newer codeHash); steps 1 and 2 ANSWERS_UNAVAILABLE; steps 3 and 4 NOT RUN
Publish, revoke—NOT RUN: answers service unconfigured
ANSWERS_BAD_RESPONSE, ANSWERS_TOO_LARGE, refusal strings (ru, en)—Read in the server source and shipped strings
law_argue refusal (producer/tool asymmetry)2026-10-03Ошибка: мир не слить: пакет "calc-obligations" объявляет семантику 0.2.2, остальные — 0.2.4 (isError, no structured code)

Previous: Safety and reliability Next: Evaluate, debug and upgrade

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

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