From fe1de63e0fdbd73847ed6ce186cb858934eff44f Mon Sep 17 00:00:00 2001 From: Torben Nehmer Date: Wed, 7 Oct 2026 06:38:40 +0200 Subject: [PATCH] feat: INSTALL.md is the English installation tutorial; new tutorial docs/tutorials/first-source-to-first-answer.md with a shipped sample source; README.md links it as its first-ingestion step (#191) Files changed: - CHANGES.md - INSTALL.md - README.md - VERSION - docs/reference/commands/types-instructions-and-docs.md - docs/tutorials/README.md - docs/tutorials/first-source-to-first-answer.md - docs/tutorials/sample-source.md - instructions/dev/doc-pull-through.md - tools/CONTRACT.md - tools/chemenu/commands/docs_verify.py - tools/chemenu/install_doc.py - tools/chemenu/tests/test_install_doc.py Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2 --- CHANGES.md | 7 +- INSTALL.md | 272 +++++++++--------- README.md | 24 +- VERSION | 2 +- .../commands/types-instructions-and-docs.md | 2 +- docs/tutorials/README.md | 9 +- .../tutorials/first-source-to-first-answer.md | 157 ++++++++++ docs/tutorials/sample-source.md | 48 ++++ instructions/dev/doc-pull-through.md | 2 +- tools/CONTRACT.md | 2 +- tools/chemenu/commands/docs_verify.py | 4 +- tools/chemenu/install_doc.py | 45 +-- tools/chemenu/tests/test_install_doc.py | 19 +- 13 files changed, 403 insertions(+), 190 deletions(-) create mode 100644 docs/tutorials/first-source-to-first-answer.md create mode 100644 docs/tutorials/sample-source.md diff --git a/CHANGES.md b/CHANGES.md index e1d0f27..797db0f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,7 @@ place only: `instructions/dev/version-parts.md` § The candidate model. --- -## 9.0.0-beta.6 - 2026-10-06 - docs/: how-to guides, configuration reference and two explanation pages carved out of README.md, INSTALL.md, INSTALL-MCP.md and EVALS.md; the last two retired +## 9.0.0-beta.7 - 2026-10-07 - INSTALL.md is an English installation tutorial; new tutorial from a first source to a first answer with a shipped sample; README.md a landing page **Author:** Torben Nehmer @@ -31,6 +31,7 @@ place only: `instructions/dev/version-parts.md` § The candidate model. - docs reference: human command reference under docs/reference/, generated from the command records - docs reference: skill catalogue and page types under docs/reference/, generated from the shipped defaults in the origin repo only - docs/: how-to guides, configuration reference and two explanation pages carved out of README.md, INSTALL.md, INSTALL-MCP.md and EVALS.md; the last two retired +- INSTALL.md is an English installation tutorial; new tutorial from a first source to a first answer with a shipped sample; README.md a landing page **Low impact** - version bump: Abschlussmeldung nennt Stichpunkt oder Themenabsatz statt pauschal Prosa @@ -38,6 +39,10 @@ place only: `instructions/dev/version-parts.md` § The candidate model. - tools/README.md module tree names reference_doc.py and the fourth view cli_contract renders +### Tutorials: installation in English, and a first source to a first answer (#191) + +`INSTALL.md` stays at the root and is now the English installation tutorial, the first one `docs/tutorials/README.md` lists; its generated prerequisites lists and `setup-question` markers are unchanged, and `docs verify`'s finding for a missing question names the English section "What the agent asks you". The new `docs/tutorials/first-source-to-first-answer.md` walks a fresh instance through one ingest and two questions, one the wiki answers with citations and one it has no source for, using `docs/tutorials/sample-source.md`, a short made-up note that ships with every instance. `README.md`'s "Your first ingestion" is a link to it, which finishes `README.md` as a landing page. + ### The root documents split into `docs/` pages (#190) The task and explanation parts of `README.md`, `INSTALL.md`, `INSTALL-MCP.md` and `EVALS.md` are now single English pages: eleven how-to guides under `docs/how-to/` (ingest, capture, guideline export, query, maintenance, weekly review, Obsidian, updating an instance, troubleshooting, the MCP read server, tracing and scoring sessions), a hand-written `docs/reference/configuration.md`, and `docs/explanation/architecture.md` and `telemetry-and-evaluation.md`. A guide says what to prepare, what to tell the agent and where it stops; it links an agent's procedure instead of retelling it. `INSTALL-MCP.md` and `EVALS.md` are gone from the root and from `dist export`, so an upgrade removes them as retired; `README.md` is a landing page, and `INSTALL.md` keeps only the installation itself. Pointers in messages and command records, among them `version check`'s, name the new pages. diff --git a/INSTALL.md b/INSTALL.md index 8e0bdc0..c667989 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,27 +1,27 @@ -# Installation +# Install a Chemenu instance -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 +This tutorial takes you from an empty folder to a running Chemenu instance. There is exactly one +way to get one: an agent installs the **latest release** into an empty folder you choose. The +agent runs the steps, following `instructions/setup-instance.md` from that same release. This +page covers what you prepare beforehand, the sentence you give the agent, what it asks you, and +what to do when it stops. The full command reference is in [tools/CONTRACT.md](tools/CONTRACT.md). -Den optionalen **MCP-Leseserver** richtet -[docs/how-to/set-up-the-mcp-server.md](docs/how-to/set-up-the-mcp-server.md) ein: derselbe -Korpus, lesend, für einen Konsumenten, der kein Terminal auf dieser Maschine ist. +The optional **MCP read server** is set up separately, once the instance runs: +[docs/how-to/set-up-the-mcp-server.md](docs/how-to/set-up-the-mcp-server.md). It serves the same +corpus, read-only, to a consumer that is not a terminal on this machine. -Wer am Stack selbst arbeiten will, klont dieses Repo. Das ist eine Entwicklungsumgebung mit -Demo-Korpus und keine Instanz; sie steht in [docs/dev/development.md](docs/dev/development.md). +To work on the stack itself, clone this repository instead. That is a development checkout with a +demo corpus, not an instance; it is described in [docs/dev/development.md](docs/dev/development.md). -## Was vorher da sein muss +## What you need first -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: +The preflight checks these programs before any `wikitool` command runs. You install them +yourself - the agent never does, not even with your permission. Where one is missing, the +preflight names the install command for your system. The list is generated from +`tools/prerequisites.txt`, the same file the preflight reads: - **Python** ≥ 3.11 @@ -29,166 +29,166 @@ der Preflight den Installationsbefehl für dein System. Die Liste wird aus - **ripgrep (rg)** -Dazu: +You also need: -- **Ein Agent-Harness**: Claude Code, GitHub Copilot (in VS Code oder als CLI), Codex CLI oder +- **An agent harness**: Claude Code, GitHub Copilot (in VS Code or as a CLI), Codex CLI or 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`. +- **An empty folder** for the instance, with your harness opened in it. Empty means nothing but a + `.git`. A freshly cloned, empty repository for your instance is exactly right: if your + repository `torben/nathan` lives in `~/src/nathan`, install there, and the agent keeps its + `origin` as the target for `publish`. -Unter Windows zusätzlich, ebenfalls vom Preflight geprüft: +On Windows, additionally, also checked by the preflight: - **PowerShell 7 (pwsh)** ≥ 7 -Und außerdem: +And on Windows also: -- **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. +- **PowerShell 7 as the default terminal in VS Code.** Windows PowerShell 5.1 is not enough, and + WSL is not supported. +- **Execution policy `RemoteSigned`** - the factory setting on many machines + (`Get-ExecutionPolicy -List` shows it). +- **Git for Windows.** It brings Git Bash, which Claude Code runs its commands in. +- **An installation folder of at most 95 characters**, for example `C:\Chemenu`. Without long + paths switched on, Windows allows only 259 characters per path, and the wiki's files need the + rest. If you have administrator rights you can switch long paths on instead + (`LongPathsEnabled`); that is not required. -## Der Satz für den Agenten +## The sentence for the agent -Öffne das leere Verzeichnis in deinem Harness und gib dem Agenten diesen Satz: +Open the empty folder in your harness and give the agent this sentence: -> 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. +> Set up a new Chemenu instance in this folder. Fetch the latest release from +> `https://gitea.nehmer.net/api/v1/repos/torben/chemenu/releases/latest` and follow its asset +> `setup-instance.md`. Before you start the preflight, read the asset `preflight.md` from the same +> 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. +With that, the agent reads the description of the latest release, and from it the two +instructions. It then downloads the matching preflight script (`preflight.ps1` for PowerShell, +`preflight.sh` for a POSIX shell) into the folder with a command of its shell - not through the +browser, so that Windows does not mark the file as "from the internet" - and runs it. The script +downloads the tarball of the same release, checks its sha256, unpacks it into the folder, deletes +itself, and then checks in the unpacked tree that everything is in place. -Die Liste aller Releases: . Das Repo ist -öffentlich; der Download braucht weder Konto noch Token. +All releases are listed at . The repository is +public; the download needs neither an account nor a token. -## Was der Agent dich fragt +## What the agent asks you -Raten darf der Agent keine dieser Antworten, und keine übernimmt er aus einem anderen Repo: +The agent may guess none of these answers, and it carries none of them over from another +repository: -- **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). -- **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`. -- **Sprache und Ton der Seiten** - sie landen in - `kb/CONVENTIONS.md`, dazu je Collection `kb//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`. -- **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`). -- **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**. -- **Umgebung** (optional) - Harness, MCP-Server, Remotes, - damit spätere Sitzungen nicht erneut fragen. „Weiß ich nicht“ ist eine gültige Antwort. -- **Telemetrie** - standardmäßig aus; der Agent fragt nur, ob du - sie einschalten willst. -- **Aufgaben-Tracker** (optional) - siehe +- **Author identity** - name and email for `git config`. This is + also the author name of every wiki page created from now on (`$WIKI_AUTHOR` overrides it when + needed). +- **Remote** - for an empty clone, only the confirmation that + `origin` is right; otherwise a URL, if you want to push to a server. Without a remote the + instance stays local, and every `publish` runs with `--no-push`. +- **Language and tone of the pages** - they go into + `kb/CONVENTIONS.md`, plus `kb//COLLECTION.md` per collection. Ready-made profiles, + including a complete German one, are in `instructions/kb-profiles.md`. Decide this **before the + first ingest**: afterwards, changing the section names is a migration of every existing page. + Titles, wikilink targets, citation ids, schema values, tags, commands and paths follow no + language - `Act Runner` is called `Act Runner` in every instance. +- **Subject area** - where this wiki draws its sources from. From + it the agent proposes a `source_type` list (for a sports club, say: statutes, minutes, match + report). That is a starting point, not a commitment: it gets corrected against real material + later (`instructions/evolve-subtypes.md`). +- **Personalization** - who operates this instance + (`USER.md`) and how it sounds (`SOUL.md`). The agent interviews you along the templates and + writes your answers down verbatim. Two questions only you answer: the **name of the persona** + and the **topics that deliberately stay out**. +- **Environment** (optional) - harness, MCP servers, remotes, + so that later sessions don't ask again. "I don't know" is a valid answer. +- **Telemetry** - off by default; the agent only asks whether + you want to switch it on. +- **Task tracker** (optional) - see [docs/reference/configuration.md](docs/reference/configuration.md#task-tracker). -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. +At the end the agent makes the first commit. The Mass-Update Gate stops it there (exit 42), +because a new instance consists of far more than ten files. That is expected: the agent shows you +the file list and the `--confirm` line, and nothing is published until you approve. Afterwards, +restart the agent session in the same folder so that it loads the skills. -## Wenn der Agent anhält +## When the agent stops -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. +The preflight stops with **exit 42** when you have to do something. Its output names, in a +numbered block, what is missing, why, the command that fixes it, and how to continue. The agent +shows you this block unchanged, adds a translation below it at most, and waits. Tell it when you +are done; then it checks again. It may not work around the stop or install anything itself. -**Beim Herunterladen und Entpacken** (das Skript aus dem Release, bevor es einen Baum gibt): +**While downloading and unpacking** (the script from the release, before there is a tree): -- **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 `. +- **Tools for downloading or unpacking are missing** (exit 42). On Linux and macOS the script + needs `curl`, `tar` and `sha256sum` (or `shasum`); on Windows only the `tar.exe` that comes + with Windows 10/11. Install what the output names; on Windows, Git for Windows brings + everything for Git Bash. +- **The folder path is too long** (exit 42, only on Windows without long paths). Take a shorter + one, such as `C:\Chemenu`, open it in the harness and give the sentence there again. +- **The folder is not empty** (exit 1). It may hold nothing but the script and a `.git`. Clean it + up yourself or take another one - the agent deletes nothing there. +- **Download failed or checksum wrong** (exit 1). Nothing was unpacked. Check the internet + connection and let it try again; the agent does not look for another way to download. Without a + direct download you can put the tarball and its `.sha256` from the release page side by side + yourself; the agent then runs the script with `--archive `. -**Im entpackten Baum** (jeder weitere Lauf ist `tools/preflight.sh` bzw. unter PowerShell +**In the unpacked tree** (every further run is `tools/preflight.sh`, or under 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 =` 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`. +- **A tool is missing** - Python, git, ripgrep, on Windows also PowerShell 7. The output names + the install command for your system. If it is installed, just somewhere else, tell the agent the + path; it passes it on with `--set =`. +- **A version is too old** - Python below 3.11, for example. Install a newer version or name its + path. +- **A path you named does not work** - the path must point at the program itself, not at its + folder. +- **The Python environment (`tools/.venv`) or its libraries could not be set up.** The output + shows what Python or pip reported. Usually a proxy or a security program blocks the download; + whoever looks after your machine can sort that out. +- **Scripts carry the "from the internet" mark** (only on Windows). That happens when the release + was downloaded in the browser and unpacked in Explorer. Once, in the folder, in PowerShell 7: + `Get-ChildItem -Recurse -File | Unblock-File`. `doctor` shows the state under `script-marks`. +- **The execution policy forbids scripts** (`Restricted` or `AllSigned`, only on Windows). The + output names the one line for a PowerShell 7 window + (`Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned`). If a group policy sets + it, only your IT department can help - or you work from Git Bash with `tools/wikitool`. `doctor` + shows the state under `execution-policy`. +- **The installation folder path is too long** (only on Windows without long paths). The instance + has to move to a shorter folder, such as `C:\Chemenu`. -Hält der Agent an einer anderen Stelle an und ist die Ursache nicht offensichtlich, bietet er dir -einen Fehlerbericht an - siehe +If the agent stops anywhere else and the cause is not obvious, it offers you a bug report - see [docs/how-to/troubleshoot.md](docs/how-to/troubleshoot.md). -## Verifikation +## Check the instance ```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. +This checks in one call: dependencies, author resolution, git identity/branch/remote, published +skills, structure (collection contracts, generated files), personalization (`USER.md`/`SOUL.md` +present **and** filled in), the optional environment note (`ENVIRONMENT.md`), the task tracker +(`.wikitool-tasks.json` - if it is missing, that is `OK`; if a provider is configured, also +whether its read path is ready and its API is answering right now, neither ever a `FAIL`) and the +session id. `OK`/`WARN` are harmless (a missing remote, for example, is a valid end state); only a +`FAIL` ends with exit 1, and every line names its own fix command. -Danach zusätzlich: +Then also: ```bash tools/wikitool docs verify tools/wikitool instructions verify ``` -## Danach +## Next steps -- **Aktualisieren** auf ein neueres Release: [docs/how-to/update-an-instance.md](docs/how-to/update-an-instance.md). -- **Konfiguration** - Umgebungsvariablen und die Dateien pro Checkout: +- **Your first source and your first answer:** + [docs/tutorials/first-source-to-first-answer.md](docs/tutorials/first-source-to-first-answer.md) + walks you through both in the instance you just set up. +- **Updating** to a newer release: [docs/how-to/update-an-instance.md](docs/how-to/update-an-instance.md). +- **Configuration** - environment variables and the files per checkout: [docs/reference/configuration.md](docs/reference/configuration.md). -- **Wenn etwas nicht geht:** [docs/how-to/troubleshoot.md](docs/how-to/troubleshoot.md). -- **Mit dem Wiki arbeiten:** [docs/how-to/](docs/how-to/README.md). +- **When something does not work:** [docs/how-to/troubleshoot.md](docs/how-to/troubleshoot.md). +- **Working with the wiki:** [docs/how-to/](docs/how-to/README.md). diff --git a/README.md b/README.md index 5f19ad1..f9fe382 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Two starting points, depending on what you're doing: The human documentation lives under `docs/`, sorted by what you want from it: - [docs/tutorials/](docs/tutorials/README.md) - learning: a guided first walk through an - instance. Installation is still [INSTALL.md](INSTALL.md). + instance, starting with the installation in [INSTALL.md](INSTALL.md). - [docs/how-to/](docs/how-to/README.md) - doing: one task per page, for the operator of an instance. - [docs/reference/](docs/reference/README.md) - looking up: commands, exit codes, skills, page @@ -61,26 +61,8 @@ How the parts fit together is [docs/explanation/architecture.md](docs/explanatio ## Your first ingestion -### First Steps - -1. Read `AGENTS.md` - the control plane (invariants, routing, gates) - then the stage contract - for whichever of `raw/`, `types/` or `kb/` you are working in, and, inside `kb/`, the - `COLLECTION.md` of the collection you are writing to -2. Add your first source to `incoming/` -3. Run: `Ingest ` -4. Review the created pages -5. Ask your first query - -### Example First Ingestion - -```bash -# Add a source -cp ~/Downloads/my-notes.md incoming/my-notes.md - -# Tell the LLM to process it -# (in your LLM agent) -Ingest incoming/my-notes.md -``` +Once the instance runs, [From your first source to your first answer](docs/tutorials/first-source-to-first-answer.md) +takes a sample document that ships with it through an ingest and a cited answer, step by step. ## Changelog diff --git a/VERSION b/VERSION index c308ce5..2ea9c76 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.0-beta.6 +9.0.0-beta.7 diff --git a/docs/reference/commands/types-instructions-and-docs.md b/docs/reference/commands/types-instructions-and-docs.md index f72e752..f086b4d 100644 --- a/docs/reference/commands/types-instructions-and-docs.md +++ b/docs/reference/commands/types-instructions-and-docs.md @@ -400,7 +400,7 @@ tools/wikitool docs prerequisites --apply **Behaviour** -- Rewrites each `` region in `INSTALL.md` (tools every platform needs) and `` region (tools only that platform needs) from the manifest: one list item per tool, its label and minimum version. The manifest's reason field stays out - it is English prose, and the region sits in a document that need not be. +- Rewrites each `` region in `INSTALL.md` (tools every platform needs) and `` region (tools only that platform needs) from the manifest: one list item per tool, its label and minimum version. The manifest's reason field stays out - it explains a tool to whoever reads the preflight's own output, while the region only says what to install. - Never places a region: where a list belongs in the human guide is that guide's own decision. A region the manifest calls for but the file lacks is an error naming the marker pair to add. - Dry-run by default (says whether the file would change); `--apply` writes. - `docs verify` checks the result stays current. diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md index 98cc0b8..5ae4267 100644 --- a/docs/tutorials/README.md +++ b/docs/tutorials/README.md @@ -4,5 +4,10 @@ Learning-oriented pages: a guided first walk through an instance, start to finis who has not used one before. A tutorial shows one path that works; it does not list options or explain why. -Nothing is here yet. The installation tutorial is still [INSTALL.md](../../INSTALL.md) at the -repository root, which stays there; a walk from the first source to the first answer is planned. +Take them in this order: + +1. [Install a Chemenu instance](../../INSTALL.md) - from an empty folder to a running instance. + It stays at the repository root, where someone who has nothing else yet finds it first. +2. [From your first source to your first answer](first-source-to-first-answer.md) - ingest a + sample document, look at the pages it became, and ask the wiki a question about it. + [sample-source.md](sample-source.md) is the document it uses. diff --git a/docs/tutorials/first-source-to-first-answer.md b/docs/tutorials/first-source-to-first-answer.md new file mode 100644 index 0000000..364ccc8 --- /dev/null +++ b/docs/tutorials/first-source-to-first-answer.md @@ -0,0 +1,157 @@ +# From your first source to your first answer + +In this tutorial you put one document into a fresh instance, let the agent turn it into wiki +pages, look at what it wrote, and then ask the wiki a question and follow the answer back to the +document. It takes about a quarter of an hour. You need nothing but the instance and your harness: +the document is a sample that ships with the instance. + +You start from an instance that [INSTALL.md](../../INSTALL.md) has just set up, with the agent +session restarted afterwards so that it has loaded its skills. Ask the agent `Which skills do you +have?` if you are unsure - `wiki-ingest` and `wiki-query` should be among them. + + +## Contents + +- [1. Put the sample source into `incoming/`](#1-put-the-sample-source-into-incoming) +- [2. Ask the agent to ingest it](#2-ask-the-agent-to-ingest-it) +- [3. Tell the agent what matters](#3-tell-the-agent-what-matters) +- [4. Answer the two capture questions](#4-answer-the-two-capture-questions) +- [5. Let the agent publish](#5-let-the-agent-publish) +- [6. Look at what it wrote](#6-look-at-what-it-wrote) +- [7. Ask a question](#7-ask-a-question) +- [8. Optional: browse it in Obsidian](#8-optional-browse-it-in-obsidian) +- [What next](#what-next) + + +## 1. Put the sample source into `incoming/` + +`incoming/` is where everything you want in the wiki waits until it is ingested. Copy the sample +there - copy, not move, so the tutorial page keeps its sample: + +```bash +cp docs/tutorials/sample-source.md incoming/home-lab-backups.md +``` + +On Windows in PowerShell 7: + +```powershell +Copy-Item docs/tutorials/sample-source.md incoming/home-lab-backups.md +``` + +Open [the sample](sample-source.md) and read it once. It is a page of notes about the backups of a +small home file server called Atlas: which tool makes them (restic), where they go, and why there +are two targets (the 3-2-1 backup rule). Knowing what it says will let you check the agent's work. + +## 2. Ask the agent to ingest it + +In the agent session, say: + +```text +Ingest +``` + +A bare `Ingest` takes the oldest entry waiting in `incoming/` - here, the only one. The agent +reads the whole file before it writes anything. + +## 3. Tell the agent what matters + +The agent comes back with the key takeaways of the document and a few questions: which points +matter most, which things should get a page of their own, and whether the document carries +something you have to *do*. Answer in your own words, for example: + +```text +Atlas, restic and the 3-2-1 rule are what matter. Nothing to do from this one. +``` + +The pages it writes next follow your answer, so this is the moment to steer. The agent may well +notice that the notes leave something untested and offer to track it as a task; "nothing to do" +declines that. + +Often the same message already asks the two questions of the next step. Then answer them in the +same reply. + +## 4. Answer the two capture questions + +Before the file leaves `incoming/`, the agent asks two things about the capture itself: + +- **`fidelity`** - how faithful is this file to the original? The sample is the notes exactly as + written, so the answer is `verbatim`. +- **`authority`** - what may the material claim about its subject? The notes describe how a setup + works; they do not set a standard anyone else has to follow. The answer is `reporting`. + +```text +verbatim, reporting +``` + +The agent never guesses these two. Only you know them, and only now: once the file is in the wiki, +nobody can tell any more where it came from. [raw/CONTRACT.md](../../raw/CONTRACT.md#capture-fields-fidelity-and-authority) +lists every value, for the sources you add later. + +## 5. Let the agent publish + +The agent now moves the file into `raw/`, writes the pages, and publishes everything as one +commit. For a source this small that goes straight through; the agent tells you what it committed. +If your instance has no remote, the commit stays on your machine. + +A larger source - ten or more new or changed pages and files, not counting the generated indexes +and log - stops at the Mass-Update Gate instead: you see the list of files and a line ending in +`--confirm `, and nothing is committed until you tell the agent to go ahead. + +## 6. Look at what it wrote + +Ask the agent where the pages are, or look yourself. You will find: + +- **The file itself under `raw/`**, in a folder for the current year and month. It is never edited + again. +- **A source page under `kb/sources/`**, summarising the notes. Its frontmatter carries your two + answers, `fidelity: verbatim` and `authority: reporting`, and `raw_files:` points at the file in + `raw/`. +- **Entity and concept pages** for what you said matters - Atlas, restic, the 3-2-1 backup rule, + in the language and naming your instance chose at setup. Each one links back to the source page, + and each claim taken from the notes carries a footnote citing it. +- **A new entry at the end of `kb/log.md`**, recording the ingest. + +The exact set of pages depends on your answer in step 3, and on the agent: two runs of this +tutorial do not produce word-for-word the same wiki. What is always the same is that every page is +tied back to the file in `raw/`. + +## 7. Ask a question + +Start a new agent session, so that the answer can only come from the wiki and not from what the +agent remembers of the ingest, and ask: + +```text +How are the backups of Atlas protected against a fire in the house? +``` + +The answer names the storage box outside the house as the copy that survives, and the 3-2-1 rule +as the reason it exists. It cites the pages it came from and the source behind them. Open one of +the cited pages and follow its footnote: it leads to the source page, and that one to the file in +`raw/` - the notes you copied in step 1. + +Now ask something the notes do not answer: + +```text +How long does a full restore of Atlas take? +``` + +The notes say only that one has never been tried. The agent tells you that the wiki has no source +for this, rather than estimating one. That is the rule behind every answer: no claim without a +source. + +## 8. Optional: browse it in Obsidian + +Open the instance's root folder as a vault in [Obsidian](https://obsidian.md). The graph view shows +the pages you just made and the links between them. Read there as much as you like, but change +pages through the agent: [Browse the wiki in Obsidian](../how-to/browse-with-obsidian.md) explains +why. + +## What next + +You have taken one source all the way from a file to a cited answer. From here on, the how-to +guides cover the everyday tasks: + +- [Ingest a source](../how-to/ingest-a-source.md) - your own files, folders and web pages +- [Query the wiki](../how-to/query-the-wiki.md) - and search it yourself +- [Maintain the wiki](../how-to/maintain-the-wiki.md) - the lint the agent suggests every ten + sources diff --git a/docs/tutorials/sample-source.md b/docs/tutorials/sample-source.md new file mode 100644 index 0000000..e0c7419 --- /dev/null +++ b/docs/tutorials/sample-source.md @@ -0,0 +1,48 @@ +# Home lab backups - how they work now + +Notes written after reworking the backups in September 2026. This file is the sample source for +the tutorial "From your first source to your first answer"; the machine and its setup are made +up for it. + +## The machine + +**Atlas** is the small file server in the hall cupboard: a fanless mini PC with two 4 TB disks +in a mirror, running Debian 13. It holds the family photo archive, scanned documents and the +configuration of every other machine in the house. Everything else on the network is replaceable; +what is on Atlas is not. + +## The tool + +Backups are made with **restic**, an open-source backup program. restic splits files into chunks, +stores each chunk only once and encrypts everything before it leaves the machine, so the backup +target never sees a file name or a byte of content in the clear. A restic *repository* is the +storage location it writes to; a *snapshot* is one backup run. + +restic was chosen over plain `rsync` for two reasons: deduplication keeps the daily runs small, +and encryption makes it safe to put a copy on storage someone else operates. + +## How it is set up + +Atlas runs restic every night at 02:30 from a systemd timer. Each run writes one snapshot to two +repositories: + +- a USB disk that stays plugged into Atlas, for fast restores; +- a rented storage box at a hosting provider, reached over SFTP, as the copy outside the house. + +Snapshots are thinned out by `restic forget` after each run: the last 7 daily, 5 weekly and +12 monthly snapshots are kept. + +## Why two targets + +The setup follows the **3-2-1 backup rule**: keep three copies of the data, on two different +kinds of storage, with one copy off site. Here the three copies are the mirror on Atlas itself, +the USB disk, and the storage box. A fire or a burglary takes the first two; the storage box is +what is left. + +The mirror alone does not count as a backup. It protects against a dead disk, but a deleted or +encrypted file is deleted or encrypted on both disks at once. + +## What is still open + +The restore has been tried once, for a single folder, from the USB disk. It worked and took +under a minute. A full restore from the storage box has not been tried. diff --git a/instructions/dev/doc-pull-through.md b/instructions/dev/doc-pull-through.md index 1d85dc1..6a2694d 100644 --- a/instructions/dev/doc-pull-through.md +++ b/instructions/dev/doc-pull-through.md @@ -41,7 +41,7 @@ touched; a row that does not apply needs no action. | A task-tracker adapter (`tools/chemenu/tasks/`), its recorded fixtures, or the live suite | [instructions/dev/tracker-testing.md](tracker-testing.md), and `MANIFEST.json` beside the fixtures when they were re-recorded | | A skill's own step sequence or catalogue | The skill's `SKILL.md` source under `instructions//` or `instructions/dev//`; for a shipped skill (not under `instructions/dev/`) whose name or `description` changed, or one added or removed, `wikitool docs reference --apply` for the skill catalogue `docs/reference/skills.md` | | A per-checkout configuration file an instance owns (`.wikitool-tasks.json`, `.wikitool-telemetry.json`, `.wikitool-remotes.json`, `.wikitool-upload.json`) | [docs/reference/configuration.md](../../docs/reference/configuration.md), where an operator looks the shape up - written by hand, since nothing generates it; the [setup-instance.md](../setup-instance.md) decision point that offers it during setup; and `doctor`'s own row in [tools/CONTRACT.md](../../tools/CONTRACT.md), since `doctor` is what reports the file's state | - | An installation instruction - [preflight.md](../preflight.md), [setup-instance.md](../setup-instance.md), [bootstrap.md](../bootstrap.md), [upgrade-instance.md](../upgrade-instance.md) - or `tools/prerequisites.txt` | [INSTALL.md](../../INSTALL.md), the human guide to the same procedure - for `upgrade-instance.md`, [docs/how-to/update-an-instance.md](../../docs/how-to/update-an-instance.md) instead. Read it against the instruction: what to prepare, the sentence for the agent, what the agent asks, where it stops and why. `docs verify` checks only the two enumerable overlaps - the prerequisites lists, which `wikitool docs prerequisites --apply` regenerates from the manifest, and the setup questions: a question the agent asks the user carries `` where it is asked in `setup-instance.md`, and `INSTALL.md` § "Was der Agent dich fragt" names it with the same marker. Every other sentence is this session's to compare. `INSTALL.md` does not retell the steps, so a change to their order or wording alone moves nothing there | + | An installation instruction - [preflight.md](../preflight.md), [setup-instance.md](../setup-instance.md), [bootstrap.md](../bootstrap.md), [upgrade-instance.md](../upgrade-instance.md) - or `tools/prerequisites.txt` | [INSTALL.md](../../INSTALL.md), the human guide to the same procedure - for `upgrade-instance.md`, [docs/how-to/update-an-instance.md](../../docs/how-to/update-an-instance.md) instead. Read it against the instruction: what to prepare, the sentence for the agent, what the agent asks, where it stops and why. `docs verify` checks only the two enumerable overlaps - the prerequisites lists, which `wikitool docs prerequisites --apply` regenerates from the manifest, and the setup questions: a question the agent asks the user carries `` where it is asked in `setup-instance.md`, and `INSTALL.md` § "What the agent asks you" names it with the same marker. Every other sentence is this session's to compare. `INSTALL.md` does not retell the steps, so a change to their order or wording alone moves nothing there | | [dev-setup.md](dev-setup.md), the release workflow (`version bump`/`version release`/`publish`/CI), or another developer-facing process | [docs/dev/development.md](../../docs/dev/development.md), read against it the same way - nothing checks this pair at all. A page under `docs/dev/` is never shipped, so it may link into `instructions/dev/` and cite issue numbers | | A new page type the stack requires, or a new collection | Its type-spec and `COLLECTION.md` (both as the `.template` an instance adopts), the collection table in [kb/CONTRACT.md](../../kb/CONTRACT.md), and **both adoption paths**: [setup-instance.md](../setup-instance.md) for a fresh instance and [upgrade-instance.md](../upgrade-instance.md) for an existing one, where an unadopted template is what `docs verify` refuses. A new or changed `root: kb` type-spec or its schema - description, `layout:`, subtype enum, required fields - also needs `wikitool docs reference --apply` for `docs/reference/page-types.md` | diff --git a/tools/CONTRACT.md b/tools/CONTRACT.md index e2604b2..edc567c 100644 --- a/tools/CONTRACT.md +++ b/tools/CONTRACT.md @@ -2651,7 +2651,7 @@ Regenerate `INSTALL.md`'s prerequisites lists from `tools/prerequisites.txt`. **NOTES** -- Rewrites each `` region in `INSTALL.md` (tools every platform needs) and `` region (tools only that platform needs) from the manifest: one list item per tool, its label and minimum version. The manifest's reason field stays out - it is English prose, and the region sits in a document that need not be. +- Rewrites each `` region in `INSTALL.md` (tools every platform needs) and `` region (tools only that platform needs) from the manifest: one list item per tool, its label and minimum version. The manifest's reason field stays out - it explains a tool to whoever reads the preflight's own output, while the region only says what to install. - Never places a region: where a list belongs in the human guide is that guide's own decision. A region the manifest calls for but the file lacks is an error naming the marker pair to add. - Dry-run by default (says whether the file would change); `--apply` writes. - `docs verify` checks the result stays current. diff --git a/tools/chemenu/commands/docs_verify.py b/tools/chemenu/commands/docs_verify.py index b20d114..549617e 100644 --- a/tools/chemenu/commands/docs_verify.py +++ b/tools/chemenu/commands/docs_verify.py @@ -1555,8 +1555,8 @@ def toc_command( "Rewrites each `` region in `INSTALL.md` (tools every " "platform needs) and `` region (tools only that " "platform needs) from the manifest: one list item per tool, its label and minimum " - "version. The manifest's reason field stays out - it is English prose, and the region " - "sits in a document that need not be.", + "version. The manifest's reason field stays out - it explains a tool to whoever reads " + "the preflight's own output, while the region only says what to install.", "Never places a region: where a list belongs in the human guide is that guide's own " "decision. A region the manifest calls for but the file lacks is an error naming the " "marker pair to add.", diff --git a/tools/chemenu/install_doc.py b/tools/chemenu/install_doc.py index c8c887a..a901b9f 100644 --- a/tools/chemenu/install_doc.py +++ b/tools/chemenu/install_doc.py @@ -1,28 +1,31 @@ """The two places `INSTALL.md` overlaps the installation instructions, held to them. -`INSTALL.md` is written for a human and in the instance's KB language; -`instructions/setup-instance.md` is written for an agent and in English. A -single file serving both was rejected: an agent reads every sentence as an -instruction, and the human needs preparation and decisions where the agent -needs steps and exit codes. So the human document does not retell the -procedure, and what it still shares with the instructions is two enumerable -lists - both checked here, so neither can drift silently: +`INSTALL.md` is written for a human, `instructions/setup-instance.md` for an +agent; both in English (`AGENTS.md` § File naming). A single file serving +both was rejected: an agent reads every sentence as an instruction, and the +human needs preparation and decisions where the agent needs steps and exit +codes. So the human document does not retell the procedure, and what it still +shares with the instructions is two enumerable lists - both checked here, so +neither can drift silently: 1. **What the machine needs.** `tools/prerequisites.txt` is the one list; the preflight and `doctor` read it. `INSTALL.md` carries it as a generated region per platform value (`` for `all`, `` otherwise), rendered from the - label and minimum version only. The manifest's `why` field stays out: it is - English prose, and the region sits in a document that is not. A region is - never placed by the tool - where it goes is the human document's own - decision - so a missing one is reported, not appended. + label and minimum version only. The manifest's `why` field stays out: it + explains a tool to whoever reads the preflight's own output, while the + region only says what to install. A region is never placed by the tool - + where it goes is the human document's own decision - so a missing one is + reported, not appended. 2. **What the agent asks the user.** Every place `setup-instance.md` puts a question to the user carries ``, and the - bullet in `INSTALL.md` that tells the human about it carries the same - marker. The two key sets must be equal: a question the human guide does not - mention catches them unprepared, and one it mentions that is no longer asked - is a claim about a procedure that no longer exists. The key is the shared - identifier precisely because the surrounding prose is in two languages. + bullet in `INSTALL.md` § "What the agent asks you" that tells the human + about it carries the same marker. The two key sets must be equal: a + question the human guide does not mention catches them unprepared, and one + it mentions that is no longer asked is a claim about a procedure that no + longer exists. The key is the shared identifier because the two texts word + the same question for different readers, and neither wording is stable + enough to match on. Everything else in `INSTALL.md` is prose no check reads; `instructions/dev/doc-pull-through.md` names it as session work. @@ -37,6 +40,10 @@ from chemenu import blocks, config, prerequisites INSTALL_DOC = "INSTALL.md" SETUP_INSTRUCTION = "instructions/setup-instance.md" +# The heading in INSTALL.md whose bullets carry the setup-question markers - +# named in check_setup_questions' finding, and held to the real file by a test. +QUESTIONS_SECTION = "What the agent asks you" + REGION_PREFIX = "prerequisites" QUESTION_RE = re.compile(r"") @@ -101,7 +108,7 @@ def refresh(text: str, manifest: prerequisites.Manifest) -> tuple[str, list[str] A region the manifest no longer has a platform for is left alone and reported by `check_prerequisite_regions`, not deleted here: the prose - introducing it ("Unter Windows zusätzlich:") would otherwise be left + introducing it ("On Windows, additionally ...") would otherwise be left standing over nothing. """ missing = [] @@ -166,8 +173,8 @@ def check_setup_questions() -> list[str]: named = question_keys(install.read_text(encoding="utf-8")) issues = [ - f"{SETUP_INSTRUCTION} asks the user `{key}`, but {INSTALL_DOC} § \"Was der Agent dich " - f"fragt\" does not name it - add a bullet carrying ``" + f"{SETUP_INSTRUCTION} asks the user `{key}`, but {INSTALL_DOC} § \"{QUESTIONS_SECTION}\" " + f"does not name it - add a bullet carrying ``" for key in sorted(asked - named) ] issues += [ diff --git a/tools/chemenu/tests/test_install_doc.py b/tools/chemenu/tests/test_install_doc.py index 4873252..83dcf5c 100644 --- a/tools/chemenu/tests/test_install_doc.py +++ b/tools/chemenu/tests/test_install_doc.py @@ -16,22 +16,22 @@ MANIFEST = prerequisites.Manifest( CURRENT = """# Installation -Vorher: +You need: - **Python** ≥ 3.11 - **Git** -Unter Windows zusätzlich: +On Windows, additionally: - **PowerShell 7 (pwsh)** ≥ 7 -## Was der Agent dich fragt +## What the agent asks you -- **Autor-Identität** - Name und E-Mail. +- **Author identity** - name and email. """ SETUP = """# Set up @@ -57,6 +57,14 @@ def test_this_repos_install_doc_names_every_setup_question(): assert install_doc.check_setup_questions() == [] +def test_this_repos_install_doc_has_the_section_the_finding_names(): + """A missing question is reported as missing from a named section; that + name is a constant, and a renamed heading would leave the finding pointing + at nothing.""" + text = install_doc.install_doc_path().read_text(encoding="utf-8") + assert f"\n## {install_doc.QUESTIONS_SECTION}\n" in text + + def test_this_repos_setup_instruction_marks_its_questions(): """The check compares two sets; an instruction that lost every marker would compare empty against empty and pass. Guard the real file's own count.""" @@ -133,10 +141,11 @@ def test_a_question_the_install_doc_does_not_name_fails(tmp_path, monkeypatch): issues = install_doc.check_setup_questions() assert len(issues) == 1 assert "`remote`" in issues[0] and "does not name it" in issues[0] + assert f'§ "{install_doc.QUESTIONS_SECTION}"' in issues[0] def test_a_question_no_longer_asked_fails(tmp_path, monkeypatch): - install = CURRENT + "- **Telemetrie**\n" + install = CURRENT + "- **Telemetry**\n" _tree(tmp_path, monkeypatch, install=install) issues = install_doc.check_setup_questions() assert len(issues) == 1