Befund aus der Prüfung von Hand auf dem Windows-Arbeitsrechner (Betreiber, 2026-10-13, Stand 10.0.0), beim Einrichten von #214 neben #186 und #213. Gegen den Baum geprüft am 2026-10-16 (Stand 10.1.0-beta.15).
Stand: abgeschlossen. Gebaut in 10.1.0-beta.16 (Commit 02bf3b0, CI-Läufe 746 und 747 grün). Beim Schließen nachgezogen in 10.1.0-beta.17 (Commit 77fb427, CI-Läufe 748 und 749 grün): Die cli_contract-Fehlertexte von export run und upload collect nennen die Backup-Ordner statt backup.target und die Laufzeit-Ablehnung von Backup- und Exportdaten.
Befund
Der Betreiber hat im Sync-Ordner einen gemeinsamen Ordner angelegt, der alles hält:
doctor/export run lehnten das Exportziel ab: FAIL export: export.targets.shared and backup.target (C:\…\Chemenu) lie in one another - every export folder is its own.
Ursache: page_export._others_paths gab das ganzebackup.target in die Überschneidungsprüfung von read_config. Geschrieben wird dort aber nur in <ziel>/git und <ziel>/<maschine> (BackupConfig.git_dir/machine_dir).
Der intake-Kanal im selben Ordner wurde angenommen: upload._folder_problem prüfte einen folder-Pfad nur gegen die Instanz, weder gegen backup.target noch gegen ein Exportziel. Die Prüfung Eingang↔Export stand nur auf der Exportseite.
Die Regel war zugleich zu streng (Export) und zu locker (Eingang).
Was zur Laufzeit schon schützte – und was nicht
export run lehnt einen nicht leeren Zielordner ohne eigenes Manifest .wikitool-export.json ab und entfernt nur, was sein Manifest listet.
backup run lehnt einen nicht leeren Maschinenordner ohne .chemenu-backup.json ab und löscht nur, was sein Manifest listet.
upload collect (collect.collect_folder) hatte keinen solchen Schutz. Es liest nur die oberste Ebene, überspringt Unterordner und versteckte Dateien und löscht jede übernommene Datei an der Quelle. Gefährdet waren: ein Kanal auf einem Maschinenordner (dessen ENVIRONMENT.md), ein Kanal in einem Maschinenordner (<ziel>/<maschine>/incoming: Einreichungen im Kreis), ein Kanal auf oder in einem Exportziel. Ein Kanal über<ziel>/git oder einem Maschinenordner ist harmlos: collect steigt nicht in Unterordner ab.
Invariante
Kein Export und kein Eingangskanal verändert eine Datei unter <ziel>/git oder unter dem Maschinenordner irgendeiner Maschine – auch nicht einer anderen Maschine, deren Name diese Instanz nicht kennt, und auch nicht, wenn eine Maschine erst nach dem Einrichten des Kanals mit dem Sichern beginnt. Kein Eingangskanal entnimmt eine Datei aus einem Exportziel.
Umsetzung (so gebaut)
Konfiguration – backup.written_folders(root) liefert <ziel>/git und <ziel>/<eigene maschine> mit Meldungsetikett; ein kaputter backup-Abschnitt ergibt eine leere Liste (der Fehler gehört backup).
page_export._others_paths nimmt diese beiden Ordner statt backup.target: gleich, darin oder darüber ist ein Fehler; ein Geschwisterordner im Ziel ist gültig.
upload._folder_problem (für folder-Kanäle): Pfad gleich oder in einem der beiden Ordner ist ein Fehler. Darüber bleibt erlaubt (entschieden 2026-10-16), sonst würde eine bis dahin gültige Konfiguration nach dem Update zum FAIL – kein Drop-in mehr. backup wird dort nur in der folder-Verzweigung lazy importiert (MCP-Server-Prozess); die Absenzprüfung in test_mcp_server.py ist grün.
Laufzeit – backup.data_marker(folder, extra_manifests) prüft den (über Links aufgelösten) Ordner und alle Vorfahren auf .chemenu-backup.json, Bare-Repo (HEAD + objects/, ohne git-Aufruf) und die übergebenen weiteren Manifeste.
collect.collect_folder: Treffer (mit .wikitool-export.json als weiterem Kennzeichen) ist das ChannelResult.error dieses Kanals – nichts gelesen, nichts gelöscht, andere Kanäle laufen weiter, Exit 1.
page_export.mirror (aus run): Treffer der Backup-Kennzeichen ist ein ExportError dieses Ziels vor dem ersten Schreiben.
Die Konfigurationsprüfung bleibt auf die eigenen Ordner beschränkt; fremde Maschinen deckt nur Punkt 2.
Versionsteil: PATCH (Drop-in-Test): Export – eine bisher abgelehnte Konfiguration wird gültig; Eingang – neu abgelehnt werden nur Ordner in Backup- oder Exportdaten, also Zustände, die bisher Daten zerstört oder Einreichungen im Kreis erzeugt hätten.
Akzeptanzkriterien
Ein Exportziel <ziel>/Export und ein folder-Kanal <ziel>/Inbox neben einem backup.target<ziel> sind gültig; doctor meldet export, upload-intake und backup ohne FAIL (test_export_intake_and_backup_may_share_one_synced_folder).
Ein Exportpfad gleich, in oder über <ziel>/git bzw. <ziel>/<eigene maschine> ist ein Fehler der Sektion export (test_an_export_equal_to_in_or_over_a_folder_the_backup_writes_is_refused, je Fall).
Ein folder-Pfad gleich oder in <ziel>/git bzw. <ziel>/<eigene maschine> ist ein Fehler der Sektion intake, doctor meldet FAIL upload-intake (test_an_intake_folder_equal_to_or_in_a_folder_the_backup_writes_is_refused, test_doctor_fails_an_intake_folder_in_a_folder_the_backup_writes).
Ein folder-Pfad auf <ziel> selbst bleibt gültig (test_an_intake_folder_beside_or_over_the_backup_folders_is_valid).
upload collect gegen Ordner mit .chemenu-backup.json, Unterordner davon, Bare-Repo, Ordner mit .wikitool-export.json (und je deren Unterordner) endet mit Fehler für diesen Kanal, der Ordner ist byte-gleich; ein zweiter Kanal im selben Lauf wird abgeholt (test_a_pull_from_backup_or_export_data_…, test_the_other_channels_still_run_…).
export run in einen Zielordner unter einem fremden Maschinenordner (und unter einem Bare-Repo) endet mit Exit 1 für dieses Ziel, nichts geschrieben (test_a_target_below_another_machines_folder_…, test_a_target_in_a_bare_repository_is_refused).
Die drei How-tos zeigen die gemeinsame Ordnerstruktur (neuer Abschnitt in back-up-to-a-synced-folder.md, Verweise in den beiden anderen); die Feldtexte in config_shapes.py und damit docs/reference/configuration.md nennen die Regel; cli_contract-Notes und Fehlertexte von upload collect und export run und tools/CONTRACT.md nachgezogen (Fehlertexte in 10.1.0-beta.17).
CI grün: Läufe 746/747 auf 02bf3b0 und 748/749 auf 77fb427 (alle success).
Betroffene Dateien
tools/chemenu/backup.py, page_export.py, upload.py, collect.py, config_shapes.py, commands/upload_cmd.py, commands/export_cmd.py; Tests test_export_run.py (der alte Test test_a_target_in_the_backup_target_or_an_intake_folder_is_refused ist ersetzt), test_collect.py; die drei How-tos, docs/reference/ (generiert), tools/CONTRACT.md (generiert), CHANGES.md über version bump. doctor.py blieb unverändert: die Meldungstexte kommen aus den Lesefunktionen. Kein docs/explanation/-Text und keine Installationsanleitung betroffen (setup-instance.md verweist nur auf die How-tos).
Befund aus der Prüfung von Hand auf dem Windows-Arbeitsrechner (Betreiber, 2026-10-13, Stand 10.0.0), beim Einrichten von #214 neben #186 und #213. Gegen den Baum geprüft am 2026-10-16 (Stand 10.1.0-beta.15).
**Stand: abgeschlossen.** Gebaut in 10.1.0-beta.16 (Commit `02bf3b0`, CI-Läufe 746 und 747 grün). Beim Schließen nachgezogen in 10.1.0-beta.17 (Commit `77fb427`, CI-Läufe 748 und 749 grün): Die `cli_contract`-Fehlertexte von `export run` und `upload collect` nennen die Backup-Ordner statt `backup.target` und die Laufzeit-Ablehnung von Backup- und Exportdaten.
## Befund
Der Betreiber hat im Sync-Ordner einen gemeinsamen Ordner angelegt, der alles hält:
```
<OneDrive - Firma>\Chemenu\ ← backup.target (#186)
git\ ← Bare-Repo, schreibt backup init/publish
<MASCHINE>\ ← Maschinendaten, schreibt backup run/publish
Inbox\ ← intake-Kanal (folder, #213) – angenommen
Export\ ← export-Ziel (#214) – abgelehnt
```
- `doctor`/`export run` lehnten das Exportziel ab: `FAIL export: export.targets.shared and backup.target (C:\…\Chemenu) lie in one another - every export folder is its own`.
- Ursache: `page_export._others_paths` gab das **ganze** `backup.target` in die Überschneidungsprüfung von `read_config`. Geschrieben wird dort aber nur in `<ziel>/git` und `<ziel>/<maschine>` (`BackupConfig.git_dir`/`machine_dir`).
- Der `intake`-Kanal im selben Ordner wurde angenommen: `upload._folder_problem` prüfte einen `folder`-Pfad nur gegen die Instanz, weder gegen `backup.target` noch gegen ein Exportziel. Die Prüfung Eingang↔Export stand nur auf der Exportseite.
Die Regel war zugleich zu streng (Export) und zu locker (Eingang).
## Was zur Laufzeit schon schützte – und was nicht
- `export run` lehnt einen nicht leeren Zielordner ohne eigenes Manifest `.wikitool-export.json` ab und entfernt nur, was sein Manifest listet.
- `backup run` lehnt einen nicht leeren Maschinenordner ohne `.chemenu-backup.json` ab und löscht nur, was sein Manifest listet.
- `upload collect` (`collect.collect_folder`) hatte **keinen** solchen Schutz. Es liest nur die oberste Ebene, überspringt Unterordner und versteckte Dateien und löscht jede übernommene Datei an der Quelle. Gefährdet waren: ein Kanal auf einem Maschinenordner (dessen `ENVIRONMENT.md`), ein Kanal in einem Maschinenordner (`<ziel>/<maschine>/incoming`: Einreichungen im Kreis), ein Kanal auf oder in einem Exportziel. Ein Kanal **über** `<ziel>/git` oder einem Maschinenordner ist harmlos: `collect` steigt nicht in Unterordner ab.
## Invariante
**Kein Export und kein Eingangskanal verändert eine Datei unter `<ziel>/git` oder unter dem Maschinenordner irgendeiner Maschine** – auch nicht einer anderen Maschine, deren Name diese Instanz nicht kennt, und auch nicht, wenn eine Maschine erst nach dem Einrichten des Kanals mit dem Sichern beginnt. **Kein Eingangskanal entnimmt eine Datei aus einem Exportziel.**
## Umsetzung (so gebaut)
1. **Konfiguration – `backup.written_folders(root)`** liefert `<ziel>/git` und `<ziel>/<eigene maschine>` mit Meldungsetikett; ein kaputter `backup`-Abschnitt ergibt eine leere Liste (der Fehler gehört `backup`).
- `page_export._others_paths` nimmt diese beiden Ordner statt `backup.target`: gleich, darin oder darüber ist ein Fehler; ein Geschwisterordner im Ziel ist gültig.
- `upload._folder_problem` (für `folder`-Kanäle): Pfad gleich oder in einem der beiden Ordner ist ein Fehler. **Darüber bleibt erlaubt** (entschieden 2026-10-16), sonst würde eine bis dahin gültige Konfiguration nach dem Update zum `FAIL` – kein Drop-in mehr. `backup` wird dort nur in der `folder`-Verzweigung lazy importiert (MCP-Server-Prozess); die Absenzprüfung in `test_mcp_server.py` ist grün.
2. **Laufzeit – `backup.data_marker(folder, extra_manifests)`** prüft den (über Links aufgelösten) Ordner und alle Vorfahren auf `.chemenu-backup.json`, Bare-Repo (`HEAD` + `objects/`, ohne git-Aufruf) und die übergebenen weiteren Manifeste.
- `collect.collect_folder`: Treffer (mit `.wikitool-export.json` als weiterem Kennzeichen) ist das `ChannelResult.error` dieses Kanals – nichts gelesen, nichts gelöscht, andere Kanäle laufen weiter, Exit 1.
- `page_export.mirror` (aus `run`): Treffer der Backup-Kennzeichen ist ein `ExportError` dieses Ziels vor dem ersten Schreiben.
3. Die Konfigurationsprüfung bleibt auf die eigenen Ordner beschränkt; fremde Maschinen deckt nur Punkt 2.
**Versionsteil: PATCH** (Drop-in-Test): Export – eine bisher abgelehnte Konfiguration wird gültig; Eingang – neu abgelehnt werden nur Ordner in Backup- oder Exportdaten, also Zustände, die bisher Daten zerstört oder Einreichungen im Kreis erzeugt hätten.
## Akzeptanzkriterien
- [x] Ein Exportziel `<ziel>/Export` und ein `folder`-Kanal `<ziel>/Inbox` neben einem `backup.target` `<ziel>` sind gültig; `doctor` meldet `export`, `upload-intake` und `backup` ohne `FAIL` (`test_export_intake_and_backup_may_share_one_synced_folder`).
- [x] Ein Exportpfad gleich, in oder über `<ziel>/git` bzw. `<ziel>/<eigene maschine>` ist ein Fehler der Sektion `export` (`test_an_export_equal_to_in_or_over_a_folder_the_backup_writes_is_refused`, je Fall).
- [x] Ein `folder`-Pfad gleich oder in `<ziel>/git` bzw. `<ziel>/<eigene maschine>` ist ein Fehler der Sektion `intake`, `doctor` meldet `FAIL upload-intake` (`test_an_intake_folder_equal_to_or_in_a_folder_the_backup_writes_is_refused`, `test_doctor_fails_an_intake_folder_in_a_folder_the_backup_writes`).
- [x] Ein `folder`-Pfad auf `<ziel>` selbst bleibt gültig (`test_an_intake_folder_beside_or_over_the_backup_folders_is_valid`).
- [x] `upload collect` gegen Ordner mit `.chemenu-backup.json`, Unterordner davon, Bare-Repo, Ordner mit `.wikitool-export.json` (und je deren Unterordner) endet mit Fehler für diesen Kanal, der Ordner ist byte-gleich; ein zweiter Kanal im selben Lauf wird abgeholt (`test_a_pull_from_backup_or_export_data_…`, `test_the_other_channels_still_run_…`).
- [x] `export run` in einen Zielordner unter einem fremden Maschinenordner (und unter einem Bare-Repo) endet mit Exit 1 für dieses Ziel, nichts geschrieben (`test_a_target_below_another_machines_folder_…`, `test_a_target_in_a_bare_repository_is_refused`).
- [x] Die drei How-tos zeigen die gemeinsame Ordnerstruktur (neuer Abschnitt in `back-up-to-a-synced-folder.md`, Verweise in den beiden anderen); die Feldtexte in `config_shapes.py` und damit `docs/reference/configuration.md` nennen die Regel; `cli_contract`-Notes **und Fehlertexte** von `upload collect` und `export run` und `tools/CONTRACT.md` nachgezogen (Fehlertexte in 10.1.0-beta.17).
- [x] `pytest` (leere Umgebung: 3119 passed, 4 skipped), `docs verify`, `instructions verify` grün.
- [x] CI grün: Läufe 746/747 auf `02bf3b0` und 748/749 auf `77fb427` (alle `success`).
## Betroffene Dateien
`tools/chemenu/backup.py`, `page_export.py`, `upload.py`, `collect.py`, `config_shapes.py`, `commands/upload_cmd.py`, `commands/export_cmd.py`; Tests `test_export_run.py` (der alte Test `test_a_target_in_the_backup_target_or_an_intake_folder_is_refused` ist ersetzt), `test_collect.py`; die drei How-tos, `docs/reference/` (generiert), `tools/CONTRACT.md` (generiert), `CHANGES.md` über `version bump`. `doctor.py` blieb unverändert: die Meldungstexte kommen aus den Lesefunktionen. Kein `docs/explanation/`-Text und keine Installationsanleitung betroffen (`setup-instance.md` verweist nur auf die How-tos).
## Bezug
#186 (Backup), #213 (Eingangskanäle), #214 (Exporte)
Changelog: Body gegen den Baum geprüft (10.1.0-beta.15), Zeilenverweise nachgetragen. Neu im Befund: collect liest nur die oberste Ebene (gefährdet sind ENVIRONMENT.md im Maschinenordner und gespiegelte incoming/-Dateien; ein Kanal über den Backup-Ordnern ist harmlos), und ein Eingangskanal in einem Exportziel wird nur von doctor gemeldet, nicht von collect abgewiesen – Invariante um diesen Satz ergänzt. Beide offenen Fragen entschieden (Betreiber, 2026-10-16): (1) ein folder-Kanal darf <ziel>/git und Maschinenordner enthalten – der Vorschlag „keinen davon enthalten“ für den Eingang ist gestrichen, weil er eine heute gültige Konfiguration brechen würde; (2) Laufzeitprüfung über Ordner und Vorfahren, in collect mit Backup-, Bare-Repo- und Export-Manifest-Kennzeichen, in export run mit den Backup-Kennzeichen. PATCH bestätigt. Akzeptanzkriterien für (1) und (2) ergänzt, upload_cmd.py/export_cmd.py (cli_contract) und config_shapes.py in die Dateien aufgenommen. kind/decision → kind/build.
**Changelog:** Body gegen den Baum geprüft (10.1.0-beta.15), Zeilenverweise nachgetragen. Neu im Befund: `collect` liest nur die oberste Ebene (gefährdet sind `ENVIRONMENT.md` im Maschinenordner und gespiegelte `incoming/`-Dateien; ein Kanal *über* den Backup-Ordnern ist harmlos), und ein Eingangskanal in einem Exportziel wird nur von `doctor` gemeldet, nicht von `collect` abgewiesen – Invariante um diesen Satz ergänzt. Beide offenen Fragen entschieden (Betreiber, 2026-10-16): (1) ein `folder`-Kanal darf `<ziel>/git` und Maschinenordner *enthalten* – der Vorschlag „keinen davon enthalten“ für den Eingang ist gestrichen, weil er eine heute gültige Konfiguration brechen würde; (2) Laufzeitprüfung über Ordner **und Vorfahren**, in `collect` mit Backup-, Bare-Repo- und Export-Manifest-Kennzeichen, in `export run` mit den Backup-Kennzeichen. PATCH bestätigt. Akzeptanzkriterien für (1) und (2) ergänzt, `upload_cmd.py`/`export_cmd.py` (`cli_contract`) und `config_shapes.py` in die Dateien aufgenommen. `kind/decision` → `kind/build`.
Changelog: Body auf den gebauten Stand gebracht. Der Entwurf ist zu „Umsetzung (so gebaut)" geworden: backup.written_folders und backup.data_marker statt der zuvor nur beschriebenen Funktionen, die Laufzeitprüfung sitzt in collect_folder und page_export.mirror. Alle Akzeptanzkriterien sind abgehakt und mit ihren Tests benannt. Zusätzlich abgedeckt und neu im Body: Export-Ziel unter einem Bare-Repo wird abgewiesen, ein kaputter backup-Abschnitt lässt die intake-Prüfung gültig. doctor.py blieb unverändert. Publiziert als 10.1.0-beta.16 (02bf3b0), CI-Läufe 746 und 747 grün.
**Changelog:** Body auf den gebauten Stand gebracht. Der Entwurf ist zu „Umsetzung (so gebaut)" geworden: `backup.written_folders` und `backup.data_marker` statt der zuvor nur beschriebenen Funktionen, die Laufzeitprüfung sitzt in `collect_folder` und `page_export.mirror`. Alle Akzeptanzkriterien sind abgehakt und mit ihren Tests benannt. Zusätzlich abgedeckt und neu im Body: Export-Ziel unter einem Bare-Repo wird abgewiesen, ein kaputter `backup`-Abschnitt lässt die `intake`-Prüfung gültig. `doctor.py` blieb unverändert. Publiziert als 10.1.0-beta.16 (`02bf3b0`), CI-Läufe 746 und 747 grün.
Changelog: Body ist im Endstand. Beim Schließen wurden zwei veraltete cli_contract-Fehlertexte gefunden. Bei export run nannte die Ursache noch backup.target statt der beiden Backup-Ordner. Bei export run und upload collect fehlte die neue Laufzeit-Ablehnung von Backup- und Exportdaten. Beide sind in 10.1.0-beta.17 nachgezogen (77fb427, CI 748/749 grün). How-tos, docs/explanation/ und Installationsanleitungen sind nicht betroffen. Keine Kette. Geschlossen.
**Changelog:** Body ist im Endstand. Beim Schließen wurden zwei veraltete `cli_contract`-Fehlertexte gefunden. Bei `export run` nannte die Ursache noch `backup.target` statt der beiden Backup-Ordner. Bei `export run` und `upload collect` fehlte die neue Laufzeit-Ablehnung von Backup- und Exportdaten. Beide sind in 10.1.0-beta.17 nachgezogen (`77fb427`, CI 748/749 grün). How-tos, `docs/explanation/` und Installationsanleitungen sind nicht betroffen. Keine Kette. Geschlossen.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Befund aus der Prüfung von Hand auf dem Windows-Arbeitsrechner (Betreiber, 2026-10-13, Stand 10.0.0), beim Einrichten von #214 neben #186 und #213. Gegen den Baum geprüft am 2026-10-16 (Stand 10.1.0-beta.15).
Stand: abgeschlossen. Gebaut in 10.1.0-beta.16 (Commit
02bf3b0, CI-Läufe 746 und 747 grün). Beim Schließen nachgezogen in 10.1.0-beta.17 (Commit77fb427, CI-Läufe 748 und 749 grün): Diecli_contract-Fehlertexte vonexport runundupload collectnennen die Backup-Ordner stattbackup.targetund die Laufzeit-Ablehnung von Backup- und Exportdaten.Befund
Der Betreiber hat im Sync-Ordner einen gemeinsamen Ordner angelegt, der alles hält:
doctor/export runlehnten das Exportziel ab:FAIL export: export.targets.shared and backup.target (C:\…\Chemenu) lie in one another - every export folder is its own.page_export._others_pathsgab das ganzebackup.targetin die Überschneidungsprüfung vonread_config. Geschrieben wird dort aber nur in<ziel>/gitund<ziel>/<maschine>(BackupConfig.git_dir/machine_dir).intake-Kanal im selben Ordner wurde angenommen:upload._folder_problemprüfte einenfolder-Pfad nur gegen die Instanz, weder gegenbackup.targetnoch gegen ein Exportziel. Die Prüfung Eingang↔Export stand nur auf der Exportseite.Die Regel war zugleich zu streng (Export) und zu locker (Eingang).
Was zur Laufzeit schon schützte – und was nicht
export runlehnt einen nicht leeren Zielordner ohne eigenes Manifest.wikitool-export.jsonab und entfernt nur, was sein Manifest listet.backup runlehnt einen nicht leeren Maschinenordner ohne.chemenu-backup.jsonab und löscht nur, was sein Manifest listet.upload collect(collect.collect_folder) hatte keinen solchen Schutz. Es liest nur die oberste Ebene, überspringt Unterordner und versteckte Dateien und löscht jede übernommene Datei an der Quelle. Gefährdet waren: ein Kanal auf einem Maschinenordner (dessenENVIRONMENT.md), ein Kanal in einem Maschinenordner (<ziel>/<maschine>/incoming: Einreichungen im Kreis), ein Kanal auf oder in einem Exportziel. Ein Kanal über<ziel>/gitoder einem Maschinenordner ist harmlos:collectsteigt nicht in Unterordner ab.Invariante
Kein Export und kein Eingangskanal verändert eine Datei unter
<ziel>/gitoder unter dem Maschinenordner irgendeiner Maschine – auch nicht einer anderen Maschine, deren Name diese Instanz nicht kennt, und auch nicht, wenn eine Maschine erst nach dem Einrichten des Kanals mit dem Sichern beginnt. Kein Eingangskanal entnimmt eine Datei aus einem Exportziel.Umsetzung (so gebaut)
backup.written_folders(root)liefert<ziel>/gitund<ziel>/<eigene maschine>mit Meldungsetikett; ein kaputterbackup-Abschnitt ergibt eine leere Liste (der Fehler gehörtbackup).page_export._others_pathsnimmt diese beiden Ordner stattbackup.target: gleich, darin oder darüber ist ein Fehler; ein Geschwisterordner im Ziel ist gültig.upload._folder_problem(fürfolder-Kanäle): Pfad gleich oder in einem der beiden Ordner ist ein Fehler. Darüber bleibt erlaubt (entschieden 2026-10-16), sonst würde eine bis dahin gültige Konfiguration nach dem Update zumFAIL– kein Drop-in mehr.backupwird dort nur in derfolder-Verzweigung lazy importiert (MCP-Server-Prozess); die Absenzprüfung intest_mcp_server.pyist grün.backup.data_marker(folder, extra_manifests)prüft den (über Links aufgelösten) Ordner und alle Vorfahren auf.chemenu-backup.json, Bare-Repo (HEAD+objects/, ohne git-Aufruf) und die übergebenen weiteren Manifeste.collect.collect_folder: Treffer (mit.wikitool-export.jsonals weiterem Kennzeichen) ist dasChannelResult.errordieses Kanals – nichts gelesen, nichts gelöscht, andere Kanäle laufen weiter, Exit 1.page_export.mirror(ausrun): Treffer der Backup-Kennzeichen ist einExportErrordieses Ziels vor dem ersten Schreiben.Versionsteil: PATCH (Drop-in-Test): Export – eine bisher abgelehnte Konfiguration wird gültig; Eingang – neu abgelehnt werden nur Ordner in Backup- oder Exportdaten, also Zustände, die bisher Daten zerstört oder Einreichungen im Kreis erzeugt hätten.
Akzeptanzkriterien
<ziel>/Exportund einfolder-Kanal<ziel>/Inboxneben einembackup.target<ziel>sind gültig;doctormeldetexport,upload-intakeundbackupohneFAIL(test_export_intake_and_backup_may_share_one_synced_folder).<ziel>/gitbzw.<ziel>/<eigene maschine>ist ein Fehler der Sektionexport(test_an_export_equal_to_in_or_over_a_folder_the_backup_writes_is_refused, je Fall).folder-Pfad gleich oder in<ziel>/gitbzw.<ziel>/<eigene maschine>ist ein Fehler der Sektionintake,doctormeldetFAIL upload-intake(test_an_intake_folder_equal_to_or_in_a_folder_the_backup_writes_is_refused,test_doctor_fails_an_intake_folder_in_a_folder_the_backup_writes).folder-Pfad auf<ziel>selbst bleibt gültig (test_an_intake_folder_beside_or_over_the_backup_folders_is_valid).upload collectgegen Ordner mit.chemenu-backup.json, Unterordner davon, Bare-Repo, Ordner mit.wikitool-export.json(und je deren Unterordner) endet mit Fehler für diesen Kanal, der Ordner ist byte-gleich; ein zweiter Kanal im selben Lauf wird abgeholt (test_a_pull_from_backup_or_export_data_…,test_the_other_channels_still_run_…).export runin einen Zielordner unter einem fremden Maschinenordner (und unter einem Bare-Repo) endet mit Exit 1 für dieses Ziel, nichts geschrieben (test_a_target_below_another_machines_folder_…,test_a_target_in_a_bare_repository_is_refused).back-up-to-a-synced-folder.md, Verweise in den beiden anderen); die Feldtexte inconfig_shapes.pyund damitdocs/reference/configuration.mdnennen die Regel;cli_contract-Notes und Fehlertexte vonupload collectundexport runundtools/CONTRACT.mdnachgezogen (Fehlertexte in 10.1.0-beta.17).pytest(leere Umgebung: 3119 passed, 4 skipped),docs verify,instructions verifygrün.02bf3b0und 748/749 auf77fb427(allesuccess).Betroffene Dateien
tools/chemenu/backup.py,page_export.py,upload.py,collect.py,config_shapes.py,commands/upload_cmd.py,commands/export_cmd.py; Teststest_export_run.py(der alte Testtest_a_target_in_the_backup_target_or_an_intake_folder_is_refusedist ersetzt),test_collect.py; die drei How-tos,docs/reference/(generiert),tools/CONTRACT.md(generiert),CHANGES.mdüberversion bump.doctor.pyblieb unverändert: die Meldungstexte kommen aus den Lesefunktionen. Keindocs/explanation/-Text und keine Installationsanleitung betroffen (setup-instance.mdverweist nur auf die How-tos).Bezug
#186 (Backup), #213 (Eingangskanäle), #214 (Exporte)
Changelog: Body gegen den Baum geprüft (10.1.0-beta.15), Zeilenverweise nachgetragen. Neu im Befund:
collectliest nur die oberste Ebene (gefährdet sindENVIRONMENT.mdim Maschinenordner und gespiegelteincoming/-Dateien; ein Kanal über den Backup-Ordnern ist harmlos), und ein Eingangskanal in einem Exportziel wird nur vondoctorgemeldet, nicht voncollectabgewiesen – Invariante um diesen Satz ergänzt. Beide offenen Fragen entschieden (Betreiber, 2026-10-16): (1) einfolder-Kanal darf<ziel>/gitund Maschinenordner enthalten – der Vorschlag „keinen davon enthalten“ für den Eingang ist gestrichen, weil er eine heute gültige Konfiguration brechen würde; (2) Laufzeitprüfung über Ordner und Vorfahren, incollectmit Backup-, Bare-Repo- und Export-Manifest-Kennzeichen, inexport runmit den Backup-Kennzeichen. PATCH bestätigt. Akzeptanzkriterien für (1) und (2) ergänzt,upload_cmd.py/export_cmd.py(cli_contract) undconfig_shapes.pyin die Dateien aufgenommen.kind/decision→kind/build.Changelog: Body auf den gebauten Stand gebracht. Der Entwurf ist zu „Umsetzung (so gebaut)" geworden:
backup.written_foldersundbackup.data_markerstatt der zuvor nur beschriebenen Funktionen, die Laufzeitprüfung sitzt incollect_folderundpage_export.mirror. Alle Akzeptanzkriterien sind abgehakt und mit ihren Tests benannt. Zusätzlich abgedeckt und neu im Body: Export-Ziel unter einem Bare-Repo wird abgewiesen, ein kaputterbackup-Abschnitt lässt dieintake-Prüfung gültig.doctor.pyblieb unverändert. Publiziert als 10.1.0-beta.16 (02bf3b0), CI-Läufe 746 und 747 grün.Changelog: Body ist im Endstand. Beim Schließen wurden zwei veraltete
cli_contract-Fehlertexte gefunden. Beiexport runnannte die Ursache nochbackup.targetstatt der beiden Backup-Ordner. Beiexport runundupload collectfehlte die neue Laufzeit-Ablehnung von Backup- und Exportdaten. Beide sind in 10.1.0-beta.17 nachgezogen (77fb427, CI 748/749 grün). How-tos,docs/explanation/und Installationsanleitungen sind nicht betroffen. Keine Kette. Geschlossen.