Files
chemenu/INSTALL.md
T
torben 5d937233b1
CI / verify (push) Successful in 5m20s
CI / pwsh (push) Successful in 1m53s
Release / release (push) Successful in 36s
fix: tools/bugreport launcher finds a Python 3.8+ itself and never starts a Store alias (#166)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2

Files changed:
- .gitea/workflows/ci.yml
- CHANGES.md
- INSTALL.md
- VERSION
- instructions/bug-report.md
- tools/README.md
- tools/bugreport
- tools/bugreport.ps1
- tools/bugreport.py
- tools/chemenu/tests/test_bugreport_launcher.py
- tools/chemenu/tests/test_instructions_shell.py
- tools/chemenu/tests/test_portability.py
2026-10-02 13:12:19 +02:00

507 lines
32 KiB
Markdown

# Installation
Dieses Dokument richtet sich an Menschen. Es gibt genau einen Weg zu einer Chemenu-Instanz: Ein
Agent installiert das **neueste Release** in ein leeres Verzeichnis, das du vorgibst. Die
Schritte führt der Agent aus, nach `instructions/setup-instance.md` aus demselben Release. Hier
steht, was du vorher bereitstellst, welchen Satz du ihm gibst, was er dich fragt und was zu tun
ist, wenn er anhält. Die vollständige Kommandoreferenz steht in
[tools/CONTRACT.md](tools/CONTRACT.md).
Den optionalen **MCP-Leseserver** installiert und betreibt
[INSTALL-MCP.md](INSTALL-MCP.md): derselbe Korpus, lesend, für einen Konsumenten, der kein
Terminal auf dieser Maschine ist.
<!-- dist:strip-start -->
Wer am Stack selbst arbeiten will, klont dieses Repo. Das ist eine Entwicklungsumgebung mit
Demo-Korpus und keine Instanz; sie steht in [DEVELOPMENT.md](DEVELOPMENT.md).
<!-- dist:strip-end -->
## Was vorher da sein muss
Diese Programme prüft der Preflight, bevor irgendein `wikitool`-Befehl läuft. Installieren musst
du sie selbst - der Agent tut es nie, auch nicht mit deiner Zustimmung. Wo eines fehlt, nennt
der Preflight den Installationsbefehl für dein System. Die Liste wird aus
`tools/prerequisites.txt` erzeugt, derselben Datei, die der Preflight liest:
<!-- wikitool:prerequisites -->
- **Python** ≥ 3.11
- **Git**
- **ripgrep (rg)**
<!-- /wikitool:prerequisites -->
Dazu:
- **Ein Agent-Harness**: Claude Code, GitHub Copilot (in VS Code oder als CLI), Codex CLI oder
Mistral Vibe.
- **Ein leeres Verzeichnis**, in dem die Instanz liegen soll, und dein Harness darin geöffnet.
Leer heißt: nichts außer einem `.git`. Ein frisch geklontes, leeres Repo für deine Instanz ist
also genau richtig - liegt dein Repo `torben/nathan` etwa in `~/src/nathan`, installierst du
dorthin, und der Agent übernimmt dessen `origin` als Ziel für `publish`.
Unter Windows zusätzlich, ebenfalls vom Preflight geprüft:
<!-- wikitool:prerequisites-windows -->
- **PowerShell 7 (pwsh)** ≥ 7
<!-- /wikitool:prerequisites-windows -->
Und außerdem:
- **PowerShell 7 als Standardterminal in VS Code.** Windows PowerShell 5.1 reicht nicht, und WSL
ist nicht vorgesehen.
- **Execution Policy `RemoteSigned`** - auf vielen Rechnern ab Werk gesetzt
(`Get-ExecutionPolicy -List` zeigt es).
- **Git for Windows.** Es bringt Git Bash mit, in dem Claude Code seine Befehle ausführt.
- **Ein Installationsverzeichnis mit höchstens 95 Zeichen**, zum Beispiel `C:\Chemenu`. Windows
erlaubt ohne eingeschaltete lange Pfade nur 259 Zeichen je Pfad, und die Dateien des Wikis
brauchen den Rest. Wer Administratorrechte hat, kann stattdessen lange Pfade einschalten
(`LongPathsEnabled`); verlangt wird das nicht.
## Der Satz für den Agenten
Öffne das leere Verzeichnis in deinem Harness und gib dem Agenten diesen Satz:
> Richte in diesem Verzeichnis eine neue Chemenu-Instanz ein. Hol dazu das neueste Release von
> `https://gitea.nehmer.net/api/v1/repos/torben/chemenu/releases/latest` und folge dessen Asset
> `setup-instance.md`. Lies vor dem Start des Preflights das Asset `preflight.md` aus demselben
> Release.
Damit liest der Agent die Beschreibung des neuesten Releases und daraus die beiden Anleitungen.
Dann lädt er das passende Preflight-Skript (`preflight.ps1` für PowerShell, `preflight.sh` für
eine POSIX-Shell) mit einem Befehl seiner Shell ins Verzeichnis - nicht über den Browser, damit
Windows die Datei nicht als „aus dem Internet“ markiert - und startet es. Das Skript lädt den
Tarball desselben Releases, prüft dessen sha256, entpackt ihn in das Verzeichnis, löscht sich
selbst und prüft dann im entpackten Baum, ob alles da ist.
Die Liste aller Releases: <https://gitea.nehmer.net/torben/chemenu/releases>. Das Repo ist
öffentlich; der Download braucht weder Konto noch Token.
## Was der Agent dich fragt
Raten darf der Agent keine dieser Antworten, und keine übernimmt er aus einem anderen Repo:
- <!-- setup-question: identity --> **Autor-Identität** - Name und E-Mail für `git config`. Das ist
zugleich der Autorname jeder künftig angelegten Wiki-Seite (`$WIKI_AUTHOR` überschreibt ihn bei
Bedarf).
- <!-- setup-question: remote --> **Remote** - bei einem leeren Klon nur die Bestätigung, dass
`origin` stimmt; sonst eine URL, wenn du auf einen Server pushen willst. Ohne Remote bleibt die
Instanz lokal, und jedes `publish` läuft mit `--no-push`.
- <!-- setup-question: kb-language --> **Sprache und Ton der Seiten** - sie landen in
`kb/CONVENTIONS.md`, dazu je Collection `kb/<name>/COLLECTION.md`. Fertige Profile, darunter ein
vollständiges deutsches, hält `instructions/kb-profiles.md` bereit. Entscheide das **vor dem
ersten Ingest**: Danach ist ein Wechsel der Abschnittsnamen eine Migration jeder bestehenden
Seite. Titel, Wikilink-Ziele, Zitat-IDs, Schema-Werte, Tags, Befehle und Pfade folgen keiner
Sprache - `Act Runner` heißt in jeder Instanz `Act Runner`.
- <!-- setup-question: domain --> **Anwendungsgebiet** - woraus dieses Wiki seine Quellen zieht.
Daraus schlägt der Agent eine `source_type`-Liste vor (bei einem Verein etwa Satzung, Protokoll,
Spielbericht). Das ist ein Startpunkt, keine Festlegung: Später wird sie an echtem Bestand
korrigiert (`instructions/evolve-subtypes.md`).
- <!-- setup-question: personalization --> **Personalisierung** - wer diese Instanz bedient
(`USER.md`) und wie sie klingt (`SOUL.md`). Der Agent interviewt dich entlang der Vorlagen und
schreibt deine Antworten wörtlich mit. Zwei Fragen beantwortest nur du: den **Namen der Persona**
und die **Themen, die bewusst draußen bleiben**.
- <!-- setup-question: environment --> **Umgebung** (optional) - Harness, MCP-Server, Remotes,
damit spätere Sitzungen nicht erneut fragen. „Weiß ich nicht“ ist eine gültige Antwort.
- <!-- setup-question: telemetry --> **Telemetrie** - standardmäßig aus; der Agent fragt nur, ob du
sie einschalten willst.
- <!-- setup-question: task-tracker --> **Aufgaben-Tracker** (optional) - siehe
[Konfiguration](#konfiguration).
Am Ende legt der Agent den ersten Commit an. Dabei hält das Mass-Update-Gate an (Exit 42), weil
eine neue Instanz aus weit mehr als zehn Dateien besteht. Das ist erwartet: Der Agent zeigt dir
die Dateiliste und die `--confirm`-Zeile, und erst nach deiner Freigabe wird veröffentlicht.
Danach startest du die Agent-Sitzung im selben Verzeichnis neu, damit sie die Skills lädt.
## Wenn der Agent anhält
Der Preflight hält mit **Exit 42** an, wenn du etwas tun musst. Seine Ausgabe nennt in einem
nummerierten Block, was fehlt, warum, den Befehl, der es behebt, und wie es weitergeht. Der
Agent zeigt dir diesen Block unverändert, setzt eine Übersetzung höchstens darunter und wartet.
Sag ihm Bescheid, wenn du fertig bist; dann prüft er erneut. Ausweichen oder selbst installieren
darf er nicht.
**Beim Herunterladen und Entpacken** (das Skript aus dem Release, bevor es einen Baum gibt):
- **Werkzeuge zum Laden oder Entpacken fehlen** (Exit 42). Unter Linux und macOS braucht das
Skript `curl`, `tar` und `sha256sum` (oder `shasum`), unter Windows nur das `tar.exe` aus
Windows 10/11. Installiere, was die Ausgabe nennt; unter Windows liefert Git for Windows alles
für Git Bash mit.
- **Das Verzeichnis ist zu lang** (Exit 42, nur Windows ohne lange Pfade). Nimm ein kürzeres,
etwa `C:\Chemenu`, öffne es im Harness und gib den Satz dort noch einmal.
- **Das Verzeichnis ist nicht leer** (Exit 1). Es darf nichts enthalten außer dem Skript und
einem `.git`. Räume es selbst auf oder nimm ein anderes - der Agent löscht dort nichts.
- **Download fehlgeschlagen oder Prüfsumme falsch** (Exit 1). Es wurde nichts entpackt. Prüf die
Internetverbindung und lass es erneut versuchen; einen anderen Download-Weg sucht der Agent
nicht. Ohne direkten Download kannst du Tarball und `.sha256` von der Release-Seite selbst
nebeneinander ablegen; der Agent startet das Skript dann mit `--archive <tarball>`.
**Im entpackten Baum** (jeder weitere Lauf ist `tools/preflight.sh` bzw. unter PowerShell
`pwsh -NoProfile -ExecutionPolicy Bypass -File tools/preflight.ps1`):
- **Ein Werkzeug fehlt** - Python, git, ripgrep, unter Windows auch PowerShell 7. Die Ausgabe
nennt den Installationsbefehl für dein System. Ist es schon installiert, nur woanders, nenn dem
Agenten den Pfad; er reicht ihn mit `--set <werkzeug>=<pfad>` weiter.
- **Eine Version ist zu alt** - zum Beispiel Python unter 3.11. Neuere Version installieren oder
deren Pfad nennen.
- **Ein genannter Pfad funktioniert nicht** - der Pfad muss auf das Programm selbst zeigen, nicht
auf seinen Ordner.
- **Die Python-Umgebung (`tools/.venv`) oder ihre Bibliotheken ließen sich nicht einrichten.** Die
Ausgabe zeigt, was Python oder pip gemeldet haben. Meist blockiert ein Proxy oder ein
Sicherheitsprogramm den Download; das klärt, wer deinen Rechner betreut.
- **Skripte tragen die Markierung „aus dem Internet“** (nur Windows). Das passiert, wenn das
Release im Browser geladen und im Explorer entpackt wurde. Einmal im Verzeichnis, in
PowerShell 7: `Get-ChildItem -Recurse -File | Unblock-File`. `doctor` zeigt den Stand unter
`script-marks`.
- **Die Execution Policy verbietet Skripte** (`Restricted` oder `AllSigned`, nur Windows). Die
Ausgabe nennt die eine Zeile für ein PowerShell-7-Fenster
(`Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned`). Setzt eine
Gruppenrichtlinie sie, hilft nur die IT - oder du arbeitest aus Git Bash mit `tools/wikitool`.
`doctor` zeigt den Stand unter `execution-policy`.
- **Das Installationsverzeichnis ist zu lang** (nur Windows ohne lange Pfade). Die Instanz muss
in ein kürzeres Verzeichnis umziehen, etwa `C:\Chemenu`.
Hält der Agent an einer anderen Stelle an und ist die Ursache nicht offensichtlich, bietet er dir
einen Fehlerbericht an - siehe [Troubleshooting](#troubleshooting).
## Version und Updates
Jede Instanz trägt die Version des **Stacks** (Werkzeuge, Typen, Instruktionen, Contracts) -
nicht die ihres Inhalts. Sie steht in `VERSION`, daneben `.wikitool-release.json` mit Herkunft
und Exportdatum des Releases.
```bash
tools/wikitool version # was läuft hier, und woher kommt es
tools/wikitool version check # gibt es ein neueres Release?
```
`version check`, `version notes` und `dist upgrade --latest` sind die einzigen Befehle, die
ins Netz gehen, und alle drei fragen denselben Release-Feed der Ursprungs-Instanz
(`$WIKITOOL_UPDATE_URL` überschreibt; sonst der Wert aus dem Stamp). `version check` ist dafür da;
`version notes` greift nur dann darauf zurück, wenn die lokale `CHANGES.md` den Eintrag nicht
hat - auf einer Instanz also immer, siehe unten - und sagt vorher auf stderr, welche URL es
fragt; `dist upgrade` fragt den Feed nur, wenn `--latest` dasteht, und lädt dann auch das Release
herunter (siehe „Eine Instanz aktualisieren"). Ein nicht erreichbarer Feed wird als Fehler
gemeldet - **nie** als „aktuell" und nie als „keine Notes".
**Was die Versionsnummer aussagt:** kompatibel ist, was in der *linkesten von Null
verschiedenen Stelle* übereinstimmt. `0.1.3 → 0.1.4` ist ein sicheres Update, `0.1.3 → 0.2.0`
nicht, und ab `1.0.0` liest sich dieselbe Regel als das gewohnte „MAJOR bricht". `version check`
sagt das direkt (`state: update` vs. `state: migration`).
Was diese Stelle beantwortet, ist **ob die neue Version ein Drop-in-Ersatz ist** - ob sich die
Maschinerie einfach darüberkopieren lässt und ob die alte danach noch zurückkann. Ob *Inhalt*
migriert werden muss, ist eine **zweite, unabhängige Frage**. Ein MAJOR-Sprung kann eine leere
Migrationskette haben und trotzdem Handarbeit verlangen: umbenannter Release-Feed, umbenanntes
Artefakt, umbenannter Import- oder Kommandoname, geänderte Envvar - `kb/` bleibt dabei
unangetastet, das Update ist trotzdem keins zum Drüberkopieren. Der Abschnitt „Sonderfall:
Update von 1.x auf 2.0.0" unten ist genau dieser Fall.
Deshalb stehen in den Release-Notes eines MAJOR zwei getrennte Zeilen, und beide sind vor dem
Update zu lesen: **Breaking Change:** sagt, was aufhört zu funktionieren und was diese Instanz
dagegen tun muss; **Migration:** sagt, ob und wie der Korpus umgeschrieben wird (`none required`,
wenn nicht). `tools/wikitool version notes` druckt beide Zeilen - im Ursprungs-Repo aus der dort
gefüllten `CHANGES.md`, auf einer ausgelieferten Instanz aus dem Release-Feed, weil die Instanz
die Datei nur als Stub bekommt und ein Update sie nie überschreibt. Der Befehl fragt dabei immer
das **neueste** Release: solange `VERSION` noch die alte Fassung nennt, antwortet er also mit
einer anderen Version als der eigenen und sagt das auf stderr dazu. Ist der Feed nicht
erreichbar, nennt die Fehlermeldung die Release-Seite, die `.wikitool-release.json` als
`release_url` führt; `--offline` verlangt diesen Weg von vornherein.
### Eine Instanz aktualisieren
Das Anwenden eines Updates schreibt in eine Instanz, die bereits Inhalt hat. Der Inhalt hat dabei
eine **eigene Version**: `.wikitool-kb.json` sagt, in welcher Form die Seiten vorliegen,
unabhängig davon, welche Maschinerie danebensteht. Genau dieser Unterschied ist der Zustand, in
dem sich jede Instanz mitten im Upgrade befindet.
**Die Durchführung selbst steht in `instructions/upgrade-instance.md`** - die Reihenfolge, was
jeder Schritt entscheidet, wo die Agent-Sitzung neu gestartet werden muss, und die beiden Stellen,
an denen heute Handarbeit nötig ist. Sie steht dort und nicht hier, weil sie von einer
Agent-Sitzung ausgeführt wird; eine zweite Fassung derselben Schrittfolge an dieser Stelle wäre
genau die Kopie, die irgendwann auseinanderläuft. Wer den Lauf selbst fahren will, liest dieselbe
Datei.
Was dieses Dokument beiträgt, ist die Entscheidung *davor* - welches Release, ob überhaupt, und
wem die Instanz als Quelle vertraut - und zwei Sonderfälle, die die Instruktion nicht abdecken
kann, weil es sie dort noch nicht gibt.
**Das Release kommt mit einem Befehl.** `tools/wikitool dist upgrade --latest --expect <version>`
fragt den Release-Feed, lädt Tarball und `.sha256` in ein Arbeitsverzeichnis, prüft den Tarball
gegen die Summe und wendet ihn an; das Arbeitsverzeichnis verschwindet bei jedem Ausgang wieder,
auch bei `--dry-run`. `<version>` ist die, die `version notes` gedruckt hat: der Feed kennt nur
sein *neuestes* Release, und `--expect` verweigert den Lauf **vor** dem Download, wenn inzwischen
ein neueres erschienen ist, statt es ungelesen einzuspielen. Beides zusammen entscheidet vor dem
Download über „schon aktuell", Downgrade und Vor-Release (`-beta.N` braucht `--pre`); fehlt dem
Release der Tarball oder die Summe, bricht der Befehl vor dem ersten Download ab und nennt die
Release-Seite. Wer offline arbeitet oder einen Tarball vom Betreiber bekommen hat, gibt statt
`--latest` weiter die Datei an (`dist upgrade <tarball>`).
**Was die Prüfsumme leistet - und was nicht.** Die `.sha256` liegt beim selben Feed wie der
Tarball. Sie schützt vor einer beschädigten Übertragung, nicht vor einem Feed, der selbst
kompromittiert ist: die Echtheit eines Releases beruht auf dem Vertrauen in den Host, dessen
Feed die Instanz fragt (`update_url` im Stamp). Es gibt keinen https-Zwang; wer einen
`http://`-Feed konfiguriert, tut das bewusst. `$WIKITOOL_UPDATE_TOKEN` geht nur an Downloads
auf demselben Host wie der Feed.
**Beim ersten Sprung auf ein Release, das `--latest` kennt, gibt es die Option in der Instanz
noch nicht** - die Instruktion, die dort steht, gehört zum Release, das die Instanz verlässt.
Dann Tarball und `.sha256` einmal von der Release-Seite holen, nebeneinander ablegen und
`dist upgrade <tarball>` geben; ab dem Release danach trägt die Instanz `--latest` selbst.
**Beim ersten Sprung auf `4.5.0` oder höher gibt es `dist upgrade` in der Instanz noch nicht** -
es kam erst mit `4.5.0`. Dann das Werkzeug aus dem entpackten *neuen* Tarball verwenden, gegen die
alte Instanz gerichtet:
```bash
tar -xzf chemenu-stack-<version>.tar.gz
CHEMENU_ROOT="$PWD" chemenu-stack-<version>/tools/wikitool \
dist upgrade chemenu-stack-<version>.tar.gz --dry-run
```
`CHEMENU_ROOT` sagt dem Paket, auf welchen Korpus es zeigen soll (siehe § Konfiguration); ohne die
Variable würde es den entpackten Tarball selbst für die Instanz halten. Ab dem zweiten Upgrade
trägt die Instanz Kommando und Instruktion selbst, und der normale Weg greift.
Was `dist upgrade` dabei genau tut, klassifiziert und verweigert, steht in
[tools/CONTRACT.md](tools/CONTRACT.md) - einschließlich des vollständigen Fehlerkontrakts. Eine
lokal veränderte Stack-Datei ist damit sichtbar, statt von Hand gegen die sha256-Summen im
`files`-Block geprüft werden zu müssen - genau der Schritt, der vor `4.5.0` hier stand.
`doctor` warnt, solange `kb_version` hinter `VERSION` zurückliegt und noch Migrationen offen
sind. Einer Instanz, die älter ist als `.wikitool-kb.json`, fehlt die Datei ganz - dann einmalig
`tools/wikitool migrate baseline <version>` aufrufen; geraten wird nichts.
**Fallstricke.** Eine Instanz ohne lokale `.wikitool-release.json` (oder eine ohne `files`-Block,
aus der Zeit vor `4.5.0`) hat für `dist upgrade` keine Basis, gegen die es eine lokale Änderung
erkennen könnte, und verweigert den Tausch - dafür gibt es heute keine Reparatur.
Mit `<tarball-oder-verzeichnis>` lädt der Befehl selbst nichts herunter; die Datei muss vorher
von der Release-Seite geholt werden. Nur `--latest` lädt, und ein Tarball muss in beiden Fällen genau ein
Top-Level-Verzeichnis enthalten - die Form, in der `.gitea/workflows/release.yml` es baut.
Vor `4.5.0` stand hier ein rein manueller Ablauf (Maschinerie von Hand kopieren, `kb/CONTRACT.md`
eingeschlossen, sha256-Vergleich von Hand). `dist upgrade` ersetzt genau diesen Teil; wer ihn
dennoch von Hand nachvollziehen will oder muss (ein Werkzeug, das `wikitool` selbst nicht
ausführen kann), findet die Dateiliste im `files`-Block der `.wikitool-release.json` und die
Ausnahmen (`kb/CONVENTIONS.md`, `kb/*/COLLECTION.md`, `.wikitool-kb.json`) in
[tools/CONTRACT.md](tools/CONTRACT.md)s `dist upgrade`-Datensatz (oder direkt:
`tools/wikitool dist upgrade -h`).
## Konfiguration
| Variable | Zweck | Fallback |
|----------|-------|----------|
| `WIKI_AUTHOR` | Override für den Autornamen neuer Source-Seiten | `git config user.name` - fehlt beides, bricht `new` mit `ERROR` ab |
| `WIKITOOL_SESSION_ID` | Scopt das Iteration-Budget-Gate auf eine Aufgabe statt auf ein Terminal | Eine vom Harness selbst gesetzte Sitzungs-Variable, wo eine bekannt ist (z. B. `CLAUDE_CODE_SESSION_ID`), sonst die Parent-Process-ID (siehe [instructions/session-setup.md](instructions/session-setup.md)) |
| `WIKITOOL_UPDATE_URL` | Release-Feed, den `version check` abfragt | Wert aus `.wikitool-release.json`, sonst der Feed der Ursprungs-Instanz |
| `WIKITOOL_UPDATE_TOKEN` | Gitea-Token für den Release-Feed | keiner - gegen `torben/chemenu` nicht nötig, nur gegen einen Feed in einem nicht öffentlichen Repo |
| `WIKITOOL_TASKS_CONFIG` | Pfad zu einer Tracker-Konfiguration, die `task`, `review` und `doctor` statt `.wikitool-tasks.json` lesen - um einen Checkout der Reihe nach gegen mehrere Tracker laufen zu lassen | die `.wikitool-tasks.json` im Repo-Root. Nennt die Variable eine Datei, die es nicht gibt, ist das ein Fehler und nie „kein Tracker konfiguriert" |
| `CHEMENU_ROOT` | Auf welchen Korpus das Paket zeigt - für einen Aufrufer, der nicht im Checkout selbst liegt | der Checkout, in dem das Paket liegt (`tools/wikitool` verhält sich ohne die Variable unverändert) |
| `WIKI_TRACE` / `WIKI_TRACE_DIR` | Telemetrie abschalten bzw. aus dem Arbeitsbaum heraus umlenken | Aus - siehe unten |
| `WIKI_TRACE_MAX_SESSION_BYTES` / `WIKI_TRACE_KEEP_SESSIONS` | Byte-Deckel je Session-Trace bzw. wie viele Session-Verzeichnisse die Retention behält | 5 MiB je Session, 250 Verzeichnisse |
**Gegen das Ursprungs-Repo braucht es kein Token.** `torben/chemenu` ist öffentlich lesbar;
`version check`, `dist upgrade --latest` und die Installation funktionieren ohne Konfiguration.
**Telemetrie ist in einer Instanz aus.** Jede Instanz trägt die `.wikitool-release.json` ihres
Releases, und daran erkennt `chemenu.telemetry.policy`, dass niemand Telemetrie bestellt hat.
Das gilt auch für jeden weiteren Klon der Instanz, weil die Datei mit dem ersten Commit ins Repo
kommt. Nur ein Klon des Ursprungs-Repos zur Entwicklung trägt keine; dort sind die Traces das
Messinstrument, mit dem der Stack sich selbst bewertet, und sie stehen auf **an**.
Wer den Default umdrehen will, legt `.wikitool-telemetry.json` im Repo-Root an (pro Checkout,
gitignored, kein `.template` - genau wie `.wikitool-remotes.json`):
```json
{ "enabled": true, "max_session_bytes": 5242880, "keep_sessions": 250 }
```
Alle drei Schlüssel sind optional. `WIKI_TRACE` überschreibt `enabled` weiterhin in beide
Richtungen und schlägt diese Datei. `wikitool doctor` meldet den aktuellen Zustand (an/aus,
warum, und die Menge gegen beide Deckel); mehr dazu in [EVALS.md](EVALS.md) § "Whether it
runs at all".
**Tool-Pfade - schreibt der Preflight, nicht der Mensch.** `.wikitool-tools.json` im Repo-Root
hält die absoluten Pfade von Python, git und ripgrep, so wie der Preflight sie auf diesem Rechner
gefunden hat; `wikitool` startet git und rg von dort statt über `PATH`. Pro Checkout und
gitignored - ein Pfad auf einem Rechner sagt über den nächsten nichts. Fehlt die Datei oder ist
sie unvollständig, startet `tools/wikitool` nicht (Exit 42) und nennt den Preflight. Liegt ein
Tool woanders, als der Preflight sucht, nennt man den Pfad mit
`tools/preflight.sh --set rg=<pfad>` (PowerShell: `pwsh -NoProfile -ExecutionPolicy Bypass -File tools/preflight.ps1 --set rg=<pfad>`); von Hand
bearbeitet wird die Datei nicht. `doctor` meldet
unter `tool-paths`, ob alle Pfade noch stimmen.
**Aufgaben-Tracker anbinden - optional.** Der Wochenrückblick (`tools/wikitool review`, Skill
`gtd-weekly-review`) gleicht die Projektseiten unter `kb/gtd/` gegen einen Aufgaben-Tracker ab. Welcher
das ist, steht in `.wikitool-tasks.json` im Repo-Root - der dritten Datei dieser Art neben
`.wikitool-telemetry.json` und `.wikitool-remotes.json`: pro Checkout, ohne `.template`, und
**gitignored, sobald ein Token darin liegt**. Fehlt sie, ist schlicht kein Tracker konfiguriert;
das ist ein gültiger Endzustand, kein Fehler. Kaputt ist sie dagegen ein `FAIL` - eine
unlesbare Konfiguration darf nicht als „kein Tracker" durchgehen.
```json
{
"schema": 1,
"provider": "superproductivity",
"thresholds": {
"stalled_waiting_days": 14,
"unpaged_project_weeks": 3,
"someday_stale_months": 5
},
"superproductivity": {
"access": "api",
"api_base_url": "http://127.0.0.1:3876",
"api_token": "<token aus den SP-Einstellungen>"
}
}
```
```json
"superproductivity": {
"access": "snapshot",
"backups_dir": "~/.config/superProductivity/backups"
}
```
`provider` wählt den Adapter - ausgeliefert werden `superproductivity` und `caldav`. Der
`thresholds`-Block trägt die drei Schwellwerte des Rückblicks (Konfiguration, nicht Schema): ab
wann ein Waiting-For überfällig ist, ab welchem Alter ein Tracker-Projekt ohne `kb/`-Seite
gemeldet wird, und ab wann ein Someday-Eintrag als verstaubt gilt. Ein Wert, den der Provider
gar nicht liefern kann - ein Waiting-Posten ohne Wiedervorlagedatum, ein Tracker-Projekt ohne
bestimmbares Anlagedatum - wird vom Rückblick nicht still übersprungen, sondern als eigene
Fundstelle gemeldet (`waiting_no_follow_up`/`project_age_unknown`).
Der gleichnamige Provider-Block trägt dessen Verbindungsangaben, und bei Super Productivity
entscheidet `access` **verpflichtend und ohne Rückfall**, welcher von zwei sich ausschließenden
Wegen das ist: eine headless bediente Instanz setzt `access: "snapshot"` und liest
ausschließlich den jüngsten Backup-Schnappschuss unter `backups_dir` (läuft auch ohne laufende
App, aber rein lesend - der Tracker ist von dort aus nicht schreibbar); eine Desktop-Instanz
setzt `access: "api"` und spricht ausschließlich die lokale REST-API an, die nur antwortet,
solange die App läuft, dafür aber auch den aktuellen Zustand liefert und den Schreibpfad trägt.
Der Block nennt nur die Felder seines eigenen Wegs - ein `backups_dir` neben `access: "api"` oder
ein `api_token` neben `access: "snapshot"` wird beim Lesen der Konfiguration abgelehnt, nicht
ignoriert. `api_token` ist bei `access: "api"` Pflicht, da jeder Endpunkt außer `GET /health`
`Authorization: Bearer <token>` verlangt.
`tools/wikitool new project` legt einen gleichnamigen Tracker-Eintrag nur auf einer
`access: "api"`-Instanz an (und auch dort nicht automatisch - siehe die Kommandotabelle). Auf
einer `access: "snapshot"`-Instanz verweigert das Kommando vollständig, exit 1: der Tracker ist
von dort aus nur lesbar. Dasselbe gilt für `tools/wikitool task new`, den zweiten Schreibweg:
es legt einen einzelnen Posten im Tracker an - ohne `kb/`-Seite - und existiert ebenfalls nur
auf einer `access: "api"`-Instanz. `tools/wikitool task close --id` ist der dritte und letzte
Schreibweg - er markiert einen Posten erledigt, löscht ihn nie - und verweigert auf
`access: "snapshot"` auf dieselbe Weise. `tools/wikitool task list --project` ist rein lesend
und beantwortet daher auf beiden Zugriffsarten.
**`caldav`** ist der standardbasierte zweite Adapter (RFC 4791/5545), gegen Nextcloud Tasks
verifiziert, mit iOS *Erinnerungen* als mobilem Client - gebaut nach dem Zuschnitt: auf dem
Telefon wird abgehakt, gepflegt wird am Schreibtisch. Anders als bei Super Productivity gibt es
nur einen Zugriffsweg - CalDAV ist immer ein Netzwerkzugriff, kein `access`-Feld nötig:
```json
"caldav": {
"url": "https://<host>/remote.php/dav/calendars/<user>/",
"username": "<login>",
"app_password": "<Nextcloud-App-Passwort>",
"inbox_list": "Inbox",
"someday_list": "Someday",
"exclude_lists": ["<vorhandene Liste, die kein Projekt ist>"]
}
```
`url` zeigt auf das CalDAV-Calendar-Home-Set des Kontos; sie darf ein Alias sein (Nextcloud
akzeptiert dort einen Kurznamen), da jede spätere Adresse ausschließlich aus den vom Server
gelieferten `href`s stammt, nie aus dieser URL und einem Namen zusammengesetzt wird. `username`
ist der Login-Name, der von der Benutzer-ID in der URL abweichen kann - ein
Nextcloud-App-Passwort wird empfohlen, nicht das Kontopasswort. `inbox_list`/`someday_list`
nennen die beiden festen Listen (je genau eine pro Instanz); `exclude_lists` nimmt vorhandene
reine Aufgabenlisten heraus, die keine Projekte sind - eine Liste mit `VEVENT`-Anteil zählt
ohnehin nie als Projekt.
Ein Projekt ist dort eine Liste, deren unterstützte Komponente ausschließlich `VTODO` ist; `tools/wikitool
new project` legt sie automatisch per `MKCALENDAR` an - anders als bei Super Productivity ohne
Rückfrage, weil CalDAV einen echten Anlage-Befehl für Listen kennt. Die Eindeutigkeitsprüfung
läuft gegen **jede** Liste im Konto, auch gegen ausgeschlossene, Inbox, Someday und gemischte
Kalender - kollidiert ein neuer Name mit einer davon, wird nichts angelegt und die Kollision
genannt; die vorhandene Liste in Nextcloud umzubenennen bleibt Handarbeit. `tools/wikitool task
new`/`task close` funktionieren auf einer `caldav`-Instanz uneingeschränkt - es gibt keinen
reinen Lesemodus wie `access: "snapshot"`. Beim Abhaken ändert `task close` ausschließlich
`STATUS`, `COMPLETED`, `PERCENT-COMPLETE`, `LAST-MODIFIED` und `DTSTAMP` an der bestehenden
`.ics`-Ressource; jede andere Eigenschaft - auch eine unbekannte `X-`-Eigenschaft oder ein Alarm
- bleibt unverändert erhalten, und eine seit dem Lesen veränderte Ressource (ETag-Konflikt)
schreibt nichts und bricht mit exit 1 ab. Gelöscht wird nie etwas.
Listen abgeschlossener Projekte bleiben nach dem Archivieren bestehen - der Adapter löscht nie
eine Liste; das übernimmt der Betreiber von Hand in Nextcloud, sobald gewünscht.
## Verifikation
```bash
tools/wikitool doctor
```
Prüft in einem Aufruf: Abhängigkeiten, Autor-Auflösung, Git-Identität/Branch/Remote,
publizierte Skills, Struktur (Collection-Contracts, generierte Dateien), Personalization
(`USER.md`/`SOUL.md` vorhanden **und** ausgefüllt), die optionale Umgebungsnotiz
(`ENVIRONMENT.md`), den Aufgaben-Tracker (`.wikitool-tasks.json` - fehlt sie, ist das `OK`; ist
ein Provider konfiguriert, zusätzlich ob sein Lesepfad bereitsteht und seine API gerade
antwortet, beides nie ein `FAIL`) und die Session-ID.
`OK`/`WARN` sind unbedenklich (ein fehlender Remote z. B. ist ein gültiger Endzustand); nur ein
`FAIL` bricht mit exit 1 ab, und jede Zeile nennt ihr eigenes Fix-Kommando.
Danach zusätzlich:
```bash
tools/wikitool docs verify
tools/wikitool instructions verify
```
## Troubleshooting
- **`tools/wikitool` endet mit `STOP - this checkout is not set up yet` (Exit 42)** - der
Preflight ist in diesem Checkout noch nicht durchgelaufen, oder seit dem letzten Update nicht
mehr: `tools/preflight.sh` ausführen, siehe
[instructions/preflight.md](instructions/preflight.md). In PowerShell 7 unter Windows heißt der
Aufruf `pwsh -NoProfile -ExecutionPolicy Bypass -File tools/preflight.ps1`; das
`-ExecutionPolicy Bypass` gilt nur für diesen einen Prozess und ändert keine Einstellung. Was
seine Ausgabe dann bedeuten kann, steht unter [Wenn der Agent anhält](#wenn-der-agent-anhält).
- **Der Agent bietet keine Skills an (`wiki-ingest`, `wiki-query`, ...)** - `.agents/skills/`
und `.claude/skills/` sind generiert und nicht committet. `tools/wikitool instructions sync`
ausführen, dann die Agent-Session neu starten (Harnesses lesen Skills nur beim Start).
- **`doctor` meldet `personalization: FAIL`** - `USER.md`/`SOUL.md` fehlen, oder sie tragen
noch die Sentinel-Zeile aus dem Template (ein umbenanntes Template ist kein ausgefülltes).
Den Personalisierungs-Schritt aus `instructions/setup-instance.md` ausführen lassen; bei einem
weiteren Klon einer älteren Instanz ist das der einzige nachzuholende Schritt.
- **`doctor` meldet `environment: WARN`** - `ENVIRONMENT.md` existiert, trägt aber noch die
Sentinel-Zeile aus dem Template. Ausfüllen (Vorlage: `ENVIRONMENT.md.template`) und die Zeile
entfernen, oder die Datei löschen - sie ist optional, und `absent` ist ein gültiger
Endzustand.
- **`new` bricht mit "No author configured" ab** - weder `$WIKI_AUTHOR` noch
`git config user.name` sind gesetzt. `git config user.name "<Name>"` ausführen, oder
`WIKI_AUTHOR` exportieren.
- **`publish` endet mit Exit-Code 42 (Mass-Update-Gate)** - erwartetes Verhalten bei ≥10
gezählten Dateien (z. B. beim allerersten Commit einer neuen Instanz). Das ist kein Fehler,
sondern die Aufforderung, die Ausgabe einem Menschen zu zeigen: sie enthält die vollständige
Dateiliste und die exakte `--confirm <token>`-Zeile, die nach Freigabe veröffentlicht.
Details: [instructions/gates.md](instructions/gates.md).
- **`publish` endet mit Exit-Code 42 (Publish-Remote-Gate)** - dieser Checkout hat eine
`.wikitool-remotes.json`, und das angesteuerte Remote steht nicht darin. Ebenfalls kein
Fehler: Die Ausgabe nennt die Push-URL, an die geschrieben würde, und die erlaubten. Anders
als beim Mass-Update-Gate gibt es hier **keinen Token und keine Flagge** - stimmt das Ziel
wirklich, trägt der Mensch dessen URL selbst in die Datei ein. Ein Agent, der die Datei
anfasst, um an der Verweigerung vorbeizukommen, öffnet ein Gate aus eigenem Antrieb.
- **Setup oder Update schlägt fehl und ich will es melden** - den Agenten
[instructions/bug-report.md](instructions/bug-report.md) ausführen lassen, oder direkt
`tools/bugreport` aufrufen (aus Bash, Git Bash und PowerShell gleich). Der Aufruf sucht sich
selbst ein Python 3.8 oder neuer und überspringt dabei die Store-Aliase, auf die `python3`
unter Windows zeigt. Er braucht weder den Preflight noch ein funktionierendes `wikitool`, und
findet er kein Python, sagt er, warum. Das Skript schreibt ein Bündel nach
`reports/bugreport-<Zeitstempel>/` samt Zip. Geheimnisse werden entfernt, und aus allem, was das Skript selbst erzeugt, bleiben
Seitentitel draußen (`--titles` nimmt sie mit). Der Sitzungs-Trace ist standardmäßig dabei
(`--no-trace` lässt ihn weg) und kann wie Chronologie und Transkripte Seiteninhalt und Titel
enthalten; das Bündel enthält außerdem Maschinen-, Benutzer- und Pfadnamen. Mit `--pseudonymise`
ersetzt das Skript Benutzer-, Host-, Pfad-, Git- und Remote-Namen durch Platzhalter, die Länge,
Leerzeichen, Bindestriche und Pfadtiefe erhalten; der Agent kann danach weitere Namen
(Personen, Firmen, Kunden, Projekte) mit `--bundle … --candidates …` nachtragen. Das ist das
Urteil eines Modells und lässt einen Rest übrig - lies das Bündel vor dem Teilen. Die
Zuordnung, die Prüfliste und die Kandidatendatei enthalten Originale und liegen neben, nie im
Bündel. Es wird nirgends hochgeladen - den Kanal wählst du selbst.