Umgesetzt und veröffentlicht in d49513b (Mechanik, Inhalt, Migration, Doku-Durchzug) und 6eb3f84 (Nachzug README-Typenbaum und docs/language-boundaries.md), Version 6.0.0-beta.6.
Befund
Ein root: kb Type-Spec (types/entity.md, concept, source, comparison) hatte zwei
Publika in einer Datei: Anleitungsprosa für den Agenten, der eine Seite schreibt, und
Seitenmaterial (## Template-Block, layout:-Titel), das wörtlich zu Seitentext wird.
Ownership gilt aber pro Datei (chemenu/ownership.py, dist_cmd._plan_types()): weil die
Seitenhälfte instanzeigen sein muss, wurde die ganze Datei als types/<name>.md.template
ausgeliefert und nach der Adoption nie wieder angefasst.
Die Folge war still. Eine Instanz, die ihre Type-Specs beim Setup adoptiert hatte, bekam nie
wieder eine Verbesserung der Anleitungsprosa - dist upgrade schrieb das .template neben die
adoptierte Datei, nie die Datei selbst. Nichts brach, nichts meldete es; die Instanz las bis in
alle Zukunft die Anleitung vom Tag ihrer Erzeugung.
Welche Hälfte am Stack hing, war umgekehrt zur ersten Fassung dieses Issues. Gegen den Baum
geprüft:
Die Frontmatter-Konfiguration ist instanzeigen, nicht stackeigen. instructions/setup-instance.md Schritt 5.5 und instructions/evolve-subtypes.md Schritt 3
weisen die Instanz an, den Enum in types/<t>.schema.yamlund den passenden layout:-Eintrag in types/<t>.md in derselben Änderung zu setzen. Die layout:-Schlüssel
sind die Enum-Werte dieser Instanz, dir: sind ihre Areas, title_prefix: "Source - " ist
Seitentext.
182 Seiten in dieser Instanz tragen type: types/<name>.md. Die Datei, die diesen Namen
behält, ist die, auf die der ganze Korpus zeigt.
Die Prosa, die der Stack verbessern will, ist maschinenabgeleitet.types/source.md
erklärte Capture-Fields, raw accept --replaces, "one raw file, one owner" und die Begründung
für den Nicht-übernommen-Abschnitt. instructions/ingest-large-tree.md - stackeigen, verbatim
ausgeliefert - zitiert diese Regel per tools/wikitool types describe source: eine stackeigene
Instruktion hing an Prosa, die in einer instanzeigenen Datei stand.
Also nicht: Type-Spec zum Stack, Seitenmaterial heraus. Sondern: der Type-Spec blieb, wo er
war, und die Anleitungsprosa ist ausgezogen.
Entscheidung (umgesetzt)
Die vier offenen Designfragen wurden vor dem ersten Commit entschieden (Betreiber, 2026-09-15).
Wo lebt die stackeigene Hälfte, und in welcher Form? Eine Geschwisterdatei types/<name>.guidance.md unter types/: stackeigen, verbatim ausgeliefert, englisch.
Verknüpft über ein optionales guidance:-Feld im Frontmatter des Type-Specs (repo-relativer
Pfad, wie schema:), nicht über eine Namenskonvention - eine Konvention lieferte die Guidance
sofort, aber doppelt, solange die adoptierte Datei die alte Prosa noch trägt. Sie hat einen
eigenen, nicht instanziierbaren Typ types/type-guidance.md (kein base_dir, wie lint-report). tools/wikitool types describe setzt beide Hälften zusammen, der Agent macht
weiter einen Aufruf.
Der Template-Pfad hat sich gar nicht bewegt: type_resolver.extract_template() liest
weiter den ersten ```markdown-Block aus types/<name>.md. wikitool new hat keinen zweiten
Ladepfad bekommen, weil es seinen ersten behalten hat.
Verworfen: Guidance als Manual-Instruktion unter instructions/ - spart die neue Dateiart,
kostet aber den zweiten Hop.
Was passiert mit den layout:-Titeln? Sie sind geblieben, wo sie waren. Die Frage hat sich
aufgelöst: geteilt wurde nur der Body, die Datei mit dem Frontmatter bleibt bei der Instanz.
Ein Typ, den eine Instanz komplett selbst schreibt?guidance: ist optional; fehlt es,
gibt es keine Stack-Hälfte und describe druckt wie bisher nur den Body.
Einmaliger Grenzübertritt oder Deprecation-Fenster? Keins von beidem - kein
Grenzübertritt. Drop-in in beide Richtungen, also --minor. Die einmalige Adoption in einer
bestehenden Instanz trägt ein Migrationsdokument mit obligation: offered - der erste Gebrauch
dieses Mechanismus, den instructions/CONTRACT.md seit 4.0.0 beschreibt und den bis hierher
kein Dokument benutzt hat.
Schnittregel für die vier Bodies: Ein Satz, der wahr bleibt, wenn die Instanz ihre Subtypen
umbenennt, ihren Enum ändert oder ihr Template neu schreibt, gehört in die Guidance-Datei. Ein
Satz, der die Enum-Werte, Areas, Verzeichnisse, Überschriften oder Templatetexte dieser Instanz
benennt, bleibt im Type-Spec. Eine Ausnahme, die beim Aufteilen aufgefallen ist: types/source.md
behält den Bullet "The title starts with 'Source - '", weil er den instanzeigenen title_prefix:-Wert wörtlich zitiert - eine andere Instanz wählt dort einen anderen Wert oder
keinen.
Was gebaut wurde
Mechanik
types/type-guidance.md + types/type-guidance.schema.yaml: stackeigener, nicht
instanziierbarer Typ.
types/type-spec.md: optionales guidance:-Feld dokumentiert (§ Required Frontmatter);
§§ "Who owns a type-spec" und "Anatomy of a type" auf den neuen Schnitt umgeschrieben - letzteres
sagt jetzt "at least two files, and a root: kb type may be three".
type_resolver.get_guidance(): lädt die verlinkte Datei über denselben load_type_spec-Pfad wie schema:, gibt None zurück, wenn kein guidance: deklariert ist.
types_core.describe_type(): neue Schlüssel guidance und guidance_path; body behält seine
Bedeutung (additiv, der MCP-Wire-Contract bleibt für ältere Clients lesbar).
commands/types_cmd.py: rendert die Guidance-Hälfte vor dem Body, TOC-Region aus beiden
gestrippt.
dist_cmd._owned_type_stem(): ein gemeinsames Prädikat für _plan_types() und find_leaks(). Beide rechneten vorher name.split(".", 1)[0] und hätten entity.guidance.md
als Stamm "entity" gelesen - die eine hätte sie als .template ausgeliefert, die andere sie als
Leak gemeldet. Jetzt prüft eine Funktion die exakte Endung (<stem>.md oder <stem>.schema.yaml).
Inhalt
types/entity.guidance.md, concept.guidance.md, source.guidance.md, comparison.guidance.md; die vier Type-Specs sind auf Frontmatter-Tabelle, ## Template und
einen Zeiger auf ihre Guidance-Datei zusammengeschnitten und tragen guidance:.
Doku-Durchzug:AGENTS.md § File naming (neue Zeile für types/<name>.guidance.md), types/type-spec.md, docs/ownership-and-templates.md (§ "Where the file boundary strains" →
"Where the file boundary used to strain", beschreibt jetzt die Entscheidung statt eines Defekts), tools/CONTRACT.md (types describe-Zeile), instructions/setup-instance.md Schritt 5.1, README.md (Typenbaum), docs/language-boundaries.md (§ "Where the line runs inside one file" →
"Where the line runs around a page type" - die Sprachachse hat sich nicht bewegt, die Dateigrenze
schon).
Akzeptanzkriterien
dist export liefert types/<name>.guidance.md verbatim aus, während types/<name>.md und types/<name>.schema.yaml weiter .template tragen; find_leaks() meldet die Guidance-Datei
nicht. (test_guidance_file_ships_verbatim_beside_a_templated_type_spec, plus test_find_leaks_is_silent_on_a_clean_plan, dessen Fixture jetzt eine Guidance-Datei enthält
und damit genau diesen Fall mitprüft.)
Ein dist upgrade gegen eine Fixture-Instanz, deren Guidance-Datei seit der Installation
unverändert ist, schreibt eine verbesserte Anleitungsprosa tatsächlich hinein.
(test_upgrade_writes_improved_guidance_prose_over_an_adopted_type_spec - prüft zusätzlich,
dass der adoptierte Type-Spec selbst unangetastet bleibt, weil er in keinem Stamp steht.)
Ein dist upgrade gegen eine Instanz mit adoptiertem Type-Spec ohneguidance: ist
beobachtbar folgenlos. (test_types_describe_a_type_with_no_guidance_omits_it_cleanly und test_get_guidance_is_none_when_the_type_spec_declares_none; der Upgrade-Pfad selbst ist
unverändert, weil eine Datei ohne guidance: keinen neuen Code erreicht.)
wikitool new liest sein Template weiter aus types/<name>.md - kein zweiter Ladepfad.
(test_extract_template_reads_only_the_type_spec_never_the_guidance_file: ein ```markdown-Block
in der Guidance-Datei wird nachweislich nicht als Scaffold genommen.)
Ein Type-Spec ohne guidance: beschreibt wie bisher.
types describe --json trägt guidance und guidance_path, body bleibt der Body des
Type-Specs. (test_types_describe_entity_composes_guidance_and_body_separately, test_types_describe_composes_guidance_ahead_of_body_in_text_output.)
Das Migrationsdokument trägt obligation: offered: migrate status listet es separat
("1 optional upgrade(s) available - none of them block"), migrate done 6.0.0 --dry-run
bestätigt "would record the optional ... Content stays at 5.0.0". Die offered-Semantik
selbst ist bereits durch die generischen kb_state/migrate_cmd-Tests abgedeckt, deshalb
hier kein zusätzlicher Test, sondern die Verifikation am realen Dokument.
version bump --minor, kein --breaking, keine Pflichtmigration. (6.0.0-beta.5 → 6.0.0-beta.6; der Kandidat stand bereits auf MAJOR, die Escalation ändert seine Basis also
nicht.)
Doku-Durchzug - siehe oben, inklusive zweier Dateien, die erst in der Schlussphase als stale
auffielen (README.md, docs/language-boundaries.md).
tools/wikitool docs verify, tools/wikitool instructions verify und pytest grün: 1261
Tests (8 neu), doctor ohne Befund außer dem üblichen session-id-WARN.
Bewusst nicht Teil davon
types/<name>.schema.yaml bleibt instanzeigen. Der Enum gehört der Instanz
(evolve-subtypes.md).
kb/CONVENTIONS.md(.template) § Language braucht keine Änderung: die Sprachachse ist mit #103 entschieden und hat sich hier nicht bewegt. Stattdessen wurde docs/language-boundaries.md nachgezogen, das genau diesen Unterschied jetzt ausspricht.
Die Lücke in types/type-spec.schema.yaml (root: und capture_fields: fehlen trotz additionalProperties: false, und niemand merkt es, weil Type-Spec-Frontmatter nirgends gegen
dieses Schema validiert wird): eigenes Issue #105. Hier wurde nur das guidance:-Feld in die
Prosa von types/type-spec.md aufgenommen, nicht ins Schema - das wäre ohnehin wirkungslos,
solange #105 offen ist.
Abweichung von der ursprünglichen Bauanweisung: das gemeinsame Prädikat liegt als _owned_type_stem() in dist_cmd.py, nicht in ownership.py. Dort leben ausschließlich
Pfade unter den Content-Stages (kb/, raw/, work/, reports/); types/ kennt das Modul
gar nicht. Beide Aufrufstellen liegen in derselben Datei, die Anti-Drift-Eigenschaft ist also
erfüllt.
Woher das kam
Aufgefallen bei der Arbeit an #99 (TOC-Scope und Sprachregeln), als der Seiten-Type-Spec in beiden
Achsen gleichzeitig angefasst wurde. Dort wurde der Schnitt innerhalb der Datei dokumentiert,
weil die Dateigrenze nicht in derselben Sitzung zu verschieben war.
Die erste Fassung dieses Bodys nahm an, die Frontmatter-Konfiguration sei stackeigen und nur
Template und layout:-Titel gehörten der Instanz. Die Prüfung gegen setup-instance.md, evolve-subtypes.md und die 182 type:-Verweise im Korpus hat das umgedreht.
**Umgesetzt und veröffentlicht** in `d49513b` (Mechanik, Inhalt, Migration, Doku-Durchzug) und
`6eb3f84` (Nachzug README-Typenbaum und `docs/language-boundaries.md`), Version `6.0.0-beta.6`.
## Befund
Ein `root: kb` Type-Spec (`types/entity.md`, `concept`, `source`, `comparison`) hatte **zwei
Publika in einer Datei**: Anleitungsprosa für den Agenten, der eine Seite schreibt, und
Seitenmaterial (`## Template`-Block, `layout:`-Titel), das wörtlich zu Seitentext wird.
Ownership gilt aber **pro Datei** (`chemenu/ownership.py`, `dist_cmd._plan_types()`): weil die
Seitenhälfte instanzeigen sein muss, wurde die ganze Datei als `types/<name>.md.template`
ausgeliefert und nach der Adoption nie wieder angefasst.
**Die Folge war still.** Eine Instanz, die ihre Type-Specs beim Setup adoptiert hatte, bekam nie
wieder eine Verbesserung der Anleitungsprosa - `dist upgrade` schrieb das `.template` neben die
adoptierte Datei, nie die Datei selbst. Nichts brach, nichts meldete es; die Instanz las bis in
alle Zukunft die Anleitung vom Tag ihrer Erzeugung.
**Welche Hälfte am Stack hing, war umgekehrt zur ersten Fassung dieses Issues.** Gegen den Baum
geprüft:
- **Die Frontmatter-Konfiguration ist instanzeigen, nicht stackeigen.**
`instructions/setup-instance.md` Schritt 5.5 und `instructions/evolve-subtypes.md` Schritt 3
weisen die *Instanz* an, den Enum in `types/<t>.schema.yaml` **und** den passenden
`layout:`-Eintrag in `types/<t>.md` in derselben Änderung zu setzen. Die `layout:`-Schlüssel
sind die Enum-Werte dieser Instanz, `dir:` sind ihre Areas, `title_prefix: "Source - "` ist
Seitentext.
- **182 Seiten in dieser Instanz tragen `type: types/<name>.md`.** Die Datei, die diesen Namen
behält, ist die, auf die der ganze Korpus zeigt.
- **Die Prosa, die der Stack verbessern will, ist maschinenabgeleitet.** `types/source.md`
erklärte Capture-Fields, `raw accept --replaces`, "one raw file, one owner" und die Begründung
für den Nicht-übernommen-Abschnitt. `instructions/ingest-large-tree.md` - stackeigen, verbatim
ausgeliefert - zitiert diese Regel per `tools/wikitool types describe source`: eine stackeigene
Instruktion hing an Prosa, die in einer instanzeigenen Datei stand.
Also nicht: Type-Spec zum Stack, Seitenmaterial heraus. Sondern: **der Type-Spec blieb, wo er
war, und die Anleitungsprosa ist ausgezogen.**
## Entscheidung (umgesetzt)
Die vier offenen Designfragen wurden vor dem ersten Commit entschieden (Betreiber, 2026-09-15).
1. **Wo lebt die stackeigene Hälfte, und in welcher Form?** Eine Geschwisterdatei
`types/<name>.guidance.md` unter `types/`: stackeigen, verbatim ausgeliefert, englisch.
Verknüpft über ein **optionales `guidance:`-Feld im Frontmatter des Type-Specs** (repo-relativer
Pfad, wie `schema:`), nicht über eine Namenskonvention - eine Konvention lieferte die Guidance
sofort, aber doppelt, solange die adoptierte Datei die alte Prosa noch trägt. Sie hat einen
eigenen, nicht instanziierbaren Typ `types/type-guidance.md` (kein `base_dir`, wie
`lint-report`). `tools/wikitool types describe` setzt beide Hälften zusammen, der Agent macht
weiter **einen** Aufruf.
Der Template-Pfad hat sich **gar nicht** bewegt: `type_resolver.extract_template()` liest
weiter den ersten ```markdown-Block aus `types/<name>.md`. `wikitool new` hat keinen zweiten
Ladepfad bekommen, weil es seinen ersten behalten hat.
*Verworfen:* Guidance als Manual-Instruktion unter `instructions/` - spart die neue Dateiart,
kostet aber den zweiten Hop.
2. **Was passiert mit den `layout:`-Titeln?** Sie sind geblieben, wo sie waren. Die Frage hat sich
aufgelöst: geteilt wurde nur der Body, die Datei mit dem Frontmatter bleibt bei der Instanz.
3. **Ein Typ, den eine Instanz komplett selbst schreibt?** `guidance:` ist optional; fehlt es,
gibt es keine Stack-Hälfte und `describe` druckt wie bisher nur den Body.
4. **Einmaliger Grenzübertritt oder Deprecation-Fenster?** Keins von beidem - **kein
Grenzübertritt**. Drop-in in beide Richtungen, also `--minor`. Die einmalige Adoption in einer
bestehenden Instanz trägt ein Migrationsdokument mit `obligation: offered` - der erste Gebrauch
dieses Mechanismus, den `instructions/CONTRACT.md` seit 4.0.0 beschreibt und den bis hierher
kein Dokument benutzt hat.
**Schnittregel für die vier Bodies:** Ein Satz, der wahr bleibt, wenn die Instanz ihre Subtypen
umbenennt, ihren Enum ändert oder ihr Template neu schreibt, gehört in die Guidance-Datei. Ein
Satz, der die Enum-Werte, Areas, Verzeichnisse, Überschriften oder Templatetexte *dieser* Instanz
benennt, bleibt im Type-Spec. Eine Ausnahme, die beim Aufteilen aufgefallen ist: `types/source.md`
behält den Bullet "The title starts with 'Source - '", weil er den instanzeigenen
`title_prefix:`-Wert wörtlich zitiert - eine andere Instanz wählt dort einen anderen Wert oder
keinen.
## Was gebaut wurde
**Mechanik**
- `types/type-guidance.md` + `types/type-guidance.schema.yaml`: stackeigener, nicht
instanziierbarer Typ.
- `types/type-spec.md`: optionales `guidance:`-Feld dokumentiert (§ Required Frontmatter);
§§ "Who owns a type-spec" und "Anatomy of a type" auf den neuen Schnitt umgeschrieben - letzteres
sagt jetzt "at least two files, and a `root: kb` type may be three".
- `type_resolver.get_guidance()`: lädt die verlinkte Datei über denselben `load_type_spec`-Pfad wie
`schema:`, gibt `None` zurück, wenn kein `guidance:` deklariert ist.
- `types_core.describe_type()`: neue Schlüssel `guidance` und `guidance_path`; `body` behält seine
Bedeutung (additiv, der MCP-Wire-Contract bleibt für ältere Clients lesbar).
- `commands/types_cmd.py`: rendert die Guidance-Hälfte vor dem Body, TOC-Region aus beiden
gestrippt.
- `dist_cmd._owned_type_stem()`: **ein** gemeinsames Prädikat für `_plan_types()` und
`find_leaks()`. Beide rechneten vorher `name.split(".", 1)[0]` und hätten `entity.guidance.md`
als Stamm "entity" gelesen - die eine hätte sie als `.template` ausgeliefert, die andere sie als
Leak gemeldet. Jetzt prüft eine Funktion die exakte Endung (`<stem>.md` oder
`<stem>.schema.yaml`).
**Inhalt**
- `types/entity.guidance.md`, `concept.guidance.md`, `source.guidance.md`,
`comparison.guidance.md`; die vier Type-Specs sind auf Frontmatter-Tabelle, `## Template` und
einen Zeiger auf ihre Guidance-Datei zusammengeschnitten und tragen `guidance:`.
- `instructions/migrations/6.0.0-type-guidance-split.md`, `obligation: offered`,
`migration_kind: assisted`.
**Doku-Durchzug:** `AGENTS.md` § File naming (neue Zeile für `types/<name>.guidance.md`),
`types/type-spec.md`, `docs/ownership-and-templates.md` (§ "Where the file boundary strains" →
"Where the file boundary used to strain", beschreibt jetzt die Entscheidung statt eines Defekts),
`tools/CONTRACT.md` (`types describe`-Zeile), `instructions/setup-instance.md` Schritt 5.1,
`README.md` (Typenbaum), `docs/language-boundaries.md` (§ "Where the line runs inside one file" →
"Where the line runs around a page type" - die Sprachachse hat sich *nicht* bewegt, die Dateigrenze
schon).
## Akzeptanzkriterien
- [x] `dist export` liefert `types/<name>.guidance.md` verbatim aus, während `types/<name>.md` und
`types/<name>.schema.yaml` weiter `.template` tragen; `find_leaks()` meldet die Guidance-Datei
nicht. (`test_guidance_file_ships_verbatim_beside_a_templated_type_spec`, plus
`test_find_leaks_is_silent_on_a_clean_plan`, dessen Fixture jetzt eine Guidance-Datei enthält
und damit genau diesen Fall mitprüft.)
- [x] Ein `dist upgrade` gegen eine Fixture-Instanz, deren Guidance-Datei seit der Installation
unverändert ist, schreibt eine verbesserte Anleitungsprosa tatsächlich hinein.
(`test_upgrade_writes_improved_guidance_prose_over_an_adopted_type_spec` - prüft zusätzlich,
dass der adoptierte Type-Spec selbst unangetastet bleibt, weil er in keinem Stamp steht.)
- [x] Ein `dist upgrade` gegen eine Instanz mit adoptiertem Type-Spec **ohne** `guidance:` ist
beobachtbar folgenlos. (`test_types_describe_a_type_with_no_guidance_omits_it_cleanly` und
`test_get_guidance_is_none_when_the_type_spec_declares_none`; der Upgrade-Pfad selbst ist
unverändert, weil eine Datei ohne `guidance:` keinen neuen Code erreicht.)
- [x] `wikitool new` liest sein Template weiter aus `types/<name>.md` - kein zweiter Ladepfad.
(`test_extract_template_reads_only_the_type_spec_never_the_guidance_file`: ein ```markdown-Block
in der Guidance-Datei wird nachweislich *nicht* als Scaffold genommen.)
- [x] Ein Type-Spec ohne `guidance:` beschreibt wie bisher.
- [x] `types describe --json` trägt `guidance` und `guidance_path`, `body` bleibt der Body des
Type-Specs. (`test_types_describe_entity_composes_guidance_and_body_separately`,
`test_types_describe_composes_guidance_ahead_of_body_in_text_output`.)
- [x] Das Migrationsdokument trägt `obligation: offered`: `migrate status` listet es separat
("1 optional upgrade(s) available - none of them block"), `migrate done 6.0.0 --dry-run`
bestätigt "would record the optional ... Content stays at 5.0.0". Die `offered`-Semantik
selbst ist bereits durch die generischen `kb_state`/`migrate_cmd`-Tests abgedeckt, deshalb
hier kein zusätzlicher Test, sondern die Verifikation am realen Dokument.
- [x] `version bump --minor`, kein `--breaking`, keine Pflichtmigration. (`6.0.0-beta.5` →
`6.0.0-beta.6`; der Kandidat stand bereits auf MAJOR, die Escalation ändert seine Basis also
nicht.)
- [x] Doku-Durchzug - siehe oben, inklusive zweier Dateien, die erst in der Schlussphase als stale
auffielen (`README.md`, `docs/language-boundaries.md`).
- [x] `tools/wikitool docs verify`, `tools/wikitool instructions verify` und `pytest` grün: 1261
Tests (8 neu), `doctor` ohne Befund außer dem üblichen `session-id`-WARN.
## Bewusst nicht Teil davon
- **`types/<name>.schema.yaml` bleibt instanzeigen.** Der Enum gehört der Instanz
(`evolve-subtypes.md`).
- **`kb/CONVENTIONS.md(.template)` § Language** braucht keine Änderung: die Sprachachse ist mit
#103 entschieden und hat sich hier nicht bewegt. Stattdessen wurde
`docs/language-boundaries.md` nachgezogen, das genau diesen Unterschied jetzt ausspricht.
- **Die Lücke in `types/type-spec.schema.yaml`** (`root:` und `capture_fields:` fehlen trotz
`additionalProperties: false`, und niemand merkt es, weil Type-Spec-Frontmatter nirgends gegen
dieses Schema validiert wird): eigenes Issue #105. Hier wurde nur das `guidance:`-Feld in die
Prosa von `types/type-spec.md` aufgenommen, nicht ins Schema - das wäre ohnehin wirkungslos,
solange #105 offen ist.
- **Abweichung von der ursprünglichen Bauanweisung:** das gemeinsame Prädikat liegt als
`_owned_type_stem()` in `dist_cmd.py`, nicht in `ownership.py`. Dort leben ausschließlich
Pfade unter den Content-Stages (`kb/`, `raw/`, `work/`, `reports/`); `types/` kennt das Modul
gar nicht. Beide Aufrufstellen liegen in derselben Datei, die Anti-Drift-Eigenschaft ist also
erfüllt.
## Woher das kam
Aufgefallen bei der Arbeit an #99 (TOC-Scope und Sprachregeln), als der Seiten-Type-Spec in beiden
Achsen gleichzeitig angefasst wurde. Dort wurde der Schnitt **innerhalb** der Datei dokumentiert,
weil die Dateigrenze nicht in derselben Sitzung zu verschieben war.
Die erste Fassung dieses Bodys nahm an, die Frontmatter-Konfiguration sei stackeigen und nur
Template und `layout:`-Titel gehörten der Instanz. Die Prüfung gegen `setup-instance.md`,
`evolve-subtypes.md` und die 182 `type:`-Verweise im Korpus hat das umgedreht.
torben
changed title from Seiten-Type-Spec am Publikum schneiden: Anleitung stackeigen, Seitenmaterial instanzeigen to Anleitungsprosa aus dem Seiten-Type-Spec herausloesen: guidance stackeigen, Type-Spec bleibt instanzeigen2026-09-15 15:16:10 +00:00
Changelog: Schnittrichtung umgedreht und Body komplett neu geschrieben. Der alte Befund nahm an, Frontmatter-Konfiguration (base_dir, layout, page_ref_fields, Enum) sei stackeigen; setup-instance.md Schritt 5.5, evolve-subtypes.md Schritt 3 und die 182 type:-Verweise im Korpus zeigen das Gegenteil. Neu: types/<name>.md bleibt instanzeigen, die maschinenabgeleitete Anleitungsprosa zieht in ein stackeigenes types/<name>.guidance.md, verknüpft über ein optionales guidance:-Frontmatterfeld und von types describe zusammengesetzt.
Designfragen 1-4 beantwortet (Richtungsentscheidung vom Betreiber, 2026-09-15): Geschwisterdatei mit eigenem Typ type-guidance; layout:-Frage löst sich auf, weil kein Frontmatter geteilt wird; Split optional für selbstgeschriebene Typen; kein Grenzübertritt - --minor statt --major, die einmalige Adoption trägt das erste obligation: offered Migrationsdokument dieses Repos.
Alte Kriterien gestrichen mit Begründung im Body: verbatim-Auslieferung von types/<name>.md, Migration der drei Eigenschaften als Pflichtmigration, --major/--breaking, kb/CONVENTIONS.md § Language. Neu dazu: Drop-in-Nachweis als Test, find_leaks-Verhalten, offered-Semantik in migrate status/migrate done, Doku-Durchzugsliste.
kind/decision → kind/build. size/L bleibt, bedeutet jetzt aber Volumen (zwei Schnitte: Mechanik, dann die vier Guidance-Dateien plus Durchzug), nicht offene Designfragen.
Nebenbefund, als eigenes Issue abgelegt: types/type-spec.schema.yaml kennt weder root: noch capture_fields: trotz additionalProperties: false, und niemand merkt es, weil Type-Spec-Frontmatter nirgends gegen dieses Schema validiert wird.
**Changelog:** Schnittrichtung umgedreht und Body komplett neu geschrieben. Der alte Befund nahm an, Frontmatter-Konfiguration (`base_dir`, `layout`, `page_ref_fields`, Enum) sei stackeigen; `setup-instance.md` Schritt 5.5, `evolve-subtypes.md` Schritt 3 und die 182 `type:`-Verweise im Korpus zeigen das Gegenteil. Neu: `types/<name>.md` bleibt instanzeigen, die maschinenabgeleitete Anleitungsprosa zieht in ein stackeigenes `types/<name>.guidance.md`, verknüpft über ein optionales `guidance:`-Frontmatterfeld und von `types describe` zusammengesetzt.
Designfragen 1-4 beantwortet (Richtungsentscheidung vom Betreiber, 2026-09-15): Geschwisterdatei mit eigenem Typ `type-guidance`; `layout:`-Frage löst sich auf, weil kein Frontmatter geteilt wird; Split optional für selbstgeschriebene Typen; **kein Grenzübertritt** - `--minor` statt `--major`, die einmalige Adoption trägt das erste `obligation: offered` Migrationsdokument dieses Repos.
Alte Kriterien gestrichen mit Begründung im Body: verbatim-Auslieferung von `types/<name>.md`, Migration der drei Eigenschaften als Pflichtmigration, `--major`/`--breaking`, `kb/CONVENTIONS.md` § Language. Neu dazu: Drop-in-Nachweis als Test, `find_leaks`-Verhalten, `offered`-Semantik in `migrate status`/`migrate done`, Doku-Durchzugsliste.
`kind/decision` → `kind/build`. `size/L` bleibt, bedeutet jetzt aber Volumen (zwei Schnitte: Mechanik, dann die vier Guidance-Dateien plus Durchzug), nicht offene Designfragen.
Nebenbefund, als eigenes Issue abgelegt: `types/type-spec.schema.yaml` kennt weder `root:` noch `capture_fields:` trotz `additionalProperties: false`, und niemand merkt es, weil Type-Spec-Frontmatter nirgends gegen dieses Schema validiert wird.
Changelog: Abschluss-Rewrite. Body von Plan auf Ergebnis umgestellt: alle Akzeptanzkriterien abgehakt, je mit dem Test bzw. der Verifikation, die sie belegt; "Was gebaut wird" → "Was gebaut wurde" mit den tatsächlichen Datei- und Funktionsnamen; Befund und Entscheidung in die Vergangenheitsform, weil der Defekt nicht mehr besteht.
Zwei Dinge sind gegenüber dem Plan neu im Body: (1) eine beim Aufteilen aufgefallene Ausnahme zur Schnittregel - der Bullet "The title starts with 'Source - '" bleibt in types/source.md, weil er den instanzeigenen title_prefix:-Wert wörtlich zitiert; (2) zwei Dateien, die erst in der Schlussphase als stale auffielen und im Nachzug-Commit mitgegangen sind (README.md-Typenbaum, docs/language-boundaries.md § "Where the line runs inside one file" → "Where the line runs around a page type" - die Sprachachse hat sich nicht bewegt, nur die Dateigrenze).
Eine Abweichung von der Bauanweisung ist als solche vermerkt: das gemeinsame Stamm-Prädikat liegt als _owned_type_stem() in dist_cmd.py statt in ownership.py, weil dort ausschließlich Pfade unter den Content-Stages leben und types/ dem Modul unbekannt ist. Beide Aufrufstellen liegen in derselben Datei, die Anti-Drift-Eigenschaft ist erfüllt.
Veröffentlicht als d49513b (26 Dateien, nach Freigabe des Mass-Update-Gates) und 6eb3f84 (Doku-Nachzug, 2 Dateien, keine Version-Gate-Pfade). Version 6.0.0-beta.6. Geschlossen.
**Changelog:** Abschluss-Rewrite. Body von Plan auf Ergebnis umgestellt: alle Akzeptanzkriterien abgehakt, je mit dem Test bzw. der Verifikation, die sie belegt; "Was gebaut wird" → "Was gebaut wurde" mit den tatsächlichen Datei- und Funktionsnamen; Befund und Entscheidung in die Vergangenheitsform, weil der Defekt nicht mehr besteht.
Zwei Dinge sind gegenüber dem Plan neu im Body: (1) eine beim Aufteilen aufgefallene Ausnahme zur Schnittregel - der Bullet "The title starts with 'Source - '" bleibt in `types/source.md`, weil er den instanzeigenen `title_prefix:`-Wert wörtlich zitiert; (2) zwei Dateien, die erst in der Schlussphase als stale auffielen und im Nachzug-Commit mitgegangen sind (`README.md`-Typenbaum, `docs/language-boundaries.md` § "Where the line runs inside one file" → "Where the line runs around a page type" - die Sprachachse hat sich nicht bewegt, nur die Dateigrenze).
Eine Abweichung von der Bauanweisung ist als solche vermerkt: das gemeinsame Stamm-Prädikat liegt als `_owned_type_stem()` in `dist_cmd.py` statt in `ownership.py`, weil dort ausschließlich Pfade unter den Content-Stages leben und `types/` dem Modul unbekannt ist. Beide Aufrufstellen liegen in derselben Datei, die Anti-Drift-Eigenschaft ist erfüllt.
Veröffentlicht als `d49513b` (26 Dateien, nach Freigabe des Mass-Update-Gates) und `6eb3f84` (Doku-Nachzug, 2 Dateien, keine Version-Gate-Pfade). Version `6.0.0-beta.6`. Geschlossen.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Umgesetzt und veröffentlicht in
d49513b(Mechanik, Inhalt, Migration, Doku-Durchzug) und6eb3f84(Nachzug README-Typenbaum unddocs/language-boundaries.md), Version6.0.0-beta.6.Befund
Ein
root: kbType-Spec (types/entity.md,concept,source,comparison) hatte zweiPublika in einer Datei: Anleitungsprosa für den Agenten, der eine Seite schreibt, und
Seitenmaterial (
## Template-Block,layout:-Titel), das wörtlich zu Seitentext wird.Ownership gilt aber pro Datei (
chemenu/ownership.py,dist_cmd._plan_types()): weil dieSeitenhälfte instanzeigen sein muss, wurde die ganze Datei als
types/<name>.md.templateausgeliefert und nach der Adoption nie wieder angefasst.
Die Folge war still. Eine Instanz, die ihre Type-Specs beim Setup adoptiert hatte, bekam nie
wieder eine Verbesserung der Anleitungsprosa -
dist upgradeschrieb das.templateneben dieadoptierte Datei, nie die Datei selbst. Nichts brach, nichts meldete es; die Instanz las bis in
alle Zukunft die Anleitung vom Tag ihrer Erzeugung.
Welche Hälfte am Stack hing, war umgekehrt zur ersten Fassung dieses Issues. Gegen den Baum
geprüft:
instructions/setup-instance.mdSchritt 5.5 undinstructions/evolve-subtypes.mdSchritt 3weisen die Instanz an, den Enum in
types/<t>.schema.yamlund den passendenlayout:-Eintrag intypes/<t>.mdin derselben Änderung zu setzen. Dielayout:-Schlüsselsind die Enum-Werte dieser Instanz,
dir:sind ihre Areas,title_prefix: "Source - "istSeitentext.
type: types/<name>.md. Die Datei, die diesen Namenbehält, ist die, auf die der ganze Korpus zeigt.
types/source.mderklärte Capture-Fields,
raw accept --replaces, "one raw file, one owner" und die Begründungfür den Nicht-übernommen-Abschnitt.
instructions/ingest-large-tree.md- stackeigen, verbatimausgeliefert - zitiert diese Regel per
tools/wikitool types describe source: eine stackeigeneInstruktion hing an Prosa, die in einer instanzeigenen Datei stand.
Also nicht: Type-Spec zum Stack, Seitenmaterial heraus. Sondern: der Type-Spec blieb, wo er
war, und die Anleitungsprosa ist ausgezogen.
Entscheidung (umgesetzt)
Die vier offenen Designfragen wurden vor dem ersten Commit entschieden (Betreiber, 2026-09-15).
Wo lebt die stackeigene Hälfte, und in welcher Form? Eine Geschwisterdatei
types/<name>.guidance.mduntertypes/: stackeigen, verbatim ausgeliefert, englisch.Verknüpft über ein optionales
guidance:-Feld im Frontmatter des Type-Specs (repo-relativerPfad, wie
schema:), nicht über eine Namenskonvention - eine Konvention lieferte die Guidancesofort, aber doppelt, solange die adoptierte Datei die alte Prosa noch trägt. Sie hat einen
eigenen, nicht instanziierbaren Typ
types/type-guidance.md(keinbase_dir, wielint-report).tools/wikitool types describesetzt beide Hälften zusammen, der Agent machtweiter einen Aufruf.
Der Template-Pfad hat sich gar nicht bewegt:
type_resolver.extract_template()liestweiter den ersten ```markdown-Block aus
types/<name>.md.wikitool newhat keinen zweitenLadepfad bekommen, weil es seinen ersten behalten hat.
Verworfen: Guidance als Manual-Instruktion unter
instructions/- spart die neue Dateiart,kostet aber den zweiten Hop.
Was passiert mit den
layout:-Titeln? Sie sind geblieben, wo sie waren. Die Frage hat sichaufgelöst: geteilt wurde nur der Body, die Datei mit dem Frontmatter bleibt bei der Instanz.
Ein Typ, den eine Instanz komplett selbst schreibt?
guidance:ist optional; fehlt es,gibt es keine Stack-Hälfte und
describedruckt wie bisher nur den Body.Einmaliger Grenzübertritt oder Deprecation-Fenster? Keins von beidem - kein
Grenzübertritt. Drop-in in beide Richtungen, also
--minor. Die einmalige Adoption in einerbestehenden Instanz trägt ein Migrationsdokument mit
obligation: offered- der erste Gebrauchdieses Mechanismus, den
instructions/CONTRACT.mdseit 4.0.0 beschreibt und den bis hierherkein Dokument benutzt hat.
Schnittregel für die vier Bodies: Ein Satz, der wahr bleibt, wenn die Instanz ihre Subtypen
umbenennt, ihren Enum ändert oder ihr Template neu schreibt, gehört in die Guidance-Datei. Ein
Satz, der die Enum-Werte, Areas, Verzeichnisse, Überschriften oder Templatetexte dieser Instanz
benennt, bleibt im Type-Spec. Eine Ausnahme, die beim Aufteilen aufgefallen ist:
types/source.mdbehält den Bullet "The title starts with 'Source - '", weil er den instanzeigenen
title_prefix:-Wert wörtlich zitiert - eine andere Instanz wählt dort einen anderen Wert oderkeinen.
Was gebaut wurde
Mechanik
types/type-guidance.md+types/type-guidance.schema.yaml: stackeigener, nichtinstanziierbarer Typ.
types/type-spec.md: optionalesguidance:-Feld dokumentiert (§ Required Frontmatter);§§ "Who owns a type-spec" und "Anatomy of a type" auf den neuen Schnitt umgeschrieben - letzteres
sagt jetzt "at least two files, and a
root: kbtype may be three".type_resolver.get_guidance(): lädt die verlinkte Datei über denselbenload_type_spec-Pfad wieschema:, gibtNonezurück, wenn keinguidance:deklariert ist.types_core.describe_type(): neue Schlüsselguidanceundguidance_path;bodybehält seineBedeutung (additiv, der MCP-Wire-Contract bleibt für ältere Clients lesbar).
commands/types_cmd.py: rendert die Guidance-Hälfte vor dem Body, TOC-Region aus beidengestrippt.
dist_cmd._owned_type_stem(): ein gemeinsames Prädikat für_plan_types()undfind_leaks(). Beide rechneten vorhername.split(".", 1)[0]und hättenentity.guidance.mdals Stamm "entity" gelesen - die eine hätte sie als
.templateausgeliefert, die andere sie alsLeak gemeldet. Jetzt prüft eine Funktion die exakte Endung (
<stem>.mdoder<stem>.schema.yaml).Inhalt
types/entity.guidance.md,concept.guidance.md,source.guidance.md,comparison.guidance.md; die vier Type-Specs sind auf Frontmatter-Tabelle,## Templateundeinen Zeiger auf ihre Guidance-Datei zusammengeschnitten und tragen
guidance:.instructions/migrations/6.0.0-type-guidance-split.md,obligation: offered,migration_kind: assisted.Doku-Durchzug:
AGENTS.md§ File naming (neue Zeile fürtypes/<name>.guidance.md),types/type-spec.md,docs/ownership-and-templates.md(§ "Where the file boundary strains" →"Where the file boundary used to strain", beschreibt jetzt die Entscheidung statt eines Defekts),
tools/CONTRACT.md(types describe-Zeile),instructions/setup-instance.mdSchritt 5.1,README.md(Typenbaum),docs/language-boundaries.md(§ "Where the line runs inside one file" →"Where the line runs around a page type" - die Sprachachse hat sich nicht bewegt, die Dateigrenze
schon).
Akzeptanzkriterien
dist exportlieferttypes/<name>.guidance.mdverbatim aus, währendtypes/<name>.mdundtypes/<name>.schema.yamlweiter.templatetragen;find_leaks()meldet die Guidance-Dateinicht. (
test_guidance_file_ships_verbatim_beside_a_templated_type_spec, plustest_find_leaks_is_silent_on_a_clean_plan, dessen Fixture jetzt eine Guidance-Datei enthältund damit genau diesen Fall mitprüft.)
dist upgradegegen eine Fixture-Instanz, deren Guidance-Datei seit der Installationunverändert ist, schreibt eine verbesserte Anleitungsprosa tatsächlich hinein.
(
test_upgrade_writes_improved_guidance_prose_over_an_adopted_type_spec- prüft zusätzlich,dass der adoptierte Type-Spec selbst unangetastet bleibt, weil er in keinem Stamp steht.)
dist upgradegegen eine Instanz mit adoptiertem Type-Spec ohneguidance:istbeobachtbar folgenlos. (
test_types_describe_a_type_with_no_guidance_omits_it_cleanlyundtest_get_guidance_is_none_when_the_type_spec_declares_none; der Upgrade-Pfad selbst istunverändert, weil eine Datei ohne
guidance:keinen neuen Code erreicht.)wikitool newliest sein Template weiter austypes/<name>.md- kein zweiter Ladepfad.(
test_extract_template_reads_only_the_type_spec_never_the_guidance_file: ein ```markdown-Blockin der Guidance-Datei wird nachweislich nicht als Scaffold genommen.)
guidance:beschreibt wie bisher.types describe --jsonträgtguidanceundguidance_path,bodybleibt der Body desType-Specs. (
test_types_describe_entity_composes_guidance_and_body_separately,test_types_describe_composes_guidance_ahead_of_body_in_text_output.)obligation: offered:migrate statuslistet es separat("1 optional upgrade(s) available - none of them block"),
migrate done 6.0.0 --dry-runbestätigt "would record the optional ... Content stays at 5.0.0". Die
offered-Semantikselbst ist bereits durch die generischen
kb_state/migrate_cmd-Tests abgedeckt, deshalbhier kein zusätzlicher Test, sondern die Verifikation am realen Dokument.
version bump --minor, kein--breaking, keine Pflichtmigration. (6.0.0-beta.5→6.0.0-beta.6; der Kandidat stand bereits auf MAJOR, die Escalation ändert seine Basis alsonicht.)
auffielen (
README.md,docs/language-boundaries.md).tools/wikitool docs verify,tools/wikitool instructions verifyundpytestgrün: 1261Tests (8 neu),
doctorohne Befund außer dem üblichensession-id-WARN.Bewusst nicht Teil davon
types/<name>.schema.yamlbleibt instanzeigen. Der Enum gehört der Instanz(
evolve-subtypes.md).kb/CONVENTIONS.md(.template)§ Language braucht keine Änderung: die Sprachachse ist mit#103 entschieden und hat sich hier nicht bewegt. Stattdessen wurde
docs/language-boundaries.mdnachgezogen, das genau diesen Unterschied jetzt ausspricht.types/type-spec.schema.yaml(root:undcapture_fields:fehlen trotzadditionalProperties: false, und niemand merkt es, weil Type-Spec-Frontmatter nirgends gegendieses Schema validiert wird): eigenes Issue #105. Hier wurde nur das
guidance:-Feld in dieProsa von
types/type-spec.mdaufgenommen, nicht ins Schema - das wäre ohnehin wirkungslos,solange #105 offen ist.
_owned_type_stem()indist_cmd.py, nicht inownership.py. Dort leben ausschließlichPfade unter den Content-Stages (
kb/,raw/,work/,reports/);types/kennt das Modulgar nicht. Beide Aufrufstellen liegen in derselben Datei, die Anti-Drift-Eigenschaft ist also
erfüllt.
Woher das kam
Aufgefallen bei der Arbeit an #99 (TOC-Scope und Sprachregeln), als der Seiten-Type-Spec in beiden
Achsen gleichzeitig angefasst wurde. Dort wurde der Schnitt innerhalb der Datei dokumentiert,
weil die Dateigrenze nicht in derselben Sitzung zu verschieben war.
Die erste Fassung dieses Bodys nahm an, die Frontmatter-Konfiguration sei stackeigen und nur
Template und
layout:-Titel gehörten der Instanz. Die Prüfung gegensetup-instance.md,evolve-subtypes.mdund die 182type:-Verweise im Korpus hat das umgedreht.Seiten-Type-Spec am Publikum schneiden: Anleitung stackeigen, Seitenmaterial instanzeigento Anleitungsprosa aus dem Seiten-Type-Spec herausloesen: guidance stackeigen, Type-Spec bleibt instanzeigenChangelog: Schnittrichtung umgedreht und Body komplett neu geschrieben. Der alte Befund nahm an, Frontmatter-Konfiguration (
base_dir,layout,page_ref_fields, Enum) sei stackeigen;setup-instance.mdSchritt 5.5,evolve-subtypes.mdSchritt 3 und die 182type:-Verweise im Korpus zeigen das Gegenteil. Neu:types/<name>.mdbleibt instanzeigen, die maschinenabgeleitete Anleitungsprosa zieht in ein stackeigenestypes/<name>.guidance.md, verknüpft über ein optionalesguidance:-Frontmatterfeld und vontypes describezusammengesetzt.Designfragen 1-4 beantwortet (Richtungsentscheidung vom Betreiber, 2026-09-15): Geschwisterdatei mit eigenem Typ
type-guidance;layout:-Frage löst sich auf, weil kein Frontmatter geteilt wird; Split optional für selbstgeschriebene Typen; kein Grenzübertritt ---minorstatt--major, die einmalige Adoption trägt das ersteobligation: offeredMigrationsdokument dieses Repos.Alte Kriterien gestrichen mit Begründung im Body: verbatim-Auslieferung von
types/<name>.md, Migration der drei Eigenschaften als Pflichtmigration,--major/--breaking,kb/CONVENTIONS.md§ Language. Neu dazu: Drop-in-Nachweis als Test,find_leaks-Verhalten,offered-Semantik inmigrate status/migrate done, Doku-Durchzugsliste.kind/decision→kind/build.size/Lbleibt, bedeutet jetzt aber Volumen (zwei Schnitte: Mechanik, dann die vier Guidance-Dateien plus Durchzug), nicht offene Designfragen.Nebenbefund, als eigenes Issue abgelegt:
types/type-spec.schema.yamlkennt wederroot:nochcapture_fields:trotzadditionalProperties: false, und niemand merkt es, weil Type-Spec-Frontmatter nirgends gegen dieses Schema validiert wird.Changelog: Abschluss-Rewrite. Body von Plan auf Ergebnis umgestellt: alle Akzeptanzkriterien abgehakt, je mit dem Test bzw. der Verifikation, die sie belegt; "Was gebaut wird" → "Was gebaut wurde" mit den tatsächlichen Datei- und Funktionsnamen; Befund und Entscheidung in die Vergangenheitsform, weil der Defekt nicht mehr besteht.
Zwei Dinge sind gegenüber dem Plan neu im Body: (1) eine beim Aufteilen aufgefallene Ausnahme zur Schnittregel - der Bullet "The title starts with 'Source - '" bleibt in
types/source.md, weil er den instanzeigenentitle_prefix:-Wert wörtlich zitiert; (2) zwei Dateien, die erst in der Schlussphase als stale auffielen und im Nachzug-Commit mitgegangen sind (README.md-Typenbaum,docs/language-boundaries.md§ "Where the line runs inside one file" → "Where the line runs around a page type" - die Sprachachse hat sich nicht bewegt, nur die Dateigrenze).Eine Abweichung von der Bauanweisung ist als solche vermerkt: das gemeinsame Stamm-Prädikat liegt als
_owned_type_stem()indist_cmd.pystatt inownership.py, weil dort ausschließlich Pfade unter den Content-Stages leben undtypes/dem Modul unbekannt ist. Beide Aufrufstellen liegen in derselben Datei, die Anti-Drift-Eigenschaft ist erfüllt.Veröffentlicht als
d49513b(26 Dateien, nach Freigabe des Mass-Update-Gates) und6eb3f84(Doku-Nachzug, 2 Dateien, keine Version-Gate-Pfade). Version6.0.0-beta.6. Geschlossen.