Umgesetzt und ausgeliefert in 4.8.0-beta.3, Commit 36d2128. Aufgeworfen und entschieden in den Sitzungen vom 2026-09-04, gebaut am 2026-09-05.
Das Problem, das es gab
raw/CONTRACT.mds Routing-Tabelle war eine Regel für Menschen: wer eine Datei ablegte, wählte articles//documents//notes//assets/ selbst. Zwei Folgen:
Es skalierte nicht. Vier flache Verzeichnisse für alles, was je eingelesen wird. Der Stack hatte sonst nirgends eine Ablageentscheidung, die ein Mensch trifft — „nie ein Verzeichnis von Hand wählen" (AGENTS.md § Routing) galt für kb/, raw/ war ausgenommen, ohne dass das je jemand entschieden hätte.
Zusammengehörige Dateien waren nicht als solche erkennbar. Beim Muster „PDF hochladen, nach Markdown konvertieren" entstehen zwei Dateien für eine logische Quelle; dass sie zusammengehören, stand ausschließlich in raw_files: der Source-Seite.
Nicht das Problem war die Tiefe selbst: config.iter_raw_files macht rglob("*"), provenance.py und sources coverage arbeiten auf repo-relativen Pfaden — raw/documents/handbuch/original.pdf funktionierte schon vorher ohne Codeänderung. Flach waren nur die Contract-Tabelle und dist_cmd.RAW_SUBDIRS. Es fehlte kein Mechanismus, es fehlte eine Zuständigkeit.
Ebenfalls nicht das Problem war der Bestand: die 29 Rohdateien liegen flach innerhalb ihrer Unterverzeichnisse (articles/ 3, documents/ 1, notes/ 25; assets/ leer und mangels Datei nicht getrackt). Unter Entscheidung 3 sind das eindateiige Quellen in vorschriftsmäßiger Form. Es wurde nichts umsortiert.
Der Weg, wie er jetzt läuft
Mehrdateiige Quelle:
incoming/documents/handbuch.pdf Ablage deklariert den Typ
↓ Ingest-Sitzung konvertiert
incoming/documents/handbuch.pdf + handbuch.md
↓ wikitool raw accept incoming/documents/handbuch.pdf incoming/documents/handbuch.md
raw/documents/handbuch/handbuch.pdf zwei Dateien → Bundle-Verzeichnis
raw/documents/handbuch/handbuch.md
↓ wikitool new source --set raw_files=…
Eindateiige Quelle:
incoming/notes/besprechung.md
↓ wikitool raw accept incoming/notes/besprechung.md
raw/notes/besprechung.md eine Datei → kein Verzeichnis
Die Entscheidungen
1. Der Eingang ist ein Top-Level-incoming/ im Repo, gitignoriert. Nicht außerhalb, nicht unter raw/.
Unter raw/ war es unmöglich: .gitignore endet mit dem Backstop !raw/**, und docs_verify.check_ignored_content schlägt auf jede ignorierte Datei unter raw|kb|work fehl — ein raw/incoming/ wäre entweder committed oder docs verify rot. Gegen „außerhalb des Repos" entschied der Nachweis: „wird nie committed" ist über docs_verify.REQUIRED_IGNORE_CANARIES mechanisch belegbar (git check-ignore --no-index beantwortet die Frage über den Regelsatz, ohne dass die Datei existieren muss); für einen Pfad außerhalb des Baums existiert dieses Beweismittel nicht, und er bräuchte zusätzlich eine Konfigurationsfläche, die es nirgends gibt.
Gilt auch für #32. Dessen schärfere Anforderung („eine Quarantäne, die kein Kommando der normalen Pipeline liest") trägt nicht der Ort, sondern die Grenze zwischen Sitzung und Pipeline-Kommando — siehe § Verhältnis zu #32.
2. Der Typ wird über Unterverzeichnisse im Eingang deklariert, nicht über ein Flag.incoming/ spiegelt RAW_SUBDIRS. Kein --type-Argument.
Keine Rückkehr zur Handsortierung, weil zwei Entscheidungen auseinanderfallen: was für ein Dokument das ist (eine Klassifikation, die kein Werkzeug ableiten kann — eine .md kann notes/ oder articles/ sein) und wo die Datei am Ende liegt (Unterverzeichnis, Bundle ja/nein, Bundle-Name, die Bewegung). Der Mensch liefert nur die erste, aus einem Vierer-Vokabular. Drei Vorteile gegenüber dem Flag: die Erklärung wird abgegeben, wenn der Mensch das Dokument in der Hand hat, und überlebt Sitzungswechsel; ein Argument weniger ist ein Fehlweg weniger; eine Datei direkt in incoming/ kann abgelehnt werden, während ein Flag immer gesetzt ist, richtig oder falsch.
3. Ein Bundle-Verzeichnis entsteht nur bei Bedarf — ab der zweiten Datei. Eine Datei → Datei, mehrere → Verzeichnis.
Das war die Kehre gegenüber der ursprünglichen Skizze („einheitlich"). Entscheidend war die Folge für den Bestand: unter „einheitlich" wären die 29 flachen Dateien eine datierte Ausnahme im Contract gewesen oder eine Umsortierung mit 29 Bewegungen plus referenzierenden Seiten — über der Mass-Update-Schwelle, also ein eigener Publish mit Freigabe. Unter „nur bei Bedarf" stellt sich der Fall nicht.
4. Der Bundle-Name kommt aus dem Stem der Primärdatei (erstes Argument, bzw. die bereits liegende Datei im Wachstumsfall), nicht aus dem Titel der Source-Seite — letzteres würde raw/ an kb/ koppeln.
5. Ein Bundle trägt den Typ der Quelle, nicht der einzelnen Datei. Ein Diagramm zu einer documents/-Quelle wird aus incoming/documents/ befördert, nicht aus incoming/assets/; assets/ ist für Quellen, die selbst ein Asset sind. Steht als Satz in raw/CONTRACT.md.
6. --page ist optional, außer im Wachstumsfall. Ohne --page bewegt accept nur und druckt die Zielpfade für new source --set raw_files=…. Mit --page erweitert es raw_files: einer bestehenden Seite; entsteht dadurch die zweite Datei, zieht es die Bundle-Beförderung der bereits liegenden Datei im selben Aufruf nach.
Die Rückwärtssuche musste nicht gebaut werden: provenance.source_pages_by_raw_file() invertiert raw_files: über alle Source-Seiten und trägt schon duplicate_raw_file_owners. Sie wird benutzt statt eines „ich weiß ja, welche Seite gemeint ist"-Kurzschlusses — eine zu bewegende Rohdatei mit mehr als einem Owner wird abgelehnt, statt die andere Seite unbemerkt zu brechen.
7. RAW_SUBDIRS ist die einzige Quelle der Liste. Sie speist vier Stellen: Eingangsverzeichnisse, raw/-Verzeichnisse, Contract-Tabelle, .gitkeep-Schleife in dist export. docs verify (check_raw_subdirs) hält die Tabelle beidseitig dagegen (Invariante 8).
8. Die Version ist MINOR. Die ursprüngliche Vermutung „eine erzwungene Umsortierung wäre die Major-Zeile" hielt dem Drop-in-Test nicht stand: eine Umsortierung dieser Instanz wäre eine Korpus-Operation und erreicht keine fremde Instanz. Boundary-crossing wäre es erst, wenn etwas die Bundle-Form validiert; nichts tut das, und unter Entscheidung 3 wird ohnehin nichts umsortiert.
Umsetzungsnotizen
Path.rename() statt git mv. Das in #16 als geteiltes Kleinstprimitiv erwartete „git mv versuchen, bei not under version control auf mv zurückfallen" wird hier nicht gebraucht: die Quelle in incoming/ ist per Definition nie getrackt, und page_ops.rename_command/move_command bewegen kb/-Dateien längst mit schlichtem Path.rename(), weil publishs git add -A eine inhaltsgleiche Bewegung ohnehin als Rename erkennt. Bewusst dem bestehenden Muster gefolgt statt einen neuen git-Subprozess-Helfer einzuführen.
doctor wurde nicht erweitert. Kein Akzeptanzkriterium verlangte es, und der Fall, um den es ginge (frischer Klon ohne incoming/), ist über instructions/bootstrap.md Schritt 2 abgedeckt. doctor meldet weiterhin nur und legt nie an — der Satz in Schritt 2 sagt das ausdrücklich.
Menschendoku über die Kriterienliste hinaus.README.md beschrieb den Ingest-Einstieg noch als „drop a file into raw/" (Verzeichnisbaum, Quickstart, „Curate sources", erste Ingestion, wiki-ingest-Zeile) — nachgezogen, weil AGENTS.md § Changelog eine Stack-Änderung erst mit der Menschendoku als fertig zählt. docs/pipeline-rationale.md bekam einen Satz: incoming/ liegt auf der Diesseits-Seite der Vertrauensgrenze, die Grenze ist die Beförderung nach raw/, nicht der Moment der Ablage. INSTALL.md, INSTALL-MCP.md, EVALS.md und tools/README.md wurden geprüft und brauchten nichts.
#32 skizziert dieselbe Mechanik, kommt aber von der anderen Seite: dort fremd eingereichtes Material, das nicht ungeprüft in ein öffentliches Repo darf, hier eigenes, das der Nutzer nicht selbst einsortieren soll. Ein Mechanismus, zwei Auslöser.
Der Lesezugriff ist gegenläufig.#32 verlangt eine Quarantäne, „die kein Kommando der normalen Pipeline liest"; ein lokales incoming/ will, dass die Ingest-Sitzung hineinsieht. Aufgelöst über die Grenze Sitzung vs. Pipeline-Kommando — und das war gratis: lint und sources coverage gehen ausschließlich über config.iter_raw_files(config.RAW_DIR), alles außerhalb raw/ ist per Konstruktion unsichtbar, ohne Ausschlussliste.
Die Abhängigkeit war nicht dieselbe.#32 ist status/blocked auf #19; der lokale Ablagepfad hing an nichts davon.
raw accept steht jetzt; #32 konsumiert es und ergänzt, was nur der Fremdeinreichung eigen ist: Auth, Kontingente, Manifest mit Einreicherzurechnung, Ablehnungspfad.
Keine Abhängigkeit, in keiner Richtung — auch nicht die, die #16s Kommentar vom 2026-09-04 20:56 behauptete. Dort ist die Richtigstellung als Kommentar hinterlegt: raw accept befördert eine Datei, die noch keine Seite referenziert, und der eine referenzierende Fall benutzt die vorhandene Inversion aus provenance.py. Der Bestandspunkt stellte sich unter Entscheidung 3 gar nicht.
Was #16 aus dieser Umsetzung erbt: die raw-Kommandogruppe existiert jetzt (tools/chemenu/commands/raw_cmd.py, registriert in cli.py), raw rename hängt sich nur noch ein.
Abgrenzung (unverändert eingehalten)
Kein Konvertierungs-Werkzeug — PDF→Markdown macht die Sitzung, nicht wikitool.
Keine Auth, keine Kontingente, kein Einreicher-Manifest — das ist #32.
Kein Löschen aus raw/; raw/ bleibt immutabel, hier entstand nur ein Weg hinein.
Keine Umsortierung des Bestands.
Akzeptanzkriterien
Nach raw accept sind die beförderten Dateien byte-identisch mit dem, was im Eingang lag, und der Eingang enthält sie nicht mehr — Test vergleicht Inhalte (test_single_file_needs_no_bundle, test_two_files_bundle_under_the_first_files_stem).
Eine einzeln beförderte Datei landet als raw/<sub>/<name> ohne Zwischenverzeichnis; mehrere in einem Aufruf als raw/<sub>/<stem>/<name>.
Eine Datei direkt in incoming/ wird mit Exit 1 abgelehnt und nennt die zulässigen Verzeichnisse; nichts wird bewegt (test_file_directly_in_incoming_is_rejected, dazu test_unknown_type_subdir_is_rejected, test_nested_too_deep_is_rejected).
Eine Datei in incoming/ erzeugt kein uncovered_raw_files-Finding (test_promoted_file_in_incoming_is_never_reported_uncovered).
git check-ignore --no-index meldet einen Pfad unter incoming/ als ignoriert, geprüft über docs_verify.REQUIRED_IGNORE_CANARIES (test_incoming_inbox_is_ignored, plus das bestehende test_no_content_is_gitignored, das die Kanarie mitprüft).
Zu keinem Zeitpunkt zeigt eine raw_files:-Referenz ins Leere; im Wachstumsfall nennt raw_files: danach alle Dateien unter neuen Pfaden, lint meldet weder broken_raw_refs noch uncovered_raw_files (test_growth_case_no_broken_or_uncovered_refs_afterwards).
Ein Wachstumsfall auf eine Rohdatei mit mehr als einem Owner wird mit Exit 1 abgelehnt und bewegt nichts (test_growth_case_rejects_a_multi_owner_raw_file).
dist_cmd.RAW_SUBDIRS und die Routing-Tabelle beschreiben dieselbe Liste; docs verify schlägt in beiden Richtungen fehl (check_raw_subdirs, test_raw_subdirs_mismatch_is_reported).
dist export legt je RAW_SUBDIRS-Element raw/<sub>/.gitkeepundincoming/<sub>/.gitkeep an (test_plan_creates_matching_incoming_subdirs, test_export_into_a_fresh_directory_works).
instructions/bootstrap.md legt die Eingangsverzeichnisse an (neuer Schritt 2, mit dem Grund: gitignoriert, doctor meldet nur).
raw/CONTRACT.md beschreibt Eingang, Bundle-Regel und den Typ-des-Bundles-Satz; die Routing-Tabelle liest sich als abgeleitetes Verhalten.
tools/CONTRACT.md trägt Zeilen für raw accept und seinen Fehlerkontrakt; docs verify erzwingt beide Richtungen.
Der wiki-ingest-Skill kennt den Weg über den Eingang (neuer Schritt 1, Schritte neu nummeriert, Kommandoliste ergänzt).
Changelog-Eintrag, MINOR — 4.8.0-beta.3 (Kandidat, kein Release).
pytest -q lokal: 1032 passed, davon 19 neu in tools/chemenu/tests/test_raw_cmd.py plus 4 in test_docs_verify.py/test_dist_cmd.py.
Manueller End-to-End-Lauf über die echte CLI: --dry-run, dann echte Beförderung incoming/notes/… → raw/notes/…, sources coverage meldete die Datei danach korrekt als ungedeckt (keine Source-Seite); Testdatei wieder entfernt.
CI zu 36d2128: Lauf 182 (verify) grün — Tests, Coverage, Verify-Tree, Version-Gate, dist export und der Frisch-Instanz-Replay des Exports. Lauf 183 (release) grün und hat korrekt kein Release geschnitten: VERSION trägt -beta.3, jüngstes Release bleibt v4.7.4.
**Umgesetzt und ausgeliefert** in `4.8.0-beta.3`, Commit `36d2128`. Aufgeworfen und entschieden in den Sitzungen vom 2026-09-04, gebaut am 2026-09-05.
## Das Problem, das es gab
`raw/CONTRACT.md`s Routing-Tabelle war eine **Regel für Menschen**: wer eine Datei ablegte, wählte `articles/`/`documents/`/`notes/`/`assets/` selbst. Zwei Folgen:
- **Es skalierte nicht.** Vier flache Verzeichnisse für alles, was je eingelesen wird. Der Stack hatte sonst nirgends eine Ablageentscheidung, die ein Mensch trifft — „nie ein Verzeichnis von Hand wählen" (AGENTS.md § Routing) galt für `kb/`, `raw/` war ausgenommen, ohne dass das je jemand entschieden hätte.
- **Zusammengehörige Dateien waren nicht als solche erkennbar.** Beim Muster „PDF hochladen, nach Markdown konvertieren" entstehen zwei Dateien für eine logische Quelle; dass sie zusammengehören, stand ausschließlich in `raw_files:` der Source-Seite.
Nicht das Problem war **die Tiefe selbst**: `config.iter_raw_files` macht `rglob("*")`, `provenance.py` und `sources coverage` arbeiten auf repo-relativen Pfaden — `raw/documents/handbuch/original.pdf` funktionierte schon vorher ohne Codeänderung. Flach waren nur die Contract-Tabelle und `dist_cmd.RAW_SUBDIRS`. Es fehlte kein Mechanismus, es fehlte eine Zuständigkeit.
Ebenfalls nicht das Problem war **der Bestand**: die 29 Rohdateien liegen flach *innerhalb* ihrer Unterverzeichnisse (`articles/` 3, `documents/` 1, `notes/` 25; `assets/` leer und mangels Datei nicht getrackt). Unter Entscheidung 3 sind das eindateiige Quellen in vorschriftsmäßiger Form. **Es wurde nichts umsortiert.**
## Der Weg, wie er jetzt läuft
Mehrdateiige Quelle:
```
incoming/documents/handbuch.pdf Ablage deklariert den Typ
↓ Ingest-Sitzung konvertiert
incoming/documents/handbuch.pdf + handbuch.md
↓ wikitool raw accept incoming/documents/handbuch.pdf incoming/documents/handbuch.md
raw/documents/handbuch/handbuch.pdf zwei Dateien → Bundle-Verzeichnis
raw/documents/handbuch/handbuch.md
↓ wikitool new source --set raw_files=…
```
Eindateiige Quelle:
```
incoming/notes/besprechung.md
↓ wikitool raw accept incoming/notes/besprechung.md
raw/notes/besprechung.md eine Datei → kein Verzeichnis
```
## Die Entscheidungen
**1. Der Eingang ist ein Top-Level-`incoming/` im Repo, gitignoriert.** Nicht außerhalb, nicht unter `raw/`.
Unter `raw/` war es unmöglich: `.gitignore` endet mit dem Backstop `!raw/**`, und `docs_verify.check_ignored_content` schlägt auf jede ignorierte Datei unter `raw|kb|work` fehl — ein `raw/incoming/` wäre entweder committed oder `docs verify` rot. Gegen „außerhalb des Repos" entschied der Nachweis: „wird nie committed" ist über `docs_verify.REQUIRED_IGNORE_CANARIES` mechanisch belegbar (`git check-ignore --no-index` beantwortet die Frage über den *Regelsatz*, ohne dass die Datei existieren muss); für einen Pfad außerhalb des Baums existiert dieses Beweismittel nicht, und er bräuchte zusätzlich eine Konfigurationsfläche, die es nirgends gibt.
**Gilt auch für #32.** Dessen schärfere Anforderung („eine Quarantäne, die kein Kommando der normalen Pipeline liest") trägt nicht der Ort, sondern die Grenze zwischen *Sitzung* und *Pipeline-Kommando* — siehe § Verhältnis zu #32.
**2. Der Typ wird über Unterverzeichnisse im Eingang deklariert, nicht über ein Flag.** `incoming/` spiegelt `RAW_SUBDIRS`. Kein `--type`-Argument.
Keine Rückkehr zur Handsortierung, weil zwei Entscheidungen auseinanderfallen: **was für ein Dokument das ist** (eine Klassifikation, die kein Werkzeug ableiten kann — eine `.md` kann `notes/` oder `articles/` sein) und **wo die Datei am Ende liegt** (Unterverzeichnis, Bundle ja/nein, Bundle-Name, die Bewegung). Der Mensch liefert nur die erste, aus einem Vierer-Vokabular. Drei Vorteile gegenüber dem Flag: die Erklärung wird abgegeben, wenn der Mensch das Dokument in der Hand hat, und überlebt Sitzungswechsel; ein Argument weniger ist ein Fehlweg weniger; eine Datei direkt in `incoming/` kann abgelehnt werden, während ein Flag immer gesetzt ist, richtig oder falsch.
**3. Ein Bundle-Verzeichnis entsteht nur bei Bedarf — ab der zweiten Datei.** Eine Datei → Datei, mehrere → Verzeichnis.
Das war die Kehre gegenüber der ursprünglichen Skizze („einheitlich"). Entscheidend war die Folge für den Bestand: unter „einheitlich" wären die 29 flachen Dateien eine datierte Ausnahme im Contract gewesen oder eine Umsortierung mit 29 Bewegungen plus referenzierenden Seiten — über der Mass-Update-Schwelle, also ein eigener Publish mit Freigabe. Unter „nur bei Bedarf" stellt sich der Fall nicht.
**4. Der Bundle-Name kommt aus dem Stem der Primärdatei** (erstes Argument, bzw. die bereits liegende Datei im Wachstumsfall), nicht aus dem Titel der Source-Seite — letzteres würde `raw/` an `kb/` koppeln.
**5. Ein Bundle trägt den Typ der *Quelle*, nicht der einzelnen Datei.** Ein Diagramm zu einer `documents/`-Quelle wird aus `incoming/documents/` befördert, nicht aus `incoming/assets/`; `assets/` ist für Quellen, die selbst ein Asset sind. Steht als Satz in `raw/CONTRACT.md`.
**6. `--page` ist optional, außer im Wachstumsfall.** Ohne `--page` bewegt `accept` nur und druckt die Zielpfade für `new source --set raw_files=…`. Mit `--page` erweitert es `raw_files:` einer bestehenden Seite; entsteht dadurch die zweite Datei, zieht es die Bundle-Beförderung der bereits liegenden Datei im selben Aufruf nach.
Die Rückwärtssuche musste nicht gebaut werden: `provenance.source_pages_by_raw_file()` invertiert `raw_files:` über alle Source-Seiten und trägt schon `duplicate_raw_file_owners`. Sie wird benutzt statt eines „ich weiß ja, welche Seite gemeint ist"-Kurzschlusses — eine zu bewegende Rohdatei mit mehr als einem Owner wird abgelehnt, statt die andere Seite unbemerkt zu brechen.
**7. `RAW_SUBDIRS` ist die einzige Quelle der Liste.** Sie speist vier Stellen: Eingangsverzeichnisse, `raw/`-Verzeichnisse, Contract-Tabelle, `.gitkeep`-Schleife in `dist export`. `docs verify` (`check_raw_subdirs`) hält die Tabelle beidseitig dagegen (Invariante 8).
**8. Die Version ist MINOR.** Die ursprüngliche Vermutung „eine erzwungene Umsortierung wäre die Major-Zeile" hielt dem Drop-in-Test nicht stand: eine Umsortierung *dieser* Instanz wäre eine Korpus-Operation und erreicht keine fremde Instanz. Boundary-crossing wäre es erst, wenn etwas die Bundle-Form *validiert*; nichts tut das, und unter Entscheidung 3 wird ohnehin nichts umsortiert.
## Umsetzungsnotizen
- **`Path.rename()` statt `git mv`.** Das in #16 als geteiltes Kleinstprimitiv erwartete „`git mv` versuchen, bei *not under version control* auf `mv` zurückfallen" wird hier **nicht** gebraucht: die Quelle in `incoming/` ist per Definition nie getrackt, und `page_ops.rename_command`/`move_command` bewegen `kb/`-Dateien längst mit schlichtem `Path.rename()`, weil `publish`s `git add -A` eine inhaltsgleiche Bewegung ohnehin als Rename erkennt. Bewusst dem bestehenden Muster gefolgt statt einen neuen git-Subprozess-Helfer einzuführen.
- **`doctor` wurde nicht erweitert.** Kein Akzeptanzkriterium verlangte es, und der Fall, um den es ginge (frischer Klon ohne `incoming/`), ist über `instructions/bootstrap.md` Schritt 2 abgedeckt. `doctor` meldet weiterhin nur und legt nie an — der Satz in Schritt 2 sagt das ausdrücklich.
- **Menschendoku über die Kriterienliste hinaus.** `README.md` beschrieb den Ingest-Einstieg noch als „drop a file into `raw/`" (Verzeichnisbaum, Quickstart, „Curate sources", erste Ingestion, `wiki-ingest`-Zeile) — nachgezogen, weil AGENTS.md § Changelog eine Stack-Änderung erst mit der Menschendoku als fertig zählt. `docs/pipeline-rationale.md` bekam einen Satz: `incoming/` liegt auf der Diesseits-Seite der Vertrauensgrenze, die Grenze ist die Beförderung nach `raw/`, nicht der Moment der Ablage. `INSTALL.md`, `INSTALL-MCP.md`, `EVALS.md` und `tools/README.md` wurden geprüft und brauchten nichts.
## Verhältnis zu #32
#32 skizziert dieselbe Mechanik, kommt aber von der anderen Seite: dort **fremd eingereichtes** Material, das nicht ungeprüft in ein öffentliches Repo darf, hier **eigenes**, das der Nutzer nicht selbst einsortieren soll. Ein Mechanismus, zwei Auslöser.
- **Der Lesezugriff ist gegenläufig.** #32 verlangt eine Quarantäne, „die kein Kommando der normalen Pipeline liest"; ein lokales `incoming/` will, dass die Ingest-Sitzung hineinsieht. Aufgelöst über die Grenze *Sitzung* vs. *Pipeline-Kommando* — und das war gratis: `lint` und `sources coverage` gehen ausschließlich über `config.iter_raw_files(config.RAW_DIR)`, alles außerhalb `raw/` ist per Konstruktion unsichtbar, ohne Ausschlussliste.
- **Die Abhängigkeit war nicht dieselbe.** #32 ist `status/blocked` auf #19; der lokale Ablagepfad hing an nichts davon.
**`raw accept` steht jetzt; #32 konsumiert es** und ergänzt, was nur der Fremdeinreichung eigen ist: Auth, Kontingente, Manifest mit Einreicherzurechnung, Ablehnungspfad.
## Verhältnis zu #16
**Keine Abhängigkeit, in keiner Richtung** — auch nicht die, die #16s Kommentar vom 2026-09-04 20:56 behauptete. Dort ist die Richtigstellung als Kommentar hinterlegt: `raw accept` befördert eine Datei, die noch keine Seite referenziert, und der eine referenzierende Fall benutzt die vorhandene Inversion aus `provenance.py`. Der Bestandspunkt stellte sich unter Entscheidung 3 gar nicht.
Was #16 aus dieser Umsetzung **erbt**: die `raw`-Kommandogruppe existiert jetzt (`tools/chemenu/commands/raw_cmd.py`, registriert in `cli.py`), `raw rename` hängt sich nur noch ein.
## Abgrenzung (unverändert eingehalten)
- Kein Konvertierungs-Werkzeug — PDF→Markdown macht die Sitzung, nicht `wikitool`.
- Keine Auth, keine Kontingente, kein Einreicher-Manifest — das ist #32.
- Kein Löschen aus `raw/`; `raw/` bleibt immutabel, hier entstand nur ein Weg **hinein**.
- Keine Umsortierung des Bestands.
## Akzeptanzkriterien
- [x] Nach `raw accept` sind die beförderten Dateien byte-identisch mit dem, was im Eingang lag, und der Eingang enthält sie nicht mehr — Test vergleicht Inhalte (`test_single_file_needs_no_bundle`, `test_two_files_bundle_under_the_first_files_stem`).
- [x] Eine einzeln beförderte Datei landet als `raw/<sub>/<name>` ohne Zwischenverzeichnis; mehrere in einem Aufruf als `raw/<sub>/<stem>/<name>`.
- [x] Eine Datei direkt in `incoming/` wird mit Exit 1 abgelehnt und nennt die zulässigen Verzeichnisse; nichts wird bewegt (`test_file_directly_in_incoming_is_rejected`, dazu `test_unknown_type_subdir_is_rejected`, `test_nested_too_deep_is_rejected`).
- [x] Eine Datei in `incoming/` erzeugt kein `uncovered_raw_files`-Finding (`test_promoted_file_in_incoming_is_never_reported_uncovered`).
- [x] `git check-ignore --no-index` meldet einen Pfad unter `incoming/` als ignoriert, geprüft über `docs_verify.REQUIRED_IGNORE_CANARIES` (`test_incoming_inbox_is_ignored`, plus das bestehende `test_no_content_is_gitignored`, das die Kanarie mitprüft).
- [x] Zu keinem Zeitpunkt zeigt eine `raw_files:`-Referenz ins Leere; im Wachstumsfall nennt `raw_files:` danach alle Dateien unter neuen Pfaden, `lint` meldet weder `broken_raw_refs` noch `uncovered_raw_files` (`test_growth_case_no_broken_or_uncovered_refs_afterwards`).
- [x] Ein Wachstumsfall auf eine Rohdatei mit mehr als einem Owner wird mit Exit 1 abgelehnt und bewegt nichts (`test_growth_case_rejects_a_multi_owner_raw_file`).
- [x] `dist_cmd.RAW_SUBDIRS` und die Routing-Tabelle beschreiben dieselbe Liste; `docs verify` schlägt in beiden Richtungen fehl (`check_raw_subdirs`, `test_raw_subdirs_mismatch_is_reported`).
- [x] `dist export` legt je `RAW_SUBDIRS`-Element `raw/<sub>/.gitkeep` **und** `incoming/<sub>/.gitkeep` an (`test_plan_creates_matching_incoming_subdirs`, `test_export_into_a_fresh_directory_works`).
- [x] `instructions/bootstrap.md` legt die Eingangsverzeichnisse an (neuer Schritt 2, mit dem Grund: gitignoriert, `doctor` meldet nur).
- [x] `raw/CONTRACT.md` beschreibt Eingang, Bundle-Regel und den Typ-des-Bundles-Satz; die Routing-Tabelle liest sich als abgeleitetes Verhalten.
- [x] `tools/CONTRACT.md` trägt Zeilen für `raw accept` und seinen Fehlerkontrakt; `docs verify` erzwingt beide Richtungen.
- [x] Der `wiki-ingest`-Skill kennt den Weg über den Eingang (neuer Schritt 1, Schritte neu nummeriert, Kommandoliste ergänzt).
- [x] Changelog-Eintrag, **MINOR** — `4.8.0-beta.3` (Kandidat, kein Release).
## Verifiziert
- `tools/wikitool docs verify` — OK, 52 Kommandos dokumentiert, 11 Ignore-Kanarien klar.
- `tools/wikitool instructions verify` — OK, 20 Instructions und 7 Skills gültig, 14 publizierte Kopien identisch.
- `pytest -q` lokal: **1032 passed**, davon 19 neu in `tools/chemenu/tests/test_raw_cmd.py` plus 4 in `test_docs_verify.py`/`test_dist_cmd.py`.
- Manueller End-to-End-Lauf über die echte CLI: `--dry-run`, dann echte Beförderung `incoming/notes/… → raw/notes/…`, `sources coverage` meldete die Datei danach korrekt als ungedeckt (keine Source-Seite); Testdatei wieder entfernt.
- CI zu `36d2128`: Lauf **182** (`verify`) grün — Tests, Coverage, Verify-Tree, Version-Gate, `dist export` und der Frisch-Instanz-Replay des Exports. Lauf **183** (`release`) grün und hat korrekt **kein** Release geschnitten: `VERSION` trägt `-beta.3`, jüngstes Release bleibt `v4.7.4`.
Schließt #58.
Changelog: Alle sechs offenen Fragen entschieden, Body auf den Entwurfsstand umgeschrieben, kind/decision → kind/build.
Zwei Entscheidungen fielen anders aus als der ursprüngliche Body vorschlug:
Bundle-Verzeichnis nur bei Bedarf statt einheitlich. Damit löst sich der Bestandsfall auf — die 29 flachen Dateien sind eindateiige Quellen in vorschriftsmäßiger Form, keine Altlast. Kein Umsortieren, kein Mass-Update-Publish, keine datierte Ausnahme im Contract.
Typ über Unterverzeichnisse in incoming/ statt über ein --type-Flag. Die Klassifikation wird abgegeben, wenn der Mensch das Dokument in der Hand hat, und überlebt den Sitzungswechsel; das Flag entfällt ersatzlos.
Weiter entschieden: Eingang als Top-Level-incoming/ im Repo (gilt auch für #32), Bundle-Name aus dem Stem der Primärdatei, Bundle trägt den Typ der Quelle, --page optional außer im Wachstumsfall, RAW_SUBDIRS bleibt einzige Quelle der Liste.
Drei Korrekturen gegen den alten Body: die Version ist MINOR, nicht MAJOR — eine Umsortierung dieser Instanz ist eine Korpus-Operation und erreicht keine fremde Instanz. Die 29 Dateien liegen nicht „flach in raw/", sondern flach innerhalb ihrer Unterverzeichnisse (3/1/25, assets/ leer). Und die Rückwärtssuche für den Wachstumsfall muss nicht gebaut werden — provenance.source_pages_by_raw_file() gibt es seit langem und lint benutzt sie bereits.
Neu ergänzt: § Verhältnis zu #16 hält fest, dass keine Abhängigkeit in beide Richtungen besteht, und notiert den Korrekturbedarf am dortigen Kommentar vom 2026-09-04 20:56, der nach diesen Entscheidungen in beiden Hälften nicht mehr gilt.
**Changelog:** Alle sechs offenen Fragen entschieden, Body auf den Entwurfsstand umgeschrieben, `kind/decision` → `kind/build`.
Zwei Entscheidungen fielen anders aus als der ursprüngliche Body vorschlug:
- **Bundle-Verzeichnis nur bei Bedarf** statt einheitlich. Damit löst sich der Bestandsfall auf — die 29 flachen Dateien sind eindateiige Quellen in vorschriftsmäßiger Form, keine Altlast. Kein Umsortieren, kein Mass-Update-Publish, keine datierte Ausnahme im Contract.
- **Typ über Unterverzeichnisse in `incoming/`** statt über ein `--type`-Flag. Die Klassifikation wird abgegeben, wenn der Mensch das Dokument in der Hand hat, und überlebt den Sitzungswechsel; das Flag entfällt ersatzlos.
Weiter entschieden: Eingang als Top-Level-`incoming/` im Repo (gilt auch für #32), Bundle-Name aus dem Stem der Primärdatei, Bundle trägt den Typ der Quelle, `--page` optional außer im Wachstumsfall, `RAW_SUBDIRS` bleibt einzige Quelle der Liste.
Drei Korrekturen gegen den alten Body: die Version ist **MINOR**, nicht MAJOR — eine Umsortierung dieser Instanz ist eine Korpus-Operation und erreicht keine fremde Instanz. Die 29 Dateien liegen nicht „flach in `raw/`", sondern flach innerhalb ihrer Unterverzeichnisse (3/1/25, `assets/` leer). Und die Rückwärtssuche für den Wachstumsfall muss nicht gebaut werden — `provenance.source_pages_by_raw_file()` gibt es seit langem und `lint` benutzt sie bereits.
Neu ergänzt: § Verhältnis zu #16 hält fest, dass keine Abhängigkeit in beide Richtungen besteht, und notiert den Korrekturbedarf am dortigen Kommentar vom 2026-09-04 20:56, der nach diesen Entscheidungen in beiden Hälften nicht mehr gilt.
Changelog: Body auf den Endstand umgeschrieben und geschlossen. Alle 14 Akzeptanzkriterien abgehakt, jeweils mit dem Test benannt, der sie hält. Das Problem steht jetzt im Präteritum, die acht Entscheidungen als getroffen.
Neu gegenüber dem Entwurfsstand ist der Abschnitt Umsetzungsnotizen mit drei Punkten, die beim Bauen entschieden wurden und aus keinem Kriterium folgten: Path.rename() statt des aus #16 erwarteten git mv-Primitivs (Quelle in incoming/ ist nie getrackt, und page_ops bewegt kb/-Dateien längst genauso); doctor bewusst nicht erweitert (der Fall liegt bei bootstrap.md Schritt 2); und die Menschendoku über die Kriterienliste hinaus — README.md beschrieb den Einstieg noch als „drop a file into raw/", docs/pipeline-rationale.md bekam einen Satz zur Vertrauensgrenze, INSTALL*.md/EVALS.md/tools/README.md geprüft und unverändert.
Der Korrekturbedarf an #16 ist erledigt statt nur vermerkt: Kommentar dort stellt beide Hälften richtig und hält fest, was #16 aus dieser Umsetzung erbt (die raw-Kommandogruppe existiert jetzt).
Verifikation im Body benannt: docs verify, instructions verify, 1032 lokale Tests, ein manueller CLI-Durchlauf, CI-Läufe 182 (verify, grün) und 183 (release, grün und korrekt ohne Release für den -beta.3-Kandidaten).
**Changelog:** Body auf den Endstand umgeschrieben und geschlossen. Alle 14 Akzeptanzkriterien abgehakt, jeweils mit dem Test benannt, der sie hält. Das Problem steht jetzt im Präteritum, die acht Entscheidungen als getroffen.
Neu gegenüber dem Entwurfsstand ist der Abschnitt **Umsetzungsnotizen** mit drei Punkten, die beim Bauen entschieden wurden und aus keinem Kriterium folgten: `Path.rename()` statt des aus #16 erwarteten `git mv`-Primitivs (Quelle in `incoming/` ist nie getrackt, und `page_ops` bewegt `kb/`-Dateien längst genauso); `doctor` bewusst nicht erweitert (der Fall liegt bei `bootstrap.md` Schritt 2); und die Menschendoku über die Kriterienliste hinaus — `README.md` beschrieb den Einstieg noch als „drop a file into `raw/`", `docs/pipeline-rationale.md` bekam einen Satz zur Vertrauensgrenze, `INSTALL*.md`/`EVALS.md`/`tools/README.md` geprüft und unverändert.
Der Korrekturbedarf an #16 ist erledigt statt nur vermerkt: [Kommentar dort](https://gitea.nehmer.net/torben/chemenu/issues/16#issuecomment-1031) stellt beide Hälften richtig und hält fest, was #16 aus dieser Umsetzung erbt (die `raw`-Kommandogruppe existiert jetzt).
Verifikation im Body benannt: `docs verify`, `instructions verify`, 1032 lokale Tests, ein manueller CLI-Durchlauf, CI-Läufe 182 (`verify`, grün) und 183 (`release`, grün und korrekt ohne Release für den `-beta.3`-Kandidaten).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Umgesetzt und ausgeliefert in
4.8.0-beta.3, Commit36d2128. Aufgeworfen und entschieden in den Sitzungen vom 2026-09-04, gebaut am 2026-09-05.Das Problem, das es gab
raw/CONTRACT.mds Routing-Tabelle war eine Regel für Menschen: wer eine Datei ablegte, wähltearticles//documents//notes//assets/selbst. Zwei Folgen:kb/,raw/war ausgenommen, ohne dass das je jemand entschieden hätte.raw_files:der Source-Seite.Nicht das Problem war die Tiefe selbst:
config.iter_raw_filesmachtrglob("*"),provenance.pyundsources coveragearbeiten auf repo-relativen Pfaden —raw/documents/handbuch/original.pdffunktionierte schon vorher ohne Codeänderung. Flach waren nur die Contract-Tabelle unddist_cmd.RAW_SUBDIRS. Es fehlte kein Mechanismus, es fehlte eine Zuständigkeit.Ebenfalls nicht das Problem war der Bestand: die 29 Rohdateien liegen flach innerhalb ihrer Unterverzeichnisse (
articles/3,documents/1,notes/25;assets/leer und mangels Datei nicht getrackt). Unter Entscheidung 3 sind das eindateiige Quellen in vorschriftsmäßiger Form. Es wurde nichts umsortiert.Der Weg, wie er jetzt läuft
Mehrdateiige Quelle:
Eindateiige Quelle:
Die Entscheidungen
1. Der Eingang ist ein Top-Level-
incoming/im Repo, gitignoriert. Nicht außerhalb, nicht unterraw/.Unter
raw/war es unmöglich:.gitignoreendet mit dem Backstop!raw/**, unddocs_verify.check_ignored_contentschlägt auf jede ignorierte Datei unterraw|kb|workfehl — einraw/incoming/wäre entweder committed oderdocs verifyrot. Gegen „außerhalb des Repos" entschied der Nachweis: „wird nie committed" ist überdocs_verify.REQUIRED_IGNORE_CANARIESmechanisch belegbar (git check-ignore --no-indexbeantwortet die Frage über den Regelsatz, ohne dass die Datei existieren muss); für einen Pfad außerhalb des Baums existiert dieses Beweismittel nicht, und er bräuchte zusätzlich eine Konfigurationsfläche, die es nirgends gibt.Gilt auch für #32. Dessen schärfere Anforderung („eine Quarantäne, die kein Kommando der normalen Pipeline liest") trägt nicht der Ort, sondern die Grenze zwischen Sitzung und Pipeline-Kommando — siehe § Verhältnis zu #32.
2. Der Typ wird über Unterverzeichnisse im Eingang deklariert, nicht über ein Flag.
incoming/spiegeltRAW_SUBDIRS. Kein--type-Argument.Keine Rückkehr zur Handsortierung, weil zwei Entscheidungen auseinanderfallen: was für ein Dokument das ist (eine Klassifikation, die kein Werkzeug ableiten kann — eine
.mdkannnotes/oderarticles/sein) und wo die Datei am Ende liegt (Unterverzeichnis, Bundle ja/nein, Bundle-Name, die Bewegung). Der Mensch liefert nur die erste, aus einem Vierer-Vokabular. Drei Vorteile gegenüber dem Flag: die Erklärung wird abgegeben, wenn der Mensch das Dokument in der Hand hat, und überlebt Sitzungswechsel; ein Argument weniger ist ein Fehlweg weniger; eine Datei direkt inincoming/kann abgelehnt werden, während ein Flag immer gesetzt ist, richtig oder falsch.3. Ein Bundle-Verzeichnis entsteht nur bei Bedarf — ab der zweiten Datei. Eine Datei → Datei, mehrere → Verzeichnis.
Das war die Kehre gegenüber der ursprünglichen Skizze („einheitlich"). Entscheidend war die Folge für den Bestand: unter „einheitlich" wären die 29 flachen Dateien eine datierte Ausnahme im Contract gewesen oder eine Umsortierung mit 29 Bewegungen plus referenzierenden Seiten — über der Mass-Update-Schwelle, also ein eigener Publish mit Freigabe. Unter „nur bei Bedarf" stellt sich der Fall nicht.
4. Der Bundle-Name kommt aus dem Stem der Primärdatei (erstes Argument, bzw. die bereits liegende Datei im Wachstumsfall), nicht aus dem Titel der Source-Seite — letzteres würde
raw/ankb/koppeln.5. Ein Bundle trägt den Typ der Quelle, nicht der einzelnen Datei. Ein Diagramm zu einer
documents/-Quelle wird ausincoming/documents/befördert, nicht ausincoming/assets/;assets/ist für Quellen, die selbst ein Asset sind. Steht als Satz inraw/CONTRACT.md.6.
--pageist optional, außer im Wachstumsfall. Ohne--pagebewegtacceptnur und druckt die Zielpfade fürnew source --set raw_files=…. Mit--pageerweitert esraw_files:einer bestehenden Seite; entsteht dadurch die zweite Datei, zieht es die Bundle-Beförderung der bereits liegenden Datei im selben Aufruf nach.Die Rückwärtssuche musste nicht gebaut werden:
provenance.source_pages_by_raw_file()invertiertraw_files:über alle Source-Seiten und trägt schonduplicate_raw_file_owners. Sie wird benutzt statt eines „ich weiß ja, welche Seite gemeint ist"-Kurzschlusses — eine zu bewegende Rohdatei mit mehr als einem Owner wird abgelehnt, statt die andere Seite unbemerkt zu brechen.7.
RAW_SUBDIRSist die einzige Quelle der Liste. Sie speist vier Stellen: Eingangsverzeichnisse,raw/-Verzeichnisse, Contract-Tabelle,.gitkeep-Schleife indist export.docs verify(check_raw_subdirs) hält die Tabelle beidseitig dagegen (Invariante 8).8. Die Version ist MINOR. Die ursprüngliche Vermutung „eine erzwungene Umsortierung wäre die Major-Zeile" hielt dem Drop-in-Test nicht stand: eine Umsortierung dieser Instanz wäre eine Korpus-Operation und erreicht keine fremde Instanz. Boundary-crossing wäre es erst, wenn etwas die Bundle-Form validiert; nichts tut das, und unter Entscheidung 3 wird ohnehin nichts umsortiert.
Umsetzungsnotizen
Path.rename()stattgit mv. Das in #16 als geteiltes Kleinstprimitiv erwartete „git mvversuchen, bei not under version control aufmvzurückfallen" wird hier nicht gebraucht: die Quelle inincoming/ist per Definition nie getrackt, undpage_ops.rename_command/move_commandbewegenkb/-Dateien längst mit schlichtemPath.rename(), weilpublishsgit add -Aeine inhaltsgleiche Bewegung ohnehin als Rename erkennt. Bewusst dem bestehenden Muster gefolgt statt einen neuen git-Subprozess-Helfer einzuführen.doctorwurde nicht erweitert. Kein Akzeptanzkriterium verlangte es, und der Fall, um den es ginge (frischer Klon ohneincoming/), ist überinstructions/bootstrap.mdSchritt 2 abgedeckt.doctormeldet weiterhin nur und legt nie an — der Satz in Schritt 2 sagt das ausdrücklich.README.mdbeschrieb den Ingest-Einstieg noch als „drop a file intoraw/" (Verzeichnisbaum, Quickstart, „Curate sources", erste Ingestion,wiki-ingest-Zeile) — nachgezogen, weil AGENTS.md § Changelog eine Stack-Änderung erst mit der Menschendoku als fertig zählt.docs/pipeline-rationale.mdbekam einen Satz:incoming/liegt auf der Diesseits-Seite der Vertrauensgrenze, die Grenze ist die Beförderung nachraw/, nicht der Moment der Ablage.INSTALL.md,INSTALL-MCP.md,EVALS.mdundtools/README.mdwurden geprüft und brauchten nichts.Verhältnis zu #32
#32 skizziert dieselbe Mechanik, kommt aber von der anderen Seite: dort fremd eingereichtes Material, das nicht ungeprüft in ein öffentliches Repo darf, hier eigenes, das der Nutzer nicht selbst einsortieren soll. Ein Mechanismus, zwei Auslöser.
incoming/will, dass die Ingest-Sitzung hineinsieht. Aufgelöst über die Grenze Sitzung vs. Pipeline-Kommando — und das war gratis:lintundsources coveragegehen ausschließlich überconfig.iter_raw_files(config.RAW_DIR), alles außerhalbraw/ist per Konstruktion unsichtbar, ohne Ausschlussliste.status/blockedauf #19; der lokale Ablagepfad hing an nichts davon.raw acceptsteht jetzt; #32 konsumiert es und ergänzt, was nur der Fremdeinreichung eigen ist: Auth, Kontingente, Manifest mit Einreicherzurechnung, Ablehnungspfad.Verhältnis zu #16
Keine Abhängigkeit, in keiner Richtung — auch nicht die, die #16s Kommentar vom 2026-09-04 20:56 behauptete. Dort ist die Richtigstellung als Kommentar hinterlegt:
raw acceptbefördert eine Datei, die noch keine Seite referenziert, und der eine referenzierende Fall benutzt die vorhandene Inversion ausprovenance.py. Der Bestandspunkt stellte sich unter Entscheidung 3 gar nicht.Was #16 aus dieser Umsetzung erbt: die
raw-Kommandogruppe existiert jetzt (tools/chemenu/commands/raw_cmd.py, registriert incli.py),raw renamehängt sich nur noch ein.Abgrenzung (unverändert eingehalten)
wikitool.raw/;raw/bleibt immutabel, hier entstand nur ein Weg hinein.Akzeptanzkriterien
raw acceptsind die beförderten Dateien byte-identisch mit dem, was im Eingang lag, und der Eingang enthält sie nicht mehr — Test vergleicht Inhalte (test_single_file_needs_no_bundle,test_two_files_bundle_under_the_first_files_stem).raw/<sub>/<name>ohne Zwischenverzeichnis; mehrere in einem Aufruf alsraw/<sub>/<stem>/<name>.incoming/wird mit Exit 1 abgelehnt und nennt die zulässigen Verzeichnisse; nichts wird bewegt (test_file_directly_in_incoming_is_rejected, dazutest_unknown_type_subdir_is_rejected,test_nested_too_deep_is_rejected).incoming/erzeugt keinuncovered_raw_files-Finding (test_promoted_file_in_incoming_is_never_reported_uncovered).git check-ignore --no-indexmeldet einen Pfad unterincoming/als ignoriert, geprüft überdocs_verify.REQUIRED_IGNORE_CANARIES(test_incoming_inbox_is_ignored, plus das bestehendetest_no_content_is_gitignored, das die Kanarie mitprüft).raw_files:-Referenz ins Leere; im Wachstumsfall nenntraw_files:danach alle Dateien unter neuen Pfaden,lintmeldet wederbroken_raw_refsnochuncovered_raw_files(test_growth_case_no_broken_or_uncovered_refs_afterwards).test_growth_case_rejects_a_multi_owner_raw_file).dist_cmd.RAW_SUBDIRSund die Routing-Tabelle beschreiben dieselbe Liste;docs verifyschlägt in beiden Richtungen fehl (check_raw_subdirs,test_raw_subdirs_mismatch_is_reported).dist exportlegt jeRAW_SUBDIRS-Elementraw/<sub>/.gitkeepundincoming/<sub>/.gitkeepan (test_plan_creates_matching_incoming_subdirs,test_export_into_a_fresh_directory_works).instructions/bootstrap.mdlegt die Eingangsverzeichnisse an (neuer Schritt 2, mit dem Grund: gitignoriert,doctormeldet nur).raw/CONTRACT.mdbeschreibt Eingang, Bundle-Regel und den Typ-des-Bundles-Satz; die Routing-Tabelle liest sich als abgeleitetes Verhalten.tools/CONTRACT.mdträgt Zeilen fürraw acceptund seinen Fehlerkontrakt;docs verifyerzwingt beide Richtungen.wiki-ingest-Skill kennt den Weg über den Eingang (neuer Schritt 1, Schritte neu nummeriert, Kommandoliste ergänzt).4.8.0-beta.3(Kandidat, kein Release).Verifiziert
tools/wikitool docs verify— OK, 52 Kommandos dokumentiert, 11 Ignore-Kanarien klar.tools/wikitool instructions verify— OK, 20 Instructions und 7 Skills gültig, 14 publizierte Kopien identisch.pytest -qlokal: 1032 passed, davon 19 neu intools/chemenu/tests/test_raw_cmd.pyplus 4 intest_docs_verify.py/test_dist_cmd.py.--dry-run, dann echte Beförderungincoming/notes/… → raw/notes/…,sources coveragemeldete die Datei danach korrekt als ungedeckt (keine Source-Seite); Testdatei wieder entfernt.36d2128: Lauf 182 (verify) grün — Tests, Coverage, Verify-Tree, Version-Gate,dist exportund der Frisch-Instanz-Replay des Exports. Lauf 183 (release) grün und hat korrekt kein Release geschnitten:VERSIONträgt-beta.3, jüngstes Release bleibtv4.7.4.Schließt #58.
Changelog: Alle sechs offenen Fragen entschieden, Body auf den Entwurfsstand umgeschrieben,
kind/decision→kind/build.Zwei Entscheidungen fielen anders aus als der ursprüngliche Body vorschlug:
incoming/statt über ein--type-Flag. Die Klassifikation wird abgegeben, wenn der Mensch das Dokument in der Hand hat, und überlebt den Sitzungswechsel; das Flag entfällt ersatzlos.Weiter entschieden: Eingang als Top-Level-
incoming/im Repo (gilt auch für #32), Bundle-Name aus dem Stem der Primärdatei, Bundle trägt den Typ der Quelle,--pageoptional außer im Wachstumsfall,RAW_SUBDIRSbleibt einzige Quelle der Liste.Drei Korrekturen gegen den alten Body: die Version ist MINOR, nicht MAJOR — eine Umsortierung dieser Instanz ist eine Korpus-Operation und erreicht keine fremde Instanz. Die 29 Dateien liegen nicht „flach in
raw/", sondern flach innerhalb ihrer Unterverzeichnisse (3/1/25,assets/leer). Und die Rückwärtssuche für den Wachstumsfall muss nicht gebaut werden —provenance.source_pages_by_raw_file()gibt es seit langem undlintbenutzt sie bereits.Neu ergänzt: § Verhältnis zu #16 hält fest, dass keine Abhängigkeit in beide Richtungen besteht, und notiert den Korrekturbedarf am dortigen Kommentar vom 2026-09-04 20:56, der nach diesen Entscheidungen in beiden Hälften nicht mehr gilt.
Changelog: Body auf den Endstand umgeschrieben und geschlossen. Alle 14 Akzeptanzkriterien abgehakt, jeweils mit dem Test benannt, der sie hält. Das Problem steht jetzt im Präteritum, die acht Entscheidungen als getroffen.
Neu gegenüber dem Entwurfsstand ist der Abschnitt Umsetzungsnotizen mit drei Punkten, die beim Bauen entschieden wurden und aus keinem Kriterium folgten:
Path.rename()statt des aus #16 erwartetengit mv-Primitivs (Quelle inincoming/ist nie getrackt, undpage_opsbewegtkb/-Dateien längst genauso);doctorbewusst nicht erweitert (der Fall liegt beibootstrap.mdSchritt 2); und die Menschendoku über die Kriterienliste hinaus —README.mdbeschrieb den Einstieg noch als „drop a file intoraw/",docs/pipeline-rationale.mdbekam einen Satz zur Vertrauensgrenze,INSTALL*.md/EVALS.md/tools/README.mdgeprüft und unverändert.Der Korrekturbedarf an #16 ist erledigt statt nur vermerkt: Kommentar dort stellt beide Hälften richtig und hält fest, was #16 aus dieser Umsetzung erbt (die
raw-Kommandogruppe existiert jetzt).Verifikation im Body benannt:
docs verify,instructions verify, 1032 lokale Tests, ein manueller CLI-Durchlauf, CI-Läufe 182 (verify, grün) und 183 (release, grün und korrekt ohne Release für den-beta.3-Kandidaten).torben referenced this issue2026-09-11 09:34:27 +00:00