Backup einer lokalen Instanz in einen Sync-Ordner (OneDrive): Bare-Repo unter <ziel>/git, Maschinendaten unter <ziel>/<maschine>/, doctor warnt vor .git im Sync-Ordner #186

Closed
opened 2026-10-06 15:07:56 +00:00 by torben · 6 comments
Owner

Ausgearbeitet aus einem status/incoming-Stub. Der Wortlaut steht im ersten Kommentar.

Stand

Erledigt (geschlossen 2026-10-13). Veröffentlicht in 10.0.0-beta.10, Commit 9736472f485333cfd08493da3e26c371f89f1f39. Verifiziert durch CI auf diesem Commit: Läufe 675 (Jobs verify und pwsh) und 676, beide grün. Lokal zuvor grün: pytest (2959 passed, 4 skipped; die neuen Tests auch unter env -i), docs verify, instructions verify. Ausgeliefert mit Release 10.0.0.

Abschlussphase: Die Prüfung auf veraltete Dokumente hat eine Lücke gefunden. docs/how-to/maintain-the-wiki.md nennt jetzt das Backup in einen Sync-Ordner und die WARN einer gescheiterten Maschinensicherung (Commit 3627168, reine docs/-Prosa ohne Bump, CI-Lauf 677 grün). Alle übrigen Dokumente waren aktuell.

Prüfung von Hand auf dem Windows-Arbeitsrechner, 2026-10-13 (Release 10.0.0, GitHub Copilot CLI): Einrichtung, Push, Maschinensicherung, doctor und das scharf geschaltete Publish-Remote Gate sind geprüft, siehe letztes Akzeptanzkriterium. Die Wiederherstellung nach dem How-to ist nach #222 Abschnitt C verschoben (Betreiberentscheidung 2026-10-13), wo alle offenen Prüfungen von Hand nach 10.0.0 in einem Termin laufen.

Abweichungen vom ursprünglichen Body sind unten an Ort und Stelle eingearbeitet und mit (Bau) markiert.

Ausgangslage

Eine berufliche Instanz entsteht gerade, auf einem Windows-Rechner (pwsh 7, nativ). Dort gibt es keinen Git-Server mit passender Privatsphäre. Der einzige sanktionierte Speicher außerhalb des Geräts ist OneDrive. Ein .git-Verzeichnis in einem Sync-Ordner gilt als problematisch. Gesucht ist ein Backup ohne Server, das auch die Daten dieser Maschine sichert, die nicht in Git stehen.

Schon heute unterstützt, ohne Änderung: Eine Instanz ohne Server ist ein gültiger Endzustand. doctor meldet ein fehlendes origin nur als WARN (tools/chemenu/commands/doctor.py, check_git_repo), und publish --no-push committet lokal. Ein Push gegen ein Bare-Repo unter einem Dateipfad ist Alltag. Die Publish-Tests laufen genau so (tools/chemenu/tests/test_git_publish.py, Fixture repo).

