Files
chemenu/docs/explanation/language-boundaries.md
torbenandClaude Code 0f0a5799fd
CI / verify (push) Successful in 8m6s
CI / pwsh (push) Successful in 2m50s
Release / release (push) Successful in 33s
Release / wiki-mirror (push) Skipped
instructions: the agent speaks the KB language in chat - AGENTS.md § Conversation language, and instructions sync compiles the language tag into every published SKILL.md (#116); 10.0.0-beta.9
Files changed:
- AGENTS.md
- CHANGES.md
- VERSION
- docs/explanation/language-boundaries.md
- docs/reference/commands/types-instructions-and-docs.md
- instructions/CONTRACT.md
- instructions/dev/stack-build/SKILL.md
- instructions/dev/stack-close/SKILL.md
- instructions/dev/stack-dev/SKILL.md
- kb/CONVENTIONS.md
- kb/CONVENTIONS.md.template
- tools/CONTRACT.md
- tools/chemenu/commands/doctor.py
- tools/chemenu/commands/instructions_cmd.py
- tools/chemenu/tests/test_doctor.py
- tools/chemenu/tests/test_instructions_cmd.py

Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
2026-10-12 06:57:51 +02:00

12 KiB

Language Boundaries

Two languages run through this repo at once. kb/ is written in whatever language the instance chose - German here, and the value lives in kb/CONVENTIONS.md's language:. Everything that tells an agent what to do - AGENTS.md, every CONTRACT.md, everything under instructions/ - is written in English, in every instance, whatever the first value says. So is the stack's own documentation for humans, for a different reason (§ "The stack's human documentation").

The rules themselves are in AGENTS.md § File naming (the language of a file) and AGENTS.md § Conversation language (the language of what an agent says). This page holds the part that is not a rule: why the line runs where it does, why the English half is not a setting, and which argument for it turned out to be wrong.

Contents

The axis is the reader, not the owner

For a long time the two halves could be told apart by asking who owned the file, and the answer came out right every time: the stack owns AGENTS.md and the contracts, which are English; the instance owns its pages and the templates that shape them, which are in the KB language. The ownership boundary is a real and load-bearing thing - ownership-and-templates.md is about what it buys - so it was easy to read the language split as one of its consequences.

It is not. The case that separates them is a page type an instance adds for itself. types/ takes a new type without a code change, so an instance can write one; that file is instance-owned from the first line to the last, ships nowhere, and is nobody's to overwrite. Its authoring guidance is still instruction addressed to an agent, and reads exactly like the guidance in the four types the stack ships. Ownership says "yours"; the audience has not moved at all.

So the question a line answers is not whose file is this but who reads this line, which is the same cut kb/CONTRACT.md already makes inside a single page between prose and identifiers - applied one level up, to the halves of a document. Ownership decides who may change a sentence. The reader decides what language it is in. The two questions were answered together for as long as they happened to agree.

Why the control plane's language is English

Not because English is better for the purpose, and not to be neutral: this instance's operator reads German, and the pages are German for that reason.

  • The control plane is almost entirely about identifiers, and the identifiers are English. base_dir, provenance: sourced, --confirm, exit 42, root: kb. A sentence in another language explaining when to set page_ref_fields is already half English by the time it reaches the verb, and the prose/identifier boundary inside it becomes something a reader has to work out line by line.
  • It quotes a body of material that is English and stays English. The harness documentation it has to agree with, the vendored skill-authoring sources under commonplace/, the tool's own --help. A contract that translates their vocabulary makes its own claims harder to check against them, not easier.
  • One language keeps instances comparable. Two instances running the same stack version hold the same control plane byte for byte, so a question about one is answerable from the other - and anything an instance changes locally shows up as a difference in content rather than in language.

Why it is not a parameter

The natural next move, once kb/CONVENTIONS.md holds language:, is a second value beside it - control_plane_language: - defaulting to English and settable by an instance that would rather read its contracts in its own language. That option is deliberately not taken.

  • The knob's cost is paid by every file; its benefit lands on the few a human reads. Every rule about writing an instruction would have to name which of the two languages it means, every example would need a note saying which one it is in, and every review of an instruction would start by establishing which language it should have been in. The stack has one mechanism for that class of problem - one rule, one place (AGENTS.md invariant 8) - and a second language value forks it everywhere at once.
  • The document the knob is for is read by an agent. An instruction, a contract, a type-spec's guidance half: the reader is a model, and a model reads the English fine. What the operator reads is unaffected by any of this - see the section below.
  • Today's local document is tomorrow's upstream candidate. An instruction an instance wrote for itself is the most likely thing it ever contributes back. Written in the KB language it would have to be translated first, and the translation would have to re-derive the prose/identifier boundary that the original author had in their head and did not write down.
  • Nothing would check it. There is no mechanical test for what language a paragraph is in - a stop-word scan flags the quoted vocabulary the rule deliberately keeps and misses a cleanly translated paragraph. A setting nothing enforces produces drift that is visible only to whoever next opens the file.

What the KB language still decides

Making the control plane English does not make the instance's language an implementation detail. kb/CONVENTIONS.md's language: decides two things, and both are the ones an operator actually experiences:

  • Page text. Every page under kb/, and inside the page type-specs exactly the parts that become page text - each one's ## Template block, its layout: titles, and the subtype templates types/<name>.<value>.md beside it, which are page text from the first line to the last.
  • What an agent says. An agent speaks the KB language, whatever the file it just read was written in. An instruction that models a sentence for the operator writes that model in English, like the rest of the control plane, and the agent delivers it in the instance's language.

So an operator who reads no English gets German pages and German answers from an agent reading English instructions. The English is what the machinery is written in, not what it says back - with one exception, the stack's documentation for humans (§ "The stack's human documentation").

Why a rule alone did not hold

For a while the conversation rule was a sentence about files' languages in AGENTS.md, and an instance on German pages still saw an agent that wrote its pages in German and everything else in English: progress notes between tool calls, the closing report. Pages were right because the step that writes a page reads kb/CONVENTIONS.md first; the chat was wrong because nothing had put the value in front of the agent by then. Three things stacked up:

  • The rule sat in the wrong section. A paragraph about the language of files does not read as a rule about speaking, and it was phrased as a statement of fact rather than an instruction. It now has a section of its own.
  • The value was one hop away. AGENTS.md ships verbatim to every instance and so cannot carry the instance's language; it names the file that does. A short ingest has written several English messages before it opens that file.
  • The context is English. Instructions, tool output and error text all are, and one short message from the operator is outweighed by them - the more so for a weaker model.

So the value is also compiled into the published copy of each skill, as a one-line block under its frontmatter that wikitool instructions sync writes: the language tag and a pointer to the rule, nothing else. The rule stays in AGENTS.md and the value in kb/CONVENTIONS.md; the block holds neither as text of its own, only puts the one in front of the task that needs it. It is a tag, not a language name, because a table of names would be the compiler's opinion about languages, which conventions.py deliberately has none of. A session with no skill is covered by the AGENTS.md section alone.

Where the line runs around a page type

A page type's contract is where the two languages meet most closely, and it is worth knowing which part is which before editing any of it. Its authoring guidance addresses an agent and is English; its ## Template block, layout: titles and subtype templates become the literal headings of pages and follow the KB language; its field names and enum values are identifiers and are translated in neither direction.

The language line did not move when the file line did. A root: kb type-spec may now put its authoring guidance in a separate, stack-owned types/<name>.guidance.md rather than carrying it beside the template, but that split was made for ownership reasons - so an upgrade can improve the guidance without overwriting what the instance chose - and it leaves this page's argument untouched: each part is still written in the language its own reader needs, and a type-spec that declares no guidance: keeps both halves in one file with exactly the same rule applying inside it. types/type-spec.md § Who owns a type-spec has the split as a table, and ownership-and-templates.md § "Where the file boundary used to strain" has what it cost to keep two audiences in one file for as long as it did.

The stack's human documentation

README.md, INSTALL.md and everything under docs/ are read by humans, and the reader axis alone would put them in the operator's language. They are English anyway, and the reason is not the reader but the fact that there is no single one. This documentation belongs to the stack, not to an instance: it ships identically to every instance, whatever each one's language: says, so there is no instance value it could follow - only a translation per language to maintain beside it, each one a second copy that drifts. And a growing part of it is generated from the code (the command reference from cli_contract records, for one), whose source is English control-plane material; a translated frame around English generated text is the half-done version of both.

The KB language keeps deciding what an operator reads of their own: pages and answers. What the stack tells every operator about itself is the same text everywhere. CHANGES.md sits outside the question - it is a log of what was true at a time, not documentation, and its older entries are in the language they were written in.

What would put this back on the table

A docs/ page goes stale when the reasoning stops holding rather than when the code changes, so it is worth naming what that would look like here. Two things would:

  • A human starts reading the control plane directly and routinely - not an operator checking a rule now and then, which is the case today, but a workflow where people rather than agents are the primary readers of instructions/. The second argument above is the one that fails first, and it is the load-bearing one.
  • The identifiers stop being English. If the tool's own vocabulary were ever localized, the first argument would invert: the prose would then be the only English left in a file that is otherwise not, which is the situation this page argues against.

Neither is close. Both are cheaper to notice here than to rediscover in an argument about a single file.

The human documentation has a trigger of its own: translations the stack maintains itself, with a check that holds each one to its English source. Without such a check a translation is exactly the drifting second copy the section above argues against; with one, the argument for a single language weakens to cost alone.