Interpreter architecture¶
How the Python reference interpreter under src/mklang/ is organized. This is
a contributor's map, not language semantics — those live in the SPEC. The
"change checklist" in CONTRIBUTING says which layers a change must touch.
Execution pipeline¶
.mkl file ──▶ loader.py ──▶ model.py ──▶ engine.py ──▶ llm/ adapter ──▶ provider
schema + dataclasses run loop: produce/judge
semantic for machine produce →
checks + states judge gates →
transition
loader.py— load and validate a.mkl: JSON-Schema (structure, fromdata/mklang.schema.json) plus semantic checks.model.py— dataclasses for a machine and its states, parsed from the plain dict post-YAML.engine.py— the runtime (SPEC §6): the produce → judge-gates → transition loop, budgets and termination (SPEC §7), fan-out,call,toolstates, provenance-taint tracking with produce-prompt fencing (SPEC §6, ADR 0025), and control-flow taint — theexternalsubset, theflow_tainteddecision mark, and the guard at the effect surface (ADR 0030), which a sub-run inherits acrosscall:. Suspension writes checkpoint frames viacheckpoint.py(ADR 0007) — frames carry the"tainted"key set (taint_framemarks resume-injected values) plus"external"/"flow_tainted"/"resume_injected"(what this resume supplied, so a stale human reply cannot confirm a later decision), all fail-safe when absent.interpolate.py—{{key.path}}interpolation and value formatting for prompts;render_delimitedfences tainted substitutions with a per-call nonce (ADR 0025).
LLM layer (llm/)¶
base.py— the interface the engine talks to. Two operations: produce and judge — plus the fixedJUDGE_SYSTEMrole and the sharedbuild_judge_user(OUTPUT/REASONING/CONTEXT always fenced, ADR 0025) used by both adapters.openai_compat.py— shared transport adapter for OpenAI-compatible providers. Provider aliases are registered explicitly; protocol policies such as DeepSeek V4 thinking-temperature handling are passed as profiles rather than inferred from arbitrary request payloads. Custom endpoints must declareprotocol: openai_compat.anthropic.py— native Anthropic adapter (mklang[anthropic]extra).mock.py— deterministic scripted LLM for tests; no network.prompts.py— reference-interpreter prompt assembly (host concern, not language): sectioned system prompts built fromstructure+execution, plus the untrusted-data rule when the user message carries a fence.context_view.py— host-side context rendering budgets (ADR 0017).
Host surfaces¶
All surfaces commission runs through the same seam, host.py.
cli.py+presentation.py— themklangcommand; typedCommandResults rendered as Rich text or stable JSON (CLI reference, ADR 0022).cli_parser.pybuilds the argparse tree from injected handlers;cli_doctor.pyis thedoctordiagnostics (ADR 0029).console/— the TUI (ADR 0015):app.py(Textual app),bridge.py(worker↔UI bridge: emit from any thread, block on human answers),session.py(crash-tolerant persistence),commands.py(slash commands),render.py(safe conversation rendering),widgets.py(activity tree, inspector),tools.py(the brain machine's hands, including bounded read-only workspace listing, search and file reads),capabilities.py(host-side consent and privacy policy for agent tools),workspace.py(workspace policy, budgets and inspection). The brain itself is a machine:data/console/agent.mkl. The brain'supdate_tasktool persists a validated goal/plan/progress ledger in the session state, while authored machines are checked before atomic workspace replacement.mcp/— the stdio MCP server (ADR 0011/0013):server.py(commissioning + provenance, live events per ADR 0019),sessions.py(suspended runs keyed by opaque handles).
Extension registries¶
Plugins hook in via entry-point groups; builtins register the same way.
| Registry | Entry-point group | Contract |
|---|---|---|
providers.py |
mklang.providers |
LLM adapter factory |
tools.py |
mklang.tools |
(dict) -> str host tool for tool: states |
hooks.py |
mklang.hooks |
(context, output) -> bool gate hook |
registry.py |
mklang.machines |
layered .mkl registry: stdlib → plugin → system → user → project → project/machines/ |
Supporting modules¶
| Module | Role |
|---|---|
config.py |
tier→model map, explicit protocol, and structural provider validation; keys from .env |
paths.py |
XDG host layout and config/machine discovery (ADR 0021) |
errors.py |
typed adapter errors the engine maps to halt reasons |
lint.py / llmlint.py |
static analysis / LLM-assisted lint (ADR 0010) |
controlflow.py |
tool effect classes + call:-following reachability for control-flow taint (ADR 0030) |
scripttest.py |
scripted-LLM harness — single source of truth shared by mklang test and the conformance suite |
search.py, kb.py, mail.py, tool_obs.py |
host tool stubs + shared observation envelope (ADR 0016/0020) |
fs.py |
filesystem data tools: workspace-confined reads, opt-in writes (ADR 0024) |
toolconfig.py |
the tools: runtime-config block shared by the backend resolvers |
data/stdlib/ |
the std_* machines (catalog, ADR 0012) |
data/mklang.schema.json |
the JSON Schema check validates against |
Where to change what¶
A language change flows SPEC → schema → interpreter → conformance → examples →
tests → docs, in that order — the full checklist is in CONTRIBUTING. A
host-only change (CLI flags, console, MCP) stays in its surface plus
presentation.py/host.py and needs no SPEC edit.