Angelegt am 2026-10-10 auf Wunsch des Betreibers („weiteres Feature wären Handler in der Instanz“), als Folge von #200. Der Stub #200 sprach von Regeln „aus einer Kombination des Stacks und der Instanz“. #200 baut den Anteil des Stacks und lässt die Instanz nur parametrieren. Dieses Paket gibt der Instanz eigene Handler. Alle Fragen hat der Betreiber am 2026-10-10 nach Empfehlung entschieden.
Blockiert war durch #200 (Registry und Bausteine) und #207 (eingecheckte Instanz-Konfiguration). Beide sind geschlossen.
Stand (2026-10-11), abgeschlossen: veröffentlicht als Commit 0f39cfd in Version 10.0.0-beta.4; CI grün: Lauf 659 (verify, Tests, Live-Tracker-Suite, Versions-Gate, Replay als frische Instanz) und Lauf 660 (pwsh). Beim Schließen fand die Dokumentationsprüfung eine Lücke: die Beschreibung der Strukturprüfung von doctor (Kommando-Record, tools/CONTRACT.md, docs/reference/commands/instance-health.md) nannte sites/CONTRACT.md nicht, das doctor seit diesem Paket verlangt. Nachgezogen als Commit f25ee6a in 10.0.0-beta.5 (Patch); CI grün: Lauf 661 (verify, Tests) und Lauf 662 (pwsh). Sonst ist kein weiteres Dokument veraltet: geprüft wurden die Seiten, die Stufen, Eigentum und Upgrade beschreiben (docs/explanation/architecture.md, ownership-and-templates.md, pipeline-rationale.md, update-an-instance.md, upgrade-instance.md, INSTALL.md, README.md). Abweichungen vom ursprünglichen Entwurf stehen unter „Abweichungen beim Bau“.
Problem
Eine Instanz hat Websites, für die der Stack nie einen Handler mitliefern wird: interne Wikis, Hersteller-Doku im beruflichen Umfeld, Nischenseiten. Bis dahin blieb dort nur der allgemeine Abruf, und der ließ Seitenbedienung im Text stehen (Befund in #200).
Randbedingung: kein Code aus der Instanz
Ein Handler als Python-Modul im Repo der Instanz wäre Code, den wikitool ausführt und den ein git pull aus einem anderen Klon ändern kann. Genau das schließt #207 Entwurf 2 aus. Handler der Instanz sind deshalb reine Daten. Sie verwenden dieselben Bausteine wie die Handler des Stacks, wie #200 sie tatsächlich gebaut hat:
höchstens 3 Zeilen; mit meta nur, wenn das Meta-Element Inhalt hat, mit count nur, wenn die Regel mindestens ein Element trifft, sonst immer
Vererbung
parent: Rewrites, drop und checks des allgemeinen Handlers derselben Website kommen zuerst
Die Bausteine „Erkennung eingeloggt“ (#169) und „Unterseiten“ (#209) hat #200 nicht vorgebaut; sie sind Erweiterungen dieses Formats in ihren eigenen Paketen.
Entscheidungen des Betreibers (2026-10-10)
Vorrang: Ein Handler der Instanz für denselben Host und dasselbe Muster wie einer des Stacks ersetzt diesen. Dafür ist das Feature da, und der Header macht die Ersetzung sichtbar.
Ein Format für beide: Die Handler des Stacks werden selbst zu Daten-Dateien. Ein Handler der Instanz kann dann einen des Stacks als Vorlage kopieren.
Ablage sites/ im Root, eine Datei pro Website. Ein Handler ist eher Inhalt als Einstellung.
Entwurf (wie gebaut)
Ablage:sites/<name>.json im Instanz-Root, eingecheckt, der Instanz gehörend; der Dateiname ohne .json ist der Handler-Name. Daneben sites/CONTRACT.md (Stack-Eigentum, wird ausgeliefert). Form: tools/chemenu/sites/site.schema.json (jsonschema), dazu in tools/chemenu/sites/data.py die Prüfungen, die ein Schema nicht kann (Muster kompiliert, Platzhalter hat sein Feld, Elternteil existiert, Dateiname, Größe, keine Symlinks). dist export liefert sites/CONTRACT.md, keine Handler-Datei.
Handler des Stacks als Daten:tools/chemenu/sites/handlers/learn-microsoft.json und learn-microsoft-powershell-module.json; das Python-Modul learn_microsoft.py entfällt. Die Ausgabe ist für die Fixture-Seite byte-identisch (.md und .checks).
Vorrang: Der Header nennt site: <name> (instance), doctor listet jede Ersetzung. Stack-Handler, die vom ersetzten erben, behalten, was sie geerbt haben.
Regex-Sicherheit: Muster laufen nur gegen die URL, nie gegen das HTML; drop-Regeln sind keine Regex.
Prüfung:doctor validiert jede Datei unter sites/; eine kaputte Datei ist ein FAIL mit Datei und Grund. raw fetch bricht mit Exit 1 ab, bevor etwas geholt oder geschrieben wird, wenn der Host einer kaputten Datei der Host der URL ist (oder keiner lesbar ist); eine kaputte Datei für einen anderen Host stört nicht.
Wer schreibt: Agent oder Mensch; die Datei wird per publish eingecheckt. Die How-to-Seite docs/how-to/write-a-site-handler.md zeigt, wie man einen Stack-Handler als Vorlage kopiert.
Routing:AGENTS.md § Routing hat eine Zeile sites/; in § File naming ist nichts Neues nötig.
Abweichungen beim Bau
Format: Der Name kommt aus dem Dateinamen, nicht aus einem Feld. drop-Regeln tragen zusätzlich attr/value und inside, weil die Learn-Handler das brauchen. checks gelten nach den Bedingungen in der Tabelle oben, nicht „nur wenn eine drop-Regel getroffen hat“.
Neue Stufe sites:sites/ ist ein eigenes Stufenverzeichnis mit sites/CONTRACT.md. Dafür: ownership.CONTENT_STAGES, dist_cmd (_CONTENT_PREFIXES; der Vertrag wird über CONTRACT_ONLY_STAGES ausgeliefert), docs_verify (Stufenverträge, Inhaltsverzeichnisse, Canary sites/example.json), toc.py, .gitignore (!sites/**), git_publish (Dateigruppe „Site handlers“), doctor (check_structure verlangt sites/CONTRACT.md).
Kaputte Dateien: eine Datei, die den Namen eines Stack-Handlers für einen anderen Host oder Pfad trägt, zwei Dateien mit gleichem Host und Pfad, ein fehlender oder zyklischer parent und ein Symlink sind „kaputt“ (FAIL, Abbruch wie oben).
Python-Module bleiben als Registrierungsweg erhalten, falls ein Handler mehr braucht als Daten.
Doku: AGENTS.md § Routing, raw/CONTRACT.md, tools/README.md, docs/reference/configuration.md, docs/explanation/architecture.md, docs/how-to/README.md, instructions/dev/doc-pull-through.md; beim Schließen dazu die doctor-Beschreibung in tools/CONTRACT.md und docs/reference/commands/instance-health.md
Akzeptanzkriterien
Eine Datei sites/example.json mit Host, drop-Regel und einer Hinweiszeile wirkt auf raw fetch gegen einen Testserver: Das Element fehlt im Text, die Zeile erscheint als CHECK, der Header nennt (instance). (test_site_files.py)
Ein Handler der Instanz mit Host und Muster eines Stack-Handlers ersetzt diesen; doctor listet die Ersetzung.
Eine Datei, die das Schema verletzt: doctor FAIL mit Datei und Fehler. raw fetch auf eine passende URL endet mit exit 1 und schreibt nichts nach incoming/.
Die Stack-Handler aus #200 als Daten-Dateien ergeben für die Fixture-Seite byte-identische .md wie vor der Umstellung.
Ohne sites/ ändert sich nichts gegenüber dem Stand nach #200.
dist export enthält sites/CONTRACT.md und keine andere Datei unter sites/.
pytest (2819 passed, 4 skipped), docs verify, instructions verify lokal grün; CI grün (Läufe 659 und 660 auf 0f39cfd, Läufe 661 und 662 auf f25ee6a).
Versionsteil
--minor. Neues, optionales Verzeichnis, das eine ältere Version ignoriert. Die Umstellung der Stack-Handler ändert keine Ausgabe. Drop-in in beide Richtungen. (Gebumpt wurde innerhalb der laufenden Kandidatin 10.0.0-beta.4; der Nachzug beim Schließen ist ein Patch-Bump auf 10.0.0-beta.5.)
> Angelegt am 2026-10-10 auf Wunsch des Betreibers („weiteres Feature wären Handler in der Instanz“), als Folge von #200. Der Stub #200 sprach von Regeln „aus einer Kombination des Stacks und der Instanz“. #200 baut den Anteil des Stacks und lässt die Instanz nur parametrieren. Dieses Paket gibt der Instanz eigene Handler. Alle Fragen hat der Betreiber am 2026-10-10 nach Empfehlung entschieden.
>
> **Blockiert war durch #200** (Registry und Bausteine) und **#207** (eingecheckte Instanz-Konfiguration). Beide sind geschlossen.
>
> **Stand (2026-10-11), abgeschlossen:** veröffentlicht als Commit `0f39cfd` in Version `10.0.0-beta.4`; CI grün: Lauf 659 (`verify`, Tests, Live-Tracker-Suite, Versions-Gate, Replay als frische Instanz) und Lauf 660 (`pwsh`). Beim Schließen fand die Dokumentationsprüfung eine Lücke: die Beschreibung der Strukturprüfung von `doctor` (Kommando-Record, `tools/CONTRACT.md`, `docs/reference/commands/instance-health.md`) nannte `sites/CONTRACT.md` nicht, das `doctor` seit diesem Paket verlangt. Nachgezogen als Commit `f25ee6a` in `10.0.0-beta.5` (Patch); CI grün: Lauf 661 (`verify`, Tests) und Lauf 662 (`pwsh`). Sonst ist kein weiteres Dokument veraltet: geprüft wurden die Seiten, die Stufen, Eigentum und Upgrade beschreiben (`docs/explanation/architecture.md`, `ownership-and-templates.md`, `pipeline-rationale.md`, `update-an-instance.md`, `upgrade-instance.md`, `INSTALL.md`, `README.md`). Abweichungen vom ursprünglichen Entwurf stehen unter „Abweichungen beim Bau“.
## Problem
Eine Instanz hat Websites, für die der Stack nie einen Handler mitliefern wird: interne Wikis, Hersteller-Doku im beruflichen Umfeld, Nischenseiten. Bis dahin blieb dort nur der allgemeine Abruf, und der ließ Seitenbedienung im Text stehen (Befund in #200).
## Randbedingung: kein Code aus der Instanz
Ein Handler als Python-Modul im Repo der Instanz wäre Code, den `wikitool` ausführt und den ein `git pull` aus einem anderen Klon ändern kann. Genau das schließt #207 Entwurf 2 aus. Handler der Instanz sind deshalb **reine Daten**. Sie verwenden dieselben Bausteine wie die Handler des Stacks, wie #200 sie tatsächlich gebaut hat:
| Baustein (aus #200) | Als Daten |
|---|---|
| Host, Pfadmuster | `host` (exakt, klein), `path` (Regex gegen den Pfadanfang; der längste Treffer gewinnt) |
| Standardform der URL | `rewrites`: höchstens 5 Regex-Ersetzungen auf der URL, `{locale}` in der Ersetzung |
| `drop` | höchstens 40 Regeln `{tag, id, class, attr, value, inside}`, keine Regex |
| `checks` | höchstens 3 Zeilen; mit `meta` nur, wenn das Meta-Element Inhalt hat, mit `count` nur, wenn die Regel mindestens ein Element trifft, sonst immer |
| Vererbung | `parent`: Rewrites, `drop` und `checks` des allgemeinen Handlers derselben Website kommen zuerst |
Die Bausteine „Erkennung eingeloggt“ (#169) und „Unterseiten“ (#209) hat #200 nicht vorgebaut; sie sind Erweiterungen dieses Formats in ihren eigenen Paketen.
## Entscheidungen des Betreibers (2026-10-10)
1. **Vorrang:** Ein Handler der Instanz für denselben Host und dasselbe Muster wie einer des Stacks **ersetzt** diesen. Dafür ist das Feature da, und der Header macht die Ersetzung sichtbar.
2. **Ein Format für beide:** Die Handler des Stacks werden selbst zu Daten-Dateien. Ein Handler der Instanz kann dann einen des Stacks als Vorlage kopieren.
3. **Ablage `sites/` im Root**, eine Datei pro Website. Ein Handler ist eher Inhalt als Einstellung.
## Entwurf (wie gebaut)
1. **Ablage:** `sites/<name>.json` im Instanz-Root, eingecheckt, der Instanz gehörend; der Dateiname ohne `.json` ist der Handler-Name. Daneben `sites/CONTRACT.md` (Stack-Eigentum, wird ausgeliefert). Form: `tools/chemenu/sites/site.schema.json` (`jsonschema`), dazu in `tools/chemenu/sites/data.py` die Prüfungen, die ein Schema nicht kann (Muster kompiliert, Platzhalter hat sein Feld, Elternteil existiert, Dateiname, Größe, keine Symlinks). `dist export` liefert `sites/CONTRACT.md`, keine Handler-Datei.
2. **Handler des Stacks als Daten:** `tools/chemenu/sites/handlers/learn-microsoft.json` und `learn-microsoft-powershell-module.json`; das Python-Modul `learn_microsoft.py` entfällt. Die Ausgabe ist für die Fixture-Seite byte-identisch (`.md` und `.checks`).
3. **Vorrang:** Der Header nennt `site: <name> (instance)`, `doctor` listet jede Ersetzung. Stack-Handler, die vom ersetzten erben, behalten, was sie geerbt haben.
4. **Regex-Sicherheit:** Muster laufen nur gegen die URL, nie gegen das HTML; `drop`-Regeln sind keine Regex.
5. **Prüfung:** `doctor` validiert jede Datei unter `sites/`; eine kaputte Datei ist ein FAIL mit Datei und Grund. `raw fetch` bricht mit Exit 1 ab, bevor etwas geholt oder geschrieben wird, wenn der Host einer kaputten Datei der Host der URL ist (oder keiner lesbar ist); eine kaputte Datei für einen anderen Host stört nicht.
6. **Wer schreibt:** Agent oder Mensch; die Datei wird per `publish` eingecheckt. Die How-to-Seite `docs/how-to/write-a-site-handler.md` zeigt, wie man einen Stack-Handler als Vorlage kopiert.
7. **Routing:** `AGENTS.md` § Routing hat eine Zeile `sites/`; in § File naming ist nichts Neues nötig.
## Abweichungen beim Bau
- **Format:** Der Name kommt aus dem Dateinamen, nicht aus einem Feld. `drop`-Regeln tragen zusätzlich `attr`/`value` und `inside`, weil die Learn-Handler das brauchen. `checks` gelten nach den Bedingungen in der Tabelle oben, nicht „nur wenn eine `drop`-Regel getroffen hat“.
- **Neue Stufe `sites`:** `sites/` ist ein eigenes Stufenverzeichnis mit `sites/CONTRACT.md`. Dafür: `ownership.CONTENT_STAGES`, `dist_cmd` (`_CONTENT_PREFIXES`; der Vertrag wird über `CONTRACT_ONLY_STAGES` ausgeliefert), `docs_verify` (Stufenverträge, Inhaltsverzeichnisse, Canary `sites/example.json`), `toc.py`, `.gitignore` (`!sites/**`), `git_publish` (Dateigruppe „Site handlers“), `doctor` (`check_structure` verlangt `sites/CONTRACT.md`).
- **Kaputte Dateien:** eine Datei, die den Namen eines Stack-Handlers für einen anderen Host oder Pfad trägt, zwei Dateien mit gleichem Host und Pfad, ein fehlender oder zyklischer `parent` und ein Symlink sind „kaputt“ (FAIL, Abbruch wie oben).
- **Python-Module** bleiben als Registrierungsweg erhalten, falls ein Handler mehr braucht als Daten.
## Betroffene Dateien
- `tools/chemenu/sites/` (`__init__.py`, `data.py`, `site.schema.json`, `handlers/`), `tools/chemenu/commands/raw/fetch.py`, `doctor.py`, `dist_cmd.py`, `docs_verify.py`, `git_publish.py`, `toc.py`, `ownership.py`, `.gitignore`
- neu: `sites/CONTRACT.md`, `docs/how-to/write-a-site-handler.md`, `tools/chemenu/tests/test_site_files.py`
- Doku: `AGENTS.md` § Routing, `raw/CONTRACT.md`, `tools/README.md`, `docs/reference/configuration.md`, `docs/explanation/architecture.md`, `docs/how-to/README.md`, `instructions/dev/doc-pull-through.md`; beim Schließen dazu die `doctor`-Beschreibung in `tools/CONTRACT.md` und `docs/reference/commands/instance-health.md`
## Akzeptanzkriterien
- [x] Eine Datei `sites/example.json` mit Host, `drop`-Regel und einer Hinweiszeile wirkt auf `raw fetch` gegen einen Testserver: Das Element fehlt im Text, die Zeile erscheint als `CHECK`, der Header nennt `(instance)`. (`test_site_files.py`)
- [x] Ein Handler der Instanz mit Host und Muster eines Stack-Handlers ersetzt diesen; `doctor` listet die Ersetzung.
- [x] Eine Datei, die das Schema verletzt: `doctor` FAIL mit Datei und Fehler. `raw fetch` auf eine passende URL endet mit exit 1 und schreibt nichts nach `incoming/`.
- [x] Die Stack-Handler aus #200 als Daten-Dateien ergeben für die Fixture-Seite byte-identische `.md` wie vor der Umstellung.
- [x] Ohne `sites/` ändert sich nichts gegenüber dem Stand nach #200.
- [x] `dist export` enthält `sites/CONTRACT.md` und keine andere Datei unter `sites/`.
- [x] `pytest` (2819 passed, 4 skipped), `docs verify`, `instructions verify` lokal grün; CI grün (Läufe 659 und 660 auf `0f39cfd`, Läufe 661 und 662 auf `f25ee6a`).
## Versionsteil
**`--minor`.** Neues, optionales Verzeichnis, das eine ältere Version ignoriert. Die Umstellung der Stack-Handler ändert keine Ausgabe. Drop-in in beide Richtungen. (Gebumpt wurde innerhalb der laufenden Kandidatin `10.0.0-beta.4`; der Nachzug beim Schließen ist ein Patch-Bump auf `10.0.0-beta.5`.)
## Kette
Kette „Beschaffung und Konfiguration“
- Vorgänger: #200
- Nachfolger: #209
Changelog: Die drei Fragen sind nach Empfehlung entschieden (2026-10-10): Die Instanz ersetzt einen Stack-Handler, ein Datenformat für beide, Ablage in sites/ im Root. Daraus folgen sites/CONTRACT.md (vom Stack, unverändert ausgeliefert), eine Routing-Zeile in AGENTS.md und die Umstellung der Stack-Handler aus #200 auf Daten ohne Änderung der Ausgabe. kind/decision → kind/build, blockiert bleibt es durch #200/#207.
**Changelog:** Die drei Fragen sind nach Empfehlung entschieden (2026-10-10): Die Instanz ersetzt einen Stack-Handler, ein Datenformat für beide, Ablage in `sites/` im Root. Daraus folgen `sites/CONTRACT.md` (vom Stack, unverändert ausgeliefert), eine Routing-Zeile in `AGENTS.md` und die Umstellung der Stack-Handler aus #200 auf Daten ohne Änderung der Ausgabe. `kind/decision` → `kind/build`, blockiert bleibt es durch #200/#207.
Nicht mehr blockiert:#200 ist geschlossen (10.0.0-beta.3, Commit 2213d68, CI-Läufe 657/658 grün), #207 war es schon - status/blocked entfernt.
Für den Bau zu beachten: Die Bausteine in #200 sind schon als Daten gebaut (Site, Rewrite, web_capture.Rule, Check in tools/chemenu/sites/), Vererbung über ein Feld parent=. Zwei Stellen dieses Bodys passen nicht mehr ganz zum gebauten Stand und sollten vor dem Bau gegen den Body von #200 (Abschnitt „Entwurf (gebauter Stand)“) abgeglichen werden:
Tabelle „Baustein → Als Daten“, Zeile checks: gebaut sind die Bedingungen „<meta name=…> hat Inhalt“ und „Selektor trifft mindestens ein Element“, nicht „wenn eine drop-Regel getroffen hat“.
Zeilen #169/#209: Diese Erweiterungspunkte sind nicht vorgebaut; sie kommen als weitere Felder von Site.
drop-Regeln können zusätzlich einen Attributwert und „innerhalb von Tag“ (inside) tragen.
**Nicht mehr blockiert:** #200 ist geschlossen (`10.0.0-beta.3`, Commit `2213d68`, CI-Läufe 657/658 grün), #207 war es schon - `status/blocked` entfernt.
Für den Bau zu beachten: Die Bausteine in #200 sind schon als Daten gebaut (`Site`, `Rewrite`, `web_capture.Rule`, `Check` in `tools/chemenu/sites/`), Vererbung über ein Feld `parent=`. Zwei Stellen dieses Bodys passen nicht mehr ganz zum gebauten Stand und sollten vor dem Bau gegen den Body von #200 (Abschnitt „Entwurf (gebauter Stand)“) abgeglichen werden:
- Tabelle „Baustein → Als Daten“, Zeile `checks`: gebaut sind die Bedingungen „`<meta name=…>` hat Inhalt“ und „Selektor trifft mindestens ein Element“, nicht „wenn eine `drop`-Regel getroffen hat“.
- Zeilen #169/#209: Diese Erweiterungspunkte sind nicht vorgebaut; sie kommen als weitere Felder von `Site`.
- `drop`-Regeln können zusätzlich einen Attributwert und „innerhalb von Tag“ (`inside`) tragen.
Changelog: Gebaut und veröffentlicht als 0f39cfd (Version 10.0.0-beta.4), CI grün (Läufe 659, 660). Eine Instanz legt eigene Site-Handler als JSON-Dateien unter sites/ ab; die zwei Handler des Stacks sind jetzt Datendateien im selben Format (tools/chemenu/sites/handlers/), ihre Ausgabe ist für die Fixture-Seite byte-identisch. Ein Handler der Instanz mit gleichem Host und Pfad ersetzt den des Stacks, der Header nennt site: <name> (instance), doctor listet die Ersetzung und meldet kaputte Dateien als FAIL, raw fetch bricht für deren Host mit Exit 1 ab. Abweichungen vom Entwurf stehen im Body unter „Abweichungen beim Bau“: neue Stufe sites mit sites/CONTRACT.md, Name aus dem Dateinamen, drop-Regeln mit attr/value/inside, checks nach Meta-/Zähl-Bedingung. Doku nachgezogen (AGENTS.md § Routing, raw/CONTRACT.md, tools/README.md, Konfigurationsreferenz, neue How-to-Seite).
**Changelog:** Gebaut und veröffentlicht als `0f39cfd` (Version `10.0.0-beta.4`), CI grün (Läufe 659, 660). Eine Instanz legt eigene Site-Handler als JSON-Dateien unter `sites/` ab; die zwei Handler des Stacks sind jetzt Datendateien im selben Format (`tools/chemenu/sites/handlers/`), ihre Ausgabe ist für die Fixture-Seite byte-identisch. Ein Handler der Instanz mit gleichem Host und Pfad ersetzt den des Stacks, der Header nennt `site: <name> (instance)`, `doctor` listet die Ersetzung und meldet kaputte Dateien als FAIL, `raw fetch` bricht für deren Host mit Exit 1 ab. Abweichungen vom Entwurf stehen im Body unter „Abweichungen beim Bau“: neue Stufe `sites` mit `sites/CONTRACT.md`, Name aus dem Dateinamen, `drop`-Regeln mit `attr`/`value`/`inside`, `checks` nach Meta-/Zähl-Bedingung. Doku nachgezogen (`AGENTS.md` § Routing, `raw/CONTRACT.md`, `tools/README.md`, Konfigurationsreferenz, neue How-to-Seite).
Changelog: Body in Endzustand gebracht und geschlossen. Neu gegenüber dem Stand beim Bau: Die Pull-through-Prüfung beim Schließen fand eine Lücke, die doctor-Beschreibung der Strukturprüfung nannte sites/CONTRACT.md nicht. Nachgezogen als f25ee6a (10.0.0-beta.5, Patch), CI grün: Läufe 661 und 662. Weitere Dokumente (architecture.md, ownership-and-templates.md, pipeline-rationale.md, Update-/Upgrade-Anleitungen, INSTALL.md, README.md) sind nicht veraltet.
**Changelog:** Body in Endzustand gebracht und geschlossen. Neu gegenüber dem Stand beim Bau: Die Pull-through-Prüfung beim Schließen fand eine Lücke, die `doctor`-Beschreibung der Strukturprüfung nannte `sites/CONTRACT.md` nicht. Nachgezogen als `f25ee6a` (`10.0.0-beta.5`, Patch), CI grün: Läufe 661 und 662. Weitere Dokumente (`architecture.md`, `ownership-and-templates.md`, `pipeline-rationale.md`, Update-/Upgrade-Anleitungen, `INSTALL.md`, `README.md`) sind nicht veraltet.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
Eine Instanz hat Websites, für die der Stack nie einen Handler mitliefern wird: interne Wikis, Hersteller-Doku im beruflichen Umfeld, Nischenseiten. Bis dahin blieb dort nur der allgemeine Abruf, und der ließ Seitenbedienung im Text stehen (Befund in #200).
Randbedingung: kein Code aus der Instanz
Ein Handler als Python-Modul im Repo der Instanz wäre Code, den
wikitoolausführt und den eingit pullaus einem anderen Klon ändern kann. Genau das schließt #207 Entwurf 2 aus. Handler der Instanz sind deshalb reine Daten. Sie verwenden dieselben Bausteine wie die Handler des Stacks, wie #200 sie tatsächlich gebaut hat:host(exakt, klein),path(Regex gegen den Pfadanfang; der längste Treffer gewinnt)rewrites: höchstens 5 Regex-Ersetzungen auf der URL,{locale}in der Ersetzungdrop{tag, id, class, attr, value, inside}, keine Regexchecksmetanur, wenn das Meta-Element Inhalt hat, mitcountnur, wenn die Regel mindestens ein Element trifft, sonst immerparent: Rewrites,dropundchecksdes allgemeinen Handlers derselben Website kommen zuerstDie Bausteine „Erkennung eingeloggt“ (#169) und „Unterseiten“ (#209) hat #200 nicht vorgebaut; sie sind Erweiterungen dieses Formats in ihren eigenen Paketen.
Entscheidungen des Betreibers (2026-10-10)
sites/im Root, eine Datei pro Website. Ein Handler ist eher Inhalt als Einstellung.Entwurf (wie gebaut)
sites/<name>.jsonim Instanz-Root, eingecheckt, der Instanz gehörend; der Dateiname ohne.jsonist der Handler-Name. Danebensites/CONTRACT.md(Stack-Eigentum, wird ausgeliefert). Form:tools/chemenu/sites/site.schema.json(jsonschema), dazu intools/chemenu/sites/data.pydie Prüfungen, die ein Schema nicht kann (Muster kompiliert, Platzhalter hat sein Feld, Elternteil existiert, Dateiname, Größe, keine Symlinks).dist exportliefertsites/CONTRACT.md, keine Handler-Datei.tools/chemenu/sites/handlers/learn-microsoft.jsonundlearn-microsoft-powershell-module.json; das Python-Modullearn_microsoft.pyentfällt. Die Ausgabe ist für die Fixture-Seite byte-identisch (.mdund.checks).site: <name> (instance),doctorlistet jede Ersetzung. Stack-Handler, die vom ersetzten erben, behalten, was sie geerbt haben.drop-Regeln sind keine Regex.doctorvalidiert jede Datei untersites/; eine kaputte Datei ist ein FAIL mit Datei und Grund.raw fetchbricht mit Exit 1 ab, bevor etwas geholt oder geschrieben wird, wenn der Host einer kaputten Datei der Host der URL ist (oder keiner lesbar ist); eine kaputte Datei für einen anderen Host stört nicht.publisheingecheckt. Die How-to-Seitedocs/how-to/write-a-site-handler.mdzeigt, wie man einen Stack-Handler als Vorlage kopiert.AGENTS.md§ Routing hat eine Zeilesites/; in § File naming ist nichts Neues nötig.Abweichungen beim Bau
drop-Regeln tragen zusätzlichattr/valueundinside, weil die Learn-Handler das brauchen.checksgelten nach den Bedingungen in der Tabelle oben, nicht „nur wenn einedrop-Regel getroffen hat“.sites:sites/ist ein eigenes Stufenverzeichnis mitsites/CONTRACT.md. Dafür:ownership.CONTENT_STAGES,dist_cmd(_CONTENT_PREFIXES; der Vertrag wird überCONTRACT_ONLY_STAGESausgeliefert),docs_verify(Stufenverträge, Inhaltsverzeichnisse, Canarysites/example.json),toc.py,.gitignore(!sites/**),git_publish(Dateigruppe „Site handlers“),doctor(check_structureverlangtsites/CONTRACT.md).parentund ein Symlink sind „kaputt“ (FAIL, Abbruch wie oben).Betroffene Dateien
tools/chemenu/sites/(__init__.py,data.py,site.schema.json,handlers/),tools/chemenu/commands/raw/fetch.py,doctor.py,dist_cmd.py,docs_verify.py,git_publish.py,toc.py,ownership.py,.gitignoresites/CONTRACT.md,docs/how-to/write-a-site-handler.md,tools/chemenu/tests/test_site_files.pyAGENTS.md§ Routing,raw/CONTRACT.md,tools/README.md,docs/reference/configuration.md,docs/explanation/architecture.md,docs/how-to/README.md,instructions/dev/doc-pull-through.md; beim Schließen dazu diedoctor-Beschreibung intools/CONTRACT.mdunddocs/reference/commands/instance-health.mdAkzeptanzkriterien
sites/example.jsonmit Host,drop-Regel und einer Hinweiszeile wirkt aufraw fetchgegen einen Testserver: Das Element fehlt im Text, die Zeile erscheint alsCHECK, der Header nennt(instance). (test_site_files.py)doctorlistet die Ersetzung.doctorFAIL mit Datei und Fehler.raw fetchauf eine passende URL endet mit exit 1 und schreibt nichts nachincoming/..mdwie vor der Umstellung.sites/ändert sich nichts gegenüber dem Stand nach #200.dist exportenthältsites/CONTRACT.mdund keine andere Datei untersites/.pytest(2819 passed, 4 skipped),docs verify,instructions verifylokal grün; CI grün (Läufe 659 und 660 auf0f39cfd, Läufe 661 und 662 auff25ee6a).Versionsteil
--minor. Neues, optionales Verzeichnis, das eine ältere Version ignoriert. Die Umstellung der Stack-Handler ändert keine Ausgabe. Drop-in in beide Richtungen. (Gebumpt wurde innerhalb der laufenden Kandidatin10.0.0-beta.4; der Nachzug beim Schließen ist ein Patch-Bump auf10.0.0-beta.5.)Kette
Kette „Beschaffung und Konfiguration“
Changelog: Die drei Fragen sind nach Empfehlung entschieden (2026-10-10): Die Instanz ersetzt einen Stack-Handler, ein Datenformat für beide, Ablage in
sites/im Root. Daraus folgensites/CONTRACT.md(vom Stack, unverändert ausgeliefert), eine Routing-Zeile inAGENTS.mdund die Umstellung der Stack-Handler aus #200 auf Daten ohne Änderung der Ausgabe.kind/decision→kind/build, blockiert bleibt es durch #200/#207.Nicht mehr blockiert: #200 ist geschlossen (
10.0.0-beta.3, Commit2213d68, CI-Läufe 657/658 grün), #207 war es schon -status/blockedentfernt.Für den Bau zu beachten: Die Bausteine in #200 sind schon als Daten gebaut (
Site,Rewrite,web_capture.Rule,Checkintools/chemenu/sites/), Vererbung über ein Feldparent=. Zwei Stellen dieses Bodys passen nicht mehr ganz zum gebauten Stand und sollten vor dem Bau gegen den Body von #200 (Abschnitt „Entwurf (gebauter Stand)“) abgeglichen werden:checks: gebaut sind die Bedingungen „<meta name=…>hat Inhalt“ und „Selektor trifft mindestens ein Element“, nicht „wenn einedrop-Regel getroffen hat“.Site.drop-Regeln können zusätzlich einen Attributwert und „innerhalb von Tag“ (inside) tragen.Changelog: Gebaut und veröffentlicht als
0f39cfd(Version10.0.0-beta.4), CI grün (Läufe 659, 660). Eine Instanz legt eigene Site-Handler als JSON-Dateien untersites/ab; die zwei Handler des Stacks sind jetzt Datendateien im selben Format (tools/chemenu/sites/handlers/), ihre Ausgabe ist für die Fixture-Seite byte-identisch. Ein Handler der Instanz mit gleichem Host und Pfad ersetzt den des Stacks, der Header nenntsite: <name> (instance),doctorlistet die Ersetzung und meldet kaputte Dateien als FAIL,raw fetchbricht für deren Host mit Exit 1 ab. Abweichungen vom Entwurf stehen im Body unter „Abweichungen beim Bau“: neue Stufesitesmitsites/CONTRACT.md, Name aus dem Dateinamen,drop-Regeln mitattr/value/inside,checksnach Meta-/Zähl-Bedingung. Doku nachgezogen (AGENTS.md§ Routing,raw/CONTRACT.md,tools/README.md, Konfigurationsreferenz, neue How-to-Seite).Changelog: Body in Endzustand gebracht und geschlossen. Neu gegenüber dem Stand beim Bau: Die Pull-through-Prüfung beim Schließen fand eine Lücke, die
doctor-Beschreibung der Strukturprüfung nanntesites/CONTRACT.mdnicht. Nachgezogen alsf25ee6a(10.0.0-beta.5, Patch), CI grün: Läufe 661 und 662. Weitere Dokumente (architecture.md,ownership-and-templates.md,pipeline-rationale.md, Update-/Upgrade-Anleitungen,INSTALL.md,README.md) sind nicht veraltet.