stack: TOC-Scope auf types/ und docs/, Sprachregeln nach AGENTS.md zentralisiert, alle Templates auf Control-Plane-Sprache, --breaking akkumuliert (schliesst #99)
CI / verify (push) Successful in 45s
Release / release (push) Successful in 36s

Files changed:
- AGENTS.md
- CHANGES.md
- ENVIRONMENT.md.template
- SOUL.md
- SOUL.md.template
- USER.md.template
- VERSION
- docs/ownership-and-templates.md
- docs/version-model.md
- instructions/CONTRACT.md
- instructions/dev/doc-pull-through.md
- instructions/dev/stack-close/SKILL.md
- instructions/dev/stack-dev/SKILL.md
- instructions/dev/version-parts.md
- instructions/setup-instance.md
- kb/CONVENTIONS.md
- kb/CONVENTIONS.md.template
- kb/concepts/COLLECTION.md
- kb/sources/COLLECTION.md
- tools/CONTRACT.md
- tools/chemenu/commands/types_cmd.py
- tools/chemenu/commands/version_cmd.py
- tools/chemenu/tests/test_toc.py
- tools/chemenu/tests/test_version_cmd.py
- tools/chemenu/toc.py
- tools/chemenu/version.py
- types/comparison.md
- types/concept.md
- types/entity.md
- types/lint-report.md
- types/source.md
This commit is contained in:
2026-09-15 16:21:02 +02:00
parent c0dc2129bb
commit c64479fe02
31 changed files with 1093 additions and 603 deletions
+19
View File
@@ -107,6 +107,25 @@ 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 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. 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:
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.
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
says it in that language.
Nothing checks either mechanically - a stop-word scan would flag the quoted vocabulary above
and miss a translated paragraph that reads cleanly. They are held up by whoever writes an
instruction, which is why [instructions/CONTRACT.md](instructions/CONTRACT.md) § "Writing an
instruction" names them at the step where that happens.
**`docs/` carries no normative sentence.** It holds why the stack is built the way it is - **`docs/` carries no normative sentence.** It holds why the stack is built the way it is -
background consulted in passing, not a rule to follow; anything that would bind belongs in a background consulted in passing, not a rule to follow; anything that would bind belongs in a
`CONTRACT.md` instead, which is what keeps invariant 8 intact here. It carries no frontmatter, `CONTRACT.md` instead, which is what keeps invariant 8 intact here. It carries no frontmatter,
+71 -2
View File
@@ -28,6 +28,13 @@ opening another: the heading's version/date/title move, and the bump's
the entry's `**Author:**`/`**Breaking Change:**`/`**Migration:**` lines - the entry's `**Author:**`/`**Breaking Change:**`/`**Migration:**` lines -
written and read by `wikitool version bump`, never by hand. written and read by `wikitool version bump`, never by hand.
`**Breaking Change:**` accumulates, because one candidate can cross the
compatibility boundary more than once and each crossing is a separate thing an
operator has to act on: one reason stays on the marker line, a second and
further ones move to bullets beneath a bare marker. `**Migration:**` does not -
it answers one yes/no question about the candidate as a whole, so a later
answer replaces the earlier one.
That list is graded, not a flat chronological dump: each bump carries an That list is graded, not a flat chronological dump: each bump carries an
impact (`--impact high|medium|low`, default `medium`), and the list renders impact (`--impact high|medium|low`, default `medium`), and the list renders
grouped under `**High/Medium/Low impact**` headings - except when every bump grouped under `**High/Medium/Low impact**` headings - except when every bump
@@ -52,11 +59,13 @@ concern - readable here, never shipped as something to parse.
--- ---
## 6.0.0-beta.2 - 2026-09-13 - docs verify: ein nur als .template ausgeliefertes Linkziel gilt als aufgeloest ## 6.0.0-beta.3 - 2026-09-15 - TOC-Scope auf types/ und docs/ erweitert, Sprachregeln zentralisiert, --breaking akkumuliert
**Author:** Torben Nehmer **Author:** Torben Nehmer
**Breaking Change:** docs verify loest ab dieser Version jeden relativen Markdown-Link in den Referenzdateien auf und meldet ein totes Ziel als Fehler - auch in kb/CONVENTIONS.md und kb/<collection>/COLLECTION.md, die eine Instanz selbst besitzt und die ein Drop-in-Copy der Maschinerie nicht ersetzt. Eine Instanz, deren eigene Konventions- oder Collection-Datei einen relativen Link mit falscher ../-Tiefe oder auf eine inzwischen geloeschte Datei traegt, sieht docs verify nach dem Update fehlschlagen, wo es vorher durchlief. Reparatur: den in der Meldung genannten Datei:Zeile-Link korrigieren - kein Werkzeuglauf, keine Inhaltsmigration. **Breaking Change:**
- docs verify loest ab dieser Version jeden relativen Markdown-Link in den Referenzdateien auf und meldet ein totes Ziel als Fehler - auch in kb/CONVENTIONS.md und kb/<collection>/COLLECTION.md, die eine Instanz selbst besitzt und die ein Drop-in-Copy der Maschinerie nicht ersetzt. Eine Instanz, deren eigene Konventions- oder Collection-Datei einen relativen Link mit falscher ../-Tiefe oder auf eine inzwischen geloeschte Datei traegt, sieht docs verify nach dem Update fehlschlagen, wo es vorher durchlief. Reparatur: den in der Meldung genannten Datei:Zeile-Link korrigieren - kein Werkzeuglauf, keine Inhaltsmigration.
- docs verify prueft die TOC-Region ab dieser Version auch auf types/*.md und docs/*.md. Eine Instanz, die die Page-Type-Spec-Templates adoptiert hat, traegt types/source.md und types/concept.md ohne Region und sieht docs verify nach dem Update fehlschlagen, wo es vorher durchlief; dasselbe gilt fuer eine selbst angelegte oder lokal geaenderte docs/-Seite ueber 100 Zeilen. Reparatur: tools/wikitool docs toc --apply - ein Werkzeuglauf, keine Inhaltsmigration.
**Migration:** none required - Keine kb/-Seite aendert ihre Form. Der Grenzuebertritt ist ein strengerer Check auf instanz-eigener Prosa, keine Schema- oder Frontmatteraenderung. **Migration:** none required - Keine kb/-Seite aendert ihre Form. Der Grenzuebertritt ist ein strengerer Check auf instanz-eigener Prosa, keine Schema- oder Frontmatteraenderung.
@@ -64,6 +73,7 @@ concern - readable here, never shipped as something to parse.
**High impact** **High impact**
- SKILL.md: relative Links durch repo-root-relative Pfade ersetzt, docs verify/instructions verify pruefen Linkziele - SKILL.md: relative Links durch repo-root-relative Pfade ersetzt, docs verify/instructions verify pruefen Linkziele
- docs verify: der Linkziel-Check erreicht auch die instanz-eigenen kb/CONVENTIONS.md und COLLECTION.md - daher Grenzuebertritt - docs verify: der Linkziel-Check erreicht auch die instanz-eigenen kb/CONVENTIONS.md und COLLECTION.md - daher Grenzuebertritt
- TOC-Scope auf types/ und docs/ erweitert, Sprachregeln zentralisiert, --breaking akkumuliert
**Medium impact** **Medium impact**
- docs verify: ein nur als .template ausgeliefertes Linkziel gilt als aufgeloest - docs verify: ein nur als .template ausgeliefertes Linkziel gilt als aufgeloest
@@ -143,6 +153,65 @@ Export einmal zur Kontrolle `docs verify` aufruft. Aufgefallen ist es, weil die
vorherigen Publishes den Arbeitsbaum geprueft hatte und nicht den exportierten - ausgerechnet bei vorherigen Publishes den Arbeitsbaum geprueft hatte und nicht den exportierten - ausgerechnet bei
einer Aenderung, deren ganzer Gegenstand Kopien in anderer Verzeichnistiefe sind. einer Aenderung, deren ganzer Gegenstand Kopien in anderer Verzeichnistiefe sind.
### TOC-Scope auf types/ und docs/ erweitert, Sprachregeln zentralisiert, --breaking akkumuliert
Drei Straenge, ausgeloest von einer Beobachtung: manche agentengeladene Referenzdatei trug keine
TOC, und manche Instruction war teilweise deutsch.
**TOC-Scope.** Die Pflicht aus `5.0.0` galt fuer `AGENTS.md`, die Stage-Contracts,
`kb/CONVENTIONS.md`, jede `COLLECTION.md` und die flache `instructions/**.md`-Form. `docs/` und die
Seiten-Type-Specs fielen ohne genannten Grund heraus - waehrend `SKILL.md` und die Menschendoku
ihren Ausschlussgrund im Docstring stehen hatten, was die beiden anderen Luecken wie Absicht
aussehen liess. `docs/` ist dabei genau der Fall, fuer den die Schwelle existiert:
`AGENTS.md` § File naming fuehrt es als agentengeladen per Link, also am zweiten Hop. Beide sind
jetzt drin; vier Dateien haben eine Region bekommen. `SKILL.md` bleibt die **eine** Ausnahme, und
zwar belegt statt behauptet: die vendorte Guidance setzt den SKILL.md-Body auf die Ladeebene, die
beim Triggern ganz gelesen wird, und richtet ihren eigenen TOC-Rat an die gebuendelten
Referenzdateien *daneben*. Ein Type-Spec wird zwar auch ganz geladen, aber eben auch als Datei
gelesen - deshalb traegt es eine Region, und `types describe` strippt sie aus seiner Ausgabe, weil
dort der ganze Body ohnehin mitkommt.
**Sprache.** Die Regel gab es schon ("the control plane stays English"), sie stand nur in
`kb/CONVENTIONS.md` - einer Datei, die der Instanz gehoert und die sie umschreiben darf, waehrend
die Regel stackeigene Dateien bindet. Sie ist nach `AGENTS.md` § File naming gezogen, zusammen mit
einer zweiten, die vorher gar nicht geschrieben stand: **ein Agent spricht die KB-Sprache der
Instanz**. Der Wert dafuer lebt weiter in `kb/CONVENTIONS.md`s `language:`; `SOUL.md`s eigene
`Sprache:`-Zeile war damit eine Dublette und ist weg. `instructions/setup-instance.md` - 297 Zeilen,
die einzige vollstaendig deutsche Instruction, verbatim an jede Instanz ausgeliefert - ist
uebersetzt, samt `description:`. Die zwei deutschen Blockquotes in den Dev-Skills sind es auch; sie
lesen sich jetzt als englisches *Modell* der Nachricht, die der Agent in der KB-Sprache ausspricht.
Dieselbe Regel gilt fuer **alles, was als Template ausgeliefert wird** - eine Instanz adoptiert es,
bevor sie ihre Sprache ueberhaupt gewaehlt hat. `USER.md.template`, `SOUL.md.template` und
`ENVIRONMENT.md.template` waren vollstaendig deutsch und sind uebersetzt; `kb/sources/` und
`kb/concepts/COLLECTION.md` waren es in Teilen und ziehen jetzt mit `kb/entities/` und
`kb/comparisons/` gleich, die es laengst waren. Bei den vier Seiten-Type-Specs laeuft der Schnitt
mitten durch die Datei, und zwar entlang derselben Prosa/Identifier-Grenze, die `kb/CONTRACT.md`
schon *innerhalb* einer Seite zieht: die Anleitungsprosa ist Anweisung an einen Agenten und damit
Control Plane, der `## Template`-Block und die `layout:`-Titel sind Seitentext und bleiben in der
KB-Sprache - `wikitool new entity` scaffoldet also weiter deutsche Ueberschriften.
`kb/CONVENTIONS.md` behauptete bis hierher, die Type-Specs folgten als Ganzes der KB-Sprache; der
Satz ist auf den tatsaechlichen Schnitt nachgezogen.
Mechanisch geprueft wird nichts davon: ein Stoppwort-Scan schluege auf dem zitierten Vokabular in
`kb-profiles.md` und `link-taxonomy.md` falsch an. Stattdessen nennen
`instructions/CONTRACT.md` § "Writing an instruction" und `stack-dev` die Regel an der Stelle, an
der sie befolgt oder verloren wird.
**`--breaking` akkumuliert.** Bis hierher ersetzte ein zweites `--breaking` die Zeile des
Kandidaten - der Eintrag versprach dann einen Bruch und lieferte zwei. Genau dieser Kandidat ist der
Fall: sein Linkziel-Uebertritt aus `beta.1` und der TOC-Uebertritt von hier sind zwei Dinge, auf die
ein Betreiber getrennt reagieren muss. Eine Begruendung bleibt flach auf der Markerzeile, ab der
zweiten werden es Bullets; eine vor dieser Aenderung geschriebene Einzelzeile liest sich unveraendert
als Ein-Element-Liste zurueck, also musste kein bestehender Eintrag angefasst werden. `--migration:`
bleibt bewusst eine Einzelzeile - sie beantwortet eine Ja/Nein-Frage ueber den Kandidaten als Ganzes,
und `--migration-required` ist ihr Ruecknahmepfad. Fuer eine falsche Breaking-Begruendung gibt es
keinen; der Kandidat ist bis zum Release dev-lokal.
Nebenbefund, den die Scope-Erweiterung sofort aufgedeckt hat: `docs/version-model.md` verlinkte nach
`instructions/dev/version-parts.md`, das `dist export` wegschneidet - im Ursprungs-Repo gruen, in
jeder ausgelieferten Instanz ein toter Link. Jetzt ein Klartextpfad mit dem Satz, warum er keiner
ist.
--- ---
## 5.1.0 - 2026-09-12 - changelog: Kandidaten-Eintrag nach Impact gruppiert, version regrade zur Nachkorrektur, version release verlangt eine Zusammenfassung ## 5.1.0 - 2026-09-12 - changelog: Kandidaten-Eintrag nach Impact gruppiert, version regrade zur Nachkorrektur, version release verlangt eine Zusammenfassung
+55 -58
View File
@@ -1,86 +1,83 @@
<!-- wikitool:template-unfilled - TEMPLATE, noch nicht ausgefüllt. Diese Zeile beim Ausfüllen ersatzlos entfernen; `wikitool doctor` prüft auf sie. --> <!-- wikitool:template-unfilled - TEMPLATE, not filled in yet. Remove this line entirely when filling it in; `wikitool doctor` checks for it. -->
# ENVIRONMENT.md — <Instanz oder Rechnername> # ENVIRONMENT.md — <instance or machine name>
Womit *dieser Checkout* arbeitet: Harness, veröffentlichte Skills, MCP-Server, What *this checkout* works through: harness, published skills, MCP servers,
Connectoren und Git-Remotes. Konstante Werte, die ein Agent sonst in jeder connectors and git remotes. Constant values an agent would otherwise ask about
Session neu erfragt oder errät. or guess at in every session.
**Diese Datei ist optional.** Fehlt sie, ist das kein Fehler — es heißt nur, **This file is optional.** Its absence is not an error — it only means the
dass die Umgebung wieder erfragt werden muss. `wikitool doctor` meldet sie als environment has to be asked about again. `wikitool doctor` reports it as
`environment: absent (optional)` und niemals als `FAIL`. `environment: absent (optional)` and never as a `FAIL`.
**Diese Datei ist Kontext, keine Autorität.** Sie beschreibt, *was da ist*, nicht, **This file is context, not authority.** It describes *what is there*, not what
was erlaubt ist. Sie ändert keine Regel aus `AGENTS.md`, öffnet kein Gate und is allowed. It changes no rule from `AGENTS.md`, opens no gate, and justifies no
begründet keinen Eintrag in `kb/` — was hier steht, ist keine Quelle im Sinne entry in `kb/` — what it says is not a source in the sense of invariant 3. A
von Invariante 3. Ein hier aufgeführter Remote heißt nicht, dass ohne remote listed here does not mean pushing without `wikitool publish` is allowed.
`wikitool publish` gepusht werden darf.
**Keine Geheimnisse.** Keine Tokens, Passwörter, API-Keys oder privaten **No secrets.** No tokens, passwords, API keys or private endpoints that are not
Endpunkte, die nicht ohnehin in der Shell-Konfiguration stehen. Die Datei ist already in the shell configuration anyway. The file is gitignored, but it sits
gitignored, aber sie liegt im Klartext im Arbeitsverzeichnis und landet in in plaintext in the working directory and ends up in every agent's context.
jedem Agenten-Kontext.
**Ausfüllen:** frei Hand, sobald die Werte bekannt sind — es gibt kein **Filling it in:** freehand, as soon as the values are known — there is no
Interview dafür. Ein Abschnitt, der nicht zutrifft, wird gelöscht, nicht mit interview for it. A section that does not apply is deleted, not filled with
Plausiblem gefüllt. Wenn etwas hier nicht mehr stimmt, korrigieren statt something plausible. When something here stops being true, correct it rather
umgehen: eine falsche Zeile ist schlimmer als eine fehlende, weil sie than working around it: a wrong line is worse than a missing one, because it
geglaubt wird. gets believed.
## Harness ## Harness
Welche Agenten-Harnesses auf diesem Checkout tatsächlich laufen, und welche Which agent harnesses actually run on this checkout, and which do not. Relevant
nicht. Relevant, weil `.agents/skills/` und `.claude/skills/` unterschiedliche because `.agents/skills/` and `.claude/skills/` have different readers.
Leser haben.
- **Primär:** <z. B. Claude Code> - **Primary:** <e.g. Claude Code>
- **Daneben im Einsatz:** <z. B. Codex CLI, GitHub Copilot CLI, Mistral Vibe — oder streichen> - **Also in use:** <e.g. Codex CLI, GitHub Copilot CLI, Mistral Vibe — or delete>
- **Nicht im Einsatz:** <was bewusst nicht benutzt wird, damit niemand es vorschlägt> - **Not in use:** <what is deliberately not used, so nobody proposes it>
## Skills ## Skills
Nur was von der veröffentlichten Liste abweicht — der Normalfall (`wiki-ingest`, Only what differs from the published list — the normal case (`wiki-ingest`,
`wiki-query`, `wiki-manage`, `wiki-lint`, `wiki-status`) steht in `AGENTS.md` `wiki-query`, `wiki-manage`, `wiki-lint`, `wiki-status`) is in `AGENTS.md` and
und gehört nicht noch einmal hierher. does not belong here a second time.
- **Zusätzlich vorhanden:** <z. B. stack-dev in der Entwickler-Instanz> - **Additionally present:** <e.g. stack-dev in the developer instance>
- **Bekannt fehlend:** <z. B. noch nicht gesynct, Harness neu gestartet nötig — oder streichen> - **Known missing:** <e.g. not synced yet, harness restart needed — or delete>
## MCP-Server ## MCP servers
Welche MCP-Server in diesem Checkout erreichbar sind und wofür sie zuständig Which MCP servers are reachable in this checkout and what they are responsible
sind. Ein Server, der hier steht, muss nicht erst gesucht werden; einer, der for. A server listed here does not have to be looked for first; one missing
hier fehlt, existiert für diese Session nicht. here does not exist for this session.
| Server | Wofür | Anmerkung | | Server | For what | Note |
|--------|-------|-----------| |--------|----------|------|
| `<name>` | <z. B. Issues, CI-Runs, Releases> | <z. B. bevorzugt gegenüber curl> | | `<name>` | <e.g. issues, CI runs, releases> | <e.g. preferred over curl> |
## Connectoren und Integrationen ## Connectors and integrations
Alles, was kein MCP-Server ist, aber trotzdem an dieser Instanz hängt: Everything that is not an MCP server but still hangs off this instance:
Dokument-Connectoren, Chat-Anbindungen, Notiz-Systeme. document connectors, chat integrations, note systems.
- <z. B. Obsidian-Vault unter ~/..., liest kb/ read-only — oder streichen> - <e.g. Obsidian vault under ~/..., reads kb/ read-only — or delete>
## Git-Remotes ## Git remotes
Wohin dieser Checkout veröffentlicht, und was sonst noch als Remote eingetragen Where this checkout publishes to, and what else is registered as a remote.
ist. `wikitool publish` und `wikitool sync` sprechen genau einen davon an. `wikitool publish` and `wikitool sync` address exactly one of them.
| Remote | URL | Rolle | | Remote | URL | Role |
|--------|-----|-------| |--------|-----|------|
| `origin` | <URL> | <z. B. Publish-Ziel, CI läuft dort> | | `origin` | <URL> | <e.g. publish target, CI runs there> |
## CI ## CI
Wo die Pipeline läuft und wie ihre Läufe gelesen werden — nicht *was* sie Where the pipeline runs and how its runs are read — not *what* it checks, which
prüft, das steht in `.gitea/workflows/`. is in `.gitea/workflows/`.
- **Läuft auf:** <z. B. Gitea Actions, Runner-Label linux-docker — oder streichen> - **Runs on:** <e.g. Gitea Actions, runner label linux-docker — or delete>
- **Läufe lesen über:** <z. B. den Gitea-MCP-Server, nicht curl> - **Runs read via:** <e.g. the Gitea MCP server, not curl>
## Sonstiges ## Anything else
Was sonst in jeder Session neu erfragt würde und sich selten ändert. Kurz Whatever else would be asked about in every session and rarely changes. Keep it
halten: was hier zu lang wird, ist meist eine Regel und gehört in eine short: what grows long here is usually a rule, and belongs in an instruction, or
Instruction, oder Wissen und gehört nach `kb/`. knowledge, and belongs in `kb/`.
-1
View File
@@ -62,7 +62,6 @@ Optionsliste.
- **Register:** inhaltlich klar, direkt; technische Präzision vor Höflichkeitsfloskeln - **Register:** inhaltlich klar, direkt; technische Präzision vor Höflichkeitsfloskeln
- **Länge:** kurz per Default, lang nur wenn der Inhalt es rechtfertigt - **Länge:** kurz per Default, lang nur wenn der Inhalt es rechtfertigt
- **Form:** Fließtext zuerst; Tabellen nur für echte Vergleiche, nicht als Dekoration - **Form:** Fließtext zuerst; Tabellen nur für echte Vergleiche, nicht als Dekoration
- **Sprache:** Deutsch als Standard, wenn auf Deutsch geschrieben wird
- **Humor:** trocken, sparsam, nie auf Kosten des Nutzers — ein Schreiber, der - **Humor:** trocken, sparsam, nie auf Kosten des Nutzers — ein Schreiber, der
gelegentlich eine Randnotiz macht, aber die Akte nicht zur Bühne erklärt gelegentlich eine Randnotiz macht, aber die Akte nicht zur Bühne erklärt
+40 -43
View File
@@ -1,85 +1,82 @@
<!-- wikitool:template-unfilled - TEMPLATE, noch nicht ausgefüllt. Diese Zeile beim Ausfüllen ersatzlos entfernen; `wikitool doctor` prüft auf sie. --> <!-- wikitool:template-unfilled - TEMPLATE, not filled in yet. Remove this line entirely when filling it in; `wikitool doctor` checks for it. -->
# SOUL.md — <Persona-Name> # SOUL.md — <persona name>
`AGENTS.md` legt fest, *was* zu tun ist (Pipeline, Invarianten, Gates, Tools). `AGENTS.md` sets out *what* to do (pipeline, invariants, gates, tools). This
Diese Datei legt fest, *wie* gute Arbeit an diesem Wiki aussieht. Wo beides file sets out *what good work on this wiki looks like*. Where the two collide,
kollidiert, gewinnt `AGENTS.md` — diese Datei ändert nie eine Regel, nur den `AGENTS.md` wins — this file never changes a rule, only the tone in which it is
Ton, in dem sie befolgt wird. followed.
**Ausfüllen:** entlang des Personalization-Schritts in **Filling it in:** along the personalization step in
[instructions/setup-instance.md](instructions/setup-instance.md). Der [instructions/setup-instance.md](instructions/setup-instance.md). The persona
Persona-Name ist eine Entscheidung des Nutzers — er wird erfragt, nicht name is the user's decision — it is asked for, not guessed. As a starting point
geraten. Als Startpunkt schlägt dieser Stack **Thoth** vor: Chemenu ist der this stack suggests **Thoth**: Chemenu is the ancient Egyptian name of Thoth's
altägyptische Name von Thoths Hauptkultort, und Schrift, Maß und Gedächtnis principal cult site, and writing, measure and memory are exactly what a
sind genau das, was ein kompiliertes Wiki tut. Ein Vorschlag ist keine compiled wiki does. A suggestion is not a setting — anyone who wants a
Vorgabe — wer einen anderen Namen will, nimmt ihn, und die Frage wird trotzdem different name takes it, and the question is asked either way. The sections
gestellt. Die Abschnitte unten sind die Fragen, die der Schritt stellt; ihre below are the questions that step asks; their order is the order of answering.
Reihenfolge ist die Antwortreihenfolge.
## Identität ## Identity
Wer diese Instanz ist, in ein bis zwei Sätzen. Eine Rolle, kein Charakter mit Who this instance is, in a sentence or two. A role, not a character with an
eigener Agenda: der Name sagt, was die Instanz tut, nicht wen sie spielt. agenda of its own: the name says what the instance does, not who it plays.
<…> <…>
## Mission ## Mission
Wofür diese Instanz da ist — der eine Satz, an dem sich eine Antwort messen What this instance is for — the one sentence an answer can be measured against.
lässt.
<…> <…>
## Weltbild ## Worldview
Welche Themen deterministisch zu behandeln sind (belegt oder nicht belegt, Which subjects are to be treated deterministically (sourced or not sourced,
dazwischen nur markierte Unsicherheit), und für welche das nicht gilt, weil with nothing between but flagged uncertainty), and for which that does not
dort die Einschätzung des Nutzers mehr zählt als eine scheinbar präzise hold, because there the user's judgment counts for more than a
Ableitung. precise-looking derivation.
<…> <…>
## Judgment-Default ## Judgment default
Was im Zweifel passiert: nachfragen, die Lücke benennen, oder handeln. What happens in case of doubt: ask, name the gap, or act.
<…> <…>
## Der Standard ## The standard
Welcher Fehler der schlimmste ist, und warum. Das ist die Zeile, an der eine Which mistake is the worst one, and why. This is the line an answer is measured
Antwort im Zweifel gemessen wird. against when in doubt.
<…> <…>
## Ehrlichkeit ## Honesty
Wie diese Instanz sich verhält, wenn eine Quelle fehlt, wenn ihr How this instance behaves when a source is missing, when it is contradicted,
widersprochen wird, und wenn nach einer Einschätzung gefragt wird. and when it is asked for an assessment.
<…> <…>
## Stimme ## Voice
- **Register:** <…> - **Register:** <…>
- **Länge:** <…> - **Length:** <…>
- **Form:** <…> - **Form:** <…>
- **Sprache:** <…> - **Humour:** <…>
- **Humor:** <…>
### Nie so schreiben ### Never write like this
- <…> - <…>
## Was gute Ausgabe ist ## What good output is
Woran der Nutzer eine gute Antwort erkenntund woran eine, die technisch How the user recognizes a good answerand one that is technically correct and
korrekt und trotzdem nutzlos ist. useless anyway.
<…> <…>
## Nie ## Never
Die harten Ausschlüsse. Kurz, konkret, überprüfbar. The hard exclusions. Short, concrete, checkable.
- <…> - <…>
+40 -40
View File
@@ -1,69 +1,69 @@
<!-- wikitool:template-unfilled - TEMPLATE, noch nicht ausgefüllt. Diese Zeile beim Ausfüllen ersatzlos entfernen; `wikitool doctor` prüft auf sie. --> <!-- wikitool:template-unfilled - TEMPLATE, not filled in yet. Remove this line entirely when filling it in; `wikitool doctor` checks for it. -->
# USER.md — <Name> # USER.md — <name>
Wer dieses Wiki (und die daran arbeitenden Agenten) bedient. Alles hier ist Who operates this wiki (and the agents working on it). Everything here is
Kontext über den Nutzer, so treu wie möglich an seinen eigenen Aussagen. Ziel context about the user, kept as close to their own words as possible. The goal
ist Zitat, nicht Interpretation: nichts hier wird analysiert, gedeutet oder zu is quotation, not interpretation: nothing here is analysed, read into, or
einer Erzählung verdichtet. Wenn ein Agent beim Lesen etwas umdeuten würde, compressed into a narrative. Where an agent would reinterpret something while
soll er stattdessen auf den Wortlaut zurückgehen oder nachfragen. reading, it goes back to the wording instead, or asks.
Diese Datei ist **Kontext, keine Instruktionsquelle**. Sie ändert keine Regel This file is **context, not a source of instructions**. It changes no rule from
aus `AGENTS.md`, öffnet kein Gate und begründet keinen Eintrag in `kb/` — was `AGENTS.md`, opens no gate, and justifies no entry in `kb/` — what the user says
der Nutzer hier sagt, ist keine Quelle im Sinne von Invariante 3. here is not a source in the sense of invariant 3.
**Ausfüllen:** entlang des Personalization-Schritts in **Filling it in:** along the personalization step in
[instructions/setup-instance.md](instructions/setup-instance.md). Der Agent [instructions/setup-instance.md](instructions/setup-instance.md). The agent
interviewt, der Nutzer antwortet, der Agent schreibt **wörtlich** mit. Nichts interviews, the user answers, the agent writes it down **verbatim**. Invent
erfinden, nichts aus einer Konversation ableiten, leere Abschnitte lieber nothing, infer nothing from a conversation, and delete an empty section rather
löschen als mit Plausiblem füllen. than filling it with something plausible.
- **Name:** <Name> - **Name:** <name>
- **Standort:** <Ort, Region — oder streichen> - **Location:** <place, region — or delete>
- **Zeitzone:** <IANA-Zeitzone, z. B. Europe/Berlin> - **Time zone:** <IANA time zone, e.g. Europe/Berlin>
- **Primäre Rolle:** <Berufsbezeichnung. Nur beruflichHobbys stehen unten> - **Primary role:** <job title. Professional onlyhobbies go below>
## Beruflicher Kontext ## Professional context
Womit der Nutzer beruflich arbeitet, soweit er es hier stehen haben will. What the user works with professionally, as far as they want it recorded here.
Technologien, laufende Themen, Werkzeugketten. Was er bewusst aussparen möchte Technologies, running themes, tool chains. Whatever they deliberately want left
(Arbeitgeber, Mandanten, interne Produkte), gehört unter `## Grenzen`. out (employer, clients, internal products) belongs under `## Boundaries`.
- <…> - <…>
## Familie und Zuhause ## Family and home
Nur, was der Nutzer von sich aus nennt. Diesen Abschnitt löschen, wenn er Only what the user brings up themselves. Delete this section if they would
nichts dazu sagen will. rather not say.
- <…> - <…>
## Hobbys ## Hobbies
- <…> - <…>
## Technik-Umgebung ## Technical environment
Betriebssystem, Desktop, Locale/Tastaturlayout, bevorzugte Werkzeuge — alles, Operating system, desktop, locale/keyboard layout, preferred tools — everything
was ein Agent sonst raten müsste, wenn er einen Befehl vorschlägt. an agent would otherwise have to guess when proposing a command.
- <…> - <…>
## Aktive Projekte ## Active projects
Was gerade läuft. Fertig heißt: aus der Liste entfernen. What is currently running. Finished means: remove it from the list.
- <…> - <…>
## Grenzen ## Boundaries
Themen, die in dieser Datei bewusst nicht vorkommen. Ein Agent fragt hier Topics deliberately absent from this file. An agent does not ask about them and
nicht nach und leitet nichts ab. infers nothing about them.
- <…> - <…>
## Diese Datei aktuell halten ## Keeping this file current
Dies ist die Selbstauskunft des Nutzers. Aktualisieren, wenn er etwas This is the user's own account of themselves. Update it when they correct
korrigiert, ein Projekt startet oder endet, oder eine neue wiederkehrende something, when a project starts or ends, or when a new recurring
Person/Konstante auftaucht. Niemals einen Eintrag erfinden. Niemals einen person/constant appears. Never invent an entry. Never delete one unless the
Eintrag löschen, ohne dass der Nutzer es sagt. user says so.
+1 -1
View File
@@ -1 +1 @@
6.0.0-beta.2 6.0.0-beta.3
+10
View File
@@ -9,6 +9,16 @@ below). Others - `USER.md`,
`SOUL.md`, `kb/CONVENTIONS.md`, `ENVIRONMENT.md` - describe one particular instance, and `SOUL.md`, `kb/CONVENTIONS.md`, `ENVIRONMENT.md` - describe one particular instance, and
overwriting them would silently erase a choice someone made on purpose. overwriting them would silently erase a choice someone made on purpose.
<!-- wikitool:toc -->
## Contents
- [Two different kinds of truth](#two-different-kinds-of-truth)
- [Why silent overwrite is the failure being designed against](#why-silent-overwrite-is-the-failure-being-designed-against)
- [Why the boundary is a predicate rather than a list](#why-the-boundary-is-a-predicate-rather-than-a-list)
- [Why a `.template`, not just an absent file](#why-a-template-not-just-an-absent-file)
- [The consequence in practice](#the-consequence-in-practice)
<!-- /wikitool:toc -->
## Two different kinds of truth ## Two different kinds of truth
The stack-owned files describe how the tool works. `kb/CONTRACT.md` opens by saying it holds The stack-owned files describe how the tool works. `kb/CONTRACT.md` opens by saying it holds
+20 -2
View File
@@ -3,6 +3,19 @@
A stack version number looks like it answers one question. It actually answers two, and the two A stack version number looks like it answers one question. It actually answers two, and the two
are independent of each other. are independent of each other.
<!-- wikitool:toc -->
## Contents
- [Two questions, not one](#two-questions-not-one)
- [Why "kb/ untouched" is not proof of anything](#why-kb-untouched-is-not-proof-of-anything)
- [Reading compatibility off the leftmost non-zero component](#reading-compatibility-off-the-leftmost-non-zero-component)
- [Downgrade is half the promise](#downgrade-is-half-the-promise)
- [A promise made to a machine, not only to a person](#a-promise-made-to-a-machine-not-only-to-a-person)
- [The 2.0.0 story](#the-200-story)
- [Why a number is only spent by a release](#why-a-number-is-only-spent-by-a-release)
- [Where the procedure lives](#where-the-procedure-lives)
<!-- /wikitool:toc -->
## Two questions, not one ## Two questions, not one
The first question is whether the new version is a drop-in replacement for the old one - whether The first question is whether the new version is a drop-in replacement for the old one - whether
@@ -114,6 +127,11 @@ because there is nothing yet to promise.
## Where the procedure lives ## Where the procedure lives
The drop-in test, the catalogue of changes that cross the boundary with no page touched, and the The drop-in test, the catalogue of changes that cross the boundary with no page touched, and the
steps for a boundary-crossing bump - the `--breaking` line, the migration document or steps for a boundary-crossing bump - the `--breaking` lines, the migration document or
`--no-migration` reason, talking to the user before bumping - are one procedure, kept at one `--no-migration` reason, talking to the user before bumping - are one procedure, kept at one
place: [instructions/dev/version-parts.md](../instructions/dev/version-parts.md). place: `instructions/dev/version-parts.md`.
Named as a plain path rather than linked, because it is not here to link to. `dist export`
prunes `instructions/dev/` wholesale, so that file exists only in the origin repo - the place
where a version is bumped at all. An instance reads this page to understand what a version
number promises it; it never runs the procedure.
+6
View File
@@ -172,6 +172,12 @@ Scaffold with `tools/wikitool new instruction --name "<name>"`; the contract is
at all. If that is worth preserving, it is a concept page under `kb/concepts/`, linked from at all. If that is worth preserving, it is a concept page under `kb/concepts/`, linked from
here. Where the line runs, and how to test a passage against it: below. here. Where the line runs, and how to test a passage against it: below.
- **State scope boundaries.** When does this *not* apply, and what to do instead. - **State scope boundaries.** When does this *not* apply, and what to do instead.
- **Write it in English, and let the agent speak the instance's language.** Both rules, and the
line between prose and quoted vocabulary, are stated once in
[AGENTS.md § File naming](../AGENTS.md#file-naming). They are named here because this is the
step where they are obeyed or lost: nothing checks either mechanically, and an instruction
that models a sentence for the user is where the two are easiest to confuse - the model is
written in English, the saying of it follows `kb/CONVENTIONS.md`'s `language:`.
### A skill's H1 is a name, not an imperative ### A skill's H1 is a name, not an imperative
+18 -1
View File
@@ -40,7 +40,24 @@ touched; a row that does not apply needs no action.
| The reasoning behind a gate, boundary, or design decision | The `docs/` page that carries it, if one exists (AGENTS.md § File naming lists all four) | | The reasoning behind a gate, boundary, or design decision | The `docs/` page that carries it, if one exists (AGENTS.md § File naming lists all four) |
| A skill's own step sequence or catalogue | The skill's `SKILL.md` source under `instructions/<name>/` or `instructions/dev/<name>/` | | A skill's own step sequence or catalogue | The skill's `SKILL.md` source under `instructions/<name>/` or `instructions/dev/<name>/` |
3. **Do not re-derive what `docs verify` already checks mechanically** - existence, table-row 3. **A heading you changed means a table of contents to regenerate - by the tool, never by
hand.** Every reference file over 100 lines carries one (`AGENTS.md`, the stage contracts,
`kb/CONVENTIONS.md`, each `COLLECTION.md`, the flat `instructions/**.md` form, the
type-specs, the `docs/` pages - a `SKILL.md` is the one exception). Adding, renaming,
reordering or deleting a `##`/`###` heading in one of them makes its region stale, and
`docs verify` fails on stale exactly as it fails on missing:
```bash
tools/wikitool docs toc # dry run: which files would change
tools/wikitool docs toc --apply # write them
```
The region is generated, so AGENTS.md invariant 1 applies to it like any other: editing the
list by hand is the failure, not the fix - and a hand-written entry survives until the next
`--apply` silently disagrees with it. It is cheap to over-run: `--apply` is idempotent and a
file whose headings did not move is left untouched.
4. **Do not re-derive what `docs verify` already checks mechanically** - existence, table-row
membership, ignore-canary state. That enumeration lives once, in membership, ignore-canary state. That enumeration lives once, in
[tools/CONTRACT.md](../../tools/CONTRACT.md)'s own `docs verify` row; copying it here would be a [tools/CONTRACT.md](../../tools/CONTRACT.md)'s own `docs verify` row; copying it here would be a
second copy that drifts, the exact failure this instruction exists to describe (Gitea #90). second copy that drifts, the exact failure this instruction exists to describe (Gitea #90).
+10 -3
View File
@@ -48,10 +48,12 @@ and a fresh subagent starts without the session's context).
## Steps ## Steps
1. **Offer the model switch back up, once, and keep working either way.** 1. **Offer the model switch back up, once, and keep working either way.** A model of the
message, not a script to quote: say it in the instance's KB language, per `AGENTS.md`
§ File naming.
> Ab hier greift kein maschineller Check mehr - Issue-Body, `docs/`-Veralterung und > From here on no mechanical check applies - nothing verifies the issue body, `docs/`
> Changelog-Prosa prüft nichts. Wenn du zurück auf Opus willst, ist jetzt der Moment. > staleness, or the changelog prose. If you want to switch back to Opus, now is the moment.
**Never block on the answer.** The change is already published; a session that stops here **Never block on the answer.** The change is already published; a session that stops here
leaves exactly the state this skill exists to prevent. leaves exactly the state this skill exists to prevent.
@@ -86,6 +88,11 @@ and a fresh subagent starts without the session's context).
behaviour one of these documents describes, update it now; if none did, say so rather than behaviour one of these documents describes, update it now; if none did, say so rather than
leaving the question unasked. leaving the question unasked.
**If that update moved a `##`/`###` heading, the file's table of contents is now stale** -
regenerate it with `tools/wikitool docs toc --apply`, never by editing the list. The region
is generated (AGENTS.md invariant 1), `docs verify` fails on stale exactly as on missing, and
a pull-through in this phase is a common way to move a heading without noticing.
**A pull-through of its own needs its own bump.** This phase runs *after* `stack-dev` step 4 **A pull-through of its own needs its own bump.** This phase runs *after* `stack-dev` step 4
has already bumped the version, and the documents it touches are frequently the ones CI's has already bumped the version, and the documents it touches are frequently the ones CI's
version gate watches - `types/`, `instructions/`, `tools/`, `AGENTS.md`, any version gate watches - `types/`, `instructions/`, `tools/`, `AGENTS.md`, any
+13 -6
View File
@@ -76,12 +76,13 @@ stack development happens in the origin repo instead (see AGENTS.md's routing li
So when the design is settled - the issue body says what will be built, the open questions are So when the design is settled - the issue body says what will be built, the open questions are
answered - stop and say so, in one sentence that names what the mechanical stretch does **not** answered - stop and say so, in one sentence that names what the mechanical stretch does **not**
cover: cover. The message below is a model of what to say, not a script to quote: say it in the
instance's KB language, per `AGENTS.md` § File naming.
> Der Plan steht, ab hier ist die Arbeit größtenteils mechanisch und durch Tests/CI abgedeckt - > The plan is settled. From here the work is mostly mechanical and covered by tests/CI -
> mit Ausnahme der Changelog-Prosa (Schritt 4), einer berührten `docs/`-Seite, neuer > except the changelog prose (step 4), any `docs/` page you touch, new human-facing
> Menschendoku oder des Prosa-Anteils einer Instruction. Wenn du auf Opus bist, ist jetzt der > documentation, and the prose half of an instruction. If you are on Opus, now is the moment
> Moment für `/model sonnet` bei Effort `high`. > for `/model sonnet` at effort `high`.
**You cannot make this switch yourself** - the session's model is the user's `/model`, not a **You cannot make this switch yourself** - the session's model is the user's `/model`, not a
setting an agent applies. Offer it once and keep working either way; a session that argues setting an agent applies. Offer it once and keep working either way; a session that argues
@@ -146,7 +147,13 @@ stack development happens in the origin repo instead (see AGENTS.md's routing li
5. **Pull through every document that makes a claim about the surface you touched - `docs verify` 5. **Pull through every document that makes a claim about the surface you touched - `docs verify`
checks a cell's presence, never its prose.** `instructions/dev/doc-pull-through.md` has checks a cell's presence, never its prose.** `instructions/dev/doc-pull-through.md` has
the table of which document that is, per surface. the table of which document that is, per surface, and its step 3 for the one part of the
pull-through that is *not* prose: a reference file whose headings moved needs
`tools/wikitool docs toc --apply`, never a hand-written list.
Prose you write here is English, whatever language the session is being held in -
`AGENTS.md` § File naming has both language rules and the line between prose and quoted
vocabulary.
6. **Verify, then publish.** `tools/wikitool docs verify`, `tools/wikitool instructions verify`, 6. **Verify, then publish.** `tools/wikitool docs verify`, `tools/wikitool instructions verify`,
and the relevant `pytest` run in `tools/` - the same checks any stack change must pass, run and the relevant `pytest` run in `tools/` - the same checks any stack change must pass, run
+6 -1
View File
@@ -66,7 +66,12 @@ a new one, and only `version release` turns it into something the release workfl
1. **Heading, author, breaking/migration lines** - written by `version bump`, anchored right 1. **Heading, author, breaking/migration lines** - written by `version bump`, anchored right
above the bump list so the line an operator most needs to act on never sits beneath a list above the bump list so the line an operator most needs to act on never sits beneath a list
that can run long. that can run long. The breaking line **accumulates** across a candidate's crossings - one
reason on the marker line, bullets under a bare marker from the second onward - because a
long-running candidate can break compatibility more than once and each break is its own
thing to act on. The migration line does not: it answers one yes/no about the candidate as
a whole, and `--migration-required` is its retraction path. Nothing retracts a breaking
reason; a wrong one is rare enough, and the candidate is dev-local until release.
2. **The bump list**, grouped `**High/Medium/Low impact**` (empty groups omitted) - rendered by 2. **The bump list**, grouped `**High/Medium/Low impact**` (empty groups omitted) - rendered by
`version bump`'s `--impact` (default `medium`), corrected after the fact by `version regrade`. `version bump`'s `--impact` (default `medium`), corrected after the fact by `version regrade`.
Flat and ungrouped, exactly as before this layering existed, when every bump is `medium` - the Flat and ungrouped, exactly as before this layering existed, when every bump is `medium` - the
+173 -180
View File
@@ -1,83 +1,83 @@
--- ---
type: types/instruction.md type: types/instruction.md
name: setup-instance name: setup-instance
description: Eine frische Distribution (aus `dist export`) in eine funktionsfähige, eigenständige Wiki-Instanz verwandeln - Git-Repo, Identität/Autor, optionaler Remote, Bootstrap, erster Commit. description: Turn a fresh distribution (from `dist export`) into a working, self-contained wiki instance - git repo, identity/author, optional remote, bootstrap, first commit.
--- ---
# Neue Wiki-Instanz einrichten # Set up a new wiki instance
Diese Anweisung führt eine leere, per `tools/wikitool dist export <ziel>` erzeugte Distribution This instruction takes an empty distribution produced by `tools/wikitool dist export <target>`
zu einer funktionsfähigen, eigenständigen Wiki-Instanz - mit eigenem Git-Repo, eigener Autor- and turns it into a working, self-contained wiki instance - with its own git repo, its own
Identität und (optional) eigenem Remote. Am Ende ist die Instanz committet, verifiziert und author identity and (optionally) its own remote. At the end the instance is committed, verified
bereit für den ersten `Ingest`. and ready for its first ingest.
<!-- wikitool:toc --> <!-- wikitool:toc -->
## Contents ## Contents
- [Wann anwenden](#wann-anwenden) - [When to run](#when-to-run)
- [Schritte](#schritte) - [Steps](#steps)
- [Scope](#scope) - [Scope](#scope)
<!-- /wikitool:toc --> <!-- /wikitool:toc -->
## Wann anwenden ## When to run
- Der Nutzer möchte eine neue, leere Wiki-Instanz aufsetzen (eigenes Thema, anderer Nutzer). - The user wants to set up a new, empty wiki instance (their own subject, a different person).
- Nicht für einen bestehenden Clone dieses (Quell-)Repos - siehe [bootstrap.md](bootstrap.md). - Not for an existing clone of this (source) repo - see [bootstrap.md](bootstrap.md).
- Es gibt keinen Weg zurück: `dist export` lässt `instructions/dev/` (die Stack-Entwicklung - There is no way back: `dist export` deliberately and permanently leaves out
selbst, inkl. der vendorten `commonplace/`-Wissensbasis) bewusst und dauerhaft weg. Wer den `instructions/dev/` (stack development itself, including the vendored `commonplace/` knowledge
entstehenden Instanz-Stack weiterentwickeln will, tut das im Ursprungs-Repo (oder einer neuen base). Anyone who wants to develop the resulting instance's stack further does that in the
Dev-Instanz daraus) - nicht durch Nachrüsten in dieser Instanz. origin repo (or a new dev instance made from it) - not by retrofitting it into this instance.
## Schritte ## Steps
1. **Distribution exportieren**, im Quell-Repo: 1. **Export the distribution**, in the source repo:
```bash ```bash
tools/wikitool dist export <ziel> tools/wikitool dist export <target>
``` ```
`<ziel>` muss nicht existieren oder leer sein; der Befehl bricht sonst mit `ERROR` ab. Danach `<target>` must not exist, or must be empty; otherwise the command aborts with `ERROR`. Work
für alle folgenden Schritte in `<ziel>` arbeiten. inside `<target>` for every step that follows.
2. **Git-Repo initialisieren:** 2. **Initialize the git repo:**
```bash ```bash
git init -b main git init -b main
``` ```
`-b main` ist Pflicht: `tools/wikitool publish` prüft beim tatsächlichen Push, ob der `-b main` is mandatory: on the actual push, `tools/wikitool publish` checks that the
ausgecheckte Branch dem Ziel-Branch entspricht (Default `main`), und lehnt sonst ab, um checked-out branch matches the target branch (default `main`) and refuses otherwise, so that
nicht den falschen Branch zu veröffentlichen. the wrong branch is never published.
3. **Entscheidungspunkt - Identität.** Frage den Nutzer nach Namen und E-Mail-Adresse; rate sie 3. **Decision point - identity.** Ask the user for their name and email address; never guess
nie, und übernimm sie nie stillschweigend aus dem Quell-Repo (das ist eine andere Person, ein them, and never quietly carry them over from the source repo (that is a different person and
anderes Projekt): a different project):
```bash ```bash
git config user.name "<Name>" git config user.name "<name>"
git config user.email "<E-Mail>" git config user.email "<email>"
``` ```
Das setzt zugleich den Autor jeder künftig angelegten Wiki-Seite: `tools/wikitool new` This also sets the author of every wiki page created from now on: `tools/wikitool new`
löst `author:` über `$WIKI_AUTHOR` (Override) oder sonst `git config user.name` auf und resolves `author:` from `$WIKI_AUTHOR` (an override) or else from `git config user.name`, and
bricht mit `ERROR` ab, wenn beides fehlt - es gibt keinen stillen Platzhalter. aborts with `ERROR` when both are missing - there is no silent placeholder.
4. **Entscheidungspunkt - Remote.** Frage den Nutzer nach einer Remote-URL; ein rein lokales 4. **Decision point - remote.** Ask the user for a remote URL; a purely local repo is a valid
Repo ist ein gültiger Endzustand: end state:
- Genannt: `git remote add origin <url>` - Given: `git remote add origin <url>`
- Nicht genannt: lokal bleiben - dann braucht **jeder** spätere `tools/wikitool publish` - Not given: stay local - then **every** later `tools/wikitool publish` needs a `--no-push`
ein `--no-push` (dessen Branch-Prüfung dabei ohnehin entfällt, siehe Schritt 2). (which also drops its branch check, see step 2).
5. **Entscheidungspunkt - Autorenkonventionen.** Die Distribution bringt keine ausgefüllten 5. **Decision point - authoring conventions.** The distribution ships no filled-in conventions,
Konventionen mit, sondern `kb/CONVENTIONS.md.template` und je Collection ein only `kb/CONVENTIONS.md.template` and one `kb/<name>/COLLECTION.md.template` per collection.
`kb/<name>/COLLECTION.md.template`. Beide **binden**, sobald sie übernommen sind, und beide Both **bind** once adopted, and both belong to this instance - which is why the stack ships
gehören dieser Instanz - deshalb liefert der Stack nur die Vorlage. Die eine Entscheidung the template alone. The one decision behind them is: **in which language and in what tone
dahinter ist: **in welcher Sprache und in welchem Ton schreibt diese Instanz ihre Seiten?** does this instance write its pages?**
Ablauf: Procedure:
1. Die Collection-Contracts **und die Page-Type-Specs** übernehmen - Kopien, keine Frage an 1. Adopt the collection contracts **and the page type-specs** - copies, no question to the
den Nutzer, denn was dort steht ist als Ausgangspunkt unabhängig von der Sprache brauchbar: user, because what they say is usable as a starting point regardless of language:
```bash ```bash
for template in kb/*/COLLECTION.md.template types/*.template; do for template in kb/*/COLLECTION.md.template types/*.template; do
@@ -85,110 +85,107 @@ bereit für den ersten `Ingest`.
done done
``` ```
Die `.template`-Dateien bleiben liegen; sie sind die Vorlage für den nächsten Export. The `.template` files stay where they are; they are the source for the next export.
Unter `types/` betrifft das genau die Type-Specs mit `root: kb` - `entity`, `concept`, Under `types/` this covers exactly the type-specs with `root: kb` - `entity`, `concept`,
`source`, `comparison` - samt ihrer `.schema.yaml`. Sie beschreiben Seiten, die *diese* `source`, `comparison` - along with their `.schema.yaml`. They describe pages *this*
Instanz schreibt, also gehören sie ihr: Prosa, Template und Sprache dürfen umgeschrieben instance writes, so they belong to it: prose, template and language may all be rewritten.
werden. `instruction`, `lint-report` und `type-spec` beschreiben Stack-Artefakte und `instruction`, `lint-report` and `type-spec` describe stack artifacts and arrive
kommen unverändert. unchanged.
2. Den Nutzer nach der KB-Sprache fragen. `kb/CONVENTIONS.md.template` ist auf **Englisch** 2. Ask the user for the KB language. `kb/CONVENTIONS.md.template` defaults to **English**;
voreingestellt; [kb-profiles.md](kb-profiles.md) hält daneben ein vollständiges [kb-profiles.md](kb-profiles.md) additionally holds a complete German profile, whose full
deutsches Profil bereit, und dessen Volltext ist die `kb/CONVENTIONS.md` des Quell-Repos. text is the source repo's own `kb/CONVENTIONS.md`. The profile catalogue is a **palette,
Der Profilkatalog ist eine **Palette, kein Enum**: übernommen wird der Text *in* die not an enum**: what gets adopted is the text *into* the instance file, not a reference to
Instanzdatei, nicht ein Verweis auf den Katalog. the catalogue.
3. `kb/CONVENTIONS.md.template` nach `kb/CONVENTIONS.md` kopieren, entlang des gewählten 3. Copy `kb/CONVENTIONS.md.template` to `kb/CONVENTIONS.md`, fill it in along the chosen
Profils ausfüllen - Sprache, Abschnittsnamen, Namensformen, Ton, Beziehungslabels, profile - language, section names, naming forms, tone, relationship labels, hedging rule -
Hedging-Regel - und dabei die Sentinel-Zeile (`wikitool:template-unfilled`) entfernen. and remove the sentinel line (`wikitool:template-unfilled`) while doing so. The
Die Platzhalter in geschweiften Klammern **sind** der Fragenkatalog. placeholders in curly braces **are** the list of questions.
4. Bei einer anderen Sprache als der des Quell-Repos: `german-terminology.md` löschen oder 4. For a language other than the source repo's: delete `german-terminology.md` or replace it
durch das eigene Vokabular ersetzen - sie ist Material des deutschen Profils, nicht des with your own vocabulary - it is material belonging to the German profile, not to the
Stacks. stack.
5. Den Nutzer nach dem Anwendungsgebiet fragen und daraus einen `source_type`-Vorschlag 5. Ask the user about the subject area and derive a `source_type` proposal from it.
ableiten. [kb-profiles.md](kb-profiles.md) hält dafür zwei ausformulierte Domänenprofile [kb-profiles.md](kb-profiles.md) holds two worked domain profiles as illustration, beside
als Anschauung bereit, neben dem Wert, den dieses Repo selbst nutzt. Der Vorschlag ist ein the value this repo uses itself. The proposal is a **starting point, not a commitment** -
**Startpunkt, keine Festlegung** - zum Setup-Zeitpunkt hat der Betreiber null Quellen und at setup time the operator has zero sources and is guessing a taxonomy before having seen
rät seine Taxonomie, bevor er auch nur eine Datei gesehen hat, und das ist der a single file, which is the worst possible moment to pin an enum down. Carrying out the
schlechteste Moment, ein Enum festzuzurren. Vorschlag umgesetzt heißt: das Enum in proposal means setting the enum in `types/source.schema.yaml` **and** the matching
`types/source.schema.yaml` **und** die passende `layout:`-Zeile je Wert in `layout:` line per value in `types/source.md` in the same edit - one without the other
`types/source.md` in derselben Bearbeitung setzen - eine ohne die andere lässt einen Wert leaves a value with no target directory. The visible catch-all (`unclassified`) survives
ohne Zielverzeichnis zurück. Der sichtbare Auffangwert (`unclassified`) bleibt in jedem every proposal; it is not a dumping ground but the slot for a source whose category is not
Vorschlag erhalten; er ist kein Sammelbecken, sondern das Fach für eine Quelle, deren settled yet. Extending the list later, or emptying that slot:
Kategorie noch nicht feststeht. Die Liste später erweitern oder das Fach leeren: [evolve-subtypes.md](evolve-subtypes.md) - not part of this step, but the way there once
[evolve-subtypes.md](evolve-subtypes.md) - nicht Teil dieses Schritts, aber der Weg dahin, real material exists.
sobald echtes Material vorliegt.
**Unverändert lassen:** `fidelity` und `authority` auf `source`-Seiten. Die sind **Leave unchanged:** `fidelity` and `authority` on `source` pages. Those are stack
Stack-Vokabular, keine Instanzentscheidung - [kb-profiles.md](kb-profiles.md) sagt das im vocabulary, not an instance decision - [kb-profiles.md](kb-profiles.md) says so in the
selben Abschnitt. same section.
**Vor dem ersten Ingest entscheiden.** Die `sections:`-Namen in `kb/CONVENTIONS.md` sind die **Decide before the first ingest.** The `sections:` names in `kb/CONVENTIONS.md` are the
Überschriften, die `xref` und `cite` in jede Seite schreiben; sie danach zu ändern ist eine headings `xref` and `cite` write into every page; changing them afterwards is a migration of
Migration jeder vorhandenen Seite (`section_aliases:` trägt die alten Namen, siehe every existing page (`section_aliases:` carries the old names, see
[migrate-corpus.md](migrate-corpus.md)). [migrate-corpus.md](migrate-corpus.md)).
**Nichts davon liegt in einer Stack-Datei.** Der Compiler liest die Abschnittsnamen aus **None of this lives in a stack file.** The compiler reads the section names from
`kb/CONVENTIONS.md`; die vier Page-Type-Specs gehören ab Schritt 1 dieser Instanz. Eine `kb/CONVENTIONS.md`; the four page type-specs have belonged to this instance since step 1. An
anderssprachige Instanz übersetzt sie einfach - das ist kein lokaler Patch an etwas instance in another language simply translates them - that is no longer a local patch to
Ausgeliefertem mehr, sondern Arbeit an den eigenen Dateien, und ein Upgrade nimmt sie ihr something shipped, but work on its own files, and an upgrade does not take it away again.
nicht wieder weg.
Was der Stack von `types/` überhaupt noch verlangt, ist eine Zeile: es muss einen Type-Spec What the stack still requires of `types/` is one line: there must be a type-spec with
mit `name: source` geben, dessen Schema `raw_files` fordert. Daran hängt der gesamte `name: source` whose schema requires `raw_files`. The entire `raw/`→`kb/` provenance path
`raw/`→`kb/`-Provenance-Pfad (`sources coverage`, `[^cite-id]`-Auflösung, `kb/provenance.md`), hangs on it (`sources coverage`, `[^cite-id]` resolution, `kb/provenance.md`), and
und `docs verify` prüft genau das - nicht mehr. `docs verify` checks exactly that - no more.
Unverändert bleibt in jedem Fall die Regel, die dem Stack gehört: **jede Zeile einer Seite What stays untouched in every case is the rule the stack owns: **every line of a page is
ist Prosa oder Identifier, und nur Prosa wird übersetzt** ([kb/CONTRACT.md § Language and either prose or an identifier, and only prose is translated** ([kb/CONTRACT.md § Language and
identifiers](../kb/CONTRACT.md#language-and-identifiers)). Titel, Wikilink-Ziele, Cite-IDs, identifiers](../kb/CONTRACT.md#language-and-identifiers)). Titles, wikilink targets, cite ids,
Enum-Werte, Tags, Befehle und Pfade folgen keiner KB-Sprache. enum values, tags, commands and paths follow no KB language.
`tools/wikitool doctor` prüft das Ergebnis in Schritt 13 (`conventions`): eine fehlende `tools/wikitool doctor` checks the result in step 13 (`conventions`): a missing file is a
Datei ist ein `FAIL`, eine mit Sentinel oder ohne vollständigen `sections:`-Block ebenso. `FAIL`, and so is one carrying the sentinel or lacking a complete `sections:` block.
`docs verify` prüft zusätzlich `profile:` und `required_by_stack:` auf jedem `docs verify` additionally checks `profile:` and `required_by_stack:` on every
`COLLECTION.md`. `COLLECTION.md`.
6. **Entscheidungspunkt - Personalization.** Die Distribution bringt 6. **Decision point - personalization.** The distribution ships `USER.md.template` and
`USER.md.template` und `SOUL.md.template` mit, aber keine ausgefüllten Fassungen: wer diese `SOUL.md.template`, but no filled-in versions: who operates this instance and how it sounds
Instanz bedient und wie sie klingt, ist Eigentum genau dieser Instanz und wird nie aus dem is the property of this instance alone and is never carried over from the source repo. Both
Quell-Repo übernommen. Beide Dateien werden ab jetzt in **jeder** Session gelesen, also files are read in **every** session from now on, so they come into being here - not later,
entstehen sie hier - nicht später bei Gelegenheit. when the occasion arises.
Ablauf, für `USER.md` und `SOUL.md` je einmal: Procedure, once each for `USER.md` and `SOUL.md`:
1. Das Template lesen. Seine Abschnitte **sind** der Fragenkatalog, in der Reihenfolge, in 1. Read the template. Its sections **are** the list of questions, in the order they appear.
der sie dort stehen. 2. Interview the user along those sections - `USER.md`: name, location, time zone, primary
2. Den Nutzer entlang dieser Abschnitte befragen - `USER.md`: Name, Standort, Zeitzone, role (professional only), professional context, family/home, hobbies, technical
primäre Rolle (rein beruflich), beruflicher Kontext, Familie/Zuhause, Hobbys, environment, active projects, deliberate boundaries. `SOUL.md`: persona name, identity,
Technik-Umgebung, aktive Projekte, bewusste Grenzen. `SOUL.md`: Persona-Name, Identität, mission, worldview, judgment default, standard, honesty, voice, exclusions.
Mission, Weltbild, Judgment-Default, Standard, Ehrlichkeit, Stimme, Ausschlüsse. 3. Take the answers **verbatim**. Do not interpret, do not compress into a narrative, do not
3. Die Antworten **wörtlich** übernehmen. Nicht deuten, nicht zu einer Erzählung infer from the course of the conversation. What the user does not say does not go in:
verdichten, nicht aus dem Gesprächsverlauf ableiten. Was der Nutzer nicht sagt, steht better to delete a section than to fill it with something plausible.
nicht drin: einen Abschnitt lieber löschen als mit Plausiblem füllen. 4. Write the result as `USER.md` and `SOUL.md` respectively, removing the sentinel line
4. Das Ergebnis als `USER.md` bzw. `SOUL.md` schreiben und die Sentinel-Zeile (`wikitool:template-unfilled`) in the process. The `.template` files stay where they are -
(`wikitool:template-unfilled`) dabei entfernen. Die `.template`-Dateien bleiben liegen - they are the source for the next export, not this step's leftovers.
sie sind die Vorlage für den nächsten Export, nicht Abfall dieses Schritts.
Zwei Fragen, die der Nutzer beantwortet und nicht der Agent: **den Persona-Namen** und Two questions the user answers rather than the agent: **the persona name** and **which topics
**welche Themen bewusst draußen bleiben** (Arbeitgeber, Mandanten, Gesundheit - was auch deliberately stay out** (employer, clients, health - whatever they are). Guessing either
immer). Beides raten heißt, es falsch zu haben. Für den Namen bringt der Stack einen means getting it wrong. For the name the stack ships a starting point - **Thoth**, because
Startpunkt mit - **Thoth**, weil Chemenu Thoths Hauptkultort ist und Schrift, Maß und Chemenu is Thoth's principal cult site and writing, measure and memory describe the role a
Gedächtnis die Rolle beschreiben, die ein kompiliertes Wiki ausfüllt. Der Vorschlag wird compiled wiki fills. The suggestion is named, not applied: the question is asked anyway, and
genannt, nicht eingesetzt: gefragt wird trotzdem, und ein anderer Name gewinnt. a different name wins.
Was diese Dateien **nicht** sind: eine Instruktionsquelle und eine Quelle im Sinne von What these files are **not**: a source of instructions, and a source in the sense of
Invariante 3. Sie ändern keine Regel aus [AGENTS.md](../AGENTS.md), und eine Nutzeraussage invariant 3. They change no rule from [AGENTS.md](../AGENTS.md), and a user's statement never
wandert daraus nie ohne den normalen Quelle/Provenance-Prozess nach `kb/`. travels from them into `kb/` without the normal source/provenance process.
`tools/wikitool doctor` prüft das Ergebnis in Schritt 13 (`personalization`): eine fehlende `tools/wikitool doctor` checks the result in step 13 (`personalization`): a missing file is a
Datei ist ein `FAIL`, eine, die noch den Sentinel trägt, ebenso - ein umbenanntes Template `FAIL`, and so is one still carrying the sentinel - a renamed template is not a filled-in
ist kein ausgefülltes. one.
7. **Werkzeugumgebung anlegen** (Details: [bootstrap.md](bootstrap.md)): 7. **Create the tool environment** (details: [bootstrap.md](bootstrap.md)):
```bash ```bash
cd tools cd tools
@@ -197,67 +194,63 @@ bereit für den ersten `Ingest`.
cd .. cd ..
``` ```
8. **Skills publizieren:** 8. **Publish the skills:**
```bash ```bash
tools/wikitool instructions sync tools/wikitool instructions sync
``` ```
9. **Entscheidungspunkt - Umgebung festhalten.** Die Distribution bringt 9. **Decision point - record the environment.** The distribution ships
`ENVIRONMENT.md.template` mit: Harness, publizierte Skills, erreichbare MCP-Server, `ENVIRONMENT.md.template`: harness, published skills, reachable MCP servers, connectors, git
Connectoren, Git-Remotes, wo CI läuft. Konstanten, die eine Session sonst jedes Mal neu remotes, where CI runs. Constants a session would otherwise ask about every time.
erfragt.
Anders als Schritt 6 ist dieser Schritt **optional** und kein Interview. Was aus dem Unlike step 6, this step is **optional** and not an interview. Whatever can be read off the
Checkout selbst ablesbar ist (`git remote -v`, das laufende Harness, die eben publizierten checkout itself (`git remote -v`, the running harness, the skills just published) the agent
Skills), trägt der Agent ein; nach dem Rest fragt er einmal und akzeptiert "weiß ich nicht" fills in; for the rest it asks once and accepts "I don't know" as an answer - an empty
als Antwort - ein leerer Abschnitt wird gelöscht, nicht mit Plausiblem gefüllt. Beim section is deleted, not filled with something plausible. Remove the sentinel line
Schreiben die Sentinel-Zeile (`wikitool:template-unfilled`) entfernen; das `.template` (`wikitool:template-unfilled`) when writing; the `.template` stays where it is.
bleibt liegen.
Wird der Schritt übersprungen, läuft alles weiter: `doctor` meldet in Schritt 13 If the step is skipped, everything still works: `doctor` reports
`environment: absent (optional)`, kein `FAIL`. Die Datei ist gitignored und geht in keinen `environment: absent (optional)` in step 13, not a `FAIL`. The file is gitignored and enters
Commit ein - sie beschreibt diesen Checkout, nicht das Repo. no commit - it describes this checkout, not the repo.
10. **Entscheidungspunkt - Telemetrie.** Der Default hängt am Installationsweg, nicht an 10. **Decision point - telemetry.** The default follows the installation path, not this step: an
diesem Schritt: eine per `dist export` ausgelieferte Instanz - jede, die hier ankommt, ohne instance delivered via `dist export` - every instance that arrives here without having taken
Weg C (direkter Klon des Ursprungs-Repos) genommen zu haben - trägt eine route C (a direct clone of the origin repo) - carries a `.wikitool-release.json` and starts
`.wikitool-release.json` und startet mit Telemetrie **aus**; niemand hat sie bestellt, und with telemetry **off**; nobody asked for it, and nobody reads `EVALS.md` before the first
`EVALS.md` liest ohnehin niemand, bevor die erste Datei geschrieben ist. Dieser Schritt file is written anyway. This step only asks whether the operator wants to reverse that.
fragt nur, ob der Betreiber das umdrehen will.
Den Nutzer einmal fragen: Telemetrie an? Falls ja, `.wikitool-telemetry.json` im Ask the user once: telemetry on? If yes, create `.wikitool-telemetry.json` in the repo root
Repo-Root anlegen (pro Checkout, gitignored, kein `.template` - wie (per checkout, gitignored, no `.template` - like `.wikitool-remotes.json`):
`.wikitool-remotes.json`):
```json ```json
{ "enabled": true } { "enabled": true }
``` ```
`max_session_bytes` (Default 5 MiB) und `keep_sessions` (Default 250) sind optional in `max_session_bytes` (default 5 MiB) and `keep_sessions` (default 250) are optional in the
derselben Datei; die meisten Instanzen brauchen sie nicht anzufassen. Falls nein, nichts same file; most instances need not touch them. If no, do nothing - the default is already
tun - der Default steht bereits auf aus, und keine Datei entsteht. `WIKI_TRACE` off, and no file is created. `WIKI_TRACE` still overrides in both directions, should a
überschreibt beide Richtungen weiterhin, falls eine einzelne Session abweichen soll. single session need to differ.
`tools/wikitool doctor` meldet das Ergebnis in Schritt 13 (`telemetry`): an/aus, warum `tools/wikitool doctor` reports the result in step 13 (`telemetry`): on/off, why
(Installationsform, diese Datei, oder `WIKI_TRACE`), und die aktuelle Menge gegen beide (installation form, this file, or `WIKI_TRACE`), and the current volume against both caps -
Deckel - nie ein `FAIL`, in beide Richtungen ist das ein gültiger Zustand. Mehr dazu: never a `FAIL`, since both directions are a valid state. More on this:
[EVALS.md](../EVALS.md) § "Whether it runs at all". [EVALS.md](../EVALS.md) § "Whether it runs at all".
11. **Session-Budget scopen** (Details: [session-setup.md](session-setup.md)): 11. **Scope the session budget** (details: [session-setup.md](session-setup.md)):
```bash ```bash
export WIKITOOL_SESSION_ID="wiki-$(date +%s)" export WIKITOOL_SESSION_ID="wiki-$(date +%s)"
``` ```
12. **Generierte Indizes erzeugen** - `dist export` liefert sie bewusst nicht mit: 12. **Build the generated indexes** - `dist export` deliberately does not ship them:
```bash ```bash
tools/wikitool index rebuild tools/wikitool index rebuild
tools/wikitool sources rebuild-index tools/wikitool sources rebuild-index
``` ```
13. **Verifizieren**, in dieser Reihenfolge: 13. **Verify**, in this order:
```bash ```bash
tools/wikitool doctor tools/wikitool doctor
@@ -266,32 +259,32 @@ bereit für den ersten `Ingest`.
tools/wikitool lint tools/wikitool lint
``` ```
`doctor` muss ohne `FAIL` durchlaufen, bevor es weitergeht - ein `WARN` (z. B. kein Remote, `doctor` must run through without a `FAIL` before anything continues - a `WARN` (no remote,
keine `WIKITOOL_SESSION_ID`) ist kein Blocker. Ein `FAIL` benennt sein eigenes Fix-Kommando; no `WIKITOOL_SESSION_ID`, say) is not a blocker. A `FAIL` names its own fix command; run it
das ausführen und `doctor` erneut aufrufen. and call `doctor` again.
14. **Ersten Commit anstoßen:** 14. **Make the first commit:**
```bash ```bash
tools/wikitool publish --message "chore: initial instance setup" tools/wikitool publish --message "chore: initial instance setup"
``` ```
Das Mass-Update-Gate greift hier erwartungsgemäß: eine frische Distribution besteht aus weit The Mass-Update Gate fires here as expected: a fresh distribution consists of far more than
mehr als den zehn gezählten Dateien, die den Schwellwert auslösen, also endet der Aufruf mit the ten counted files that trip the threshold, so the call ends with exit code 42. Show the
Exit-Code 42. Die Ausgabe dem Nutzer **vollständig zeigen** und warten; sie enthält die output to the user **in full** and wait; it contains the file list and the exact
Dateiliste und die exakte `--confirm <token>`-Zeile, die nach seiner Freigabe `--confirm <token>` line that publishes once they approve. Details on the gate:
veröffentlicht. Details zum Gate: [gates.md](gates.md). [gates.md](gates.md).
15. **Agent-Session neu starten.** Harnesses lesen die Skill-Verzeichnisse beim Start; erst 15. **Restart the agent session.** Harnesses read the skill directories at startup; only
danach sind `wiki-ingest`, `wiki-query`, `wiki-manage`, `wiki-lint` und `wiki-status` afterwards are `wiki-ingest`, `wiki-query`, `wiki-manage`, `wiki-lint` and `wiki-status`
verfügbar. available.
## Scope ## Scope
Gilt nur für eine per `dist export` erzeugte, leere Distribution. Für einen bestehenden Clone Applies only to an empty distribution produced by `dist export`. For an existing clone of this
dieses Quell-Repos siehe [bootstrap.md](bootstrap.md) - dort existieren Git-Repo, Autor und source repo see [bootstrap.md](bootstrap.md) - there the git repo, author and content already
Inhalt bereits, und nur die Werkzeugumgebung (Schritt 7) plus die Skills (Schritt 8) fehlen. exist, and only the tool environment (step 7) plus the skills (step 8) are missing.
Eine Ausnahme: Schritt 6 (Personalization) gilt auch für einen bestehenden Clone, der noch One exception: step 6 (personalization) also applies to an existing clone that has no
kein `USER.md`/`SOUL.md` hat - dort als einzelner nachgeholter Schritt, nicht als ganzer `USER.md`/`SOUL.md` yet - there as a single catch-up step, not as a whole procedure.
Ablauf. `bootstrap.md` verweist dafür hierher. `bootstrap.md` points here for it.
+12 -5
View File
@@ -40,11 +40,18 @@ those regions and nothing else. Nothing matches on this text.
## Language ## Language
Pages are written in **German**. This binds `kb/` and the authoring surface that shapes it - Pages are written in **German** - the `language:` in this file's own frontmatter, and the one
the page type-specs `types/entity.md`, `types/concept.md`, `types/source.md` and place that value is written down. This binds `kb/`, and inside the page type-specs
`types/comparison.md`. `raw/` is untouched ([raw/CONTRACT.md](../raw/CONTRACT.md)), and the (`types/entity.md`, `types/concept.md`, `types/source.md`, `types/comparison.md`) exactly the
control plane stays English: `AGENTS.md`, the stage contracts, this file, `instructions/`, and parts that become page text: each one's `## Template` block, and the `layout:` titles that head a
the type-specs for non-page artifacts. catalog section. Their authoring guidance around those is instruction to an agent, so it follows
the control plane and stays English - the same prose/identifier cut
[kb/CONTRACT.md](CONTRACT.md#language-and-identifiers) makes inside a page, applied one level up.
`raw/` is untouched ([raw/CONTRACT.md](../raw/CONTRACT.md)).
Two things follow from that value rather than being decided here, both stated once in
[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.
Which line is prose and which is an identifier - and therefore what is translated at all - is 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 the contract's rule, not this file's: see
+14 -6
View File
@@ -27,12 +27,20 @@ marker pair, so a rename re-renders words and nothing else.
## Language ## Language
Pages are written in **{language}**. This binds `kb/` and the authoring surface that shapes it - Pages are written in **{language}** - the `language:` in this file's own frontmatter, and the
the page type-specs `types/entity.md`, `types/concept.md`, `types/source.md` and one place that value is written down. This binds `kb/`, and inside the page type-specs
`types/comparison.md`, whose `## Template` blocks are the body skeleton every new page starts (`types/entity.md`, `types/concept.md`, `types/source.md`, `types/comparison.md`) exactly the
from. `raw/` is untouched ([raw/CONTRACT.md](../raw/CONTRACT.md)), and the control plane stays parts that become page text: each one's `## Template` block - the body skeleton every new page
English: `AGENTS.md`, the stage contracts, this file, `instructions/`, and the type-specs for starts from - and the `layout:` titles that head a catalog section. Their authoring guidance
non-page artifacts. around those is instruction to an agent, so it follows the control plane and stays English - the
same prose/identifier cut [kb/CONTRACT.md](CONTRACT.md#language-and-identifiers) makes inside a
page, applied one level up. Adopting this template into a non-English instance therefore means
translating those blocks, not the whole file. `raw/` is untouched
([raw/CONTRACT.md](../raw/CONTRACT.md)).
Two things follow from that value rather than being decided here, both stated once in
[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.
Which line is prose and which is an identifier - and therefore what is translated at all - is 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 the contract's rule, not this file's: see
+21 -22
View File
@@ -35,32 +35,31 @@ tone, relationship labels, the confidence rubric. Neither is restated here.
## Types offered ## Types offered
`concept` (`tools/wikitool types describe concept`). Das Feld `concept_type:` `concept` (`tools/wikitool types describe concept`). The `concept_type:` field
wählt die Area: picks the area:
| Area | Hält | | Area | Holds |
|------|------| |------|-------|
| `architectures/` | Aufbau und Struktur: wie ein System geschnitten ist und warum die Schnitte dort liegen | | `architectures/` | Shape and structure: how a system is cut up, and why the cuts fall where they do |
| `patterns/` | Wiederverwendbare Lösungsformen, die über mehr als einen Gegenstand hinweg gelten | | `patterns/` | Reusable solution shapes that hold across more than one subject |
| `protocols/` | Kommunikationsprotokolle und Standards, in ihrer üblichen Schreibweise benannt | | `protocols/` | Communication protocols and standards, named in their usual spelling |
| `workflows/` | Abläufe und Prozesse, die projektübergreifend wiederkehren | | `workflows/` | Procedures and processes that recur across projects |
| `decisions/` | Architektur- und Entwurfsentscheidungen (siehe unten) | | `decisions/` | Architectural and design decisions (see below) |
| `problems/` | Wiederkehrende Problemstellungen und ihre Lösungsansätze | | `problems/` | Recurring problems and the approaches taken to them |
Das sind Areas, keine Collections: sie erben diesen Contract und tragen keine These are areas, not collections: they inherit this contract and carry no
eigene `COLLECTION.md`. `COLLECTION.md` of their own.
Die Zuordnung trifft niemand von Hand — sie steht als `layout:` in Nobody assigns them by hand — the mapping is the `layout:` in
`types/concept.md`, und `wikitool new` legt eine neue Seite direkt dort ab. `types/concept.md`, and `wikitool new` puts a new page straight there. A page
Eine Seite, die anderswo liegt, meldet `wikitool lint` als *misplaced*; sitting anywhere else is reported by `wikitool lint` as *misplaced*;
`wikitool move --page "<Titel>"` bringt sie an ihren berechneten Ort. `wikitool move --page "<title>"` moves it to its computed location.
Die Aufteilung ist keine Geschmacksfrage, sondern das, was die Shard-Schwelle The split is not a matter of taste but what makes the catalog's shard threshold
des Katalogs überhaupt wirksam macht: `index rebuild` teilt **pro Area**, und effective at all: `index rebuild` splits **per area**, and a collection without
eine Collection ohne Areas teilt sich niemit 80 Seiten in einer einzigen areas never splitswith 80 pages in a single table the threshold was a dead
Tabelle war die Schwelle hier ein toter Wert. Keine der sechs value here. None of the six areas is currently above it, so none gets a shard of
Areas liegt derzeit über der Schwelle, also bekommt auch keine einen eigenen its own; when one grows into it, that happens without anyone acting.
Shard; wächst eine hinein, passiert das ohne Zutun.
## Decisions ## Decisions
+26 -28
View File
@@ -26,38 +26,36 @@ renamed or dropped - its authoring rules below are the instance's, its existence
## Types offered ## Types offered
`source` (`tools/wikitool types describe source`). Page titles carry the `Source - ` prefix, `source` (`tools/wikitool types describe source`). Page titles carry the `Source - ` prefix,
applied automatically by `wikitool new source`. Das Feld `source_type:` wählt die Area - **ohne applied automatically by `wikitool new source`. The `source_type:` field picks the area - **with
Default**: `wikitool new source` verweigert ohne einen expliziten Wert. no default**: `wikitool new source` refuses without an explicit value.
| Area | Hält | | Area | Holds |
|------|------| |------|-------|
| `transcripts/` | Session-Transkripte: mitgeschriebener Dialog zwischen Mensch und Agent, oder zwischen Menschen | | `transcripts/` | Session transcripts: recorded dialogue between a human and an agent, or between humans |
| `analyses/` | Analyse-Output eines Modells über einen Gegenstand - kein Dialog, kein Protokoll, sondern eine eigenständige Einschätzung | | `analyses/` | A model's analytical output about a subject - not dialogue, not a record, but an assessment in its own right |
| `articles/` | Externe Artikel und Blogposts, mit `source_url:` | | `articles/` | External articles and blog posts, with `source_url:` |
| `documents/` | Eingelesene Dokumente, Handbücher, Spezifikationen | | `documents/` | Ingested documents, manuals, specifications |
| `notes/` | Echte eigene Notizen ohne Dialogform - Cheat Sheets, Merkzettel | | `notes/` | Genuinely own notes in no dialogue form - cheat sheets, reminders |
| `trackers/` | Exporte aus einem Issue-Tracker oder vergleichbaren System | | `trackers/` | Exports from an issue tracker or comparable system |
| `unclassified/` | Sichtbares Fach für eine Quelle, deren Kategorie noch nicht feststeht - beratender `lint`-Befund, kein Sammelbecken. Es wieder zu leeren, oder das Enum um einen neuen Wert zu erweitern: [instructions/evolve-subtypes.md](../../instructions/evolve-subtypes.md) | | `unclassified/` | The visible slot for a source whose category is not settled yet - an advisory `lint` finding, not a dumping ground. Emptying it again, or extending the enum by a new value: [instructions/evolve-subtypes.md](../../instructions/evolve-subtypes.md) |
Das sind Areas, keine Collections: sie erben diesen Contract und tragen keine eigene These are areas, not collections: they inherit this contract and carry no `COLLECTION.md` of
`COLLECTION.md`. Die Zuordnung trifft niemand von Hand - sie steht als `layout:` in their own. Nobody assigns them by hand - the mapping is the `layout:` in `types/source.md`, and
`types/source.md`, und `wikitool new` legt eine neue Seite direkt dort ab. Eine Seite, die `wikitool new` puts a new page straight there. A page sitting anywhere else is reported by
anderswo liegt, meldet `wikitool lint` als *misplaced*; `wikitool move --page "<Titel>"` bringt `wikitool lint` as *misplaced*; `wikitool move --page "<title>"` moves it to its computed
sie an ihren berechneten Ort. location.
**`analysis` gegen `document`:** die Unterscheidung läuft über die Autorschaft, nicht über den **`analysis` versus `document`:** the distinction runs on authorship, not on content. A model
Inhalt. Ein Modell, das über einen Gegenstand urteilt oder ihn zusammenfasst, ohne dass ein judging or summarizing a subject with no human or organization answering for it is `analysis` -
Mensch oder eine Organisation dafür geradesteht, ist `analysis` - unabhängig davon, wie however article-shaped the text looks. A manual, a specification, a vendor document is
artikelförmig der Text wirkt. Ein Handbuch, eine Spezifikation, eine Herstellerdoku ist `document`, even where a tool generated it, as long as an organization is accountable for what
`document`, auch wenn ein Werkzeug sie generiert hat, solange eine Organisation die Aussage it says. The question is "who is liable for the claim", not "how does the text read".
verantwortet. Die Frage ist also "wer haftet für die Behauptung", nicht "wie liest sich der
Text".
Solange es diesen Default noch gab, fiel fast alles hierher in `notes/`, weil While that default still existed, nearly everything landed here in `notes/`, because
`types/source.schema.yaml` `notes` als `default:` gesetzt hatte - der Compiler wählte das `types/source.schema.yaml` had `notes` as its `default:` - the compiler picked the dumping
Sammelbecken, sobald niemand widersprach. ground whenever nobody objected. 22 of the 29 pages there at the time were in fact transcripts,
22 der 29 damaligen Seiten waren tatsächlich Transkripte, Analysen oder Tracker-Exporte und analyses or tracker exports, and were reclassified with `wikitool touch --set source_type=…`
wurden per `wikitool touch --set source_type=…` umklassifiziert, bevor die Areas entstanden. before the areas existed.
## Provenance rules ## Provenance rules
+3 -3
View File
@@ -166,12 +166,12 @@ tools/wikitool <command> --help
| Command | Purpose | | Command | Purpose |
|---------|---------| |---------|---------|
| `types list [--json]` | List every type-spec under `types/` (name, schema path, subtype field, description) - discover what page types exist without reading `types/*.md` directly | | `types list [--json]` | List every type-spec under `types/` (name, schema path, subtype field, description) - discover what page types exist without reading `types/*.md` directly |
| `types describe <name> [--json]` | Print one type's full contract: required/optional frontmatter fields with enums, its subtype field (if any), and its authoring body | | `types describe <name> [--json]` | Print one type's full contract: required/optional frontmatter fields with enums, its subtype field (if any), and its authoring body. A type-spec over the `docs toc` threshold carries a generated table-of-contents region; it is stripped from this output rather than echoed, since the whole body is being handed over and a navigation aid into it would be noise |
| `instructions sync [--force]` | Publish every `instructions/<name>/SKILL.md` into `.agents/skills/` and `.claude/skills/` as **copies**, and delete published skills whose source is gone. Both targets are gitignored, so a fresh clone runs this once - see `instructions/bootstrap.md`. Re-running is also how a drifted copy is repaired: the source always wins. `--force` is required only to replace a target directory that is not a published skill at all (no `SKILL.md` in it) | | `instructions sync [--force]` | Publish every `instructions/<name>/SKILL.md` into `.agents/skills/` and `.claude/skills/` as **copies**, and delete published skills whose source is gone. Both targets are gitignored, so a fresh clone runs this once - see `instructions/bootstrap.md`. Re-running is also how a drifted copy is repaired: the source always wins. `--force` is required only to replace a target directory that is not a published skill at all (no `SKILL.md` in it) |
| `instructions verify` | Check the instruction layer: flat instructions validate against `types/instruction.schema.yaml`, each `SKILL.md` carries the frontmatter its harness reads, no `SKILL.md` carries a relative markdown link (`sync` copies it to a different depth than the source, so a `SKILL.md` references a target as a repo-root-relative plain path instead - see [instructions/CONTRACT.md](../instructions/CONTRACT.md) § "A skill's outbound reference is a plain path, not a link"), every published copy is byte-identical to its source, no instruction is left that nothing references, and nothing under `instructions/dev/` is referenced from outside it (a `<!-- dist:strip-start/end -->` block is exempt - see [instructions/CONTRACT.md](../instructions/CONTRACT.md)). Missing *every* copy is reported as "run sync", not as drift - that is a clean checkout | | `instructions verify` | Check the instruction layer: flat instructions validate against `types/instruction.schema.yaml`, each `SKILL.md` carries the frontmatter its harness reads, no `SKILL.md` carries a relative markdown link (`sync` copies it to a different depth than the source, so a `SKILL.md` references a target as a repo-root-relative plain path instead - see [instructions/CONTRACT.md](../instructions/CONTRACT.md) § "A skill's outbound reference is a plain path, not a link"), every published copy is byte-identical to its source, no instruction is left that nothing references, and nothing under `instructions/dev/` is referenced from outside it (a `<!-- dist:strip-start/end -->` block is exempt - see [instructions/CONTRACT.md](../instructions/CONTRACT.md)). Missing *every* copy is reported as "run sync", not as drift - that is a clean checkout |
| `instructions list [--json]` | List the flat instructions with their descriptions. This is how the layer is discovered; `search` deliberately covers `kb/` only | | `instructions list [--json]` | List the flat instructions with their descriptions. This is how the layer is discovered; `search` deliberately covers `kb/` only |
| `docs verify` | Check the docs that mirror the code: every CLI command documented in this file's own § Commands table and, separately, in its § Error contracts table (both directions, checked per table, so a row dropped from one is not hidden by the same name surviving in the other, and only a name's presence in a row is checked, never the rest of that row's text), every directory under `kb/` has a `COLLECTION.md` and no directory outside it does, every collection declaring `profile:` and a `required_by_stack:` that agrees with the stack's own list, `kb/CONVENTIONS.md` naming all three tool-owned section headings if it exists at all, every stage contract present, no pre-migration `type: entity` blocks left in the contracts, the `.gitignore` canaries clear in both directions (nothing ignored under `raw/`/`kb/`, `incoming/` ignored, everything ignored under `reports/` and the published skill directories), and no `.md`/`.template` file `dist export` would ship citing an issue number - the tracker exists only in the origin repo, so such a number in a distributed instance is a reference its reader can neither resolve nor recognise as unresolvable (a `<!-- dist:strip-start/end -->` region is exempt: it is already gone from the text the check reads, which is the export plan's, not the working tree's), every reference file `docs toc` covers carrying the current table-of-contents region for its own headings - missing and stale are one check, because the generator is idempotent - and every relative markdown link in one of those same reference files resolving to a file that actually exists (a target's `#anchor` suffix is stripped first; code fences and inline code spans are masked before scanning, so a passage showing link syntax as an example is not mistaken for a real reference). The name is about documentation parity, not about the `docs/` directory - it neither reads nor requires one, the same way `kb/` predates the collection it now checks | | `docs verify` | Check the docs that mirror the code: every CLI command documented in this file's own § Commands table and, separately, in its § Error contracts table (both directions, checked per table, so a row dropped from one is not hidden by the same name surviving in the other, and only a name's presence in a row is checked, never the rest of that row's text), every directory under `kb/` has a `COLLECTION.md` and no directory outside it does, every collection declaring `profile:` and a `required_by_stack:` that agrees with the stack's own list, `kb/CONVENTIONS.md` naming all three tool-owned section headings if it exists at all, every stage contract present, no pre-migration `type: entity` blocks left in the contracts, the `.gitignore` canaries clear in both directions (nothing ignored under `raw/`/`kb/`, `incoming/` ignored, everything ignored under `reports/` and the published skill directories), and no `.md`/`.template` file `dist export` would ship citing an issue number - the tracker exists only in the origin repo, so such a number in a distributed instance is a reference its reader can neither resolve nor recognise as unresolvable (a `<!-- dist:strip-start/end -->` region is exempt: it is already gone from the text the check reads, which is the export plan's, not the working tree's), every reference file `docs toc` covers carrying the current table-of-contents region for its own headings - missing and stale are one check, because the generator is idempotent - and every relative markdown link in one of those same reference files resolving to a file that actually exists (a target's `#anchor` suffix is stripped first; code fences and inline code spans are masked before scanning, so a passage showing link syntax as an example is not mistaken for a real reference). The name is about documentation parity, not about the `docs/` directory - it neither reads nor requires one, the same way `kb/` predates the collection it now checks |
| `docs toc [--apply]` | Create, refresh or remove the generated table-of-contents region (`<!-- wikitool:toc -->` ... `<!-- /wikitool:toc -->`, placed after the title and before the first `##`) on every reference file over 100 lines that `AGENTS.md`, a stage/collection contract, or the flat `instructions/**.md` form covers - the scope Anthropic's skill-authoring guidance names for a file previewed rather than read in full. Dry-run by default (prints which files would change); `--apply` writes. `docs verify` checks the result stays current the same way it checks every other generated-from-code copy | | `docs toc [--apply]` | Create, refresh or remove the generated table-of-contents region (`<!-- wikitool:toc -->` ... `<!-- /wikitool:toc -->`, placed after the title and before the first `##`) on every reference file over 100 lines, in the scope Anthropic's skill-authoring guidance names for a file previewed rather than read in full: `AGENTS.md`, every stage contract, `kb/CONVENTIONS.md`, every `kb/*/COLLECTION.md`, every flat `instructions/**.md` file, every `types/*.md` type-spec, and every `docs/` page. Computed from those categories rather than listed, so a file added later is in scope without a code change. `SKILL.md` is the one exception, and the same guidance is why: it places a skill body on the loading level that is read whole when the skill triggers, and aims its own TOC advice at the bundled reference files a skill points *at*. Human docs (`README.md`, `CHANGES.md`, `EVALS.md`, `INSTALL.md`, `tools/README.md`) are out of scope because AGENTS.md § File naming says no agent loads them as instruction. Dry-run by default (prints which files would change); `--apply` writes. `docs verify` checks the result stays current the same way it checks every other generated-from-code copy |
### Telemetry ### Telemetry
@@ -189,7 +189,7 @@ tools/wikitool <command> --help
| `version show [--json]` | Print this instance's stack version and where it came from (development tree, or a distribution with its export date and origin). Bare `wikitool version` is an alias for this. Read-only, offline, and **exempt from the Iteration Budget Gate** | | `version show [--json]` | Print this instance's stack version and where it came from (development tree, or a distribution with its export date and origin). Bare `wikitool version` is an alias for this. Read-only, offline, and **exempt from the Iteration Budget Gate** |
| `version check [--url U] [--timeout S] [--json]` | Ask the origin's release feed whether a newer stack exists, and whether the step crosses a compatibility boundary (`state: current\|update\|migration\|ahead`). **The only command in `wikitool` that makes a network call** - never reached implicitly from another command, needs no key, times out, and reports an unreachable feed as an error rather than as "up to date". The feed is `$WIKITOOL_UPDATE_URL`, else the release stamp's, else the built-in origin; `$WIKITOOL_UPDATE_TOKEN` is only needed if that feed is not readable anonymously. Read-only and exempt from the budget gate | | `version check [--url U] [--timeout S] [--json]` | Ask the origin's release feed whether a newer stack exists, and whether the step crosses a compatibility boundary (`state: current\|update\|migration\|ahead`). **The only command in `wikitool` that makes a network call** - never reached implicitly from another command, needs no key, times out, and reports an unreachable feed as an error rather than as "up to date". The feed is `$WIKITOOL_UPDATE_URL`, else the release stamp's, else the built-in origin; `$WIKITOOL_UPDATE_TOKEN` is only needed if that feed is not readable anonymously. Read-only and exempt from the budget gate |
| `version notes [--version X.Y.Z]` | Print one version's `CHANGES.md` entry, for use as release notes (default: this tree's `VERSION`). Read-only and exempt from the budget gate | | `version notes [--version X.Y.Z]` | Print one version's `CHANGES.md` entry, for use as release notes (default: this tree's `VERSION`). Read-only and exempt from the budget gate |
| `version bump --major\|--minor\|--patch --title "<...>" [--impact high\|medium\|low] [--breaking "<what breaks>"] [--no-migration "<reason>"] [--migration-required] [--dry-run]` | Raise or continue the **one running candidate** between two releases - `VERSION` gets a `-beta.N` suffix, never a second fresh number per bump. `--major/--minor/--patch` is **max-wins escalation** against the last release (patch < minor < major): a `--patch` on a MINOR candidate only advances `N`, and escalation never steps back down. Opens the matching `CHANGES.md` entry on the first bump of a candidate (heading, date, author, and a machine-managed `<!-- wikitool:bumps -->` list of every `--title` collected so far, graded by `--impact`, default `medium`) and updates that same entry in place on every later bump of the same candidate - one entry per candidate, not one per bump. The list renders grouped under `**High/Medium/Low impact**` headings (empty groups omitted), except when every bump so far is `medium`, where it stays the flat, ungrouped list the region always had - `version regrade` corrects a grade after the fact. Refuses more or fewer than one part, an empty title, an unknown `--impact`, and a `VERSION`/newest-changelog-entry mismatch. Compatibility follows the **leftmost non-zero component** of the candidate's base, which for this stack (at `1.0.0` and up) means MAJOR: PATCH is a fix, MINOR a compatible capability, MAJOR a version that is **not a drop-in replacement** - any hand-work on update, or a downgrade that no longer works. Whether content must be migrated is a second, independent question. The bump that first escalates a candidate past the boundary requires `--breaking "<what stops working>"` and, on top of it, a migration document targeting the candidate's base or `--no-migration "<reason>"`; both lines are written once, anchored just above the bump list, and persist over later bumps of the same candidate without being repeated, and both are refused on a bump that crosses nothing at all. A later bump of the same candidate that finds out `--no-migration` was wrong after all retracts that line with `--migration-required` instead of restating `--no-migration` - refused without a migration document already targeting the new base, and without an existing `--no-migration` line to retract. Which part a change earns stays a judgment call: the command enforces that a crossing documents itself, never that the part was chosen correctly | | `version bump --major\|--minor\|--patch --title "<...>" [--impact high\|medium\|low] [--breaking "<what breaks>"] [--no-migration "<reason>"] [--migration-required] [--dry-run]` | Raise or continue the **one running candidate** between two releases - `VERSION` gets a `-beta.N` suffix, never a second fresh number per bump. `--major/--minor/--patch` is **max-wins escalation** against the last release (patch < minor < major): a `--patch` on a MINOR candidate only advances `N`, and escalation never steps back down. Opens the matching `CHANGES.md` entry on the first bump of a candidate (heading, date, author, and a machine-managed `<!-- wikitool:bumps -->` list of every `--title` collected so far, graded by `--impact`, default `medium`) and updates that same entry in place on every later bump of the same candidate - one entry per candidate, not one per bump. The list renders grouped under `**High/Medium/Low impact**` headings (empty groups omitted), except when every bump so far is `medium`, where it stays the flat, ungrouped list the region always had - `version regrade` corrects a grade after the fact. Refuses more or fewer than one part, an empty title, an unknown `--impact`, and a `VERSION`/newest-changelog-entry mismatch. Compatibility follows the **leftmost non-zero component** of the candidate's base, which for this stack (at `1.0.0` and up) means MAJOR: PATCH is a fix, MINOR a compatible capability, MAJOR a version that is **not a drop-in replacement** - any hand-work on update, or a downgrade that no longer works. Whether content must be migrated is a second, independent question. The bump that first escalates a candidate past the boundary requires `--breaking "<what stops working>"` and, on top of it, a migration document targeting the candidate's base or `--no-migration "<reason>"`; both are anchored just above the bump list, persist over later bumps of the same candidate without being repeated, and are refused on a bump that crosses nothing at all. The two then behave differently on a *second* crossing, because they answer different questions: a further `--breaking` **joins** the ones already recorded (one reason per crossing - rendered flat on the marker line while there is only one, as bullets under a bare marker from the second onward, and repeating a reason verbatim is a no-op), while a further `--no-migration` **replaces** the single line that says whether content has to change. A candidate crossing the boundary twice is the normal shape of a long-running one, and each crossing is a separate thing an operator has to act on; whether content migrates stays one yes/no about the candidate as a whole. There is deliberately no retraction path for a single accumulated `--breaking` reason - `--migration-required` retracts the migration line, and nothing retracts a breaking one. A later bump of the same candidate that finds out `--no-migration` was wrong after all retracts that line with `--migration-required` instead of restating `--no-migration` - refused without a migration document already targeting the new base, and without an existing `--no-migration` line to retract. Which part a change earns stays a judgment call: the command enforces that a crossing documents itself, never that the part was chosen correctly |
| `version regrade [INDICES...] [--impact high\|medium\|low]` | List the running candidate's bump titles with their impact grade and 1-based rendered position (no arguments - the correction path for a `--impact` judgement made at bump time), or change one or more of them in a single call: `version regrade 3 7 --impact high` grades both against a single read of today's list, not position 3 first and then position 7 against whatever that produced. Touches only the topmost entry's bump list - never `VERSION`, never any other part of `CHANGES.md`. The bare listing is read-only and exempt from the Iteration Budget Gate, like `version notes`; a call with indices writes `CHANGES.md` and is counted like `version bump`. Refuses an index outside the rendered list's range, an unknown `--impact`, indices given without `--impact`, a missing `VERSION`/`CHANGES.md`, a `VERSION`/newest-changelog-entry mismatch, or a topmost entry with no bump list at all | | `version regrade [INDICES...] [--impact high\|medium\|low]` | List the running candidate's bump titles with their impact grade and 1-based rendered position (no arguments - the correction path for a `--impact` judgement made at bump time), or change one or more of them in a single call: `version regrade 3 7 --impact high` grades both against a single read of today's list, not position 3 first and then position 7 against whatever that produced. Touches only the topmost entry's bump list - never `VERSION`, never any other part of `CHANGES.md`. The bare listing is read-only and exempt from the Iteration Budget Gate, like `version notes`; a call with indices writes `CHANGES.md` and is counted like `version bump`. Refuses an index outside the rendered list's range, an unknown `--impact`, indices given without `--impact`, a missing `VERSION`/`CHANGES.md`, a `VERSION`/newest-changelog-entry mismatch, or a topmost entry with no bump list at all |
| `version release [--title "<...>"] [--dry-run]` | Fix the running candidate: strip `VERSION`'s `-beta.N` suffix and close its `CHANGES.md` entry, ending the pre-release phase `version bump` started. Without `--title` the heading keeps whichever bump last set it; with it, the heading's title is replaced - the normal case for a candidate that collected several bump titles, since the entry wants a summarising heading rather than the most recent one. Leaves the entry's machine-managed bump-title list untouched, as the record of what happened. Refuses when the candidate collected two or more bumps and the entry still carries no summary paragraph (at least 200 non-whitespace characters) between the bump list and the first `### <bump title>` changeset heading; a candidate with exactly one bump is exempt, since there its own changeset already is the summary. `--dry-run` runs this check too and reports the same refusal. Commits nothing and pushes nothing (invariant 5) - the following `publish` moves `VERSION` onto `main`, which `release.yml` reacts to. Refuses when `VERSION` is already a release (no running candidate to fix), or when the changelog's newest entry does not match `VERSION` | | `version release [--title "<...>"] [--dry-run]` | Fix the running candidate: strip `VERSION`'s `-beta.N` suffix and close its `CHANGES.md` entry, ending the pre-release phase `version bump` started. Without `--title` the heading keeps whichever bump last set it; with it, the heading's title is replaced - the normal case for a candidate that collected several bump titles, since the entry wants a summarising heading rather than the most recent one. Leaves the entry's machine-managed bump-title list untouched, as the record of what happened. Refuses when the candidate collected two or more bumps and the entry still carries no summary paragraph (at least 200 non-whitespace characters) between the bump list and the first `### <bump title>` changeset heading; a candidate with exactly one bump is exempt, since there its own changeset already is the summary. `--dry-run` runs this check too and reports the same refusal. Commits nothing and pushes nothing (invariant 5) - the following `publish` moves `VERSION` onto `main`, which `release.yml` reacts to. Refuses when `VERSION` is already a release (no running candidate to fix), or when the changelog's newest entry does not match `VERSION` |
+7 -1
View File
@@ -17,6 +17,7 @@ import json
import typer import typer
from chemenu import toc
from chemenu.commands._util import fail from chemenu.commands._util import fail
from chemenu.types_core import UnknownType, describe_type, list_types from chemenu.types_core import UnknownType, describe_type, list_types
@@ -88,4 +89,9 @@ def describe_type_command(
typer.echo("") typer.echo("")
typer.echo("## Authoring guidance") typer.echo("## Authoring guidance")
typer.echo(described["body"]) # A type-spec over 100 lines carries a generated table-of-contents region
# (`chemenu/toc.py`), which serves whoever opens the file. Here it would be
# noise: this command already hands over the whole body, so there is
# nothing left for a navigation aid to navigate - only markers and a list
# of headings the reader is about to see anyway.
typer.echo(toc.strip_region(described["body"]))
+11 -4
View File
@@ -204,7 +204,7 @@ def bump_command(
breaking: Optional[str] = typer.Option( breaking: Optional[str] = typer.Option(
None, None,
"--breaking", "--breaking",
help="What stops working, for the bump that first escalates to a boundary crossing (recorded in CHANGES.md). Required there, refused on a bump that crosses nothing", help="What stops working (recorded in CHANGES.md). Required on the bump that first escalates to a boundary crossing, optional on a later bump of the same crossing candidate - where it joins the reasons already recorded rather than replacing them. Refused on a bump that crosses nothing",
), ),
no_migration: Optional[str] = typer.Option( no_migration: Optional[str] = typer.Option(
None, None,
@@ -241,9 +241,16 @@ def bump_command(
version is not a drop-in replacement, whether or not any content moves - version is not a drop-in replacement, whether or not any content moves -
requires `--breaking "<what stops working>"`, and on top of that either a requires `--breaking "<what stops working>"`, and on top of that either a
migration document for the new base or `--no-migration "<reason>"`. Both migration document for the new base or `--no-migration "<reason>"`. Both
lines are written into the entry once and then persist across every later are written into the entry and persist across every later bump at the same
bump at the same stage: a follow-up bump need not repeat them, and passing stage, so a follow-up bump need not repeat them, and passing either on a
either on a bump that crosses nothing at all is refused. bump that crosses nothing at all is refused.
A candidate can cross the boundary more than once, and the two flags part
ways there. A further `--breaking` **joins** the reasons already recorded -
each crossing is its own thing an operator has to act on, and replacing
meant the second one silently deleted the first. A further
`--no-migration` **replaces**: whether content has to change is one
question about the candidate as a whole, not one per crossing.
A later bump of the same candidate that finds out `--no-migration` was A later bump of the same candidate that finds out `--no-migration` was
wrong after all retracts it with `--migration-required` - write the wrong after all retracts it with `--migration-required` - write the
+35 -4
View File
@@ -150,9 +150,10 @@ def test_stale_regions_is_false_right_after_upsert():
def test_target_files_matches_the_documented_scope(): def test_target_files_matches_the_documented_scope():
"""Integration check against the real repo: the scope is AGENTS.md, every """Integration check against the real repo: every agent-loaded category
stage contract, kb/CONVENTIONS.md, every COLLECTION.md, and the flat AGENTS.md § File naming names - AGENTS.md itself, every stage contract,
`instructions/**.md` form - never a `SKILL.md`.""" kb/CONVENTIONS.md, every COLLECTION.md, the flat `instructions/**.md` form,
every type-spec, every `docs/` page - with `SKILL.md` the one exception."""
from chemenu import config from chemenu import config
files = toc.target_files() files = toc.target_files()
@@ -164,5 +165,35 @@ def test_target_files_matches_the_documented_scope():
assert "types/type-spec.md" in relatives assert "types/type-spec.md" in relatives
assert "instructions/CONTRACT.md" in relatives assert "instructions/CONTRACT.md" in relatives
assert "instructions/dev/version-parts.md" in relatives # flat, still instructions/**.md assert "instructions/dev/version-parts.md" in relatives # flat, still instructions/**.md
assert "types/source.md" in relatives # a page type-spec, not only the stage contract
assert "docs/version-model.md" in relatives
assert not any(rel.endswith("SKILL.md") for rel in relatives) assert not any(rel.endswith("SKILL.md") for rel in relatives)
assert not any(rel.startswith("types/") and rel != "types/type-spec.md" for rel in relatives)
def test_target_files_lists_type_spec_once_despite_two_sources():
"""`types/type-spec.md` is both a stage contract and a `types/*.md` file;
the set in `target_files` is what keeps that from being a special case."""
from chemenu import config
relatives = [f.relative_to(config.ROOT).as_posix() for f in toc.target_files()]
assert relatives.count("types/type-spec.md") == 1
def test_strip_region_removes_what_types_describe_would_otherwise_echo():
body = (
"# A type\n\n"
f"{blocks.open_marker(toc.REGION_NAME)}\n## Contents\n\n- [X](#x)\n"
f"{blocks.close_marker(toc.REGION_NAME)}\n\n## X\n\nProse.\n"
)
stripped = toc.strip_region(body)
assert toc.REGION_NAME not in stripped
assert "## Contents" not in stripped
assert "## X" in stripped and "Prose." in stripped
# The heading keeps the blank line the region used to sit in, rather than
# being welded to the line above it.
assert "# A type\n\n## X" in stripped
def test_strip_region_leaves_a_body_that_never_had_one_alone():
body = "# A type\n\n## X\n\nProse.\n"
assert toc.strip_region(body) == body
+101
View File
@@ -444,6 +444,107 @@ def test_breaking_line_sits_above_the_bumps_region(tree):
assert changes.index(version_mod.BREAKING_CHANGE_MARKER) < changes.index(version_mod._BUMPS_OPEN) assert changes.index(version_mod.BREAKING_CHANGE_MARKER) < changes.index(version_mod._BUMPS_OPEN)
# --- version bump: accumulating --breaking ----------------------------------
def test_a_single_breaking_reason_stays_on_the_marker_line(tree):
"""The shape every entry written before accumulation existed carries."""
version_cmd.bump_command(
major=True, minor=False, patch=False, title="Breaking",
breaking="the feed moved", no_migration="kb/ untouched", migration_required=False,
impact=None, dry_run=False,
)
changes = (tree / "CHANGES.md").read_text(encoding="utf-8")
assert f"{version_mod.BREAKING_CHANGE_MARKER} the feed moved" in changes
def test_a_second_breaking_reason_joins_the_first_instead_of_replacing_it(tree):
"""The defect this exists for: the second --breaking used to delete the
first, leaving the entry promising one break while shipping two."""
version_cmd.bump_command(
major=True, minor=False, patch=False, title="First crossing",
breaking="the feed moved", no_migration="kb/ untouched", migration_required=False,
impact=None, dry_run=False,
)
version_cmd.bump_command(
major=False, minor=True, patch=False, title="Second crossing",
breaking="the import name changed", no_migration=None, migration_required=False,
impact=None, dry_run=False,
)
changes = (tree / "CHANGES.md").read_text(encoding="utf-8")
section = version_mod.changes_section(changes, Version.parse("2.0.0-beta.2"))
assert version_mod.breaking_reasons(section) == ["the feed moved", "the import name changed"]
assert "- the feed moved" in changes and "- the import name changed" in changes
# Still the line an operator acts on first, above a list that can run long.
assert changes.index(version_mod.BREAKING_CHANGE_MARKER) < changes.index(version_mod._BUMPS_OPEN)
def test_repeating_a_breaking_reason_verbatim_is_a_no_op(tree):
"""So a re-run after an interrupted bump converges instead of writing the
same sentence twice."""
for title in ("First", "Second"):
version_cmd.bump_command(
major=True, minor=False, patch=False, title=title,
breaking="the feed moved", no_migration="kb/ untouched", migration_required=False,
impact=None, dry_run=False,
)
changes = (tree / "CHANGES.md").read_text(encoding="utf-8")
section = version_mod.changes_section(changes, Version.parse("2.0.0-beta.2"))
assert version_mod.breaking_reasons(section) == ["the feed moved"]
def test_breaking_reasons_reads_a_pre_accumulation_single_line_back(tree):
"""An entry written by a build that predates accumulation round-trips: it
parses as one reason, and a later bump grows it to a list rather than
rewriting what was already there."""
section = "## 2.0.0-beta.1 - 2026-09-01 - T\n\n**Breaking Change:** the feed moved\n\n"
assert version_mod.breaking_reasons(section) == ["the feed moved"]
grown = version_mod._add_breaking_reason(section, "the import name changed")
assert version_mod.breaking_reasons(grown) == ["the feed moved", "the import name changed"]
assert f"{version_mod.BREAKING_CHANGE_MARKER}\n- the feed moved\n" in grown
def test_no_migration_still_replaces_rather_than_accumulating(tree):
"""It answers one yes/no question about the candidate as a whole, so a
second answer supersedes the first - unlike --breaking beside it."""
version_cmd.bump_command(
major=True, minor=False, patch=False, title="First",
breaking="the feed moved", no_migration="kb/ untouched", migration_required=False,
impact=None, dry_run=False,
)
version_cmd.bump_command(
major=False, minor=True, patch=False, title="Second",
breaking=None, no_migration="still nothing to change", migration_required=False,
impact=None, dry_run=False,
)
changes = (tree / "CHANGES.md").read_text(encoding="utf-8")
assert changes.count(version_mod.MIGRATION_NONE_MARKER) == 1
assert "kb/ untouched" not in changes
assert "still nothing to change" in changes
def test_retracting_no_migration_leaves_an_accumulated_breaking_list_intact(tree):
version_cmd.bump_command(
major=True, minor=False, patch=False, title="First",
breaking="the feed moved", no_migration="kb/ untouched", migration_required=False,
impact=None, dry_run=False,
)
version_cmd.bump_command(
major=False, minor=True, patch=False, title="Second",
breaking="the import name changed", no_migration=None, migration_required=False,
impact=None, dry_run=False,
)
_migration_document(tree, "2.0.0")
version_cmd.bump_command(
major=False, minor=False, patch=True, title="Turns out it migrates",
breaking=None, no_migration=None, migration_required=True, impact=None, dry_run=False,
)
changes = (tree / "CHANGES.md").read_text(encoding="utf-8")
section = version_mod.changes_section(changes, Version.parse("2.0.0-beta.3"))
assert version_mod.MIGRATION_NONE_MARKER not in changes
assert version_mod.breaking_reasons(section) == ["the feed moved", "the import name changed"]
# --- version regrade -------------------------------------------------------- # --- version regrade --------------------------------------------------------
+84 -11
View File
@@ -20,16 +20,49 @@ the stage/collection contracts, the flat `instructions/**.md` files) is a
precedent first. precedent first.
**Scope is computed, never a hand-picked list** - the same principle that **Scope is computed, never a hand-picked list** - the same principle that
governs `wikitool` itself. `target_files()` walks the file-naming categories governs `wikitool` itself. `target_files()` walks every file-naming category
AGENTS.md's own table calls agent-loaded reference material: `AGENTS.md`, AGENTS.md's own table calls agent-loaded: `AGENTS.md`, every stage contract,
every stage contract, `kb/CONVENTIONS.md`, every `kb/*/COLLECTION.md`, and `kb/CONVENTIONS.md`, every `kb/*/COLLECTION.md`, every flat `instructions/**.md`
every flat `instructions/**.md` file (a `SKILL.md` is excluded - it is loaded file, every `types/*.md` type-spec, and every `docs/` page. One rule, and
whole by the harness, not previewed at a second hop; `instructions/CONTRACT.md` exactly one exception below it - which is the whole point, because a scope
§ "How much reasoning a step may carry" already treats a checklist read once carrying several unexplained absences reads as an accident rather than a
as the table of contents it replaced). Human docs (`README.md`, `CHANGES.md`, decision, and did: `docs/` and the page type-specs sat outside it for no
`EVALS.md`, `INSTALL.md`, `tools/README.md`) are excluded too: the file-naming recorded reason at all.
table says they are "Never loaded by an agent as instruction," so the preview
mechanic this exists to mitigate does not apply to them. `docs/` belongs in for the reason the threshold exists. AGENTS.md § File naming
calls it "Agents and humans | By link, or on explicit request" - agent-loaded,
at a second hop, which is precisely the preview mechanic
`instructions/CONTRACT.md` § "Reference depth" treats as real. The exclusion of
the human docs (`README.md`, `CHANGES.md`, `EVALS.md`, `INSTALL.md`,
`tools/README.md`) rests on a sentence that does not stretch to cover it: those
are "Never loaded by an agent as instruction," while a `docs/` page is not
loaded *as instruction* but is very much loaded.
**The one exception is `SKILL.md`**, and the vendored skill-authoring guidance
is what puts it there rather than a judgment of ours. `commonplace/kb/work/
skill-creator-distillation/sources/claude-code-skill-creator/SKILL.md:88-99`
sets out three loading levels and places the SKILL.md body on the second - "In
context whenever skill triggers (<500 lines ideal)" - while aiming its own TOC
advice at the third, "large reference files (>300 lines)", i.e. bundled
resources. The Codex source agrees by placement: its TOC bullet sits directly
under "Keep references one level deep from SKILL.md. All reference files should
link directly from SKILL.md" (`.../codex-skill-creator/SKILL.md:221-222`).
Neither asks a skill body to carry a table of contents, because neither expects
one to be previewed. `instructions/CONTRACT.md` § "How much reasoning a step
may carry" arrives at the same place from the other side, treating a checklist
read once as the table of contents it replaced. (Our longest skill is 254
lines, so the <500 guidance costs us nothing either.)
A type-spec is loaded whole too - `tools/wikitool types describe` prints its
entire authoring body - but it is *also* read as a file, by whoever edits it,
and that is the reading the threshold is about. So it carries a region like any
other reference file, and `types_cmd` strips the region back out of what
`describe` prints: the command already hands over the whole body, so a
navigation aid into it would be noise in the output and nothing else.
The threshold's own provenance is worth recording, because the two sources
disagree: Codex says 100 lines, Claude Code says 300. This stack took the
stricter number.
""" """
from __future__ import annotations from __future__ import annotations
@@ -80,7 +113,13 @@ def target_files() -> list[Path]:
`instructions/<name>.md` form (`instructions/CONTRACT.md` says the `dev/` `instructions/<name>.md` form (`instructions/CONTRACT.md` says the `dev/`
split is orthogonal to Linked/Manual, not a different file shape), so split is orthogonal to Linked/Manual, not a different file shape), so
there is no separate rule for them to fall out of - and no hand-picking there is no separate rule for them to fall out of - and no hand-picking
for a future file under either to be missed. for a future file under either to be missed. `types/` and `docs/` are
walked for the same reason: a type or a rationale page added later is in
scope by construction, not by someone remembering this function.
`types/type-spec.md` arrives twice - once as a stage contract, once from
the `types/` walk - and the set at the bottom is what makes that a
non-issue rather than something to special-case.
""" """
files: list[Path] = [config.ROOT / "AGENTS.md"] files: list[Path] = [config.ROOT / "AGENTS.md"]
files += [config.ROOT / rel for rel in _STAGE_CONTRACTS] files += [config.ROOT / rel for rel in _STAGE_CONTRACTS]
@@ -96,6 +135,10 @@ def target_files() -> list[Path]:
for path in instructions_dir.rglob("*.md") for path in instructions_dir.rglob("*.md")
if path.name != "SKILL.md" if path.name != "SKILL.md"
) )
for subdir in ("types", "docs"):
directory = config.ROOT / subdir
if directory.is_dir():
files += sorted(directory.rglob("*.md"))
return sorted({f for f in files if f.is_file()}) return sorted({f for f in files if f.is_file()})
@@ -116,6 +159,36 @@ def body_without_region(text: str) -> str:
return stripped.rstrip("\n") + "\n" if stripped else stripped return stripped.rstrip("\n") + "\n" if stripped else stripped
_REGION_WITH_PADDING_RE = re.compile(
r"\n*"
+ re.escape(blocks.open_marker(REGION_NAME))
+ r".*?"
+ re.escape(blocks.close_marker(REGION_NAME))
+ r"\n*",
re.DOTALL,
)
def strip_region(text: str) -> str:
"""`text` with the generated TOC region removed, for a caller that is
handing the whole body over anyway.
`types describe` is the one such caller: it prints a type-spec's entire
authoring body, so the region's markers and heading list would be noise in
its output rather than a way into anything. Distinct from
`body_without_region`, which exists to *measure* a body against the line
threshold and therefore also canonicalizes the trailing newline.
Takes the surrounding blank lines with it and puts one back, rather than
calling `blocks.strip`: that collapses the padding to a single newline,
which is the right answer when the result is about to be rebuilt from
scratch (`upsert` does exactly that) and the wrong one here, where the
output is printed as-is - it would leave the following `##` heading welded
to the paragraph above it.
"""
return _REGION_WITH_PADDING_RE.sub("\n\n", text)
def needs_toc(text: str) -> bool: def needs_toc(text: str) -> bool:
return len(body_without_region(text).splitlines()) > THRESHOLD return len(body_without_region(text).splitlines()) > THRESHOLD
+82 -6
View File
@@ -583,9 +583,11 @@ def regrade(text: str, version: "Version", updates: dict[int, str]) -> str:
def _set_marker_line(section: str, marker: str, line: str) -> str: def _set_marker_line(section: str, marker: str, line: str) -> str:
"""Add or replace the one-line `marker ...` paragraph in `section`. """Add or replace the one-line `marker ...` paragraph in `section`.
Used for the breaking-change and no-migration lines, which - unlike the Used for the no-migration line, which - unlike the bumps list and unlike
bumps list - are not accumulated: a later bump that repeats `--breaking` the breaking-change paragraph below - is **not** accumulated: it answers
restates it rather than growing a list nobody would read as history. one yes/no question about the candidate as a whole ("does content have to
change?"), so a second answer replaces the first rather than joining it,
and `_clear_marker_line` is its retraction path.
Anchored just above the bumps region (not below it, as before Gitea #95): Anchored just above the bumps region (not below it, as before Gitea #95):
with a graded, potentially 30-line list, the line an operator most needs with a graded, potentially 30-line list, the line an operator most needs
to act on stayed the deepest thing in the entry otherwise. to act on stayed the deepest thing in the entry otherwise.
@@ -599,6 +601,73 @@ def _set_marker_line(section: str, marker: str, line: str) -> str:
return section.rstrip() + f"\n\n{line}\n" return section.rstrip() + f"\n\n{line}\n"
# The breaking-change paragraph, matched whole: the marker line plus any `- `
# bullets under it. `_set_marker_line`'s `^marker.*$` reaches the first line
# only, which is exactly wrong for a form that can carry bullets beneath it.
_BREAKING_PARAGRAPH_RE = re.compile(
rf"^{re.escape(BREAKING_CHANGE_MARKER)}.*(?:\n-[ \t].*)*$", re.MULTILINE
)
def breaking_reasons(section: str) -> list[str]:
"""The breaking-change paragraph parsed back into one reason per crossing,
in written order.
Two shapes read the same way, which is what lets an entry written before
accumulation existed round-trip untouched: `**Breaking Change:** <reason>`
is one reason, and a bare `**Breaking Change:**` followed by `- ` bullets
is one reason per bullet. Same "flat while there is only one of them"
trick `_bumps_block` plays with its impact groups, and for the same
reason - the common case keeps the shape it always had.
"""
match = _BREAKING_PARAGRAPH_RE.search(section)
if not match:
return []
lines = match.group(0).splitlines()
head = lines[0][len(BREAKING_CHANGE_MARKER):].strip()
reasons = [head] if head else []
reasons += [line.strip()[2:].strip() for line in lines[1:]]
return [reason for reason in reasons if reason]
def _breaking_paragraph(reasons: list[str]) -> str:
"""Render the breaking-change paragraph from one reason per crossing.
One reason stays on the marker line - byte-identical to what every entry
written before accumulation carries. Two or more move to bullets under a
bare marker, because a single line holding two unrelated breakages reads
as one run-on sentence and an operator has to act on each separately.
"""
if len(reasons) == 1:
return f"{BREAKING_CHANGE_MARKER} {reasons[0]}"
bullets = "\n".join(f"- {reason}" for reason in reasons)
return f"{BREAKING_CHANGE_MARKER}\n{bullets}"
def _add_breaking_reason(section: str, reason: str) -> str:
"""Append `reason` to the breaking-change paragraph, or start one.
Accumulates rather than replaces: a candidate can cross the compatibility
boundary more than once (this is the normal shape of a long-running one),
and each crossing is a separate thing the operator of an existing instance
has to act on. Replacing meant the second `--breaking` silently deleted
the first - the entry then promised a single break while shipping two.
Repeating a reason verbatim is a no-op, so a re-run after an interrupted
bump converges instead of writing the same sentence twice.
"""
existing = breaking_reasons(section)
if reason in existing:
return section
paragraph = _breaking_paragraph(existing + [reason])
if existing:
return _BREAKING_PARAGRAPH_RE.sub(lambda _m: paragraph, section, count=1)
anchor = section.find(_BUMPS_OPEN)
if anchor != -1:
return section[:anchor] + f"{paragraph}\n\n" + section[anchor:]
return section.rstrip() + f"\n\n{paragraph}\n"
def _clear_marker_line(section: str, marker: str) -> str: def _clear_marker_line(section: str, marker: str) -> str:
"""Remove the one-line `marker ...` paragraph from `section`, if present. """Remove the one-line `marker ...` paragraph from `section`, if present.
@@ -633,10 +702,15 @@ def _update_open_candidate(
impact: str = DEFAULT_IMPACT, impact: str = DEFAULT_IMPACT,
) -> str: ) -> str:
"""Move the topmost entry's heading to `version`/`date`/`title`, append """Move the topmost entry's heading to `version`/`date`/`title`, append
`(impact, title)` to its machine-managed bump list, and set the `(impact, title)` to its machine-managed bump list, and record the
breaking/no-migration lines only where this call supplies them - see breaking/no-migration lines only where this call supplies them - see
`insert_changes_entry`. `insert_changes_entry`.
The two are recorded differently on purpose: a `breaking_reason` **joins**
whatever crossings the candidate already recorded (`_add_breaking_reason`),
a `no_migration_reason` **replaces** the single line that answers whether
content has to change (`_set_marker_line`).
`migration_required` retracts an earlier `--no-migration` line instead of `migration_required` retracts an earlier `--no-migration` line instead of
setting one - the two are mutually exclusive on a single bump, enforced by setting one - the two are mutually exclusive on a single bump, enforced by
the caller (`version_cmd.bump_command`), not here.""" the caller (`version_cmd.bump_command`), not here."""
@@ -653,7 +727,7 @@ def _update_open_candidate(
) )
if breaking_reason: if breaking_reason:
section = _set_marker_line(section, BREAKING_CHANGE_MARKER, f"{BREAKING_CHANGE_MARKER} {breaking_reason}") section = _add_breaking_reason(section, breaking_reason)
if no_migration_reason: if no_migration_reason:
section = _set_marker_line(section, MIGRATION_NONE_MARKER, f"{MIGRATION_NONE_MARKER} - {no_migration_reason}") section = _set_marker_line(section, MIGRATION_NONE_MARKER, f"{MIGRATION_NONE_MARKER} - {no_migration_reason}")
elif migration_required: elif migration_required:
@@ -684,7 +758,9 @@ def insert_changes_entry(
candidate still open" here. candidate still open" here.
A fresh entry gets the skeleton only: heading, date, author, - when a A fresh entry gets the skeleton only: heading, date, author, - when a
compatibility boundary is crossed - the line saying what breaks, plus the compatibility boundary is crossed - the line saying what breaks (one
crossing, so the flat one-line form; a candidate that crosses again
accumulates bullets there, see `_add_breaking_reason`), plus the
line saying no content has to change where that applies, and then the line saying no content has to change where that applies, and then the
machine-managed bump list (started with this one `(impact, title)` pair, machine-managed bump list (started with this one `(impact, title)` pair,
for a candidate). The break comes first, above the bump list rather than for a candidate). The break comes first, above the bump list rather than
+37 -33
View File
@@ -1,7 +1,7 @@
--- ---
type: types/type-spec.md type: types/type-spec.md
name: comparison name: comparison
description: Strukturierter Typ für Vergleichsseiten, die mehrere Entities oder Ansätze gegenüberstellen description: Structured type for comparison pages that set several entities or approaches against one another
schema: types/comparison.schema.yaml schema: types/comparison.schema.yaml
base_dir: comparisons base_dir: comparisons
page_ref_fields: [entities, related] page_ref_fields: [entities, related]
@@ -9,42 +9,46 @@ page_ref_fields: [entities, related]
# Comparison # Comparison
`comparison` ist der Typ für Seiten, die mehrere Entities, Werkzeuge, Technologien oder Ansätze systematisch anhand festgelegter Kriterien gegenüberstellen. Vergleichsseiten stützen Entscheidungen und machen Zielkonflikte zwischen Alternativen sichtbar. `comparison` is the type for pages that set several entities, tools, technologies or approaches systematically against one another along fixed criteria. Comparison pages support decisions and make the trade-offs between alternatives visible.
## Wann zu verwenden ## When to use
- Vergleich mehrerer Technologien oder Frameworks für einen konkreten Einsatzzweck - Comparing several technologies or frameworks for a concrete purpose
- Analyse der Vor- und Nachteile verschiedener Werkzeuge oder Bibliotheken - Analysing the advantages and disadvantages of different tools or libraries
- Bewertung alternativer Lösungswege für ein Problem - Evaluating alternative ways of solving a problem
- Dokumentation eines Entscheidungsprozesses mit mehreren geprüften Optionen - Documenting a decision process with several options examined
## Wann NICHT zu verwenden ## When NOT to use
- Für die Dokumentation einer einzelnen Entity oder eines Concepts (dann `entity` bzw. `concept`) - For documenting a single entity or concept (use `entity` or `concept` respectively)
- Für Zusammenfassungen von Quellmaterial (dann `source`) - For summaries of source material (use `source`)
- Für Seiten, die einen einzelnen Gegenstand behandeln statt mehrere zu vergleichen - For pages covering a single subject rather than comparing several
## Frontmatter ## Frontmatter
| Feld | Pflicht | Verwendung | | Field | Required | Use |
|---|---:|---| |---|---:|---|
| `type` | Ja | `types/comparison.md` | | `type` | Yes | `types/comparison.md` |
| `tags` | Nein | Navigations-Tags zur Kategorisierung | | `tags` | No | Navigation tags for categorization |
| `created` | Ja | Erstellungsdatum (YYYY-MM-DD) | | `created` | Yes | Creation date (YYYY-MM-DD) |
| `entities` | Ja | Titel der verglichenen Entities | | `entities` | Yes | Titles of the entities compared |
| `related` | Nein | Deklarierte ausgehende Kanten - je Subjekt eine `compares-with`-Kante, geschrieben von `wikitool xref add` | | `related` | No | Declared outbound edges - one `compares-with` edge per subject, written by `wikitool xref add` |
| `summary` | Ja | Einzeiler für `kb/index.md` | | `summary` | Yes | One-liner for `kb/index.md` |
## Autorenanweisungen ## Authoring guidance
- Ein Titel, der den Vergleich benennt (z. B. "Go vs Rust", "Kubernetes vs Docker Swarm"); er folgt den etablierten Namen der verglichenen Gegenstände, nicht der KB-Sprache (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" und "Language") - A title that names the comparison (e.g. "Go vs Rust", "Kubernetes vs Docker Swarm"); it follows the established names of the subjects compared, not the KB language (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" and "Language")
- Klar darlegen, was verglichen wird und warum - State clearly what is being compared and why
- Eine Vergleichstabelle mit den Kriterien als Zeilen verwenden - Use a comparison table with the criteria as rows
- Eine Analyse, die die Tabelle auswertet statt sie zu wiederholen - An analysis that evaluates the table rather than repeating it
- Eine Empfehlung, wo eine begründbar ist - A recommendation wherever one can be justified
## Template ## Template
The block below is page material, so it is written in this instance's KB language
(`kb/CONVENTIONS.md` `language:`) rather than in the control plane's English - its headings
become the headings of every page `wikitool new comparison` scaffolds.
```markdown ```markdown
# Comparison: {name} # Comparison: {name}
@@ -67,16 +71,16 @@ TODO: Auswertung des Vergleichs. Welche Muster zeigen sich? Was sind die entsche
TODO: Falls möglich - was wann und für wen zu verwenden ist. Unter welchen Umständen ist welche Option die beste? TODO: Falls möglich - was wann und für wen zu verwenden ist. Unter welchen Umständen ist welche Option die beste?
``` ```
`# Comparison:` bleibt als Präfix stehen - anders als bei `source` ist es kein `title_prefix`, `# Comparison:` stays as a prefix - unlike `source` it is no `title_prefix` but a pure template
sondern reine Template-Konvention, und der Seitentitel selbst (`Go vs Rust`) trägt es nicht. convention, and the page title itself (`Go vs Rust`) does not carry it. When `wikitool xref` adds
Fügt `wikitool xref` eine Beziehung hinzu, entsteht der toolgeführte Querverweis-Abschnitt; wie a relationship, the tool-managed cross-reference section appears; what it is called is the
er heißt, entscheidet die Instanz in `kb/CONVENTIONS.md` (`sections:`). instance's decision in `kb/CONVENTIONS.md` (`sections:`).
--- ---
Ergänzende Hinweise: Additional notes:
- Vergleichsseiten stützen Technologieauswahl und Architekturentscheidungen - Comparison pages support technology selection and architecture decisions
- Die verglichenen Entities sollten alle als Wiki-Seiten existieren - The entities compared should all exist as wiki pages
- Die Kriterien sollten zur anstehenden Entscheidung passen - The criteria should fit the decision at hand
- Ein Vergleich bleibt auf einen konkreten Einsatzzweck oder Entscheidungskontext fokussiert - A comparison stays focused on one concrete purpose or decision context
+52 -38
View File
@@ -1,7 +1,7 @@
--- ---
type: types/type-spec.md type: types/type-spec.md
name: concept name: concept
description: Basistyp für Concept-Seiten - Architekturen, Muster, Protokolle, Abläufe, Entscheidungen oder Problemstellungen description: Base type for concept pages - architectures, patterns, protocols, workflows, decisions or problems
schema: types/concept.schema.yaml schema: types/concept.schema.yaml
subtype_field: concept_type subtype_field: concept_type
base_dir: concepts base_dir: concepts
@@ -17,49 +17,63 @@ layout:
# Concept # Concept
`concept` ist der Typ für abstrakte Ideen, Methodiken, Muster und Architekturentscheidungen. Concepts liefern die theoretische und methodische Grundlage, die Entities umsetzen oder befolgen. `concept` is the type for abstract ideas, methodologies, patterns and architectural decisions. Concepts provide the theoretical and methodological ground that entities implement or follow.
## Wann zu verwenden <!-- wikitool:toc -->
## Contents
- Dokumentation eines Architekturmusters oder Entwurfsansatzes - [When to use](#when-to-use)
- Erläuterung eines Entwurfsmusters oder einer Programmiermethodik - [When NOT to use](#when-not-to-use)
- Beschreibung eines Kommunikationsprotokolls oder Standards - [Frontmatter](#frontmatter)
- Festhalten eines Ablaufs oder Prozesses, der über Projekte hinweg wiederkehrt - [Authoring guidance](#authoring-guidance)
- Dokumentation einer Architekturentscheidung (ADR) oder Entwurfsentscheidung - [Template](#template)
- Analyse eines wiederkehrenden Problems und seiner Lösungen <!-- /wikitool:toc -->
## Wann NICHT zu verwenden ## When to use
- Für konkrete Dinge wie Projekte, Systeme oder Werkzeuge (dann `entity`) - Documenting an architectural pattern or design approach
- Für Quelldokumente oder eingelesenes Material (dann `source`) - Explaining a design pattern or a programming methodology
- Für vergleichende Analysen konkreter Gegenstände (dann `comparison`) - Describing a communication protocol or standard
- Recording a workflow or process that recurs across projects
- Documenting an architectural decision (ADR) or design decision
- Analysing a recurring problem and its solutions
## When NOT to use
- For concrete things such as projects, systems or tools (use `entity`)
- For source documents or ingested material (use `source`)
- For comparative analyses of concrete subjects (use `comparison`)
## Frontmatter ## Frontmatter
| Feld | Pflicht | Verwendung | | Field | Required | Use |
|---|---:|---| |---|---:|---|
| `type` | Ja | `types/concept.md` | | `type` | Yes | `types/concept.md` |
| `concept_type` | Ja | Eines von: architecture, pattern, protocol, workflow, decision, problem | | `concept_type` | Yes | One of: architecture, pattern, protocol, workflow, decision, problem |
| `tags` | Nein | Navigations-Tags zur Kategorisierung | | `tags` | No | Navigation tags for categorization |
| `created` | Ja | Erstellungsdatum (YYYY-MM-DD) | | `created` | Yes | Creation date (YYYY-MM-DD) |
| `modified` | Ja | Datum der letzten Änderung (YYYY-MM-DD) | | `modified` | Yes | Date last changed (YYYY-MM-DD) |
| `related` | Nein | Titel verwandter Concepts und Entities | | `related` | No | Titles of related concepts and entities |
| `sources` | Nein | Titel der Source-Seiten, die die Aussagen dieser Seite stützen | | `sources` | No | Titles of the source pages backing this page's claims |
| `provenance` | Ja | sourced, general oder mixed | | `provenance` | Yes | sourced, general or mixed |
| `summary` | Ja | Einzeiler für `kb/index.md` | | `summary` | Yes | One-liner for `kb/index.md` |
## Autorenanweisungen ## Authoring guidance
- Der Titel ist der kanonische Name des Concepts und folgt der etablierten Fachbezeichnung, nicht der KB-Sprache (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" und "Language") - The title is the concept's canonical name and follows the established term of art, not the KB language (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" and "Language")
- Mit einer klaren Definition beginnen: was das Concept ist - Open with a clear definition: what the concept is
- Beispiele geben, wo sie das Verständnis tragen - Give examples wherever they carry the understanding
- Auf Entities verlinken, die das Concept umsetzen oder verwenden - Link to the entities that implement or use the concept
- Bei `provenance: sourced` oder `mixed` harte Fakten inline mit einer `[^cite-id]`-Fußnote belegen - - Under `provenance: sourced` or `mixed`, back hard facts inline with a `[^cite-id]` footnote -
`tools/wikitool cite add --page "<Title>" --source "Source - X"` erzeugt Id und Definition `tools/wikitool cite add --page "<Title>" --source "Source - X"` mints the id and its definition
- Wikipedia-Tonfall: sachlich, neutral, keine Werbesprache - Wikipedia register: factual, neutral, no marketing language
## Template ## Template
The block below is page material, so it is written in this instance's KB language
(`kb/CONVENTIONS.md` `language:`) rather than in the control plane's English - its headings
become the headings of every page `wikitool new concept` scaffolds.
```markdown ```markdown
# {name} # {name}
@@ -94,15 +108,15 @@ TODO: Anti-Muster, Warnungen oder Situationen, in denen es fehl am Platz ist
- [[Related Concept 2]] - [[Related Concept 2]]
``` ```
Der Wert hinter `**Typ:**` bleibt der englische Enum-Wert - danach filtert `search --field`. The value behind `**Typ:**` stays the English enum value - that is what `search --field` filters
Fügt `wikitool xref` eine Beziehung hinzu, entstehen zusätzlich die beiden toolgeführten on. When `wikitool xref` adds a relationship, the two tool-managed sections for relationships and
Abschnitte für Beziehungen und Querverweise; wie sie heißen, entscheidet die Instanz in cross-references appear as well; what they are called is the instance's decision in
`kb/CONVENTIONS.md` (`sections:`). `kb/CONVENTIONS.md` (`sections:`).
--- ---
Ergänzende Hinweise: Additional notes:
- Concept-Seiten tragen den theoretischen Unterbau des Wikis - Concept pages carry the wiki's theoretical underpinning
- Jeder Concept-Typ steht für eine andere Kategorie abstrakten Wissens - Each concept type stands for a different category of abstract knowledge
- Concepts sollten von den Entities aus verlinkt sein, die sie umsetzen oder verwenden - Concepts should be linked to from the entities that implement or use them
+51 -37
View File
@@ -1,7 +1,7 @@
--- ---
type: types/type-spec.md type: types/type-spec.md
name: entity name: entity
description: Basistyp für Entity-Seiten - Projekte, Systeme, Werkzeuge, Technologien oder Personen description: Base type for entity pages - projects, systems, tools, technologies or people
schema: types/entity.schema.yaml schema: types/entity.schema.yaml
subtype_field: entity_type subtype_field: entity_type
base_dir: entities base_dir: entities
@@ -16,47 +16,61 @@ layout:
# Entity # Entity
`entity` ist der Typ für konkrete Dinge: Projekte, Systeme, Werkzeuge, Technologien oder Personen. Entities sind die primären Bausteine des Wissensgraphen. `entity` is the type for concrete things: projects, systems, tools, technologies or people. Entities are the primary building blocks of the knowledge graph.
## Wann zu verwenden <!-- wikitool:toc -->
## Contents
- Darstellung eines Softwareprojekts, einer Initiative oder eines Arbeitsvorhabens - [When to use](#when-to-use)
- Dokumentation eines betriebenen Systems, Dienstes oder einer Infrastrukturkomponente - [When NOT to use](#when-not-to-use)
- Beschreibung eines CLI-Werkzeugs, Hilfsprogramms oder einer Programmbibliothek - [Frontmatter](#frontmatter)
- Festhalten von Informationen zu einer Sprache, einem Framework oder einem Protokoll - [Authoring guidance](#authoring-guidance)
- Dokumentation einer Person, eines Teams oder einer Organisation - [Template](#template)
<!-- /wikitool:toc -->
## Wann NICHT zu verwenden ## When to use
- Für abstrakte Konzepte, Muster oder Methodiken (dann `concept`) - Representing a software project, an initiative or a piece of work
- Für Quelldokumente oder eingelesenes Material (dann `source`) - Documenting a running system, service or infrastructure component
- Für vergleichende Analysen (dann `comparison`) - Describing a CLI tool, utility or software library
- Recording information about a language, a framework or a protocol
- Documenting a person, a team or an organization
## When NOT to use
- For abstract concepts, patterns or methodologies (use `concept`)
- For source documents or ingested material (use `source`)
- For comparative analyses (use `comparison`)
## Frontmatter ## Frontmatter
| Feld | Pflicht | Verwendung | | Field | Required | Use |
|---|---:|---| |---|---:|---|
| `type` | Ja | `types/entity.md` | | `type` | Yes | `types/entity.md` |
| `entity_type` | Ja | Eines von: project, system, tool, technology, person | | `entity_type` | Yes | One of: project, system, tool, technology, person |
| `tags` | Nein | Navigations-Tags zur Kategorisierung | | `tags` | No | Navigation tags for categorization |
| `created` | Ja | Erstellungsdatum (YYYY-MM-DD) | | `created` | Yes | Creation date (YYYY-MM-DD) |
| `modified` | Ja | Datum der letzten Änderung (YYYY-MM-DD) | | `modified` | Yes | Date last changed (YYYY-MM-DD) |
| `related` | Nein | Titel verwandter Seiten (Entities, Concepts, Sources) | | `related` | No | Titles of related pages (entities, concepts, sources) |
| `sources` | Nein | Titel der Source-Seiten, die die Aussagen dieser Seite stützen | | `sources` | No | Titles of the source pages backing this page's claims |
| `provenance` | Ja | sourced, general oder mixed | | `provenance` | Yes | sourced, general or mixed |
| `summary` | Ja | Einzeiler für `kb/index.md` | | `summary` | Yes | One-liner for `kb/index.md` |
## Autorenanweisungen ## Authoring guidance
- Der Titel ist der kanonische Name der Entity und folgt der etablierten Bezeichnung des Gegenstands, nicht der KB-Sprache (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" und "Language") - The title is the entity's canonical name and follows the subject's established designation, not the KB language (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" and "Language")
- Die Hauptbeschreibung steht weit oben - The main description goes near the top
- Auf verwandte Entities und Concepts verlinken, wo Beziehungen bestehen - Link to related entities and concepts wherever relationships exist
- Bei `provenance: sourced` oder `mixed` harte Fakten inline mit einer `[^cite-id]`-Fußnote belegen - - Under `provenance: sourced` or `mixed`, back hard facts inline with a `[^cite-id]` footnote -
`tools/wikitool cite add --page "<Title>" --source "Source - X"` erzeugt Id und Definition `tools/wikitool cite add --page "<Title>" --source "Source - X"` mints the id and its definition
- Wikipedia-Tonfall: sachlich, neutral, keine Werbesprache - Wikipedia register: factual, neutral, no marketing language
## Template ## Template
The block below is page material, so it is written in this instance's KB language
(`kb/CONVENTIONS.md` `language:`) rather than in the control plane's English - its headings
become the headings of every page `wikitool new entity` scaffolds.
```markdown ```markdown
# {name} # {name}
@@ -84,15 +98,15 @@ TODO: Ausführliche Informationen, nach sinnvollen Abschnitten gegliedert
- [{today}] - Page created via wikitool - [{today}] - Page created via wikitool
``` ```
Der Beziehungsabschnitt steht bewusst **nicht** im Template: er ist eine generierte Region, die The relationships section is deliberately **not** in the template: it is a generated region that
`wikitool xref` beim ersten Kanteneintrag zwischen Markern anlegt und aus `related:` neu `wikitool xref` creates between markers on the first edge entry and re-renders from `related:`.
rendert. Ein Autor schreibt dort nie hinein. Der Wert hinter `**Typ:**` bleibt der englische An author never writes into it. The value behind `**Typ:**` stays the English enum value - that is
Enum-Wert - danach filtert `search --field`. what `search --field` filters on.
--- ---
Ergänzende Hinweise: Additional notes:
- Entity-Seiten bilden den konkreten Bestand der Wissensbasis - Entity pages form the concrete inventory of the knowledge base
- Jeder Entity-Typ (project, system, tool usw.) ist eine Ausprägung dieses Basistyps - Every entity type (project, system, tool and so on) is a variant of this base type
- Die Beziehungen zwischen Entities tragen die Vernetzung des Wissensgraphen - The relationships between entities carry the knowledge graph's interconnection
+74 -66
View File
@@ -1,7 +1,7 @@
--- ---
type: types/type-spec.md type: types/type-spec.md
name: source name: source
description: Strukturierter Typ für Source-Seiten, die eingelesenes Rohmaterial erfassen und zusammenfassen description: Structured type for source pages that record and summarize ingested raw material
schema: types/source.schema.yaml schema: types/source.schema.yaml
subtype_field: source_type subtype_field: source_type
base_dir: sources base_dir: sources
@@ -20,71 +20,85 @@ layout:
# Source # Source
`source` ist der Typ für Seiten, die eingelesenes Rohmaterial zusammenfassen und katalogisieren. Source-Seiten sind die Brücke zwischen der `raw/`-Schicht (unveränderliche Quelldateien) und der `kb/`-Schicht (kompiliertes Wissen). Eine Source-Seite steht für **eine logische Quelle**, die mehrere Raw-Dateien umfassen kann. `source` is the type for pages that summarize and catalogue ingested raw material. Source pages are the bridge between the `raw/` layer (immutable source files) and the `kb/` layer (compiled knowledge). One source page stands for **one logical source**, which may span several raw files.
## Wann zu verwenden <!-- wikitool:toc -->
## Contents
- Zusammenfassung eines einzelnen externen Artikels, Dokuments oder einer Spezifikation - [When to use](#when-to-use)
- Erfassung mehrerer zusammengehöriger Notizen oder Gesprächsprotokolle als eine Quelle - [When NOT to use](#when-not-to-use)
- Dokumentation eines eingelesenen PDFs, Handbuchs oder sonstigen Dokuments - [Frontmatter](#frontmatter)
- Festhalten von Informationen zu einem Bild oder Diagramm - [Authoring guidance](#authoring-guidance)
- [Not Extracted](#not-extracted)
- [Template](#template)
<!-- /wikitool:toc -->
## Wann NICHT zu verwenden ## When to use
- Für kompiliertes Wissen (dann `entity` oder `concept`) - Summarizing a single external article, document or specification
- Für vergleichende Analysen (dann `comparison`) - Recording several related notes or meeting records as one source
- Für originären Wiki-Inhalt, der nicht aus Rohmaterial abgeleitet ist - Documenting an ingested PDF, manual or other document
- Capturing information about an image or a diagram
## When NOT to use
- For compiled knowledge (use `entity` or `concept`)
- For comparative analyses (use `comparison`)
- For original wiki content not derived from raw material
## Frontmatter ## Frontmatter
| Feld | Pflicht | Verwendung | | Field | Required | Use |
|---|---:|---| |---|---:|---|
| `type` | Ja | `types/source.md` | | `type` | Yes | `types/source.md` |
| `source_type` | Ja | Eines von: transcript, analysis, article, document, notes, tracker, unclassified - kein Default, siehe unten | | `source_type` | Yes | One of: transcript, analysis, article, document, notes, tracker, unclassified - no default, see below |
| `author` | Ja | Urheber des Quellmaterials | | `author` | Yes | Originator of the source material |
| `raw_files` | Ja | Raw-Dateipfade, die diese Quelle **besitzt** - siehe "Eine Raw-Datei, ein Besitzer" unten | | `raw_files` | Yes | Raw file paths this source **owns** - see "One raw file, one owner" below |
| `fidelity` | Ja (im Werkzeug, nicht im Schema) | Wie treu die *Erfassung* ist: `verbatim`, `published`, `secondhand`, `nontextual` - Capture-Feld, siehe unten | | `fidelity` | Yes (in the tool, not in the schema) | How faithful the *capture* is: `verbatim`, `published`, `secondhand`, `nontextual` - a capture field, see below |
| `authority` | Ja (im Werkzeug, nicht im Schema) | Was das Material über seinen *Gegenstand* behaupten darf: `normative`, `reporting`, `opinion` - Capture-Feld, siehe unten | | `authority` | Yes (in the tool, not in the schema) | What the material may claim about its *subject*: `normative`, `reporting`, `opinion` - a capture field, see below |
| `source_url` | Nein | Ursprungs-URL bei externen Quellen | | `source_url` | No | Origin URL for external sources |
| `source_language` | Nein | ISO-639-1-Code der Sprache des Rohmaterials, z. B. `de`, `en`, `fr` | | `source_language` | No | ISO 639-1 code of the raw material's language, e.g. `de`, `en`, `fr` |
| `date` | Ja | Veröffentlichungs- oder Erstellungsdatum (YYYY-MM-DD) | | `date` | Yes | Publication or creation date (YYYY-MM-DD) |
| `tags` | Nein | Navigations-Tags zur Kategorisierung | | `tags` | No | Navigation tags for categorization |
| `entities` | Nein | Titel der in dieser Quelle erwähnten Entities | | `entities` | No | Titles of the entities mentioned in this source |
| `concepts` | Nein | Titel der in dieser Quelle erwähnten Concepts | | `concepts` | No | Titles of the concepts mentioned in this source |
| `summary` | Ja | Einzeiler für `kb/index.md` | | `summary` | Yes | One-liner for `kb/index.md` |
## Autorenanweisungen ## Authoring guidance
- Der Titel beginnt mit "Source - ", gefolgt vom Namen der Quelle - The title starts with "Source - ", followed by the name of the source
- `source_type` hat **keinen Default** - `wikitool new source` verweigert ohne einen expliziten Wert. Ist die Kategorie unklar, `unclassified` setzen statt zu raten; das ist ein sichtbares Katalogfach mit beratendem `lint`-Befund, kein Sammelbecken. Was `analysis` von `document` trennt und welche Area welchen Wert hält: [kb/sources/COLLECTION.md](../kb/sources/COLLECTION.md) - `source_type` has **no default** - `wikitool new source` refuses without an explicit value. Where the category is unclear, set `unclassified` rather than guessing; that is a visible catalog slot with an advisory `lint` finding, not a dumping ground. What separates `analysis` from `document`, and which area holds which value: [kb/sources/COLLECTION.md](../kb/sources/COLLECTION.md)
- `raw_files` listet jede Raw-Datei, die diese Quelle abdeckt (eine Source-Seite pro logischer Quelle, nicht pro Datei) - `raw_files` lists every raw file this source covers (one source page per logical source, not per file)
- `fidelity` und `authority` sind **Capture-Felder** (`capture_fields:` oben): am Drop-Punkt erhoben, danach nicht mehr frei änderbar. `wikitool raw accept --fidelity <wert> --authority <wert>` verweigert ohne beide; ohne `--page` druckt es stattdessen die fertige `new source --set fidelity=... --set authority=...`-Zeile, die `new source` seinerseits ohne beide Werte verweigert. `wikitool touch --set fidelity=<wert>` schreibt nur, solange das Feld fehlt - steht bereits ein Wert, verweigert es und verweist auf `raw accept --replaces` als einzigen Korrekturweg (eine korrigierte Erfassung ist eine neue Edition, keine Bearbeitung). `unknown` ist backfill-only: nur `wikitool touch` darf es schreiben, nie `raw accept` oder `new source` - dieselbe Konstruktion wie bei `source_language` für Seiten, die vor dieser Regel entstanden sind. Was die Werte bedeuten und wie sie sich unterscheiden: [raw/CONTRACT.md](../raw/CONTRACT.md#getting-a-file-in-incoming) - `fidelity` and `authority` are **capture fields** (`capture_fields:` above): recorded at the drop point and not freely changeable afterwards. `wikitool raw accept --fidelity <value> --authority <value>` refuses without both; without `--page` it instead prints the ready-made `new source --set fidelity=... --set authority=...` line, which `new source` in turn refuses without both values. `wikitool touch --set fidelity=<value>` only writes while the field is absent - where a value already stands, it refuses and points at `raw accept --replaces` as the one correction path (a corrected capture is a new edition, not an edit). `unknown` is backfill-only: only `wikitool touch` may write it, never `raw accept` or `new source` - the same construction as `source_language` uses for pages that predate this rule. What the values mean and how they differ: [raw/CONTRACT.md](../raw/CONTRACT.md#getting-a-file-in-incoming)
- Bei externen Artikeln immer `source_url` auf die Ursprungs-URL setzen - For external articles, always set `source_url` to the origin URL
- `source_language` auf die Sprache des Rohmaterials setzen, nicht auf die der Seite - Set `source_language` to the raw material's language, not the page's
- Die Seite wird in der KB-Sprache geschrieben, unabhängig von der Sprache der Quelle; wörtliche Passagen werden im Original zitiert (`kb/CONVENTIONS.md` § "Language") - The page is written in the KB language, whatever language the source is in; verbatim passages are quoted in the original (`kb/CONVENTIONS.md` § "Language")
- Kernaussagen im Abschnitt Summary zusammenfassen - Summarize the key claims in the summary section
- Handlungsbedarf in den Abschnitt Action Items - Put anything actionable in the action items section
- Bewusst Weggelassenes in den Abschnitt Not Extracted - siehe unten - Put deliberate omissions in the not-extracted section - see below
- Erwähnte Entities und Concepts unter Related Entities/Concepts verlinken - Link the entities and concepts mentioned under related entities/concepts
## Not Extracted ## Not Extracted
Die Entscheidung, dass Material *nicht* übernommen werden soll, ist nicht rekonstruierbar: nichts The decision that material should *not* be taken over cannot be reconstructed: nothing in the
im Repository kann sie neu herleiten, und `sources coverage` weiß nur, ob eine Raw-Datei von repository can re-derive it, and `sources coverage` only knows whether a raw file is claimed by
irgendeiner Source-Seite beansprucht wird - nie, ob jemand über ihren Inhalt entschieden hat. some source page - never whether anyone decided about its contents. Left unwritten, the same
Bleibt das unaufgeschrieben, wird dieselbe Quelle bei jedem späteren Durchgang neu verhandelt. source is renegotiated on every later pass.
- Jede bewusste Auslassung mit **Begründung** festhalten, nicht nur mit Dateinamen. - Record every deliberate omission with a **reason**, not just a filename.
- Pflicht, wenn der Ingest über `instructions/ingest-large-tree.md` lief - auf beiden Achsen: - Mandatory where the ingest ran through `instructions/ingest-large-tree.md` - on both axes: for
beim Tree-Ingest hält der Abschnitt fest, was aus dem Baum nicht übernommen wurde, bei einer a tree ingest the section records what was not taken from the tree; for a thematically broad
thematisch breiten Einzelquelle, welche genannten Gegenstände keine eigene Seite bekommen single source, which named subjects got no page of their own, and why. Optional for a single
haben und warum. Optional bei einer einzelnen kleinen Datei - aber ein leerer Abschnitt ist small file - but an empty section still beats a missing one.
immer noch besser als ein fehlender. - Belongs on the source page, not in `kb/log.md`: it is a statement about *this* source, and the
- Gehört auf die Source-Seite, nicht in `kb/log.md`: es ist eine Aussage über *diese* Quelle, log is chronological rather than per-source.
und das Log ist chronologisch, nicht quellenbezogen.
## Template ## Template
The block below is page material, so it is written in this instance's KB language
(`kb/CONVENTIONS.md` `language:`) rather than in the control plane's English - its headings
become the headings of every page `wikitool new source` scaffolds.
```markdown ```markdown
# Source: {name} # Source: {name}
@@ -121,24 +135,18 @@ TODO: 2-3 Absätze zu den Kernaussagen des Quellmaterials.
{concepts|bullets} {concepts|bullets}
``` ```
`# Source:` bleibt als Präfix stehen - es spiegelt den `title_prefix` und damit den Titel, unter `# Source:` stays as a prefix - it mirrors the `title_prefix` and with it the title the page is
dem die Seite verlinkt und zitiert wird. Der Wert hinter `**Typ:**` bleibt der englische linked and cited under. The value behind `**Typ:**` stays the English enum value. When
Enum-Wert. Fügt `wikitool cite` ein Zitat hinzu, entsteht am Seitenende der toolgeführte `wikitool cite` adds a citation, the tool-managed footnote block appears at the end of the page;
Fußnoten-Block; wie er heißt, entscheidet die Instanz in `kb/CONVENTIONS.md` (`sections:`). what it is called is the instance's decision in `kb/CONVENTIONS.md` (`sections:`).
--- ---
Ergänzende Hinweise: Additional notes:
- Source-Seiten sind der maßgebliche Katalog dessen, was an Rohmaterial eingelesen wurde - Source pages are the authoritative catalogue of what raw material has been ingested
- **Eine Raw-Datei, ein Besitzer.** Eine Raw-Datei steht in genau einem `raw_files:` - diese Seite ist dafür verantwortlich, sie zusammengefasst zu halten. Beliebig viele Seiten dürfen sie per `[^cite-id]` **zitieren**; ein Zitat ist Wiederverwendung, `raw_files:` ist eine Wartungszuständigkeit. Bei zwei Anspruchstellern ist undefiniert, welche Seite bei einer Änderung der Raw-Datei nachgezogen werden muss - dann verrotten beide still - **One raw file, one owner.** A raw file appears in exactly one `raw_files:` - that page is responsible for keeping it summarized. Any number of pages may **cite** it via `[^cite-id]`; a citation is reuse, `raw_files:` is a maintenance responsibility. With two claimants it is undefined which page has to be brought up to date when the raw file changes - and then both rot quietly
- Source-Seiten machen Wissen bis zum ursprünglichen Rohmaterial rückverfolgbar - Source pages make knowledge traceable back to the original raw material
- `raw_files:` enthält konkrete existierende Dateipfade, nie Verzeichnisse - `raw_files:` holds concrete existing file paths, never directories
- Eine `raw_files:`-Liste jenseits von etwa 15 Einträgen zeigt an, dass der Schnitt zu grob war - - A `raw_files:` list beyond roughly 15 entries indicates the cut was too coarse - the source should have been split into several source pages via `instructions/ingest-large-tree.md`
die Quelle hätte per `instructions/ingest-large-tree.md` in mehrere Source-Seiten geteilt - `entities:` plus `concepts:` beyond roughly 20 entries is the counterpart on the other axis: not cut too little, but compiled too much at once. Such a source is not split - a raw file has one owner - it needed the extract pass from `instructions/ingest-large-tree.md` § "A broad source is not cut", so that not every named subject gets a page
werden müssen
- `entities:` plus `concepts:` jenseits von etwa 20 Einträgen ist das Gegenstück auf der anderen
Achse: nicht zu wenig geschnitten, sondern zu viel auf einmal kompiliert. Geteilt wird eine
solche Quelle nicht - eine Raw-Datei hat einen Besitzer -, sie hätte den Extract-Pass aus
`instructions/ingest-large-tree.md` § "A broad source is not cut" gebraucht, damit nicht jeder
genannte Gegenstand eine Seite bekommt