From f350999053df3ab110bf6e3892d5c1973a240830 Mon Sep 17 00:00:00 2001 From: Torben Nehmer Date: Tue, 15 Sep 2026 16:58:55 +0200 Subject: [PATCH] stack: Control-Plane-Sprache universell - Achse ist das Publikum, kein Instanz-Schalter (#103) Files changed: - AGENTS.md - CHANGES.md - VERSION - docs/language-boundaries.md - kb/CONVENTIONS.md - kb/CONVENTIONS.md.template - tools/chemenu/commands/dist_cmd.py --- AGENTS.md | 30 +++++-- CHANGES.md | 36 ++++++++- VERSION | 2 +- docs/language-boundaries.md | 126 +++++++++++++++++++++++++++++ kb/CONVENTIONS.md | 6 ++ kb/CONVENTIONS.md.template | 6 ++ tools/chemenu/commands/dist_cmd.py | 14 +++- 7 files changed, 206 insertions(+), 14 deletions(-) create mode 100644 docs/language-boundaries.md diff --git a/AGENTS.md b/AGENTS.md index 989cd99..0bb1f45 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -107,15 +107,24 @@ documents. What it may not carry is the same content twice - a README that resta contract is a second copy that drifts. `docs verify` enforces the specific case that already happened once: no README may hold a copy of the `wikitool` command table. -**Two languages, and which is which.** Every file in the table above belongs to the stack and -ships to instances that share none of this instance's language choices, so: +**Two languages, and which is which.** Which one a line is written in follows from the *For* +column above - who reads it - and from nothing else: not from who owns the file, and not from +whether it ever leaves this checkout. 1. **The control plane is written in English** - this file, `CLAUDE.md`, every `CONTRACT.md`, everything under `instructions/`, and the type-specs for non-page artifacts. Quoted vocabulary is not prose and stays as it is: a section name, a relationship label or a - translated term cited as evidence. What the *instance* owns goes the other way - `kb/` pages - and the page type-specs that shape them follow `kb/CONVENTIONS.md`, which is also where the - instance's own terminology material is reached from. + translated term cited as evidence. What addresses the *page* goes the other way - `kb/` pages, + and inside a page type-spec the parts that become page text - and follows + `kb/CONVENTIONS.md`, which is also where the instance's own terminology material is reached + from. + + This holds for a control-plane file an instance writes **only for itself** and never ships: + an instruction of its own, a page type it added (`types/` takes one without a code change), + a further stage contract. Such a file is instance-owned end to end, which settles who may + change it, not who reads it - and the reader is still an agent. There is deliberately no + second language value beside `kb/CONVENTIONS.md`'s `language:`, and no instance setting that + moves this rule; [docs/language-boundaries.md](docs/language-boundaries.md) has the reasoning. 2. **An agent speaks the instance's KB language**, whatever this file is written in. The value lives in `kb/CONVENTIONS.md`'s `language:` and nowhere else; an instruction that models a sentence for the user writes it in English like the rest of the control plane, and the agent @@ -132,14 +141,19 @@ background consulted in passing, not a rule to follow; anything that would bind type, index, lint or provenance; `dist export` ships it verbatim and no other `tools/wikitool` command touches it. -Four pages exist today, each read by link rather than automatically: +Five pages are reached from this file, each by link rather than automatically: [docs/pipeline-rationale.md](docs/pipeline-rationale.md) (why the pipeline has four stages), [docs/ownership-and-templates.md](docs/ownership-and-templates.md) (why a `.template` split exists, and why silent overwrite is the failure it guards against), -[docs/why-gates-are-code.md](docs/why-gates-are-code.md) (why the four gates in +[docs/language-boundaries.md](docs/language-boundaries.md) (why the control plane is English +everywhere and the KB language is a value, and why the axis is the reader rather than the +owner), [docs/why-gates-are-code.md](docs/why-gates-are-code.md) (why the four gates in [Gates](#gates) are code rather than instruction), and [docs/version-model.md](docs/version-model.md) (why a version number answers a compatibility -question and a migration question separately). +question and a migration question separately). A sixth, +`docs/model-and-effort-selection.md`, is deliberately not linked here but from `CLAUDE.md`: it +decides something only that harness has to decide, and a link here would load it into the other +three. ## Personalization diff --git a/CHANGES.md b/CHANGES.md index c91b31c..87ef603 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse. --- -## 6.0.0-beta.4 - 2026-09-15 - types/type-spec.md: Ownership und Sprache getrennt benannt (Nachzug zu #99) +## 6.0.0-beta.5 - 2026-09-15 - Control-Plane-Sprache universell: Achse ist das Publikum, kein Instanz-Schalter **Author:** Torben Nehmer @@ -77,6 +77,7 @@ concern - readable here, never shipped as something to parse. **Medium impact** - docs verify: ein nur als .template ausgeliefertes Linkziel gilt als aufgeloest +- Control-Plane-Sprache universell: Achse ist das Publikum, kein Instanz-Schalter **Low impact** - gates.md/session-setup.md: die Budget-Ausnahme von version regrade haengt an der Aufrufform @@ -230,7 +231,38 @@ nicht haelt. Der Satz bindet ausdruecklich auch einen Type-Spec, den eine Instan schreibt: der ist zwar durchgaengig instanzeigen, aber seine Anleitungshaelfte hat trotzdem einen Agenten als Leser. ---- +### Control-Plane-Sprache universell: Achse ist das Publikum, kein Instanz-Schalter + +Die Sprachregel in `AGENTS.md` ruhte auf einer Begruendung, die schmaler war als sie selbst: +"Every file in the table above belongs to the stack and **ships to instances** that share none of +this instance's language choices, so:". Das traegt nur fuer ausgeliefertes Material und laesst +offen, was fuer ein Control-Plane-Dokument gilt, das eine Instanz nur fuer sich selbst schreibt - +eine eigene Instruction, ein selbst angelegter Seitentyp (`types/` nimmt einen ohne Code-Aenderung +auf), ein weiterer Stage-Contract. Genau dort fallen Ownership und Publikum auseinander: die Datei +ist durchgaengig instanzeigen, ihre Anleitungshaelfte hat trotzdem einen Agenten als Leser. + +Der Vorsatz nennt jetzt die tatsaechliche Achse - die *For*-Spalte der Tabelle darueber, also wer +die Zeile liest, und weder wem die Datei gehoert noch ob sie den Checkout je verlaesst. Regel 1 +sagt ausdruecklich, dass sie auch fuer ein nie ausgeliefertes Control-Plane-Dokument gilt und dass +es neben `kb/CONVENTIONS.md`s `language:` bewusst keinen zweiten Sprachwert gibt. +`kb/CONVENTIONS.md` und ihr `.template` sagen dasselbe von ihrer Seite aus: die +Control-Plane-Sprache ist keine Einstellung, die diese Datei zurueckhaelt - es ist gar keine. + +Die Begruendung dazu steht als neue `docs/`-Seite +([docs/language-boundaries.md](docs/language-boundaries.md)), weil sie sonst in einem Jahr neu +verhandelt wird: warum Englisch (der Stack redet fast nur ueber Identifier, und die sind +englisch), warum kein Parameter (die Kosten traegt jede Datei, den Nutzen haette ein Dokument, das +ohnehin nur ein Agent liest), und was die Entscheidung wieder aufmachen wuerde. Die Seite haelt +zugleich fest, welches Argument falsch war: "Sprache folgt der Ownership" hat funktioniert, +solange nur ausgeliefertes Material betrachtet wurde, und faellt am instanz-eigenen Typ. + +Nebenbei zwei Befunde derselben Ecke behoben. Der Docstring von +`dist_cmd.instance_owned_type_stems()` behauptete weiter, "its prose, its template and its +**language** are the instance's business" - Stand vor dem TOC-/Sprach-Bump oben. Und die +Aufzaehlung der `docs/`-Seiten in `AGENTS.md` sagte "Four pages exist today", waehrend das +Verzeichnis fuenf trug: `docs/model-and-effort-selection.md` fehlte, und zwar absichtlich, weil +ein Link dorthin die Claude-Code-eigene Entscheidung in die anderen drei Harnesses laden wuerde. +Der Satz zaehlt jetzt, was von hier aus verlinkt ist, und benennt die sechste Seite samt Grund. ## 5.1.0 - 2026-09-12 - changelog: Kandidaten-Eintrag nach Impact gruppiert, version regrade zur Nachkorrektur, version release verlangt eine Zusammenfassung diff --git a/VERSION b/VERSION index 09e55f8..7e3b0e0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.0-beta.4 +6.0.0-beta.5 diff --git a/docs/language-boundaries.md b/docs/language-boundaries.md new file mode 100644 index 0000000..bad73aa --- /dev/null +++ b/docs/language-boundaries.md @@ -0,0 +1,126 @@ +# 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](../AGENTS.md), every `CONTRACT.md`, everything under +`instructions/` - is written in English, in every instance, whatever the first value says. + +The rule itself is in [AGENTS.md § File naming](../AGENTS.md#file-naming). 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](#the-axis-is-the-reader-not-the-owner) +- [Why the control plane's language is English](#why-the-control-planes-language-is-english) +- [Why it is not a parameter](#why-it-is-not-a-parameter) +- [What the KB language still decides](#what-the-kb-language-still-decides) +- [Where the line runs inside one file](#where-the-line-runs-inside-one-file) +- [What would put this back on the table](#what-would-put-this-back-on-the-table) + + +## 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](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](../kb/CONTRACT.md#language-and-identifiers) 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 and its `layout:` titles. +- **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. + +## Where the line runs inside one file + +One file shape carries both halves, and it is worth knowing which is which before editing it: a +page type-spec (`types/entity.md` and its three siblings). Its authoring guidance addresses an +agent; its `## Template` block and `layout:` titles become the literal headings of pages; its +field names and enum values are identifiers and are translated in neither direction. +[types/type-spec.md § Who owns a type-spec](../types/type-spec.md#who-owns-a-type-spec) has that +split as a table, and [ownership-and-templates.md](ownership-and-templates.md) § "Where the file +boundary strains" has what it costs to keep two audiences in one file. + +## 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. diff --git a/kb/CONVENTIONS.md b/kb/CONVENTIONS.md index 923a820..4d9d4d0 100644 --- a/kb/CONVENTIONS.md +++ b/kb/CONVENTIONS.md @@ -53,6 +53,12 @@ Two things follow from that value rather than being decided here, both stated on [AGENTS.md § File naming](../AGENTS.md#file-naming): the control plane stays English whatever an instance writes its pages in, and an agent *speaks* the language named above. +The first of those is not a setting this file withholds - it is not a setting at all. `language:` +above is the only language value in the tree, and what it binds is page text; a control-plane +document is English even when this instance wrote it for itself and never ships it. Why that is +an architecture decision rather than an unset parameter: +[docs/language-boundaries.md](../docs/language-boundaries.md). + Which line is prose and which is an identifier - and therefore what is translated at all - is the contract's rule, not this file's: see [kb/CONTRACT.md § Language and identifiers](CONTRACT.md#language-and-identifiers). diff --git a/kb/CONVENTIONS.md.template b/kb/CONVENTIONS.md.template index 201a041..95b4639 100644 --- a/kb/CONVENTIONS.md.template +++ b/kb/CONVENTIONS.md.template @@ -42,6 +42,12 @@ Two things follow from that value rather than being decided here, both stated on [AGENTS.md § File naming](../AGENTS.md#file-naming): the control plane stays English whatever an instance writes its pages in, and an agent *speaks* the language named above. +The first of those is not a setting this file withholds - it is not a setting at all. `language:` +above is the only language value in the tree, and what it binds is page text; a control-plane +document is English even when this instance wrote it for itself and never ships it. Why that is +an architecture decision rather than an unset parameter: +[docs/language-boundaries.md](../docs/language-boundaries.md). + Which line is prose and which is an identifier - and therefore what is translated at all - is the contract's rule, not this file's: see [kb/CONTRACT.md § Language and identifiers](CONTRACT.md#language-and-identifiers). diff --git a/tools/chemenu/commands/dist_cmd.py b/tools/chemenu/commands/dist_cmd.py index e179330..f8f35f6 100644 --- a/tools/chemenu/commands/dist_cmd.py +++ b/tools/chemenu/commands/dist_cmd.py @@ -308,9 +308,17 @@ def instance_owned_type_stems() -> set[str]: The line is `root:`, and it was already in the frontmatter before anyone drew it: `root: kb` means the type describes a page the instance writes, so - its prose, its template and its language are the instance's business. - Anything else - `instruction` (`root: repo`), `lint-report` (no `base_dir` - at all), `type-spec` itself - describes a stack artifact and ships verbatim. + the file is the instance's to change. Anything else - `instruction` + (`root: repo`), `lint-report` (no `base_dir` at all), `type-spec` itself - + describes a stack artifact and ships verbatim. + + Ownership, not language. What such a file is *written in* is decided by who + reads each half, not by who owns the file: its `## Template` block and its + `layout:` titles become page text and follow `kb/CONVENTIONS.md`, while the + authoring guidance around them addresses an agent and stays English like the + rest of the control plane (AGENTS.md § File naming, types/type-spec.md + § Who owns a type-spec). That the two halves share one file, and what it + costs, is docs/ownership-and-templates.md § Where the file boundary strains. Read from `types/` rather than listed, so an instance adding its own page type gets the same treatment without a code change.