Entscheidungen

  1. Das Arbeitsrepo liegt lokal außerhalb jedes Sync-Ordners. Gesichert wird in ein konfiguriertes Ziel im Sync-Ordner, mit dieser Struktur:

    <ziel>/git/           Bare-Repo, ist origin; publish pusht hierher
    <ziel>/<maschine>/    Spiegel der Maschinendaten, plus Manifest .chemenu-backup.json
    

    Ins Bare-Repo wird nur beim Push geschrieben, mit genau einem Schreiber. Der Sync-Client sieht fast nur neue, unveränderliche Objektdateien.

  2. Maschinendaten sind eine feste Liste plus extra. Der Schalter dafür ist backup.machine_data.

    Pfad Gesichert Grund
    .wikitool-local.json ja Checkout-Einstellungen. Private Klartext-Werte wie interne Hostnamen landen damit im OneDrive. Das ist vom Betreiber akzeptiert, das How-to sagt es ausdrücklich.
    ENVIRONMENT.md ja, falls vorhanden von Hand gepflegt
    incoming/* außer .gitkeep ja Wartende Quellen sind sonst nirgends gesichert.
    quarantine/ (bis 9.x mcp-upload/, #213) nein (Betreiber) nicht gesichtetes fremdes Material
    reports/ nein Lint-Berichte lassen sich neu erzeugen. Telemetrie enthält Prompts.
    .wikitool-tools.json, tools/.venv/, tools/.wikitool_session/, Caches, .agents/skills/, .claude/skills/ nein lassen sich neu erzeugen bzw. schreibt der Preflight neu
    Secrets-Datei (%APPDATA%\chemenu\<instance_id>\secrets.json, darin auch die Sitzungen von raw login) nie liegt außerhalb der Instanz und ist durch die Regel unten ausgeschlossen
    backup.extra ja zusätzliche Pfade relativ zur Instanz, z. B. .claude/settings.local.json, .obsidian/
  3. Auslöser:

    • Ist machine_data: true gesetzt, sichert publish die Maschinendaten nach einem erfolgreichen Push. (Bau) Ein publish, das nichts zu pushen hatte („Nothing to commit“ ohne lokalen Vorsprung), pusht nicht und sichert daher auch nicht.
    • Scheitert diese Sicherung, gibt es nur eine WARN, und publish endet mit Exit 0, weil Commit und Push schon geschehen sind.
    • backup run sichert von Hand.
    • Bekannte Grenze: Eine Datei, die gerade in incoming/ abgelegt wurde, ist erst nach dem nächsten publish mit Push oder backup run gesichert (Betreiber: passt).
  4. backup init richtet das Ziel ein. Es legt <ziel>/git als Bare-Repo an und setzt origin, aber nur, wenn noch keins existiert. Den Eintrag für remotes.allowed_push_urls gibt es nur aus und schreibt ihn nie, denn das Feld ist ein Gate. Gibt es keine remotes-Sektion, ist das Gate nicht scharf, und backup init gibt nichts aus (in der Prüfung von Hand so bestätigt).

  5. Verworfen:

    • Repo samt .git direkt im Sync-Ordner:

      • Unter Windows sperrt OneDrive Dateien kurzzeitig. Das stört git gc/repack („unlink failed“).
      • Files On-Demand lagert Objekte als Platzhalter aus.
      • Bei einem zweiten Gerät entstehen Konfliktkopien in .git.
      • Echter Sync zwischen Geräten über Git in OneDrive scheidet aus denselben Gründen aus.
    • Ganz ohne Git: Git ist im Stack die lokale Absicherung, nicht der Upload-Weg. Ohne Git fallen weg:

      • das Mass-Update Gate (zählt über git diff --cached),
      • das Rückgängigmachen einer Agenten-Sitzung,
      • migrate verify.

      Außerdem läuft dist upgrade dann ohne die Prüfung auf einen sauberen Arbeitsstand (dist_cmd.py), und doctor meldet FAIL. Der Wunsch dahinter, dass Microsoft-Werkzeuge die KB lesen, geht über #214 und #215.

    • git bundle als Einzeldatei: kein Remote. Nur falls sich das Bare-Repo in der Praxis nicht bewährt.

Konfiguration

Neue Sektion backup in .wikitool-local.json (config_shapes.BACKUP):

{
  "backup": {
    "target": "C:\\Users\\<user>\\OneDrive - <Firma>\\Chemenu-Backup",
    "machine_data": true,
    "machine_name": "WORK-LAPTOP",
    "extra": [".claude/settings.local.json"]
  }
}
Feld Tier Typ Pflicht Default Bedeutung
target checkout, gate string ja - Das Ziel. Es muss ein absoluter Pfad außerhalb der Instanz sein. (Bau) Außerdem darf die Instanz nicht in <ziel>/git oder <ziel>/<maschine> liegen.
machine_data checkout, gate boolean nein false Maschinendaten sichern, nach publish und mit backup run. (Bau) Nur ein echter JSON-Boolean, kein "yes".
machine_name checkout string nein Hostname (socket.gethostname()) Name des Unterordners. Wird auf [A-Za-z0-9_-] bereinigt (alles andere wird -). (Bau) Danach werden führende und folgende - entfernt. Ist er dann leer oder heißt er git (Groß-/Kleinschreibung egal), ist das ein Fehler – auch ein ausdrücklich leerer String, der nicht als „Hostname“ gelesen wird.
extra checkout, gate array of strings nein [] Zusätzliche Pfade relativ zur Instanz, Dateien oder Verzeichnisse. (Bau) Ein fehlender Pfad wird übersprungen und gemeldet, ist kein Fehler.
  • Fehlt die Sektion: Nichts ändert sich. publish verhält sich wie heute, backup init und backup run enden mit Exit 1 und nennen die Sektion.
  • Ist die Sektion fehlerhaft: Das ist ein Fehler. publish stoppt mit Exit 1, bevor es etwas committet, backup init und backup run enden mit Exit 1, und doctor meldet FAIL. Eine kaputte Sektion wird nie als „kein Backup“ gelesen.

target, machine_data und extra sind gate, weil sie bestimmen, welche Daten die Maschine verlassen. config set lehnt sie mit Exit 42 ab, der Betreiber pflegt sie von Hand (Muster wie remotes/intake).

Was gebaut wurde

  1. wikitool backup init (schreibend, idempotent; tools/chemenu/commands/backup_cmd.py, Logik in tools/chemenu/backup.py):
    • Legt <ziel>/ und <ziel>/git an, Letzteres als Bare-Repo (git init --bare -b main).
    • Ist <ziel>/git schon ein Bare-Repo, bleibt es unverändert. (Bau) Ein leeres Verzeichnis dort wird wie ein fehlendes behandelt. Existiert dort etwas anderes, endet der Befehl mit Exit 1 und verändert nichts.
    • Gibt es kein origin, setzt es origin auf <ziel>/git. Zeigt ein bestehendes origin woandershin, bleibt es unverändert. Der Befehl endet dann mit Exit 1, nennt beide URLs und überlässt dem Betreiber, ob er umstellt. (Bau) Alle Vorbedingungen werden zuerst geprüft; in diesem Fall wird auch <ziel>/git nicht angelegt.
    • Existiert remotes.allowed_push_urls und fehlt <ziel>/git darin, gibt es den exakten JSON-Eintrag zum Selbst-Eintragen aus (die ganze remotes-Sektion mit ergänzter URL).
  2. wikitool backup run (schreibend, idempotent): spiegelt die Maschinendaten nach <ziel>/<maschine>/ und schreibt das Manifest .chemenu-backup.json (Zeitpunkt, Stack-Version, Maschinenname, je Datei relativer Pfad, Größe und sha256). Bei machine_data: false endet es mit Exit 1. (Bau) Existiert <ziel> nicht als Verzeichnis (Sync-Ordner fehlt), endet es mit Exit 1 und legt nichts an. Eine Datei, deren Größe und sha256 zum vorigen Manifest passen, wird nicht neu geschrieben. Ersetzt wird mit bis zu fünf Versuchen, weil OneDrive eine Datei kurz offen hält.
  3. publish: Nach einem erfolgreichen Push und mit machine_data: true ruft es denselben Code wie backup run auf. Scheitert er, gibt es eine WARN-Zeile mit dem Grund, Exit 0. Bei --no-push oder einem gescheiterten Push wird nicht gesichert.
  4. doctor:
    • sync-folder: WARN, wenn das Git-Verzeichnis (git rev-parse --absolute-git-dir, Links bzw. Junctions aufgelöst) unter einem der Pfade aus OneDrive, OneDriveCommercial oder OneDriveConsumer liegt (Windows) oder unter ~/Library/CloudStorage/ (macOS). Unter Windows ohne Groß-/Kleinschreibung verglichen. Der Hinweis nennt das How-to und den typischen Fall, dass „Dokumente“ per Known Folder Move unbemerkt in OneDrive liegt. Sonst OK, nie FAIL.
    • backup: (Bau) Ohne Sektion OK mit „absent - no backup section in the settings“ – doctor kennt nur OK/WARN/FAIL; einen Status ABSENT, wie ursprünglich hier stand, gibt es nicht, und upload-intake/tasks-provider melden eine fehlende Sektion genauso. Mit Sektion:
      • WARN, wenn <ziel>/git kein Bare-Repo ist oder origin nicht dorthin pusht.
      • Bei machine_data: true das Alter der letzten Sicherung laut Manifest, WARN, wenn es keine gibt.
      • FAIL nur bei einer fehlerhaften Sektion.
    • Die bestehende git-remote-WARN (kein Remote) nennt das How-to.
  5. How-to docs/how-to/back-up-to-a-synced-folder.md:
    • Warum der Arbeitsbaum nicht in den Sync-Ordner gehört.
    • Die Sektion backup von Hand anlegen, dann backup init und den Eintrag in allowed_push_urls ergänzen.
    • Was gesichert wird und was nicht. Dazu gehört ausdrücklich: Private Werte aus .wikitool-local.json landen im Sync-Ordner, die Secrets nie.
    • Wiederherstellung:
      1. git clone <ziel>/git <neu>, dann git fsck --full.
      2. Inhalt von <ziel>/<maschine>/ ohne das Manifest in die Instanz kopieren.
      3. Bootstrap (instructions/bootstrap.md).
      4. Secrets neu hinterlegen.
    • Nur ein Gerät schreibt ins Bare-Repo.
  6. Verweise: instructions/setup-instance.md Schritt 3 (Remote-Frage) bietet „Backup in einen Sync-Ordner“ als Option an und verlinkt das How-to. Die Remote-Frage in INSTALL.md tut das ebenso. instructions/publish-cycle.md nennt die WARN einer gescheiterten Maschinensicherung: Sie wird dem Betreiber gezeigt und nicht durch einen erneuten publish behoben, sondern durch backup run nach Behebung der Ursache. (Bau) Zusätzlich: docs/explanation/configuration-tiers.md sagt, dass ein privater Klartext-Wert über das Backup die Maschine verlässt und ein Verweis das verhindert; docs/how-to/maintain-the-wiki.md nennt das Backup und die WARN (Abschlussphase).

Was eine Sicherung garantieren muss

  • Geschrieben und gelöscht wird nur in <ziel>/<maschine>/. Außerhalb davon verändert backup run nichts, auch nicht <ziel>/git oder Ordner anderer Maschinen. (Bau) Auch nicht über einen Link im Maschinenordner: ein Link auf dem Weg zu einer Zieldatei bricht ab, und ein Manifest-Eintrag, der den Ordner verlässt (.., Laufwerk, Backslash), ebenso.
  • Gelöscht wird nur, was das vorige Manifest aufführt und lokal nicht mehr existiert, etwa eine per raw accept übernommene Quelle aus incoming/. Eine Datei in <ziel>/<maschine>/, die nicht im Manifest steht, bleibt unberührt. (Bau) Das gilt für Pfade, die der Lauf nicht selbst spiegelt; liegt eine fremde Datei genau unter dem Pfad einer gespiegelten Datei, wird sie überschrieben.
  • Ist <ziel>/<maschine>/ nicht leer und ohne Manifest, wird abgelehnt (Exit 1, nichts geschrieben). Es wird nie in einen fremden Ordner hineingespiegelt. (Bau) Ein unlesbares Manifest wird ebenso abgelehnt.
  • Nichts außerhalb der Instanz wird gelesen. Ein Pfad aus extra, der nach dem Auflösen außerhalb der Instanz liegt, ist ein Fehler, damit auch über extra kein Secret in den Sync-Ordner kommt. Symlinks innerhalb der gesicherten Verzeichnisse werden nicht verfolgt, sondern übersprungen und gemeldet. .git/ wird nie gesichert, auch nicht über extra.
  • Jede Datei wird atomar geschrieben (temporäre Datei im selben Verzeichnis, dann os.replace). Das Manifest wird zuletzt geschrieben. Ein abgebrochener Lauf hinterlässt nur ganze Dateien und das Manifest des vorigen Laufs.

Akzeptanzkriterien

Tests in tools/chemenu/tests/test_backup.py, soweit nicht anders genannt. Abgehakt ist, was mit 9736472 veröffentlicht und von den CI-Läufen 675 und 676 bestätigt ist.

  • Nach backup init mit leerem Ziel ist <ziel>/git ein Bare-Repo, und git remote get-url --push origin ergibt <ziel>/git. Ein zweiter Aufruf endet mit Exit 0 und ändert nichts (Test).
  • Zeigt origin vorher woandershin, endet backup init mit Exit 1, und origin ist unverändert (Test). Liegt unter <ziel>/git etwas, das kein Bare-Repo ist, endet es mit Exit 1, und der Inhalt ist unverändert (Test).
  • Existiert remotes.allowed_push_urls ohne <ziel>/git, gibt backup init den Eintrag aus, und .wikitool-local.json ist danach byte-gleich (Test).
  • publish pusht gegen <ziel>/git unter einem Pfad mit Leerzeichen. Danach zeigt main dort auf den lokalen HEAD. Mit machine_data: true enthält <ziel>/<maschine>/ danach genau .wikitool-local.json, ENVIRONMENT.md (falls vorhanden), die Dateien aus incoming/ außer .gitkeep, die extra-Pfade und das Manifest (Test).
  • Nach backup run liegt in <ziel>/<maschine>/ keine Datei aus der Quarantäne, reports/, .wikitool-tools.json oder .git/ (Test mit befüllten Verzeichnissen; die Quarantäne heißt seit #213 quarantine/).
  • Eine Datei, die seit dem vorigen Lauf aus incoming/ verschwunden ist, fehlt danach auch im Ziel. Eine fremde Datei in <ziel>/<maschine>/, die nicht im Manifest steht, ist danach byte-gleich vorhanden (Test).
  • Ist <ziel>/<maschine>/ nicht leer und ohne Manifest, endet backup run mit Exit 1, und der Ordner ist unverändert (Test).
  • Ein extra-Eintrag, der außerhalb der Instanz auflöst (.. oder ein Symlink nach außen), führt zu Exit 1, bevor etwas geschrieben wird (Test). Ein Symlink in incoming/ wird nicht verfolgt und erscheint nicht im Ziel (Test).
  • Ein machine_name, das nach dem Bereinigen leer ist oder git/GIT heißt, ist ein Fehler der Sektion (Test).
  • Scheitert die Maschinensicherung nach erfolgreichem Push, z. B. weil das Ziel nicht beschreibbar ist, endet publish mit Exit 0 und einer WARN-Zeile, und der Push ist erfolgt (Test). Bei --no-push entsteht kein <ziel>/<maschine>/ (Test).
  • Mit fehlerhafter backup-Sektion endet publish mit Exit 1, ohne dass ein Commit entsteht (Test).
  • doctor:
    • sync-folder ist WARN unter einem Pfad aus OneDrive/OneDriveCommercial/OneDriveConsumer oder ~/Library/CloudStorage/, auch wenn das Repo über einen Symlink dorthin erreicht wird, und sonst OK.
    • Windows-Vergleich ohne Groß-/Kleinschreibung.
    • backup meldet OK (absent), WARN (fehlendes <ziel>/git, abweichendes origin, keine Maschinensicherung) und FAIL (fehlerhafte Sektion) wie beschrieben.
    • Keine der beiden Prüfungen setzt den Exit-Code von doctor außer bei FAIL (Tests mit auf tmp_path gesetzten Umgebungsvariablen; die drei OneDrive-Variablen löscht jetzt auch die hermetische Fixture in conftest.py).
  • Das How-to existiert, steht in docs/how-to/README.md und deckt Punkt 5 vollständig ab. setup-instance.md, INSTALL.md, publish-cycle.md und die git-remote-WARN verweisen darauf wie in Punkt 6.
  • Einmalig von Hand auf dem Windows-Arbeitsrechner geprüft (Betreiber, 2026-10-13, Release 10.0.0, GitHub Copilot CLI; Ziel C:\OneDrive - <Firma>\Chemenu, OneDrive außerhalb des Benutzerprofils):
    • backup init legt <ziel>\git als Bare-Repo an und setzt origin. Ohne remotes-Sektion gibt es nichts aus (Gate nicht scharf; doctor: „Gate not armed“).
    • publish pusht die bestehende lokale Historie nach <ziel>\git und spiegelt 5 Dateien nach <ziel>\<maschine>\; git fsck --full auf dem Bare-Repo ohne Fehler.
    • doctor meldet sync-folder OK (Arbeitsbaum C:\Repositories\… außerhalb jedes Sync-Ordners), backup OK mit Alter der letzten Sicherung, git-remote mit dem Pfad im Sync-Ordner.
    • Mit von Hand eingetragenem remotes.allowed_push_urls (<ziel>\git) ist das Gate scharf, und publish läuft weiter durch.
    • Eine Wiederherstellung nach dem How-to ergibt eine Instanz, bei der doctor ohne FAIL durchläuft. Nach #222 Abschnitt C verschoben (Betreiberentscheidung 2026-10-13).
    • Nebenbefund ohne Folge für den Stack: Copilot CLI zerlegt bei seiner Pfad-Freigabe einen Pfad mit Leerzeichen ('C:\OneDrive - <Firma>\…') und fragt nach \OneDrive; der Befehl selbst war richtig und lief.
  • pytest, docs verify, instructions verify grün (lokal und in CI-Lauf 675). Die generierten Referenzen (docs contract, docs reference) sind neu erzeugt, und die Sektion backup steht in docs/reference/configuration.md.

Versionsteil

MINOR. Es sind neue Kommandos, eine neue Sektion und neue doctor-Prüfungen. Ohne backup-Sektion verhält sich alles wie bisher, also drop-in in beide Richtungen. Eine ältere Version, die eine backup-Sektion vorfindet, meldet sie in doctor als unbekannte Sektion (WARN) und läuft weiter. Der laufende Kandidat stand schon auf 10.0.0; der MINOR-Bump hob ihn nicht weiter an und ergab 10.0.0-beta.10.

Betroffene Dateien und Flächen

  • Neu: tools/chemenu/backup.py (Init, Spiegel, Manifest, Sync-Ordner-Erkennung), tools/chemenu/commands/backup_cmd.py (backup init/backup run mit cli_contract-Einträgen, Gruppe „Git“), Registrierung in tools/chemenu/cli.py.
  • Settings: config_shapes.BACKUP in tools/chemenu/config_shapes.py, Region reference-config-backup in tools/chemenu/reference_doc.py.
  • tools/chemenu/commands/git_publish.py: Validierung der Sektion vor dem Commit, Schritt nach dem Push, cli_contract-Notizen von publish.
  • tools/chemenu/commands/doctor.py: die Prüfungen sync-folder und backup, der Hinweis bei git-remote, der cli_contract-Eintrag.
  • Tests: tools/chemenu/tests/test_backup.py (neu), test_config_shapes.py, conftest.py (OneDrive-Variablen).
  • Generiert: tools/CONTRACT.md, docs/reference/commands/git.md, instance-health.md, README.md, die Region in docs/reference/configuration.md
  • Docs: docs/how-to/back-up-to-a-synced-folder.md (neu), docs/how-to/README.md, docs/how-to/maintain-the-wiki.md, docs/reference/configuration.md (Prosa), docs/reference/commands/git.md (Kopfsatz), docs/explanation/configuration-tiers.md, instructions/setup-instance.md (Schritt 3), instructions/publish-cycle.md, INSTALL.md (Remote-Frage), tools/README.md
  • CHANGES.md über version bump

Nicht in diesem Paket

  • Eingang über einen OneDrive-Ordner: #213
  • Lesefassung für Microsoft 365: #214
  • Live-Zugriff über MCP: #215 (nicht Teil der Kette)
  • Ein Kommando backup restore: Die Wiederherstellung steht vorerst als Handgriffe im How-to.
  • Export- und Eingangsordner als Geschwister im Backup-Ziel: #216
  • Prüfung der Wiederherstellung von Hand: #222

Kette

Kette „OneDrive/Microsoft 365“

Ausgearbeitet aus einem `status/incoming`-Stub. Der Wortlaut steht im ersten Kommentar. ## Stand **Erledigt (geschlossen 2026-10-13).** Veröffentlicht in `10.0.0-beta.10`, Commit `9736472f485333cfd08493da3e26c371f89f1f39`. **Verifiziert durch CI auf diesem Commit: Läufe [675](https://gitea.nehmer.net/torben/chemenu/actions/runs/675) (Jobs `verify` und `pwsh`) und [676](https://gitea.nehmer.net/torben/chemenu/actions/runs/676), beide grün.** Lokal zuvor grün: `pytest` (2959 passed, 4 skipped; die neuen Tests auch unter `env -i`), `docs verify`, `instructions verify`. Ausgeliefert mit Release 10.0.0. Abschlussphase: Die Prüfung auf veraltete Dokumente hat eine Lücke gefunden. `docs/how-to/maintain-the-wiki.md` nennt jetzt das Backup in einen Sync-Ordner und die `WARN` einer gescheiterten Maschinensicherung (Commit `3627168`, reine `docs/`-Prosa ohne Bump, CI-Lauf [677](https://gitea.nehmer.net/torben/chemenu/actions/runs/677) grün). Alle übrigen Dokumente waren aktuell. **Prüfung von Hand auf dem Windows-Arbeitsrechner, 2026-10-13 (Release 10.0.0, GitHub Copilot CLI):** Einrichtung, Push, Maschinensicherung, `doctor` und das scharf geschaltete Publish-Remote Gate sind geprüft, siehe letztes Akzeptanzkriterium. **Die Wiederherstellung nach dem How-to ist nach #222 Abschnitt C verschoben** (Betreiberentscheidung 2026-10-13), wo alle offenen Prüfungen von Hand nach 10.0.0 in einem Termin laufen. Abweichungen vom ursprünglichen Body sind unten an Ort und Stelle eingearbeitet und mit **(Bau)** markiert. ## Ausgangslage Eine berufliche Instanz entsteht gerade, auf **einem** Windows-Rechner (pwsh 7, nativ). Dort gibt es keinen Git-Server mit passender Privatsphäre. Der einzige sanktionierte Speicher außerhalb des Geräts ist OneDrive. Ein `.git`-Verzeichnis in einem Sync-Ordner gilt als problematisch. Gesucht ist ein Backup ohne Server, das **auch die Daten dieser Maschine** sichert, die nicht in Git stehen. **Schon heute unterstützt, ohne Änderung:** Eine Instanz ohne Server ist ein gültiger Endzustand. `doctor` meldet ein fehlendes `origin` nur als `WARN` (`tools/chemenu/commands/doctor.py`, `check_git_repo`), und `publish --no-push` committet lokal. Ein Push gegen ein Bare-Repo unter einem Dateipfad ist Alltag. Die Publish-Tests laufen genau so (`tools/chemenu/tests/test_git_publish.py`, Fixture `repo`). ## Entscheidungen 1. **Das Arbeitsrepo liegt lokal außerhalb jedes Sync-Ordners.** Gesichert wird in ein konfiguriertes Ziel im Sync-Ordner, mit dieser Struktur: ``` <ziel>/git/ Bare-Repo, ist origin; publish pusht hierher <ziel>/<maschine>/ Spiegel der Maschinendaten, plus Manifest .chemenu-backup.json ``` Ins Bare-Repo wird nur beim Push geschrieben, mit genau einem Schreiber. Der Sync-Client sieht fast nur neue, unveränderliche Objektdateien. 2. **Maschinendaten sind eine feste Liste plus `extra`.** Der Schalter dafür ist `backup.machine_data`. | Pfad | Gesichert | Grund | |---|---|---| | `.wikitool-local.json` | ja | Checkout-Einstellungen. Private Klartext-Werte wie interne Hostnamen landen damit im OneDrive. Das ist vom Betreiber akzeptiert, das How-to sagt es ausdrücklich. | | `ENVIRONMENT.md` | ja, falls vorhanden | von Hand gepflegt | | `incoming/*` außer `.gitkeep` | ja | Wartende Quellen sind sonst nirgends gesichert. | | `quarantine/` (bis 9.x `mcp-upload/`, #213) | **nein** (Betreiber) | nicht gesichtetes fremdes Material | | `reports/` | nein | Lint-Berichte lassen sich neu erzeugen. Telemetrie enthält Prompts. | | `.wikitool-tools.json`, `tools/.venv/`, `tools/.wikitool_session/`, Caches, `.agents/skills/`, `.claude/skills/` | nein | lassen sich neu erzeugen bzw. schreibt der Preflight neu | | Secrets-Datei (`%APPDATA%\chemenu\<instance_id>\secrets.json`, darin auch die Sitzungen von `raw login`) | **nie** | liegt außerhalb der Instanz und ist durch die Regel unten ausgeschlossen | | `backup.extra` | ja | zusätzliche Pfade relativ zur Instanz, z. B. `.claude/settings.local.json`, `.obsidian/` | 3. **Auslöser:** - Ist `machine_data: true` gesetzt, sichert `publish` die Maschinendaten **nach einem erfolgreichen Push**. **(Bau)** Ein `publish`, das nichts zu pushen hatte („Nothing to commit“ ohne lokalen Vorsprung), pusht nicht und sichert daher auch nicht. - Scheitert diese Sicherung, gibt es nur eine `WARN`, und `publish` endet mit Exit 0, weil Commit und Push schon geschehen sind. - `backup run` sichert von Hand. - Bekannte Grenze: Eine Datei, die gerade in `incoming/` abgelegt wurde, ist erst nach dem nächsten `publish` mit Push oder `backup run` gesichert (Betreiber: passt). 4. **`backup init` richtet das Ziel ein.** Es legt `<ziel>/git` als Bare-Repo an und setzt `origin`, aber nur, wenn noch keins existiert. Den Eintrag für `remotes.allowed_push_urls` gibt es nur aus und schreibt ihn nie, denn das Feld ist ein Gate. Gibt es keine `remotes`-Sektion, ist das Gate nicht scharf, und `backup init` gibt nichts aus (in der Prüfung von Hand so bestätigt). 5. **Verworfen:** - **Repo samt `.git` direkt im Sync-Ordner:** - Unter Windows sperrt OneDrive Dateien kurzzeitig. Das stört `git gc`/`repack` („unlink failed“). - Files On-Demand lagert Objekte als Platzhalter aus. - Bei einem zweiten Gerät entstehen Konfliktkopien in `.git`. - Echter Sync zwischen Geräten über Git in OneDrive scheidet aus denselben Gründen aus. - **Ganz ohne Git:** Git ist im Stack die lokale Absicherung, nicht der Upload-Weg. Ohne Git fallen weg: - das Mass-Update Gate (zählt über `git diff --cached`), - das Rückgängigmachen einer Agenten-Sitzung, - `migrate verify`. Außerdem läuft `dist upgrade` dann ohne die Prüfung auf einen sauberen Arbeitsstand (`dist_cmd.py`), und `doctor` meldet `FAIL`. Der Wunsch dahinter, dass Microsoft-Werkzeuge die KB lesen, geht über #214 und #215. - **`git bundle` als Einzeldatei:** kein Remote. Nur falls sich das Bare-Repo in der Praxis nicht bewährt. ## Konfiguration Neue Sektion `backup` in `.wikitool-local.json` (`config_shapes.BACKUP`): ```json { "backup": { "target": "C:\\Users\\<user>\\OneDrive - <Firma>\\Chemenu-Backup", "machine_data": true, "machine_name": "WORK-LAPTOP", "extra": [".claude/settings.local.json"] } } ``` | Feld | Tier | Typ | Pflicht | Default | Bedeutung | |---|---|---|---|---|---| | `target` | checkout, gate | string | ja | - | Das Ziel. Es muss ein absoluter Pfad außerhalb der Instanz sein. **(Bau)** Außerdem darf die Instanz nicht in `<ziel>/git` oder `<ziel>/<maschine>` liegen. | | `machine_data` | checkout, gate | boolean | nein | `false` | Maschinendaten sichern, nach `publish` und mit `backup run`. **(Bau)** Nur ein echter JSON-Boolean, kein `"yes"`. | | `machine_name` | checkout | string | nein | Hostname (`socket.gethostname()`) | Name des Unterordners. Wird auf `[A-Za-z0-9_-]` bereinigt (alles andere wird `-`). **(Bau)** Danach werden führende und folgende `-` entfernt. Ist er dann leer oder heißt er `git` (Groß-/Kleinschreibung egal), ist das ein Fehler – auch ein ausdrücklich leerer String, der nicht als „Hostname“ gelesen wird. | | `extra` | checkout, gate | array of strings | nein | `[]` | Zusätzliche Pfade relativ zur Instanz, Dateien oder Verzeichnisse. **(Bau)** Ein fehlender Pfad wird übersprungen und gemeldet, ist kein Fehler. | - **Fehlt die Sektion:** Nichts ändert sich. `publish` verhält sich wie heute, `backup init` und `backup run` enden mit Exit 1 und nennen die Sektion. - **Ist die Sektion fehlerhaft:** Das ist ein Fehler. `publish` stoppt mit Exit 1, **bevor** es etwas committet, `backup init` und `backup run` enden mit Exit 1, und `doctor` meldet `FAIL`. Eine kaputte Sektion wird nie als „kein Backup“ gelesen. `target`, `machine_data` und `extra` sind `gate`, weil sie bestimmen, welche Daten die Maschine verlassen. `config set` lehnt sie mit Exit 42 ab, der Betreiber pflegt sie von Hand (Muster wie `remotes`/`intake`). ## Was gebaut wurde 1. **`wikitool backup init`** (schreibend, idempotent; `tools/chemenu/commands/backup_cmd.py`, Logik in `tools/chemenu/backup.py`): - Legt `<ziel>/` und `<ziel>/git` an, Letzteres als Bare-Repo (`git init --bare -b main`). - Ist `<ziel>/git` schon ein Bare-Repo, bleibt es unverändert. **(Bau)** Ein leeres Verzeichnis dort wird wie ein fehlendes behandelt. Existiert dort etwas anderes, endet der Befehl mit Exit 1 **und verändert nichts**. - Gibt es kein `origin`, setzt es `origin` auf `<ziel>/git`. Zeigt ein bestehendes `origin` woandershin, bleibt es unverändert. Der Befehl endet dann mit Exit 1, nennt beide URLs und überlässt dem Betreiber, ob er umstellt. **(Bau)** Alle Vorbedingungen werden zuerst geprüft; in diesem Fall wird auch `<ziel>/git` nicht angelegt. - Existiert `remotes.allowed_push_urls` und fehlt `<ziel>/git` darin, gibt es den exakten JSON-Eintrag zum Selbst-Eintragen aus (die ganze `remotes`-Sektion mit ergänzter URL). 2. **`wikitool backup run`** (schreibend, idempotent): spiegelt die Maschinendaten nach `<ziel>/<maschine>/` und schreibt das Manifest `.chemenu-backup.json` (Zeitpunkt, Stack-Version, Maschinenname, je Datei relativer Pfad, Größe und sha256). Bei `machine_data: false` endet es mit Exit 1. **(Bau)** Existiert `<ziel>` nicht als Verzeichnis (Sync-Ordner fehlt), endet es mit Exit 1 und legt nichts an. Eine Datei, deren Größe und sha256 zum vorigen Manifest passen, wird nicht neu geschrieben. Ersetzt wird mit bis zu fünf Versuchen, weil OneDrive eine Datei kurz offen hält. 3. **`publish`:** Nach einem erfolgreichen Push und mit `machine_data: true` ruft es denselben Code wie `backup run` auf. Scheitert er, gibt es eine `WARN`-Zeile mit dem Grund, Exit 0. Bei `--no-push` oder einem gescheiterten Push wird nicht gesichert. 4. **`doctor`:** - **`sync-folder`:** `WARN`, wenn das Git-Verzeichnis (`git rev-parse --absolute-git-dir`, Links bzw. Junctions aufgelöst) unter einem der Pfade aus `OneDrive`, `OneDriveCommercial` oder `OneDriveConsumer` liegt (Windows) oder unter `~/Library/CloudStorage/` (macOS). Unter Windows ohne Groß-/Kleinschreibung verglichen. Der Hinweis nennt das How-to und den typischen Fall, dass „Dokumente“ per Known Folder Move unbemerkt in OneDrive liegt. Sonst `OK`, nie `FAIL`. - **`backup`:** **(Bau)** Ohne Sektion `OK` mit „absent - no backup section in the settings“ – `doctor` kennt nur `OK`/`WARN`/`FAIL`; einen Status `ABSENT`, wie ursprünglich hier stand, gibt es nicht, und `upload-intake`/`tasks-provider` melden eine fehlende Sektion genauso. Mit Sektion: - `WARN`, wenn `<ziel>/git` kein Bare-Repo ist oder `origin` nicht dorthin pusht. - Bei `machine_data: true` das Alter der letzten Sicherung laut Manifest, `WARN`, wenn es keine gibt. - `FAIL` nur bei einer fehlerhaften Sektion. - Die bestehende `git-remote`-WARN (kein Remote) nennt das How-to. 5. **How-to `docs/how-to/back-up-to-a-synced-folder.md`:** - Warum der Arbeitsbaum nicht in den Sync-Ordner gehört. - Die Sektion `backup` von Hand anlegen, dann `backup init` und den Eintrag in `allowed_push_urls` ergänzen. - Was gesichert wird und was nicht. Dazu gehört ausdrücklich: Private Werte aus `.wikitool-local.json` landen im Sync-Ordner, die Secrets nie. - Wiederherstellung: 1. `git clone <ziel>/git <neu>`, dann `git fsck --full`. 2. Inhalt von `<ziel>/<maschine>/` ohne das Manifest in die Instanz kopieren. 3. Bootstrap (`instructions/bootstrap.md`). 4. Secrets neu hinterlegen. - Nur ein Gerät schreibt ins Bare-Repo. 6. **Verweise:** `instructions/setup-instance.md` Schritt 3 (Remote-Frage) bietet „Backup in einen Sync-Ordner“ als Option an und verlinkt das How-to. Die Remote-Frage in `INSTALL.md` tut das ebenso. `instructions/publish-cycle.md` nennt die `WARN` einer gescheiterten Maschinensicherung: Sie wird dem Betreiber gezeigt und **nicht** durch einen erneuten `publish` behoben, sondern durch `backup run` nach Behebung der Ursache. **(Bau)** Zusätzlich: `docs/explanation/configuration-tiers.md` sagt, dass ein privater Klartext-Wert über das Backup die Maschine verlässt und ein Verweis das verhindert; `docs/how-to/maintain-the-wiki.md` nennt das Backup und die `WARN` (Abschlussphase). ## Was eine Sicherung garantieren muss - **Geschrieben und gelöscht wird nur in `<ziel>/<maschine>/`.** Außerhalb davon verändert `backup run` nichts, auch nicht `<ziel>/git` oder Ordner anderer Maschinen. **(Bau)** Auch nicht über einen Link im Maschinenordner: ein Link auf dem Weg zu einer Zieldatei bricht ab, und ein Manifest-Eintrag, der den Ordner verlässt (`..`, Laufwerk, Backslash), ebenso. - **Gelöscht wird nur, was das vorige Manifest aufführt und lokal nicht mehr existiert**, etwa eine per `raw accept` übernommene Quelle aus `incoming/`. Eine Datei in `<ziel>/<maschine>/`, die nicht im Manifest steht, bleibt unberührt. **(Bau)** Das gilt für Pfade, die der Lauf nicht selbst spiegelt; liegt eine fremde Datei genau unter dem Pfad einer gespiegelten Datei, wird sie überschrieben. - **Ist `<ziel>/<maschine>/` nicht leer und ohne Manifest, wird abgelehnt** (Exit 1, nichts geschrieben). Es wird nie in einen fremden Ordner hineingespiegelt. **(Bau)** Ein unlesbares Manifest wird ebenso abgelehnt. - **Nichts außerhalb der Instanz wird gelesen.** Ein Pfad aus `extra`, der nach dem Auflösen außerhalb der Instanz liegt, ist ein Fehler, damit auch über `extra` kein Secret in den Sync-Ordner kommt. Symlinks innerhalb der gesicherten Verzeichnisse werden nicht verfolgt, sondern übersprungen und gemeldet. `.git/` wird nie gesichert, auch nicht über `extra`. - **Jede Datei wird atomar geschrieben** (temporäre Datei im selben Verzeichnis, dann `os.replace`). Das Manifest wird zuletzt geschrieben. Ein abgebrochener Lauf hinterlässt nur ganze Dateien und das Manifest des vorigen Laufs. ## Akzeptanzkriterien Tests in `tools/chemenu/tests/test_backup.py`, soweit nicht anders genannt. Abgehakt ist, was mit `9736472` veröffentlicht und von den CI-Läufen 675 und 676 bestätigt ist. - [x] Nach `backup init` mit leerem Ziel ist `<ziel>/git` ein Bare-Repo, und `git remote get-url --push origin` ergibt `<ziel>/git`. Ein zweiter Aufruf endet mit Exit 0 und ändert nichts (Test). - [x] Zeigt `origin` vorher woandershin, endet `backup init` mit Exit 1, und `origin` ist unverändert (Test). Liegt unter `<ziel>/git` etwas, das kein Bare-Repo ist, endet es mit Exit 1, und der Inhalt ist unverändert (Test). - [x] Existiert `remotes.allowed_push_urls` ohne `<ziel>/git`, gibt `backup init` den Eintrag aus, und `.wikitool-local.json` ist danach byte-gleich (Test). - [x] `publish` pusht gegen `<ziel>/git` unter einem Pfad **mit Leerzeichen**. Danach zeigt `main` dort auf den lokalen `HEAD`. Mit `machine_data: true` enthält `<ziel>/<maschine>/` danach genau `.wikitool-local.json`, `ENVIRONMENT.md` (falls vorhanden), die Dateien aus `incoming/` außer `.gitkeep`, die `extra`-Pfade und das Manifest (Test). - [x] Nach `backup run` liegt in `<ziel>/<maschine>/` keine Datei aus der Quarantäne, `reports/`, `.wikitool-tools.json` oder `.git/` (Test mit befüllten Verzeichnissen; die Quarantäne heißt seit #213 `quarantine/`). - [x] Eine Datei, die seit dem vorigen Lauf aus `incoming/` verschwunden ist, fehlt danach auch im Ziel. Eine fremde Datei in `<ziel>/<maschine>/`, die nicht im Manifest steht, ist danach byte-gleich vorhanden (Test). - [x] Ist `<ziel>/<maschine>/` nicht leer und ohne Manifest, endet `backup run` mit Exit 1, und der Ordner ist unverändert (Test). - [x] Ein `extra`-Eintrag, der außerhalb der Instanz auflöst (`..` oder ein Symlink nach außen), führt zu Exit 1, bevor etwas geschrieben wird (Test). Ein Symlink in `incoming/` wird nicht verfolgt und erscheint nicht im Ziel (Test). - [x] Ein `machine_name`, das nach dem Bereinigen leer ist oder `git`/`GIT` heißt, ist ein Fehler der Sektion (Test). - [x] Scheitert die Maschinensicherung nach erfolgreichem Push, z. B. weil das Ziel nicht beschreibbar ist, endet `publish` mit Exit 0 und einer `WARN`-Zeile, und der Push ist erfolgt (Test). Bei `--no-push` entsteht kein `<ziel>/<maschine>/` (Test). - [x] Mit fehlerhafter `backup`-Sektion endet `publish` mit Exit 1, ohne dass ein Commit entsteht (Test). - [x] `doctor`: - `sync-folder` ist `WARN` unter einem Pfad aus `OneDrive`/`OneDriveCommercial`/`OneDriveConsumer` oder `~/Library/CloudStorage/`, auch wenn das Repo über einen Symlink dorthin erreicht wird, und sonst `OK`. - Windows-Vergleich ohne Groß-/Kleinschreibung. - `backup` meldet `OK` (absent), `WARN` (fehlendes `<ziel>/git`, abweichendes `origin`, keine Maschinensicherung) und `FAIL` (fehlerhafte Sektion) wie beschrieben. - Keine der beiden Prüfungen setzt den Exit-Code von `doctor` außer bei `FAIL` (Tests mit auf `tmp_path` gesetzten Umgebungsvariablen; die drei OneDrive-Variablen löscht jetzt auch die hermetische Fixture in `conftest.py`). - [x] Das How-to existiert, steht in `docs/how-to/README.md` und deckt Punkt 5 vollständig ab. `setup-instance.md`, `INSTALL.md`, `publish-cycle.md` und die `git-remote`-WARN verweisen darauf wie in Punkt 6. - [x] Einmalig von Hand auf dem Windows-Arbeitsrechner geprüft (Betreiber, 2026-10-13, Release 10.0.0, GitHub Copilot CLI; Ziel `C:\OneDrive - <Firma>\Chemenu`, OneDrive außerhalb des Benutzerprofils): - [x] `backup init` legt `<ziel>\git` als Bare-Repo an und setzt `origin`. Ohne `remotes`-Sektion gibt es nichts aus (Gate nicht scharf; `doctor`: „Gate not armed“). - [x] `publish` pusht die bestehende lokale Historie nach `<ziel>\git` und spiegelt 5 Dateien nach `<ziel>\<maschine>\`; `git fsck --full` auf dem Bare-Repo ohne Fehler. - [x] `doctor` meldet `sync-folder` OK (Arbeitsbaum `C:\Repositories\…` außerhalb jedes Sync-Ordners), `backup` OK mit Alter der letzten Sicherung, `git-remote` mit dem Pfad im Sync-Ordner. - [x] Mit von Hand eingetragenem `remotes.allowed_push_urls` (`<ziel>\git`) ist das Gate scharf, und `publish` läuft weiter durch. - [ ] ~~Eine Wiederherstellung nach dem How-to ergibt eine Instanz, bei der `doctor` ohne `FAIL` durchläuft.~~ **Nach #222 Abschnitt C verschoben** (Betreiberentscheidung 2026-10-13). - Nebenbefund ohne Folge für den Stack: Copilot CLI zerlegt bei seiner Pfad-Freigabe einen Pfad mit Leerzeichen (`'C:\OneDrive - <Firma>\…'`) und fragt nach `\OneDrive`; der Befehl selbst war richtig und lief. - [x] `pytest`, `docs verify`, `instructions verify` grün (lokal und in CI-Lauf 675). Die generierten Referenzen (`docs contract`, `docs reference`) sind neu erzeugt, und die Sektion `backup` steht in `docs/reference/configuration.md`. ## Versionsteil **MINOR.** Es sind neue Kommandos, eine neue Sektion und neue `doctor`-Prüfungen. Ohne `backup`-Sektion verhält sich alles wie bisher, also drop-in in beide Richtungen. Eine ältere Version, die eine `backup`-Sektion vorfindet, meldet sie in `doctor` als unbekannte Sektion (`WARN`) und läuft weiter. Der laufende Kandidat stand schon auf `10.0.0`; der MINOR-Bump hob ihn nicht weiter an und ergab `10.0.0-beta.10`. ## Betroffene Dateien und Flächen - **Neu:** `tools/chemenu/backup.py` (Init, Spiegel, Manifest, Sync-Ordner-Erkennung), `tools/chemenu/commands/backup_cmd.py` (`backup init`/`backup run` mit `cli_contract`-Einträgen, Gruppe „Git“), Registrierung in `tools/chemenu/cli.py`. - **Settings:** `config_shapes.BACKUP` in `tools/chemenu/config_shapes.py`, Region `reference-config-backup` in `tools/chemenu/reference_doc.py`. - `tools/chemenu/commands/git_publish.py`: Validierung der Sektion vor dem Commit, Schritt nach dem Push, `cli_contract`-Notizen von `publish`. - `tools/chemenu/commands/doctor.py`: die Prüfungen `sync-folder` und `backup`, der Hinweis bei `git-remote`, der `cli_contract`-Eintrag. - **Tests:** `tools/chemenu/tests/test_backup.py` (neu), `test_config_shapes.py`, `conftest.py` (OneDrive-Variablen). - **Generiert:** `tools/CONTRACT.md`, `docs/reference/commands/git.md`, `instance-health.md`, `README.md`, die Region in `docs/reference/configuration.md` - **Docs:** `docs/how-to/back-up-to-a-synced-folder.md` (neu), `docs/how-to/README.md`, `docs/how-to/maintain-the-wiki.md`, `docs/reference/configuration.md` (Prosa), `docs/reference/commands/git.md` (Kopfsatz), `docs/explanation/configuration-tiers.md`, `instructions/setup-instance.md` (Schritt 3), `instructions/publish-cycle.md`, `INSTALL.md` (Remote-Frage), `tools/README.md` - `CHANGES.md` über `version bump` ## Nicht in diesem Paket - Eingang über einen OneDrive-Ordner: #213 - Lesefassung für Microsoft 365: #214 - Live-Zugriff über MCP: #215 (nicht Teil der Kette) - Ein Kommando `backup restore`: Die Wiederherstellung steht vorerst als Handgriffe im How-to. - Export- und Eingangsordner als Geschwister im Backup-Ziel: #216 - Prüfung der Wiederherstellung von Hand: #222 ## Kette Kette „OneDrive/Microsoft 365“ - Vorgänger: keiner - erstes Glied - Nachfolger: #213
torben added the status/incoming label 2026-10-06 15:07:56 +00:00
Author
Owner

Ausgearbeitet aus dem Stub. Ursprünglicher Text, wörtlich:

Szenario von der Arbeit:

  • Daten liegen lokag
  • Ich habe keinen Git Server, der mir passende Privatsphäre bietet
  • Einziger schnelle Ansatz ist mein OneDrive, dort heißt es aber, dass .git Folder dort nicht gut aufgehoben sind.
    Wie könnte so ein Szenario aussehen?
  • Wäre eine Alternative, ganz auf Git zu verzichten für ein lokales Arbeitswiki und OneDrive (oder z.B. Nextcloud) als Alternative zu setzen?

Changelog: Body neu geschrieben als Bau-Spezifikation für das Backup. Ansatz A gewählt (lokales Repo, Bare-Repo im OneDrive als origin), plus eine doctor-Warnung für .git im Sync-Ordner. „Ganz ohne Git“ verworfen, Begründung steht im Body. Ideen aus derselben Diskussion als eigene Pakete abgespalten:

Kette „OneDrive/Microsoft 365“: #186 → #213 → #214. status/incoming entfernt, Labels gesetzt (area/workflow, kind/build, prio/planned, size/M).

**Ausgearbeitet aus dem Stub.** Ursprünglicher Text, wörtlich: > Szenario von der Arbeit: > - Daten liegen lokag > - Ich habe keinen Git Server, der mir passende Privatsphäre bietet > - Einziger schnelle Ansatz ist mein OneDrive, dort heißt es aber, dass .git Folder dort nicht gut aufgehoben sind. > Wie könnte so ein Szenario aussehen? > - Wäre eine Alternative, ganz auf Git zu verzichten für ein lokales Arbeitswiki und OneDrive (oder z.B. Nextcloud) als Alternative zu setzen? **Changelog:** Body neu geschrieben als Bau-Spezifikation für das Backup. Ansatz A gewählt (lokales Repo, Bare-Repo im OneDrive als `origin`), plus eine `doctor`-Warnung für `.git` im Sync-Ordner. „Ganz ohne Git“ verworfen, Begründung steht im Body. Ideen aus derselben Diskussion als eigene Pakete abgespalten: - Eingangskanäle: #213 - Exporte: #214 - MCP aus Microsoft 365: #215 Kette „OneDrive/Microsoft 365“: #186 → #213 → #214. `status/incoming` entfernt, Labels gesetzt (`area/workflow`, `kind/build`, `prio/planned`, `size/M`).
torben changed title from Chemenu Repo ohne Upload aber mit OneDrive Backup to Backup einer lokalen Instanz in einen Sync-Ordner (OneDrive): Bare-Repo als `origin`, `doctor` warnt vor `.git` im Sync-Ordner 2026-10-12 16:29:48 +00:00
torben added prio/plannedsize/Marea/workflowkind/build and removed status/incoming labels 2026-10-12 16:29:48 +00:00
torben changed title from Backup einer lokalen Instanz in einen Sync-Ordner (OneDrive): Bare-Repo als `origin`, `doctor` warnt vor `.git` im Sync-Ordner to Backup einer lokalen Instanz in einen Sync-Ordner (OneDrive): Bare-Repo unter `<ziel>/git`, Maschinendaten unter `<ziel>/<maschine>/`, `doctor` warnt vor `.git` im Sync-Ordner 2026-10-12 16:53:35 +00:00
Author
Owner

Changelog: Das Backup sichert jetzt auch Maschinendaten.

  • Zielstruktur: <ziel>/git (Bare-Repo, origin) und <ziel>/<maschine>/ (Spiegel plus Manifest).
  • Neue Sektion backup: target, machine_data, machine_name, extra.
  • Neue Kommandos: backup init und backup run. publish sichert nach dem Push, eine gescheiterte Sicherung gibt nur eine WARN.
  • Neue doctor-Prüfung: backup.

Entscheidungen des Betreibers:

  • mcp-upload/ wird nicht gesichert.
  • Auslöser ist „nach erfolgreichem Push plus von Hand“.
  • backup init setzt origin nur, wenn noch keins existiert.
  • Private Klartext-Werte aus .wikitool-local.json dürfen ins OneDrive.

Garantien gegen Datenverlust und gegen Secrets im Sync-Ordner sowie die Akzeptanzkriterien sind entsprechend erweitert. Versionsteil bleibt MINOR, size/M bleibt (am oberen Rand).

**Changelog:** Das Backup sichert jetzt auch Maschinendaten. - **Zielstruktur:** `<ziel>/git` (Bare-Repo, `origin`) und `<ziel>/<maschine>/` (Spiegel plus Manifest). - **Neue Sektion `backup`:** `target`, `machine_data`, `machine_name`, `extra`. - **Neue Kommandos:** `backup init` und `backup run`. `publish` sichert nach dem Push, eine gescheiterte Sicherung gibt nur eine `WARN`. - **Neue `doctor`-Prüfung:** `backup`. Entscheidungen des Betreibers: - `mcp-upload/` wird nicht gesichert. - Auslöser ist „nach erfolgreichem Push plus von Hand“. - `backup init` setzt `origin` nur, wenn noch keins existiert. - Private Klartext-Werte aus `.wikitool-local.json` dürfen ins OneDrive. Garantien gegen Datenverlust und gegen Secrets im Sync-Ordner sowie die Akzeptanzkriterien sind entsprechend erweitert. Versionsteil bleibt MINOR, `size/M` bleibt (am oberen Rand).
Author
Owner

Changelog: Gebaut und veröffentlicht in 10.0.0-beta.10 (Commit 9736472), CI-Läufe 675 und 676 grün. Neuer Abschnitt „Stand“. Alle Kriterien außer der Windows-Prüfung von Hand abgehakt. Abweichungen als (Bau) eingearbeitet: doctor meldet eine fehlende Sektion als OK „absent“ statt ABSENT (diesen Status gibt es nicht); backup init nimmt ein leeres <ziel>/git an und legt bei fremdem origin gar nichts an; machine_name verliert zusätzlich führende und folgende -; backup run verlangt ein vorhandenes Ziel und schreibt unveränderte Dateien nicht neu; ein publish ohne Push sichert nicht; eine fremde Datei unter einem gespiegelten Pfad wird überschrieben. Zusätzlich gepflegt: docs/explanation/configuration-tiers.md, tools/README.md. Dateiliste auf den tatsächlichen Stand gebracht.

**Changelog:** Gebaut und veröffentlicht in `10.0.0-beta.10` (Commit `9736472`), CI-Läufe 675 und 676 grün. Neuer Abschnitt „Stand“. Alle Kriterien außer der Windows-Prüfung von Hand abgehakt. Abweichungen als **(Bau)** eingearbeitet: `doctor` meldet eine fehlende Sektion als `OK` „absent“ statt `ABSENT` (diesen Status gibt es nicht); `backup init` nimmt ein leeres `<ziel>/git` an und legt bei fremdem `origin` gar nichts an; `machine_name` verliert zusätzlich führende und folgende `-`; `backup run` verlangt ein vorhandenes Ziel und schreibt unveränderte Dateien nicht neu; ein `publish` ohne Push sichert nicht; eine fremde Datei unter einem gespiegelten Pfad wird überschrieben. Zusätzlich gepflegt: `docs/explanation/configuration-tiers.md`, `tools/README.md`. Dateiliste auf den tatsächlichen Stand gebracht.
Author
Owner

Changelog: Abschlussphase begonnen, nicht geschlossen. Prüfung auf veraltete Dokumente: docs/how-to/maintain-the-wiki.md nachgezogen (Commit 3627168, CI-Lauf 677 grün), sonst nichts veraltet. Betreiberentscheidung: Das Issue bleibt offen, bis die Windows-Prüfung von Hand erfolgt ist; danach /stack-close erneut.

**Changelog:** Abschlussphase begonnen, nicht geschlossen. Prüfung auf veraltete Dokumente: `docs/how-to/maintain-the-wiki.md` nachgezogen (Commit `3627168`, CI-Lauf 677 grün), sonst nichts veraltet. Betreiberentscheidung: Das Issue bleibt offen, bis die Windows-Prüfung von Hand erfolgt ist; danach `/stack-close` erneut.
Author
Owner

Changelog: Prüfung von Hand am 2026-10-13 (Arbeitsinstanz, Release 10.0.0, Copilot CLI) im letzten Kriterium eingetragen. Geprüft sind: backup init, Push und Maschinensicherung gegen ein Ziel im OneDrive, git fsck, doctor (sync-folder, backup) und das von Hand scharf geschaltete Publish-Remote Gate. Offen bleibt nur die Wiederherstellung. Entscheidung 4 nennt jetzt, dass backup init ohne remotes-Sektion nichts ausgibt. Die Quarantäne heißt im Body quarantine/ (#213). Neuer Verweis auf #216 (Export- und Eingangsordner als Geschwister im Backup-Ziel).

**Changelog:** Prüfung von Hand am 2026-10-13 (Arbeitsinstanz, Release 10.0.0, Copilot CLI) im letzten Kriterium eingetragen. Geprüft sind: `backup init`, Push und Maschinensicherung gegen ein Ziel im OneDrive, `git fsck`, `doctor` (`sync-folder`, `backup`) und das von Hand scharf geschaltete Publish-Remote Gate. Offen bleibt nur die Wiederherstellung. Entscheidung 4 nennt jetzt, dass `backup init` ohne `remotes`-Sektion nichts ausgibt. Die Quarantäne heißt im Body `quarantine/` (#213). Neuer Verweis auf #216 (Export- und Eingangsordner als Geschwister im Backup-Ziel).
Author
Owner

Changelog: Geschlossen auf Entscheidung des Betreibers (2026-10-13). Die Wiederherstellung, der letzte offene Punkt der Prüfung von Hand, ist durchgestrichen und nach #222 Abschnitt C verschoben. Alles andere ist auf Windows mit OneDrive bestätigt. Changelog-Zeile: Backup einer Instanz ohne Git-Server in einen Sync-Ordner (backup init/backup run, Maschinendaten nach publish), mit 10.0.0 ausgeliefert. Kette „OneDrive/Microsoft 365“ ist damit vollständig geschlossen (#186, #213, #214).

**Changelog:** Geschlossen auf Entscheidung des Betreibers (2026-10-13). Die Wiederherstellung, der letzte offene Punkt der Prüfung von Hand, ist durchgestrichen und nach #222 Abschnitt C verschoben. Alles andere ist auf Windows mit OneDrive bestätigt. Changelog-Zeile: Backup einer Instanz ohne Git-Server in einen Sync-Ordner (`backup init`/`backup run`, Maschinendaten nach `publish`), mit 10.0.0 ausgeliefert. Kette „OneDrive/Microsoft 365“ ist damit vollständig geschlossen (#186, #213, #214).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: torben/chemenu#186