Sammel-Issue für den Weg zum MCP-Leseserver. Es hat nie selbst implementiert — es hielt die
Reihenfolge, die getroffenen Entscheidungen und die Messungen, auf denen der Entwurf steht, damit
eine Sitzung kalt einsteigen kann, ohne die Debatte vom 2026-09-01 zu wiederholen.
Ziel war:chemenu bekommt einen zweiten Konsumenten. CLI und MCP-Server sind zwei Adapter
auf denselben Kern — nicht ein CLI mit angeschraubter Netzwerkschnittstelle. Das ist erreicht.
Geschlossen am 2026-09-04. Die Sequenz ist abgearbeitet, der Server läuft, alle zehn Befunde
sind adressiert. Das letzte offene Kriterium — der Nachweis über ein Deployment — ist nach #37
übernommen, wo es hingehört: es hängt vollständig am Container-Image und an keiner Frage, die
dieses Issue noch beantworten könnte. #37 ist damit das einzige verbleibende Issue auf diesem
Weg. Was hier stehen bleibt, ist das Entscheidungsprotokoll und die gemessene Grundlage; beides
ist ausdrücklich nicht neu aufzumachen.
Der Server liegt in tools/chemenu/mcp/ und läuft auf beiden Transports (stdio, streamable-http) gegen den echten Korpus. Menschendoku: INSTALL-MCP.md (2.4.1, wird von dist export ausgeliefert), Betriebsablauf in instructions/mcp-read-server.md. Einzelheiten je
Issue in dessen Abschlusskommentar.
Nicht mehr auf dem kritischen Pfad, unverändert offen:#32 (Ingest-Queue, hing an #19, dessen
Vorbedingung erfüllt ist; teilt sich die Mechanik mit #16) · #35 (semantisches Backend, Auslöser
ist ein Korpus, an dem rg nicht mehr trägt) · #6 (Backlink-Ranking, läuft durch dieselbe Fusion
wie #35 und ist seit 2026-09-04 als status/blocked an #35 gehängt).
Entschieden — nicht neu aufmachen
Frage
Entscheidung
In einem Satz
Plattform
Python. Kein Go, auch nicht als Option
Die drei Go-Argumente lösen sich am eigenen Vorhaben auf; Herleitung im Kommentar zu #19
Eine Suchanfrage ist nicht zurechenbar, eine Einreichung schon
Staleness
Commit-SHA in jeder Antwort
Macht aus einer stillen veralteten Antwort eine sichtbare; derselbe SHA ist der Cache-Key
Sync
Polling (git fetch && git reset --hard)
Kein eingehender Endpunkt, keine Signaturprüfung. Webhook ist spätere Optimierung
Retrieval
hinter der vorhandenen Backend-Registry
search/registry.py: „a new module plus one line here"
Deployment
gehört nicht in dieses Repo
Private Infrastruktur, öffentlicher Tracker
Gemessene Grundlage — Prognose hat gehalten
Ausgangsmessung 2026-09-01, Nachmessung nach #33/#31 am 2026-09-02:
Prognose
gemessen
Korpus-Parse (176 Seiten, CSafeLoader + Cache)
246 → 82 ms
265 → 54 ms
wikitool search end-to-end
593 ms
593 → 347 ms
Die verbleibenden ~262 ms sind Modulimport (typer, rich, jsonschema, yaml); im residenten
Prozess entfallen sie, womit die ursprüngliche Schätzung „grob 100 ms" steht. Der
Korpus-Parse skaliert linear mit der Korpusgröße; Zielgröße der privaten Instanz sind wenige
Tausend Seiten.
Korrektur zum Alias-Befund: das Regressionsdokument ist 267 Byte → 672.603 Knoten (9ⁿ), nicht
322 Byte → 797.161 (3ⁿ) — dieselbe Eigenschaft, eigene Messung.
Weitere belastbare Zahlen: Lese-Kern importiert an Fremdcode genau zwei Pakete (yaml, jsonschema) — die „~14 transitiven Pakete" sind ein pip list-Artefakt des CLI-Kopfs.
Lese-Slice 2.442 von 11.895 Zeilen Produktivcode. git_publish.py (1.140 Zeilen) liegt
strukturell außerhalb.
Die zehn Befunde — alle adressiert
Alle in der Sitzung vom 2026-09-01 reproduziert, keiner vermutet; Stand nach 2.4.0:
ReDoS — search/ripgrep.py:147 gab nutzergesteuerten Regex an Pythons
Backtracking-Engine. → #33, behoben
Kein Subprozess-Timeout — search/ripgrep.py:102. → #33, behoben
YAML ohne Alias-Budget — 267 B → 672.603 Knoten. → #33, behoben
lint liest raw/-Dateinamen — lint.py:230. In #19entschieden: bleibt exponiert,
weil er ohne mehrere Einreicher nichts zu schützen hat. Die Entscheidung gehört zu #32 und
wird dort wieder aufgenommen, sobald es Fremdeinreichungen gibt.
Bibliotheksgrenze fehlt — run_search/run_lint in Modulen mit typer-Import auf
Modulebene. → #31, behoben
ripgrep-Aufruf ist sauber — kein shell=True, --fixed-strings, ---Terminator. Kein
Befund, sondern eine zu erhaltende Eigenschaft: steht seit dem Umbau als dritte tragende
Zusage im Modul-Docstring.
Zusätzlich vom Umbau aufgedeckt und in #31 behoben: die Testsuite hing an zwei
Abhängigkeiten, die nur hielten, weil TYPES_DIR und TypeResolver.repo_root beim Import
gebunden waren; und resolve() reichte den Root nicht ans Suchbackend durch — die Anfrage aus
dem einen Baum beantwortet, die Seiten aus dem anderen gelesen.
Ausdrücklich nicht Gegenstand
Go. Entschieden und geschlossen.
Deployment-Details (Cluster, Ingress-Hosts, Secret-Store) — private Infrastruktur, gehört
nicht in einen öffentlichen Tracker.
Schreibpfad nach kb/. Der Server exponiert keine Schreib-Tools, und zwar weil die
Funktionen dort nicht existieren, nicht weil sie gefiltert werden. #32 legt ausschließlich in
eine Quarantäne ab und ruft nie den Compiler.
Recherche-Fähigkeit (#15). Läuft in die Gegenrichtung, bewusst nicht dieselbe Oberfläche.
Lose Enden — beide erledigt
kb/entities/tools/qmd.md sagt „wahrscheinlich Go oder Rust" bei confidence: 0.85. Korrigiert am 2026-09-02: direkt gegen tobi/qmd auf GitHub geprüft — TypeScript,
Laufzeit Node.js/Bun, sqlite-vec + node-llama-cpp. Neue Quelle Source - qmd - GitHub Repository, confidence_base auf 0.80 neu begründet.
Es gibt keinen dauerhaften Ort für Architekturentscheidungen. Wurde #38, geschlossen
am 2026-09-03 mit 4.3.0: docs/ als ausgelieferter, inerter Hintergrundort, Decision-Seiten
bleiben in kb/, Decay-Ausnahme für concept_type: decision.
Nicht mitgelöst und weiterhin offen: #23 — nichts erzwingt, dass eine neue Env-Var in _WIKITOOL_ENV landet. CHEMENU_ROOT steht dort inzwischen drin, aber von Hand eingetragen:
genau der Weg, den #23 als unzuverlässig beschreibt. Der Befund ist damit belegt statt vermutet.
Abschluss
#34, #33, #31, #19 geschlossen — die Sequenz ist vollständig abgearbeitet
Alle zehn Befunde adressiert; Befund 8 mit begründeter Entscheidung (bleibt exponiert,
wird in #32 wieder aufgenommen)
Prognose gegen Messung gehalten, Zahlen oben festgehalten
[→] Ein Konsument führt über die Traefik-Middleware nachweislich search gegen den Server aus
— nach #37 übernommen. Nicht gestrichen und nicht erledigt: der Nachweis braucht ein
Deployment und dafür ein Container-Image, und dort liegen auch die neun Entscheidungen, an
denen er hängt (Korpus im Image oder als Volume, Sync-Mechanismus, Basis-Image,
Healthcheck, Registry-Pfad). Im Repo existiert weiterhin kein Dockerfile und kein
Image-Workflow; .gitea/workflows/ hat ci.yml, nightly.yml, release.yml.
Warum dieses Issue trotzdem schließt: ein Master-Issue, dessen einzige verbleibende Zeile in
einem anderen Issue steht, hält nur noch sich selbst offen. Der Wert des Bodies — Entscheidungen
und Messungen — ist unabhängig von seinem Zustand lesbar, und ein geschlossenes Protokoll ist
ehrlicher über den Stand als ein offener Sammelposten, der nichts mehr steuert.
Sammel-Issue für den Weg zum MCP-Leseserver. Es hat nie selbst implementiert — es hielt die
Reihenfolge, die getroffenen Entscheidungen und die Messungen, auf denen der Entwurf steht, damit
eine Sitzung kalt einsteigen kann, ohne die Debatte vom 2026-09-01 zu wiederholen.
**Ziel war:** `chemenu` bekommt einen zweiten Konsumenten. CLI und MCP-Server sind zwei Adapter
auf denselben Kern — nicht ein CLI mit angeschraubter Netzwerkschnittstelle. Das ist erreicht.
**Geschlossen am 2026-09-04.** Die Sequenz ist abgearbeitet, der Server läuft, alle zehn Befunde
sind adressiert. Das letzte offene Kriterium — der Nachweis über ein Deployment — ist nach **#37**
übernommen, wo es hingehört: es hängt vollständig am Container-Image und an keiner Frage, die
dieses Issue noch beantworten könnte. **#37 ist damit das einzige verbleibende Issue auf diesem
Weg.** Was hier stehen bleibt, ist das Entscheidungsprotokoll und die gemessene Grundlage; beides
ist ausdrücklich nicht neu aufzumachen.
---
## Sequenz — abgeschlossen am 2026-09-02
| # | Issue | Version | Status |
|---|---|---|---|
| 0 | #34 — Publish-Remote-Gate scharf stellen | 2.2.3 | **geschlossen** |
| 1 | #33 — Härtung des Lesepfads | 2.2.4 | **geschlossen** |
| 2 | #31 — Root-Auflösung + Bibliotheksgrenze | 2.3.0 | **geschlossen** |
| 3 | #19 — MCP-Leseserver | 2.4.0 | **geschlossen** |
Der Server liegt in `tools/chemenu/mcp/` und läuft auf beiden Transports (`stdio`,
`streamable-http`) gegen den echten Korpus. Menschendoku: `INSTALL-MCP.md` (2.4.1, wird von
`dist export` ausgeliefert), Betriebsablauf in `instructions/mcp-read-server.md`. Einzelheiten je
Issue in dessen Abschlusskommentar.
**Nicht mehr auf dem kritischen Pfad, unverändert offen:** #32 (Ingest-Queue, hing an #19, dessen
Vorbedingung erfüllt ist; teilt sich die Mechanik mit #16) · #35 (semantisches Backend, Auslöser
ist ein Korpus, an dem `rg` nicht mehr trägt) · #6 (Backlink-Ranking, läuft durch dieselbe Fusion
wie #35 und ist seit 2026-09-04 als `status/blocked` an #35 gehängt).
---
## Entschieden — nicht neu aufmachen
| Frage | Entscheidung | In einem Satz |
|---|---|---|
| **Plattform** | **Python. Kein Go, auch nicht als Option** | Die drei Go-Argumente lösen sich am eigenen Vorhaben auf; Herleitung im Kommentar zu #19 |
| **Authentifizierung** | **Traefik-Middleware** | Nicht sauber authentifizierte Zugriffe erreichen den Python-Prozess nicht. Umgesetzt: <https://gitea.nehmer.net/torben/gitea-mcp-forward-auth> |
| **Rate Limiting** | ebenfalls Traefik | Vor den Prozess, **nicht** in den Iteration Budget Gate |
| **Aufrufweg** | Direktimport, nicht Subprocess | Subprocess läuft nach 60 `lint`/`types`-Aufrufen in den Budget-Gate; eine argv-Allowlist kostet so viel wie der Direktimport |
| **Wire-Contract** | die `--json`-Formen der CLI | Ein Contract statt zwei; die CLI wird zur ausführbaren Spezifikation, Golden-Test hält beide zusammen |
| **Budget-Gate im Server** | nein | Er begrenzt Agenten-Sessions, nicht Nutzer. Ihn als Rate Limiter zu benutzen verwässert ihn |
| **Session-Scoping** | erst mit #32 | Eine Suchanfrage ist nicht zurechenbar, eine Einreichung schon |
| **Staleness** | Commit-SHA in jeder Antwort | Macht aus einer stillen veralteten Antwort eine sichtbare; derselbe SHA ist der Cache-Key |
| **Sync** | Polling (`git fetch && git reset --hard`) | Kein eingehender Endpunkt, keine Signaturprüfung. Webhook ist spätere Optimierung |
| **Retrieval** | hinter der vorhandenen Backend-Registry | `search/registry.py`: „a new module plus one line here" |
| **Deployment** | gehört **nicht** in dieses Repo | Private Infrastruktur, öffentlicher Tracker |
---
## Gemessene Grundlage — Prognose hat gehalten
Ausgangsmessung 2026-09-01, Nachmessung nach #33/#31 am 2026-09-02:
| | Prognose | gemessen |
|---|---|---|
| Korpus-Parse (176 Seiten, `CSafeLoader` + Cache) | 246 → 82 ms | **265 → 54 ms** |
| `wikitool search` end-to-end | 593 ms | **593 → 347 ms** |
Die verbleibenden ~262 ms sind Modulimport (typer, rich, jsonschema, yaml); im residenten
Prozess entfallen sie, womit die ursprüngliche Schätzung **„grob 100 ms"** steht. Der
Korpus-Parse skaliert linear mit der Korpusgröße; Zielgröße der privaten Instanz sind wenige
Tausend Seiten.
Korrektur zum Alias-Befund: das Regressionsdokument ist 267 Byte → 672.603 Knoten (9ⁿ), nicht
322 Byte → 797.161 (3ⁿ) — dieselbe Eigenschaft, eigene Messung.
Weitere belastbare Zahlen: Lese-Kern importiert an Fremdcode genau zwei Pakete (`yaml`,
`jsonschema`) — die „~14 transitiven Pakete" sind ein `pip list`-Artefakt des CLI-Kopfs.
Lese-Slice 2.442 von 11.895 Zeilen Produktivcode. `git_publish.py` (1.140 Zeilen) liegt
strukturell außerhalb.
---
## Die zehn Befunde — alle adressiert
Alle in der Sitzung vom 2026-09-01 reproduziert, keiner vermutet; Stand nach 2.4.0:
1. **ReDoS** — `search/ripgrep.py:147` gab nutzergesteuerten Regex an Pythons
Backtracking-Engine. → #33, **behoben**
2. **Kein Subprozess-Timeout** — `search/ripgrep.py:102`. → #33, **behoben**
3. **YAML ohne Alias-Budget** — 267 B → 672.603 Knoten. → #33, **behoben**
4. **`CSafeLoader` ungenutzt** — Faktor 3 lag brach. → #33, **behoben** (265 → 54 ms)
5. **Stiller Frontmatter-Verlust** — kaputtes YAML wurde `{}`. → #33, **behoben**
6. **Korpus-Reparse pro Aufruf** — `commands/search.py:199`. → #33, **behoben** (Cache am
Commit-SHA)
7. **Publish-Remote-Gate inert** — `.wikitool-remotes.json` fehlte. → #34, **behoben**
8. **`lint` liest `raw/`-Dateinamen** — `lint.py:230`. In #19 **entschieden: bleibt exponiert**,
weil er ohne mehrere Einreicher nichts zu schützen hat. Die Entscheidung gehört zu #32 und
wird dort wieder aufgenommen, sobald es Fremdeinreichungen gibt.
9. **Bibliotheksgrenze fehlt** — `run_search`/`run_lint` in Modulen mit `typer`-Import auf
Modulebene. → #31, **behoben**
10. **ripgrep-Aufruf ist sauber** — kein `shell=True`, `--fixed-strings`, `--`-Terminator. Kein
Befund, sondern eine zu erhaltende Eigenschaft: steht seit dem Umbau als dritte tragende
Zusage im Modul-Docstring.
**Zusätzlich vom Umbau aufgedeckt und in #31 behoben:** die Testsuite hing an zwei
Abhängigkeiten, die nur hielten, weil `TYPES_DIR` und `TypeResolver.repo_root` beim Import
gebunden waren; und `resolve()` reichte den Root nicht ans Suchbackend durch — die Anfrage aus
dem einen Baum beantwortet, die Seiten aus dem anderen gelesen.
---
## Ausdrücklich nicht Gegenstand
- **Go.** Entschieden und geschlossen.
- **Deployment-Details** (Cluster, Ingress-Hosts, Secret-Store) — private Infrastruktur, gehört
nicht in einen öffentlichen Tracker.
- **Schreibpfad nach `kb/`.** Der Server exponiert keine Schreib-Tools, und zwar weil die
Funktionen dort nicht existieren, nicht weil sie gefiltert werden. #32 legt ausschließlich in
eine Quarantäne ab und ruft nie den Compiler.
- **Recherche-Fähigkeit (#15).** Läuft in die Gegenrichtung, bewusst nicht dieselbe Oberfläche.
---
## Lose Enden — beide erledigt
1. ~~`kb/entities/tools/qmd.md` sagt „wahrscheinlich Go oder Rust" bei `confidence: 0.85`.~~
**Korrigiert am 2026-09-02**: direkt gegen `tobi/qmd` auf GitHub geprüft — TypeScript,
Laufzeit Node.js/Bun, `sqlite-vec` + `node-llama-cpp`. Neue Quelle
`Source - qmd - GitHub Repository`, `confidence_base` auf 0.80 neu begründet.
2. ~~Es gibt keinen dauerhaften Ort für Architekturentscheidungen.~~ Wurde **#38**, geschlossen
am 2026-09-03 mit 4.3.0: `docs/` als ausgelieferter, inerter Hintergrundort, Decision-Seiten
bleiben in `kb/`, Decay-Ausnahme für `concept_type: decision`.
**Nicht mitgelöst und weiterhin offen: #23** — nichts erzwingt, dass eine neue Env-Var in
`_WIKITOOL_ENV` landet. `CHEMENU_ROOT` steht dort inzwischen drin, aber von Hand eingetragen:
genau der Weg, den #23 als unzuverlässig beschreibt. Der Befund ist damit belegt statt vermutet.
## Abschluss
- [x] #34, #33, #31, #19 geschlossen — die Sequenz ist vollständig abgearbeitet
- [x] Alle zehn Befunde adressiert; Befund 8 mit begründeter Entscheidung (bleibt exponiert,
wird in #32 wieder aufgenommen)
- [x] Prognose gegen Messung gehalten, Zahlen oben festgehalten
- [x] Menschendoku steht: `INSTALL-MCP.md`, `instructions/mcp-read-server.md`
- [→] Ein Konsument führt über die Traefik-Middleware nachweislich `search` gegen den Server aus
— **nach #37 übernommen.** Nicht gestrichen und nicht erledigt: der Nachweis braucht ein
Deployment und dafür ein Container-Image, und dort liegen auch die neun Entscheidungen, an
denen er hängt (Korpus im Image oder als Volume, Sync-Mechanismus, Basis-Image,
Healthcheck, Registry-Pfad). Im Repo existiert weiterhin kein Dockerfile und kein
Image-Workflow; `.gitea/workflows/` hat `ci.yml`, `nightly.yml`, `release.yml`.
**Warum dieses Issue trotzdem schließt:** ein Master-Issue, dessen einzige verbleibende Zeile in
einem anderen Issue steht, hält nur noch sich selbst offen. Der Wert des Bodies — Entscheidungen
und Messungen — ist unabhängig von seinem Zustand lesbar, und ein geschlossenes Protokoll ist
ehrlicher über den Stand als ein offener Sammelposten, der nichts mehr steuert.
Einzelheiten je Issue in dessen Abschlusskommentar; hier nur, was die Sequenz als Ganzes
betrifft.
Die gemessene Grundlage hat gehalten, mit einer Korrektur nach unten und einer Präzisierung:
Prognose im Master
gemessen
Korpus-Parse (176 Seiten)
246 → 82 ms
265 → 54 ms
wikitool search end-to-end
593 ms
593 → 347 ms
Die verbleibenden ~262 ms sind Modulimport; im residenten Prozess entfallen sie, womit die
Schätzung „grob 100 ms" steht. Der Alias-Befund: mein Regressionsdokument ist 267 Byte →
672.603 Knoten (9ⁿ) statt 322 Byte → 797.161 (3ⁿ) — dieselbe Eigenschaft, eigene Messung.
Alle zehn Befunde adressiert. 1–7 und 9 behoben. Befund 10 (sauberer rg-Aufruf) ist
erhalten und steht jetzt als dritte tragende Zusage im Modul-Docstring. Befund 8 (lint liest raw/-Dateinamen) wurde wie vorgesehen in #19 entschieden: bleibt exponiert, weil er ohne
mehrere Einreicher nichts zu schützen hat; die Entscheidung gehört zu #32.
Was der Umbau zusätzlich aufgedeckt hat — beides in #31 dokumentiert und behoben: die
Testsuite hing an zwei Abhängigkeiten, die nur deshalb hielten, weil TYPES_DIR und TypeResolver.repo_root beim Import gebunden waren. Und resolve() reichte den Root nicht ans
Suchbackend durch: die Anfrage aus dem einen Baum beantwortet, die Seiten aus dem anderen
gelesen.
Abschluss dieses Issues
Das genannte Kriterium — „ein Konsument führt über die Traefik-Middleware nachweislich search
gegen den Server aus" — ist noch nicht erfüllt. Der Server läuft auf beiden Transports gegen
den echten Korpus, die Middleware existiert
(https://gitea.nehmer.net/torben/gitea-mcp-forward-auth), aber das Zusammenspiel braucht ein
Deployment. Dafür fehlt ein Container-Image: #37, mit den Entscheidungen, die dort noch
anstehen.
Vorschlag: dieses Issue offen lassen, bis #37 durch ist und der Nachweis vorliegt. Es hat
nur noch diesen einen Punkt.
Nicht mehr auf dem kritischen Pfad, unverändert offen:#32 (Ingest-Queue), #35 (semantisches
Backend), #6 (Backlink-Ranking).
Zwei lose Enden aus dem Master-Text sind es geblieben:
kb/entities/tools/qmd.md sagt weiterhin „wahrscheinlich Go oder Rust" bei confidence: 0.85; verifiziert falsch (TypeScript/Bun). Korpusarbeit über wiki-manage, kein
Stack-Issue — bislang nicht gemacht.
Es gibt weiterhin keinen dauerhaften Ort für Architekturentscheidungen. Diese Sitzung hat
das Problem eher vergrößert: die Transport- und Ablage-Entscheidung zu #19 liegt jetzt
ebenfalls in einem Issue-Kommentar, und CHANGES.md sagt, was ausgeliefert wurde, nicht, wogegen entschieden wurde. Braucht eine Entscheidung des Betreibers, ob eine ADR-Ebene
dazukommt.
Nicht mitgelöst:#23 (nichts erzwingt, dass eine neue Env-Var in _WIKITOOL_ENV landet). CHEMENU_ROOT ist von Hand eingetragen — genau der Weg, den #23 als unzuverlässig beschreibt.
## Sequenz abgearbeitet (2026-09-02)
| # | Issue | Version | Status |
|---|---|---|---|
| 0 | #34 Publish-Remote-Gate | 2.2.3 | **geschlossen** |
| 1 | #33 Härtung des Lesepfads | 2.2.4 | **geschlossen** |
| 2 | #31 Root-Auflösung + Bibliotheksgrenze | 2.3.0 | **geschlossen** |
| 3 | #19 MCP-Leseserver | 2.4.0 | **geschlossen** |
Einzelheiten je Issue in dessen Abschlusskommentar; hier nur, was die Sequenz als Ganzes
betrifft.
**Die gemessene Grundlage hat gehalten**, mit einer Korrektur nach unten und einer Präzisierung:
| | Prognose im Master | gemessen |
|---|---|---|
| Korpus-Parse (176 Seiten) | 246 → 82 ms | **265 → 54 ms** |
| `wikitool search` end-to-end | 593 ms | **593 → 347 ms** |
Die verbleibenden ~262 ms sind Modulimport; im residenten Prozess entfallen sie, womit die
Schätzung „grob 100 ms" steht. Der Alias-Befund: mein Regressionsdokument ist 267 Byte →
672.603 Knoten (9ⁿ) statt 322 Byte → 797.161 (3ⁿ) — dieselbe Eigenschaft, eigene Messung.
**Alle zehn Befunde adressiert.** 1–7 und 9 behoben. Befund 10 (sauberer `rg`-Aufruf) ist
erhalten und steht jetzt als dritte tragende Zusage im Modul-Docstring. Befund 8 (`lint` liest
`raw/`-Dateinamen) wurde wie vorgesehen in #19 entschieden: bleibt exponiert, weil er ohne
mehrere Einreicher nichts zu schützen hat; die Entscheidung gehört zu #32.
**Was der Umbau zusätzlich aufgedeckt hat** — beides in #31 dokumentiert und behoben: die
Testsuite hing an zwei Abhängigkeiten, die nur deshalb hielten, weil `TYPES_DIR` und
`TypeResolver.repo_root` beim Import gebunden waren. Und `resolve()` reichte den Root nicht ans
Suchbackend durch: die Anfrage aus dem einen Baum beantwortet, die Seiten aus dem anderen
gelesen.
---
## Abschluss dieses Issues
Das genannte Kriterium — „ein Konsument führt über die Traefik-Middleware nachweislich `search`
gegen den Server aus" — ist **noch nicht erfüllt**. Der Server läuft auf beiden Transports gegen
den echten Korpus, die Middleware existiert
(<https://gitea.nehmer.net/torben/gitea-mcp-forward-auth>), aber das Zusammenspiel braucht ein
Deployment. Dafür fehlt ein Container-Image: **#37**, mit den Entscheidungen, die dort noch
anstehen.
Vorschlag: dieses Issue **offen lassen**, bis #37 durch ist und der Nachweis vorliegt. Es hat
nur noch diesen einen Punkt.
**Nicht mehr auf dem kritischen Pfad, unverändert offen:** #32 (Ingest-Queue), #35 (semantisches
Backend), #6 (Backlink-Ranking).
**Zwei lose Enden aus dem Master-Text sind es geblieben:**
1. `kb/entities/tools/qmd.md` sagt weiterhin „wahrscheinlich Go oder Rust" bei
`confidence: 0.85`; verifiziert falsch (TypeScript/Bun). Korpusarbeit über `wiki-manage`, kein
Stack-Issue — bislang nicht gemacht.
2. **Es gibt weiterhin keinen dauerhaften Ort für Architekturentscheidungen.** Diese Sitzung hat
das Problem eher vergrößert: die Transport- und Ablage-Entscheidung zu #19 liegt jetzt
ebenfalls in einem Issue-Kommentar, und `CHANGES.md` sagt, *was* ausgeliefert wurde, nicht,
*wogegen* entschieden wurde. Braucht eine Entscheidung des Betreibers, ob eine ADR-Ebene
dazukommt.
**Nicht mitgelöst:** #23 (nichts erzwingt, dass eine neue Env-Var in `_WIKITOOL_ENV` landet).
`CHEMENU_ROOT` ist von Hand eingetragen — genau der Weg, den #23 als unzuverlässig beschreibt.
Nachtrag zum ersten losen Ende: kb/entities/tools/qmd.md ist korrigiert. Direkt gegen tobi/qmd auf GitHub geprüft (Repo-Metadaten, package.json, README) - TypeScript, Laufzeit
Node.js/Bun, sqlite-vec + node-llama-cpp statt der geratenen „Go oder Rust"-Angabe bei einer
dafür zu hohen confidence: 0.85. Neue Quelle Source - qmd - GitHub Repository, confidence_base auf 0.80 neu begründet. Verbleibt: die ADR-Frage.
Nachtrag zum ersten losen Ende: `kb/entities/tools/qmd.md` ist korrigiert. Direkt gegen
`tobi/qmd` auf GitHub geprüft (Repo-Metadaten, `package.json`, README) - TypeScript, Laufzeit
Node.js/Bun, `sqlite-vec` + `node-llama-cpp` statt der geratenen „Go oder Rust"-Angabe bei einer
dafür zu hohen `confidence: 0.85`. Neue Quelle `Source - qmd - GitHub Repository`,
`confidence_base` auf 0.80 neu begründet. Verbleibt: die ADR-Frage.
Zweites loses Ende (die fehlende ADR-Ebene) ist jetzt #38 - mit Befund statt nur Beobachtung:
die adr-NNN--Konvention aus kb/concepts/COLLECTION.md existiert auf Papier und wird nirgends
benutzt, und confidence decay läuft blind über die sieben existierenden Decision-Seiten, was
in dieser Sitzung konkret gegen die direkte Nutzung des Stack-Tooling sprach. Enthält eine
Interims-Lösung (Decay-Ausnahme) und eine Roadmap-Frage (wo Entscheidungsseiten leben) für den
Betreiber.
Zweites loses Ende (die fehlende ADR-Ebene) ist jetzt #38 - mit Befund statt nur Beobachtung:
die `adr-NNN-`-Konvention aus `kb/concepts/COLLECTION.md` existiert auf Papier und wird nirgends
benutzt, und `confidence decay` läuft blind über die sieben existierenden Decision-Seiten, was
in dieser Sitzung konkret gegen die direkte Nutzung des Stack-Tooling sprach. Enthält eine
Interims-Lösung (Decay-Ausnahme) und eine Roadmap-Frage (wo Entscheidungsseiten leben) für den
Betreiber.
Changelog: Body auf den Ist-Stand nachgezogen — er stand seit dem 2026-09-02 auf dem
Planungsstand vom 2026-09-01. Sequenztabelle jetzt „geschlossen + Version" statt viermal
„offen"; gemessene Grundlage auf die Nachmessung (265 → 54 ms, 593 → 347 ms) und die
Alias-Korrektur (267 B → 672.603 Knoten, 9ⁿ) umgestellt; die zehn Befunde tragen ihren
Erledigungsstand, Befund 8 als getroffene Entscheidung statt als offene Frage. Beide losen Enden
als erledigt markiert (qmd korrigiert; ADR-Frage → #38, geschlossen mit 4.3.0), #23 als weiterhin
offen ausgewiesen. Abschlussabschnitt auf die eine verbliebene Bedingung reduziert: Nachweis über
ein Deployment, das an #37 hängt (im Repo weiterhin kein Dockerfile, kein Image-Workflow).
Inhaltlich nichts Neues entschieden — die Information stand bereits im Abschlusskommentar vom
2026-09-02 und in #38/#37.
**Changelog:** Body auf den Ist-Stand nachgezogen — er stand seit dem 2026-09-02 auf dem
Planungsstand vom 2026-09-01. Sequenztabelle jetzt „geschlossen + Version" statt viermal
„offen"; gemessene Grundlage auf die Nachmessung (265 → 54 ms, 593 → 347 ms) und die
Alias-Korrektur (267 B → 672.603 Knoten, 9ⁿ) umgestellt; die zehn Befunde tragen ihren
Erledigungsstand, Befund 8 als getroffene Entscheidung statt als offene Frage. Beide losen Enden
als erledigt markiert (qmd korrigiert; ADR-Frage → #38, geschlossen mit 4.3.0), #23 als weiterhin
offen ausgewiesen. Abschlussabschnitt auf die eine verbliebene Bedingung reduziert: Nachweis über
ein Deployment, das an #37 hängt (im Repo weiterhin kein Dockerfile, kein Image-Workflow).
Inhaltlich nichts Neues entschieden — die Information stand bereits im Abschlusskommentar vom
2026-09-02 und in #38/#37.
Changelog: Body auf den Endstand umgeschrieben und geschlossen (Betreiberentscheidung, Triage-Sitzung 2026-09-04).
Geändert gegenüber dem vorherigen Stand:
Der Abschlussblock ist von einer offenen Kriterienliste zu einem gehakten Protokoll geworden. Vier Punkte abgehakt; das fünfte — der search-Nachweis über die Traefik-Middleware — ist als [→] nach #37 übernommen und dort als eigenes Akzeptanzkriterium eingetragen, mit der Begründung, warum es dorthin und nicht hierher gehört.
Der Kopf sagt jetzt „geschlossen am 2026-09-04" statt „Stand: die Sequenz ist abgearbeitet", und benennt #37 als das einzige verbleibende Issue auf diesem Weg.
Loses Ende zu #23 korrigiert: CHEMENU_ROOT steht inzwischen in _WIKITOOL_ENV — von Hand eingetragen, also ist der Befund von #23 jetzt belegt statt vermutet. Vorher las sich die Zeile, als fehle der Eintrag noch.
#32-Zeile nachgezogen (Vorbedingung #19 erfüllt, teilt Mechanik mit #16), #6-Zeile um die neue status/blocked-Kopplung an #35 ergänzt.
Neuer Schlussabsatz, der begründet, warum ein Master-Issue schließt, dessen letzte Zeile in einem anderen Issue steht.
Nicht geändert: Entscheidungstabelle, Messungen und die zehn Befunde. Das ist der Teil, dessentwegen der Body erhalten bleibt, und er ist ausdrücklich nicht neu aufzumachen.
**Changelog:** Body auf den Endstand umgeschrieben und geschlossen (Betreiberentscheidung, Triage-Sitzung 2026-09-04).
Geändert gegenüber dem vorherigen Stand:
- Der Abschlussblock ist von einer offenen Kriterienliste zu einem gehakten Protokoll geworden. Vier Punkte abgehakt; das fünfte — der `search`-Nachweis über die Traefik-Middleware — ist als `[→]` nach **#37** übernommen und dort als eigenes Akzeptanzkriterium eingetragen, mit der Begründung, warum es dorthin und nicht hierher gehört.
- Der Kopf sagt jetzt „geschlossen am 2026-09-04" statt „Stand: die Sequenz ist abgearbeitet", und benennt #37 als das einzige verbleibende Issue auf diesem Weg.
- Loses Ende zu #23 korrigiert: `CHEMENU_ROOT` steht inzwischen in `_WIKITOOL_ENV` — von Hand eingetragen, also ist der Befund von #23 jetzt belegt statt vermutet. Vorher las sich die Zeile, als fehle der Eintrag noch.
- #32-Zeile nachgezogen (Vorbedingung #19 erfüllt, teilt Mechanik mit #16), #6-Zeile um die neue `status/blocked`-Kopplung an #35 ergänzt.
- Neuer Schlussabsatz, der begründet, warum ein Master-Issue schließt, dessen letzte Zeile in einem anderen Issue steht.
**Nicht geändert:** Entscheidungstabelle, Messungen und die zehn Befunde. Das ist der Teil, dessentwegen der Body erhalten bleibt, und er ist ausdrücklich nicht neu aufzumachen.
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.
Sammel-Issue für den Weg zum MCP-Leseserver. Es hat nie selbst implementiert — es hielt die
Reihenfolge, die getroffenen Entscheidungen und die Messungen, auf denen der Entwurf steht, damit
eine Sitzung kalt einsteigen kann, ohne die Debatte vom 2026-09-01 zu wiederholen.
Ziel war:
chemenubekommt einen zweiten Konsumenten. CLI und MCP-Server sind zwei Adapterauf denselben Kern — nicht ein CLI mit angeschraubter Netzwerkschnittstelle. Das ist erreicht.
Geschlossen am 2026-09-04. Die Sequenz ist abgearbeitet, der Server läuft, alle zehn Befunde
sind adressiert. Das letzte offene Kriterium — der Nachweis über ein Deployment — ist nach #37
übernommen, wo es hingehört: es hängt vollständig am Container-Image und an keiner Frage, die
dieses Issue noch beantworten könnte. #37 ist damit das einzige verbleibende Issue auf diesem
Weg. Was hier stehen bleibt, ist das Entscheidungsprotokoll und die gemessene Grundlage; beides
ist ausdrücklich nicht neu aufzumachen.
Sequenz — abgeschlossen am 2026-09-02
Der Server liegt in
tools/chemenu/mcp/und läuft auf beiden Transports (stdio,streamable-http) gegen den echten Korpus. Menschendoku:INSTALL-MCP.md(2.4.1, wird vondist exportausgeliefert), Betriebsablauf ininstructions/mcp-read-server.md. Einzelheiten jeIssue in dessen Abschlusskommentar.
Nicht mehr auf dem kritischen Pfad, unverändert offen: #32 (Ingest-Queue, hing an #19, dessen
Vorbedingung erfüllt ist; teilt sich die Mechanik mit #16) · #35 (semantisches Backend, Auslöser
ist ein Korpus, an dem
rgnicht mehr trägt) · #6 (Backlink-Ranking, läuft durch dieselbe Fusionwie #35 und ist seit 2026-09-04 als
status/blockedan #35 gehängt).Entschieden — nicht neu aufmachen
lint/types-Aufrufen in den Budget-Gate; eine argv-Allowlist kostet so viel wie der Direktimport--json-Formen der CLIgit fetch && git reset --hard)search/registry.py: „a new module plus one line here"Gemessene Grundlage — Prognose hat gehalten
Ausgangsmessung 2026-09-01, Nachmessung nach #33/#31 am 2026-09-02:
CSafeLoader+ Cache)wikitool searchend-to-endDie verbleibenden ~262 ms sind Modulimport (typer, rich, jsonschema, yaml); im residenten
Prozess entfallen sie, womit die ursprüngliche Schätzung „grob 100 ms" steht. Der
Korpus-Parse skaliert linear mit der Korpusgröße; Zielgröße der privaten Instanz sind wenige
Tausend Seiten.
Korrektur zum Alias-Befund: das Regressionsdokument ist 267 Byte → 672.603 Knoten (9ⁿ), nicht
322 Byte → 797.161 (3ⁿ) — dieselbe Eigenschaft, eigene Messung.
Weitere belastbare Zahlen: Lese-Kern importiert an Fremdcode genau zwei Pakete (
yaml,jsonschema) — die „~14 transitiven Pakete" sind einpip list-Artefakt des CLI-Kopfs.Lese-Slice 2.442 von 11.895 Zeilen Produktivcode.
git_publish.py(1.140 Zeilen) liegtstrukturell außerhalb.
Die zehn Befunde — alle adressiert
Alle in der Sitzung vom 2026-09-01 reproduziert, keiner vermutet; Stand nach 2.4.0:
search/ripgrep.py:147gab nutzergesteuerten Regex an PythonsBacktracking-Engine. → #33, behoben
search/ripgrep.py:102. → #33, behobenCSafeLoaderungenutzt — Faktor 3 lag brach. → #33, behoben (265 → 54 ms){}. → #33, behobencommands/search.py:199. → #33, behoben (Cache amCommit-SHA)
.wikitool-remotes.jsonfehlte. → #34, behobenlintliestraw/-Dateinamen —lint.py:230. In #19 entschieden: bleibt exponiert,weil er ohne mehrere Einreicher nichts zu schützen hat. Die Entscheidung gehört zu #32 und
wird dort wieder aufgenommen, sobald es Fremdeinreichungen gibt.
run_search/run_lintin Modulen mittyper-Import aufModulebene. → #31, behoben
shell=True,--fixed-strings,---Terminator. KeinBefund, sondern eine zu erhaltende Eigenschaft: steht seit dem Umbau als dritte tragende
Zusage im Modul-Docstring.
Zusätzlich vom Umbau aufgedeckt und in #31 behoben: die Testsuite hing an zwei
Abhängigkeiten, die nur hielten, weil
TYPES_DIRundTypeResolver.repo_rootbeim Importgebunden waren; und
resolve()reichte den Root nicht ans Suchbackend durch — die Anfrage ausdem einen Baum beantwortet, die Seiten aus dem anderen gelesen.
Ausdrücklich nicht Gegenstand
nicht in einen öffentlichen Tracker.
kb/. Der Server exponiert keine Schreib-Tools, und zwar weil dieFunktionen dort nicht existieren, nicht weil sie gefiltert werden. #32 legt ausschließlich in
eine Quarantäne ab und ruft nie den Compiler.
Lose Enden — beide erledigt
kb/entities/tools/qmd.mdsagt „wahrscheinlich Go oder Rust" beiconfidence: 0.85.Korrigiert am 2026-09-02: direkt gegen
tobi/qmdauf GitHub geprüft — TypeScript,Laufzeit Node.js/Bun,
sqlite-vec+node-llama-cpp. Neue QuelleSource - qmd - GitHub Repository,confidence_baseauf 0.80 neu begründet.Es gibt keinen dauerhaften Ort für Architekturentscheidungen.Wurde #38, geschlossenam 2026-09-03 mit 4.3.0:
docs/als ausgelieferter, inerter Hintergrundort, Decision-Seitenbleiben in
kb/, Decay-Ausnahme fürconcept_type: decision.Nicht mitgelöst und weiterhin offen: #23 — nichts erzwingt, dass eine neue Env-Var in
_WIKITOOL_ENVlandet.CHEMENU_ROOTsteht dort inzwischen drin, aber von Hand eingetragen:genau der Weg, den #23 als unzuverlässig beschreibt. Der Befund ist damit belegt statt vermutet.
Abschluss
wird in #32 wieder aufgenommen)
INSTALL-MCP.md,instructions/mcp-read-server.mdsearchgegen den Server aus— nach #37 übernommen. Nicht gestrichen und nicht erledigt: der Nachweis braucht ein
Deployment und dafür ein Container-Image, und dort liegen auch die neun Entscheidungen, an
denen er hängt (Korpus im Image oder als Volume, Sync-Mechanismus, Basis-Image,
Healthcheck, Registry-Pfad). Im Repo existiert weiterhin kein Dockerfile und kein
Image-Workflow;
.gitea/workflows/hatci.yml,nightly.yml,release.yml.Warum dieses Issue trotzdem schließt: ein Master-Issue, dessen einzige verbleibende Zeile in
einem anderen Issue steht, hält nur noch sich selbst offen. Der Wert des Bodies — Entscheidungen
und Messungen — ist unabhängig von seinem Zustand lesbar, und ein geschlossenes Protokoll ist
ehrlicher über den Stand als ein offener Sammelposten, der nichts mehr steuert.
Sequenz abgearbeitet (2026-09-02)
Einzelheiten je Issue in dessen Abschlusskommentar; hier nur, was die Sequenz als Ganzes
betrifft.
Die gemessene Grundlage hat gehalten, mit einer Korrektur nach unten und einer Präzisierung:
wikitool searchend-to-endDie verbleibenden ~262 ms sind Modulimport; im residenten Prozess entfallen sie, womit die
Schätzung „grob 100 ms" steht. Der Alias-Befund: mein Regressionsdokument ist 267 Byte →
672.603 Knoten (9ⁿ) statt 322 Byte → 797.161 (3ⁿ) — dieselbe Eigenschaft, eigene Messung.
Alle zehn Befunde adressiert. 1–7 und 9 behoben. Befund 10 (sauberer
rg-Aufruf) isterhalten und steht jetzt als dritte tragende Zusage im Modul-Docstring. Befund 8 (
lintliestraw/-Dateinamen) wurde wie vorgesehen in #19 entschieden: bleibt exponiert, weil er ohnemehrere Einreicher nichts zu schützen hat; die Entscheidung gehört zu #32.
Was der Umbau zusätzlich aufgedeckt hat — beides in #31 dokumentiert und behoben: die
Testsuite hing an zwei Abhängigkeiten, die nur deshalb hielten, weil
TYPES_DIRundTypeResolver.repo_rootbeim Import gebunden waren. Undresolve()reichte den Root nicht ansSuchbackend durch: die Anfrage aus dem einen Baum beantwortet, die Seiten aus dem anderen
gelesen.
Abschluss dieses Issues
Das genannte Kriterium — „ein Konsument führt über die Traefik-Middleware nachweislich
searchgegen den Server aus" — ist noch nicht erfüllt. Der Server läuft auf beiden Transports gegen
den echten Korpus, die Middleware existiert
(https://gitea.nehmer.net/torben/gitea-mcp-forward-auth), aber das Zusammenspiel braucht ein
Deployment. Dafür fehlt ein Container-Image: #37, mit den Entscheidungen, die dort noch
anstehen.
Vorschlag: dieses Issue offen lassen, bis #37 durch ist und der Nachweis vorliegt. Es hat
nur noch diesen einen Punkt.
Nicht mehr auf dem kritischen Pfad, unverändert offen: #32 (Ingest-Queue), #35 (semantisches
Backend), #6 (Backlink-Ranking).
Zwei lose Enden aus dem Master-Text sind es geblieben:
kb/entities/tools/qmd.mdsagt weiterhin „wahrscheinlich Go oder Rust" beiconfidence: 0.85; verifiziert falsch (TypeScript/Bun). Korpusarbeit überwiki-manage, keinStack-Issue — bislang nicht gemacht.
das Problem eher vergrößert: die Transport- und Ablage-Entscheidung zu #19 liegt jetzt
ebenfalls in einem Issue-Kommentar, und
CHANGES.mdsagt, was ausgeliefert wurde, nicht,wogegen entschieden wurde. Braucht eine Entscheidung des Betreibers, ob eine ADR-Ebene
dazukommt.
Nicht mitgelöst: #23 (nichts erzwingt, dass eine neue Env-Var in
_WIKITOOL_ENVlandet).CHEMENU_ROOTist von Hand eingetragen — genau der Weg, den #23 als unzuverlässig beschreibt.Nachtrag zum ersten losen Ende:
kb/entities/tools/qmd.mdist korrigiert. Direkt gegentobi/qmdauf GitHub geprüft (Repo-Metadaten,package.json, README) - TypeScript, LaufzeitNode.js/Bun,
sqlite-vec+node-llama-cppstatt der geratenen „Go oder Rust"-Angabe bei einerdafür zu hohen
confidence: 0.85. Neue QuelleSource - qmd - GitHub Repository,confidence_baseauf 0.80 neu begründet. Verbleibt: die ADR-Frage.Zweites loses Ende (die fehlende ADR-Ebene) ist jetzt #38 - mit Befund statt nur Beobachtung:
die
adr-NNN--Konvention auskb/concepts/COLLECTION.mdexistiert auf Papier und wird nirgendsbenutzt, und
confidence decayläuft blind über die sieben existierenden Decision-Seiten, wasin dieser Sitzung konkret gegen die direkte Nutzung des Stack-Tooling sprach. Enthält eine
Interims-Lösung (Decay-Ausnahme) und eine Roadmap-Frage (wo Entscheidungsseiten leben) für den
Betreiber.
Changelog: Body auf den Ist-Stand nachgezogen — er stand seit dem 2026-09-02 auf dem
Planungsstand vom 2026-09-01. Sequenztabelle jetzt „geschlossen + Version" statt viermal
„offen"; gemessene Grundlage auf die Nachmessung (265 → 54 ms, 593 → 347 ms) und die
Alias-Korrektur (267 B → 672.603 Knoten, 9ⁿ) umgestellt; die zehn Befunde tragen ihren
Erledigungsstand, Befund 8 als getroffene Entscheidung statt als offene Frage. Beide losen Enden
als erledigt markiert (qmd korrigiert; ADR-Frage → #38, geschlossen mit 4.3.0), #23 als weiterhin
offen ausgewiesen. Abschlussabschnitt auf die eine verbliebene Bedingung reduziert: Nachweis über
ein Deployment, das an #37 hängt (im Repo weiterhin kein Dockerfile, kein Image-Workflow).
Inhaltlich nichts Neues entschieden — die Information stand bereits im Abschlusskommentar vom
2026-09-02 und in #38/#37.
Changelog: Body auf den Endstand umgeschrieben und geschlossen (Betreiberentscheidung, Triage-Sitzung 2026-09-04).
Geändert gegenüber dem vorherigen Stand:
search-Nachweis über die Traefik-Middleware — ist als[→]nach #37 übernommen und dort als eigenes Akzeptanzkriterium eingetragen, mit der Begründung, warum es dorthin und nicht hierher gehört.CHEMENU_ROOTsteht inzwischen in_WIKITOOL_ENV— von Hand eingetragen, also ist der Befund von #23 jetzt belegt statt vermutet. Vorher las sich die Zeile, als fehle der Eintrag noch.status/blocked-Kopplung an #35 ergänzt.Nicht geändert: Entscheidungstabelle, Messungen und die zehn Befunde. Das ist der Teil, dessentwegen der Body erhalten bleibt, und er ist ausdrücklich nicht neu aufzumachen.