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
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
+34
-2
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
<!-- wikitool:toc -->
|
||||
## 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)
|
||||
<!-- /wikitool:toc -->
|
||||
|
||||
## 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.
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user