# DKE MCP — Wire & Tool Contract **Version:** alpha · API 1 **Status:** Published contract for the DKE service wire. **Audience:** Tenants and third-party tooling that call the DKE service at `dke.langsyn.net` — the tool surface, request shapes, response envelope, error model, and billing. The language you send over this wire is [DKE Python](/ref/dke/python/); the unit you send it in is a [module](/ref/dke/python/#modules). If you are connecting for the first time, start with the [MCP Tutorial](/tutorial/dke/mcp/). **This document as Markdown:** [dke-mcp-ref.md](/ref/dke/mcp/dke-mcp-ref.md) — the same text this page is rendered from, for readers and tools that would rather have the source than the page. --- ## 1. Model DKE is a **deterministic language runtime addressed over MCP**. You write a program in DKE Python and submit it; DKE returns a response that is a deterministic, reproducible function of the program and the store it runs against — reproducible bit-for-bit, and metered per operation rather than per token. The program world is **two levels**: - A **module** is the unit you submit: a `.dpy` source with a run-once body. It is a named, stored object. - A module defines zero or more **scripts** (its `def`s). Each script is addressed **`module.script`** — module-qualified, so two modules may each define `restock` without collision. Two verbs produce a *computed* response — **`compile`** (store a module) and **`run`** (execute). Everything else is management and introspection: where modules, scripts, and data live, and how you inspect, protect, and remove them. The service stores only the **compiled form** of what you submit, never its source text: there is no source readback, for your own modules or for shared ones. You keep your own source. --- ## 2. Tool surface Thirteen tools, in five groups: | Group | Tools | |---|---| | **Compute** | `compile`, `run` | | **Introspection** | `spec`, `info`, `list`, `info_module`, `list_modules` | | **Diagnosis** | `checkup` | | **Lifecycle** | `forget`, `forget_rule`, `forget_constraint`, `forget_module` | | **Store** | `stores` | Script-level verbs (`info`, `forget`) address a script by its qualified `module.script` name; `forget_rule` addresses a standing rule the same way (`module.rule`) and `forget_constraint` a standing constraint (`module.constraint`); module-level verbs (`info_module`, `forget_module`) address a module by its bare name. Your key addresses exactly one store, created for you from your account. Stores are provisioned, shared, and deleted from the account dashboard, not from this wire. --- ## 3. Connect - **Endpoint:** `https://dke.langsyn.net` — MCP over HTTPS, JSON-RPC 2.0, protocol version `2024-11-05`. - **Auth:** `Authorization: Bearer dke_c_<…>` (a consumer key). Tenant keys (`dke_t_…`) are rejected on this endpoint — they administer the account, not the engine. - **Server identity:** `serverInfo = { name: "dke", version: "1" }`. The version is the public API version, deliberately decoupled from the build version. - **Handshake:** standard MCP `initialize` → `tools/list` → `tools/call`. The `clientInfo` a client sends about itself is recorded against the key it connected with, and surfaces in the dashboard. It is **self-declared**: it labels a connection, never authenticates one, and never affects access. - **Store selection:** every store-touching tool accepts an optional `store_id` (string, default `"default"`), at params level or inside `arguments`. - **One key, one client.** A key is the identity your writes are recorded under — `written_by` ([Reference §11.2](/ref/dke/python/#field-access-by-type)) reports it on every claim — so two clients sharing a key produce a history that cannot be separated afterwards. Mint each client its own from the dashboard. An additional key is issued **read-only** by default; raise its level only when that client writes. A key's store is fixed for its lifetime; its access level is not, and is changed from the dashboard. --- ## 4. The two compute verbs `compile` and `run` are the two ways to submit a source, and they differ in exactly one respect: **`compile` keeps the compilation; `run` does not.** "Keep it" versus "try it." ### 4.1 `compile` — validate and store a module Turn a module's source into a named, stored unit ("compile and keep"). This is the only path that creates durable state (stored modules, scripts, rules, kinds). A `.dpy` source is a module ([Reference §9.1](/ref/dke/python/#modules)): `compile` stores the module text under `name` and **runs its body** — executing its data declarations, installing its rules, and making its `def`s callable. - **Params:** - `name` *(required, string)* — the module's name in the store. It MUST be a single identifier of the same shape `import ` accepts in DKE Python: an XID_Start character followed by XID_Continue characters, with no dot and no hyphen. A module you cannot name in an `import` statement is a module you cannot use, so the name is held to the language's own rule. - `source` *(string, optional)* — the module text; omit to **reload** an already-stored module (re-run its body). - `version` *(string, optional)* — your label for **this revision** of the module, e.g. `"2.1.0"`. Recorded as metadata and reported by `info_module`; it changes nothing about how the module resolves or runs. Compared for equality and never ordered, so any different value is a change — including going back to a label you used before. Omitting it on a later `compile` leaves the version already recorded alone, so an unrelated edit cannot erase a label. A module you never versioned simply has none. - `language` *(enum, default `"eng"`)* — see §8. - `store_id` *(string, default `"default"`)*. - **Success:** `_meta.status = "success"`, the module's identity and the `module.script` names it installed. - **Invalid source:** `isError = true`, `_meta.status = "compile_error"`, `_meta.compile_status ∈ { parse_error, type_error }`, `_meta.diagnostics = [{ class, line, col, message, fix? }]`. Nothing is stored: a module that does not compile leaves the store as it was. The service MUST: 1. Lex, parse, and typecheck the source. 2. If any check fails, refuse the operation; no storage occurs. 3. Otherwise, store every declaration of the unit under its own declared name, atomically (all of the unit or none of it), each executable by name, and run the module body once. A unit that is exactly one `def` is just a one-declaration module — `compile` submits it like any other; there is no separate single-script verb. **What the response says.** It acknowledges the submission, states **what it installed** — the classes, scripts, rules and computed fields the unit declared — and then **reports what the module body did**: its `print` output and its store effects, in execution order, because running the body is part of submitting the source (step 3 above). Each part appears only when it has something to say. A module that declares nothing installable — a data declaration alone — states no installs; a module with no body reports none; a module with neither is acknowledged on its own. The installs are what **this submission** installed, so reloading a stored module by name states none: its declarations are already stored, and only its body is run again. Name resolution, re-submission reconciliation, and namespacing are specified in [Reference §9](/ref/dke/python/#modules). ### 4.2 `run` — execute Execute **instructions** and return a transcript plus usage. - `source` *(string, required)* — a program: statements that read, write, and call compiled scripts. Compiled and executed once, **not stored**; anything it `remember`s persists, but nothing is left callable and nothing appears in `list`. A source with no statements has nothing to execute and is refused. - `language` *(enum, default `"eng"`)*, `store_id`. A program has **no parameters** — write the values you want into the instructions — and it **cannot create definitions**. A source declaring a `def`, a `class`, a `@rule`, or a computed field is refused, with a message directing you to `compile` it. A program cannot `return` either; a value it wants to report, it prints or binds. To execute something you compiled, import its module and call it: ```json {"source": "import inventory\ninventory.restock(\"widget-1\")"} ``` A **value script** composes the same way, its result bound in the program (`n = inventory.count_on_hand("widget-1")`); the binding appears in the transcript as a typed entry carrying the value. **What a run reports.** The run executes against the store's current state. The transcript renders in the natural language the source was compiled in (§8), and covers **everything the run did**, in execution order, including the effects of any module the program imported. Its call frame is the response header, not a body line: a successful run is headed `OK program: ` and a failed one `error: program failed — `. The verdict tallies what the run did — the count of **claims** written, **claims** removed, and `print` lines. Both store counts are in claims, not statements, and **a statement that changed no state reports no effect at all** — it earns no place in a tally of what changed, and its target is not listed among the effects either. One `forget` removes every claim at the address it names, so a statement that removed three reports three and one that found nothing to remove reports nothing. A **range** `forget` is the exception to that first clause and not to the second: it names the older versions before an instant rather than everything at the address, so it can report fewer than the address holds — and it never removes the value in force, which is why the count it reports can be zero at a cell that plainly has a value. A `remember` of a value a source has **already** recorded at that cell is likewise no change: the store is idempotent, so the write succeeds, adds no claim, and is reported as adding none. (`update` replaces rather than matching, so it records a claim even when the value is the one already there.) The body then lists, in execution order, the run's **output** (the values the program `print`s) and its **store effects** (the `remember`/`update`/`forget` writes, each naming its target cell). Statements that change no state stay silent — assignment bindings, control flow (`if`/`match`/`for`), transaction verbs (`branch`/`commit`/`rollback`), and read/query calls — so the body carries the run's outputs and durable effects, not its execution mechanics. This human-readable render is **presentation, not a versioned contract**: a conforming implementation MAY format the header, verdict, and effect lines differently. What is pinned is that the render is **deterministic** (same source + same store → byte-identical transcript) and that the machine-readable result carries the run's terminal status and its `print` sequence. A run **is not transactional**: a failed run does not undo writes already executed by earlier statements (its data writes persist either way); only a `branch` block groups writes transactionally. --- ## 5. Response envelope Every `tools/call` returns a JSON-RPC success envelope `{ jsonrpc, id, result }` where `result` is: ``` result = { content: [ { type: "text", text: }, ], isError: , // true if the call did not succeed _meta: { status: "success" | "refuse" | "engine_error" | "compile_error", tenant_id, key_id, // from auth, never from params answered_at, // RFC-3339 UTC ops, // usage (see §7) refuse_reason?, // see §6 // compile: compile_status, diagnostics[] // run: invoke_status, transcript[] } } ``` - A value a program computes rides in `transcript[]`, as the typed entry for the binding that holds it. A program does not return, so there is no separate returned-value field. - Each entry carries a **`kind`**, naming which statement of yours produced it. There are exactly two, and both are named for the DKE Python construct they record: | `kind` | The statement that produced it | |--------|--------------------------------| | `assign` | a bare assignment — `cur = current(K.s.a)`. The entry carries the bound value, its `type`, and **`name`** — the identifier you bound it to. | | `print` | a `print(...)` call. The entry carries the printed value, and no `name`, because a print binds nothing. | **`name` is how you tell two bindings apart.** A program that binds several intermediates produces several entries of the same `kind`, and without the name the only thing separating them is `line`. Match on `name` when you want a particular value out of a transcript; it is the identifier from your own source, spelled exactly as you wrote it. **Only value-bearing statements appear.** Control flow — `if`, `for`, `match`, `try`, `with branch` — runs without adding entries, and so do write verbs like `remember`. A transcript is the values your program produced in order, not a trace of everything it did. If you need to see that a branch was taken, print something in it. - Each `transcript[]` entry carries a `line`, and **`line` is a line of the source the entry came from — which is not always the source you submitted.** A module's lines run as part of your run in two ways: its body runs when you load it, and its scripts run when you call them. Either way its entries join your transcript and carry **`from_module`**, naming the module they came from; your own entries carry no such field. Read `line` against `from_module` when it is present, and against your own source when it is not. Where one module reaches another, the field names the module an entry is actually from, not the one that reached it — so a line number of yours and a line number of a module's are never confusable, even when they are the same number. - **A line that runs many times is reported, not repeated.** A loop would otherwise emit one entry per iteration, making the response as long as the work rather than as long as the answer. So for each line, the first few entries and **always the last** are kept, and the repetitions between them are dropped. The last is always kept because it holds the value that line finally produced — the answer. `print` output is never dropped at all: it is your program's own output. When anything was dropped, **`transcript_omitted`** gives the count, so a shortened transcript is never mistaken for a short one; a program that repeats nothing carries no such field. - `_meta` is the **canonical** structured channel, and carries the whole run. - Because some MCP clients do not surface `_meta` to the model, the same structured block is **mirrored** as a trailing `content[]` text item (leading `\n`, JSON-parseable). Clients SHOULD prefer `_meta`; the mirror is a compatibility affordance. - The mirror carries the same **fields**, but not the whole run — it is your **results**: every `print`, and the final value of each statement you wrote. A module's own working, which carries `from_module`, is not among them; it is on `_meta`. Its own `transcript_omitted` counts what the mirror left out, which is more than `_meta` left out. Read the mirror to see what a program answered, and `_meta` to see how it got there. The envelope reports outcome, usage, diagnostics, and a transcript of the program's visible outputs and store effects — the observable result of running your program, nothing about how the service is built internally. --- ## 6. Error model A failure reaches you at one of three levels, and a client has to handle all three. The first is the one people miss: **not every response is a JSON-RPC envelope.** A request that never gets as far as the tool surface is answered with an HTTP status and a small JSON body, so parsing every response as JSON-RPC breaks on the first mistyped key. **HTTP (no JSON-RPC envelope; body is `{"code": "…"}`):** | Status | `code` | Meaning | |---|---|---| | `401` | `AUTH_MISSING` | no `Authorization` header | | `401` | `AUTH_MALFORMED` | present but not a well-formed bearer key | | `401` | `AUTH_EMPTY_TOKEN` | `Bearer` with nothing after it | | `429` | `RATE_LIMITED` | too many requests — wait and retry | | `503` | `UPSTREAM_UNAVAILABLE` | the service is not reachable right now — retry | A `401` also carries `WWW-Authenticate: Bearer realm="DKE"`. Everything past this level answers `200` with a JSON-RPC body, including every error below — so **an HTTP status other than `200` means you never reached the tool surface**, and is the cheapest check a client can make first. **JSON-RPC (HTTP `200`, `error` member):** | Code | Meaning | |---|---| | `-32700` | parse error (malformed JSON body) | | `-32600` | invalid request (not valid JSON-RPC 2.0) | | `-32601` | method / tool not found — including a retired tool name (see §10) | | `-32602` | invalid params (missing `source`; unsupported edition/language; an unqualified `forget`/`info` script name) | | `-32000` | server error (an internal exception, returned as a generic `"engine error"`) | | `-32001` | payment required — the wallet cannot cover the call, the account is suspended, or the account does not include API access. Clears by paying: top up at `langsyn.com/buy/` | | `-32002` | rate limited — clears by waiting. Carries a `Retry-After` header in seconds | **`-32001` and `-32002` are deliberately different codes**, because the two clear by opposite actions: one by paying, one by waiting. A client that cannot tell them apart will back off when it should top up, or top up when it should back off. Route on the code, never on the message. **Application (transport-success, `isError = true`):** - `_meta.status ∈ { refuse, engine_error, compile_error }` — never `"success"`. `isError` is exactly `_meta.status != "success"`, so the two can never disagree. - `_meta.refuse_reason ∈ { OutOfSurface, IncompleteSlots, Ambiguous, ScopeForbidden }`. - `compile` diagnostics: `class ∈ { parse_error, type_error }` with `line`/`col`. The three failing statuses differ in whose problem it is, which is what you route on: `compile_error` means your source did not compile — read `diagnostics` and fix it; `refuse` means the call was declined — read `refuse_reason`; `engine_error` means the service could not carry the call out — retrying is reasonable. Naming something that does not exist — a script, a module — is an `engine_error`, and is the one case where retrying will not help: the call is well-formed and there is nothing at the address, so `list` or `list_modules` to find the right name. **Every failure reaches you on the status**, so routing on `isError` alone is sufficient to tell a completed call from a failed one; you never have to read the prose to find out which happened. Refusals name a category, not an internal cause: a scope refusal does not say which permission was missing, and a capacity refusal is a generic "service at capacity." ### 6.1 The repair loop — a rejected submission, its `fix`, and the resubmit A `compile_error` is not a dead end, and this is the loop most agent workflows spend most of their turns in. Every diagnostic carries `message` for a person and, where the correction is mechanical, **`fix` for a program**. They are separate fields on purpose: one response serves a reader and a client without serving either badly, and a client applying `fix` never has to parse prose. Submit a module written on Python reflexes: ```json { "name": "watch", "source": "def watch(n: int):\n i = 0\n while i < n:\n print(\"checking\")\n" } ``` The submission is refused whole — nothing is stored — and the diagnostic says both what is wrong and what to write instead: ```json { "isError": true, "_meta": { "status": "compile_error", "compile_status": "parse_error", "diagnostics": [{ "class": "parse_error", "line": 3, "col": 5, "message": "DKE Python has no `while` loop — iteration is bounded: `for in :` over a store-derived collection (e.g. `list attributes of K`). Bounded iteration is the termination guarantee; there is no unbounded loop.", "fix": "for in :" }] }} ``` `line` and `col` say where, `fix` says what to write, and the resubmission is the same call with that edit applied: ```json { "name": "watch", "source": "def watch(k: string):\n for a in list attributes of k:\n print(a)\n" } ``` Two properties make this a loop a client can drive rather than a message a person must read: - **A rejected `compile` stores nothing.** The store is exactly as it was, so a retry is a retry and never a partial write to undo (§4.1). - **`fix` is present only when the correction is mechanical.** Its absence is not an omission — it means the diagnostic has no single edit to offer, and `message` is the whole of what the service can say. Treat `fix` as optional and fall back to `message`; never require it. The diagnostics most worth expecting are the ones where a Python habit is the wrong reflex here — no `while`, no recursion, procedures do not return a value, and a list has no `.append`. Each of those refuses with the DKE Python form to write instead, so the first submission an agent writes from Python memory is usually one edit from compiling rather than a rewrite. --- ## 7. Usage & billing - `_meta.ops` — the number of **operations** the call consumed, an opaque non-negative integer. It counts what the program ASKED FOR, so the same question costs the same however you spell it. - An operation is counted **each time it runs**, not once for each time you write it. An operation inside a loop over a hundred subjects is a hundred operations, so a program's cost follows the data it works over rather than its length. - **1 LCN buys 100,000 operations**, debited from the account wallet after the response is served. It is a capability measure, not a cost breakdown. - A run over a suspended key is refused with `-32001` before it executes — the service never runs on credit. --- ## 8. Language selection Language is chosen **out of band**, in params — never in a source header: - `edition` *(enum, default `"python"`)* — the concrete language. Today `"python"` (DKE Python) is the only accepted value. - `language` *(enum, default `"eng"`)* — the natural-language keyword surface. Accepted values are `"eng"` and `"nob"`. Any other value is rejected with `-32602` at the parameter gate, before the engine. More languages are planned; a new concrete language is a new `edition` value and a new keyword surface is a new `language` value, both over this same wire and the same response contract. ### 8.1 What `language` selects `language` selects the **keyword surface** — the words the runtime reserves — and nothing else. Verbs, keywords and the words in the response are in the tag you ask for; the kinds, subjects, attributes and values *you* supply are your data and are stored and returned exactly as written. The same program in both surfaces: ```json {"name": "run", "arguments": { "language": "eng", "source": "remember(Sensor.s1.room, \"kitchen\", \"survey\")\nc = current(Sensor.s1.room)\nprint(c.value)"}} ``` ```json {"name": "run", "arguments": { "language": "nob", "source": "husk(Sensor.s1.room, \"kitchen\", \"survey\")\nc = gjeldende(Sensor.s1.room)\nskriv(c.value)"}} ``` Both write the same claim and both print `kitchen`. `Sensor`, `s1`, `room`, `kitchen` and `survey` are yours and do not change; `remember`/`husk`, `current`/`gjeldende` and `print`/`skriv` are ours and do. `value` does not: a field of a result is addressed by the same name in every surface. This has three consequences worth stating plainly: - **A store is not language-scoped.** A module compiled under `"nob"` is read, invoked and forgotten by exactly the same names as one compiled under `"eng"`, and the two see each other's claims. There is no separate Norwegian store and no migration between surfaces. - **Two words are English on every surface.** The two diagnostic verbs, which exist only in English — a program in any surface writes them in English or the call is not recognised. Everything else in the keyword surface localises, the `except` handler kinds and the compound verb heads included. Reference §12 carries the same list, and the same check holds both. **English on every surface.** `why_not` `what_needs` - **A module remembers the surface it was compiled in.** `info_module` reports it as the module's init language, and a script's transcript renders in that surface — so a Norwegian module keeps answering in Norwegian even when a later call omits `language` and takes the `"eng"` default. The verbs that operate on a **stored** script — `list`, `info`, `forget` — are edition-neutral and take no edition: only the source-carrying verbs, `compile` and `run`, select one. --- ## 9. Introspection & management ### 9.1 Language - `spec(edition, topic?)` — serve the DKE Python language contract, full or a topic slice `∈ { header, types, verbs, statements, scripts }`. ### 9.2 Scripts — address by `module.script` - `list(target = scripts | rules | kinds)` — stored `module.script` signatures, standing-rule names, or the kinds your data is stored under (§9.2.1). A script's line carries its full call signature: the name and its typed parameters in declaration order (`inventory.ingest(id: int, payload: string, dry_run: bool)`). Listing order is implementation-defined but MUST be stable across reads with no intervening `compile` / `forget`. In expression position inside a program, `list_scripts()` evaluates to a `string_list` of those names — the one lifecycle read that is also callable in-language, because it is a read-only data read. - `info(name)` — a script's **compile provenance**: the compiling authority, the natural language the source was compiled in, and the manifest version the compiled form was produced against. Invocations are not tracked — `info` describes the compilation event, not any run. (For a data derivation, use the in-language `why(K.s.a)`, which returns a `proof_tree`; `info`'s response is text, not a typed value.) - `forget(name)` — remove one script. Idempotent: if no script with that name exists, the call succeeds. #### 9.2.1 `list(target = kinds)` — what the store already holds The kinds your data sits under: what a `class` declares, and what a fact's path names first. One per line, ordered as `list`'s other targets are — stable across reads with no intervening write. It answers a question the other reads cannot, and the difference is worth stating because it is easy to be misled by. `checkup` reports what a store is *missing* for the reasoning standing over it, so it names a kind only when no rule reads it — write a rule over a kind and it stops appearing. `subjects(...)` and `current(...)` report what is *in force*, so a recorded value whose validity window does not cover the moment asked about reads exactly like a cell nobody wrote. This target reports what is *there*. Ask it before writing facts into a store you did not start from empty. A kind you did not expect means data you did not put there, and a read that finds nothing may be reading past it rather than past nothing — `get(K.s.a)` shows every claim recorded at a cell, in force or not. The engine's own storage kinds are not yours and never appear here. ### 9.3 Standing declarations — address by `module.` A rule and a constraint are both standing declarations a module installs, and both are addressed the same way. They are removed by different verbs because they remove different amounts: one withdraws what it derived, the other has derived nothing. - `forget_rule(name)` — remove one standing rule, leaving the rest of its module in place. The facts the rule concluded are removed with it; the facts it concluded *from* are untouched, so re-`compile`ing the module brings the rule and its conclusions back. A conclusion that another standing rule still supports survives. Removing a rule that is already gone succeeds. Use `list(target = rules)` to see the names. - `forget_constraint(name)` — remove one standing constraint, leaving the rest of its module in place. **Nothing is reclaimed**: a constraint concludes nothing, so there is no derived fact to withdraw, and the data it watched is untouched — including the subjects that breached it. Stopping the watch is not fixing the breach. Re-`compile`ing the module brings the constraint back. Removing one that is already gone succeeds. `checkup()` prints the names, in its `violated` findings. ### 9.4 Modules — address by bare module name - `list_modules()` — module handles (name, script count, created-at). Listing order is implementation-defined but MUST be stable across reads with no intervening `compile` / `forget_module`. - `info_module(name)` — a module's record: its **init language** (the natural language its body was compiled in), the `module.script` names it defines, the classes it declares when it declares any, its `version` if it has one, **updated-at**, and created-at. `created` is when the module was first stored; `updated` is when its content last changed, and a `compile` that submits identical content leaves it alone. Worth reading when a module you `import` may have moved: a call is resolved when the **calling** module is compiled, so a caller keeps running the callee body present at that moment until you compile the caller again. An unknown module is a clean not-found. - `forget_module(name, confirm=true)` — remove a module as a unit. `confirm=true` is required; it cascade-removes the module's scripts, kinds, and their data, and is **not reversible**. It **refuses**, naming the referrer, if another stored module still references the target — so no dangling references can exist. Forgetting a module that does not exist is a clean not-found. `name` here is the **bare module name**, never a `module.script` address. ### 9.5 Store - `stores()` — your store's id, size, and created-at. Your key addresses exactly one store. Everything that acts on the store ITSELF — creating it, deleting it, snapshotting it, restoring it from a snapshot — happens in the account dashboard, not on this wire. Your key governs what may be done to the DATA INSIDE your store; the store's own lifecycle belongs to the account that owns it. ### 9.6 Checkup — does your store meet the reasoning over it? - `checkup()` — no arguments, changes nothing, needs only a read key. Reach for it when a query returns nothing and you cannot tell whether the answer is genuinely no. A rule that never fired and a rule that fired and found nothing produce the same empty result, and when the reasoning came from a module you did not write, nothing in the result tells you which one happened. The report is a counts line followed by findings, one per line: | Line | Means | |---|---| | `unmet . no-such-kind` | a standing rule reads this cell and nothing of that shape exists in your store | | `unmet . no-values` | the kind is there and that field is empty | | `unmet . not-in-force` | you did record a value at that field, and its validity window does not cover the time asked about — the fact is in your store and is not current, so what it needs is a window or an anchor, not another write | | `unread ` | this kind of yours holds data and no standing rule reads it | | `near A ~ B` | an unread kind and an unmet one differ by a letter or two | | `counts rules=… unfirable=… kinds=… unread=…` | how many rules stand, how many cannot fire as things are, how many kinds hold data, how many nothing reads | A `near` line is the one to read first. Writing your facts under a kind spelled one letter differently from the one the reasoning reads produces *both* an `unread` line and an `unmet` line, and the pair names the mis-spelling in a way neither line does alone. Findings name **cells, not rules**: what an unmet cell needs is your data, and the address of the data is the actionable part. Rules are counted rather than named. An empty store reports its counts and nothing else. Listing order is stable across reads with no intervening change. --- ## 10. What lives elsewhere Store **snapshots** are a store-lifecycle control, not an engine operation, so they are not on this wire: they live in your account dashboard beside your store's other lifecycle controls, and restoring a store from a snapshot is available there. A key addresses the data within one store; the store's own lifecycle belongs to the account that owns it. Any tool name this document does not list is not served, and returns `-32601`.