operations_research/CLAUDE.md
dschlueter 3d85488d7c Dokumentation nachgezogen - und dabei den neunten Fund gemacht
Der Leser-Wegweiser Operations_Research_mit_Python_Version_04/README.md, die
Datei, die Leser als erstes oeffnen, beschrieb noch die Struktur der
Version 03:

* 12 der 15 Kapitelnummern falsch. "20_Lineare_Programmierung.md | 4" - es ist
  Kapitel 5. Bei den Finanzkapiteln lag die Abweichung bei sieben: README 11,
  tatsaechlich 18.
* 8 der 23 Kapitel fehlten ganz (Metaheuristiken, Spaltengenerierung,
  Mehrziel, Predict-then-Optimize, Testing und drei weitere).
* 93_Anhang_Spickzettel.md fehlte in der Anhangtabelle; der Bauabschnitt nannte
  dreimal build_version_03.py statt _04 und beschrieb --html als
  "Single-Page-HTML", obwohl es laengst eine mehrseitige Website erzeugt.

Dieselbe Regel wie acht Mal zuvor, dieselbe Behandlung: Die Tabelle wird
erzeugt, nicht korrigiert. schreibe_kapiteltabelle() ersetzt den Block
zwischen den KAPITELTABELLE-Marken aus der Struktur, die
baue_seiten_registry() ohnehin ermittelt - derselben Quelle, aus der die
Nummern im Buch stammen. --check meldet einen veralteten Block und bricht ab.
Gegenprobe: alle 37 Dateien aus DATEIEN stehen in der Uebersicht.

Weiter nachgezogen:

* CLAUDE.md kannte veroeffentliche_04.py nicht und nannte .env.beispiel nicht.
  Der Abschnitt "Veroeffentlichung" beschrieb noch das Kopieren von Hand samt
  curl-Kontrolle - das erledigt jetzt das Skript.
* Fundzahl der Nummern-Regel: CLAUDE.md sagte sieben, README acht, PROGRESS
  dokumentiert einen "Achter Fund". Jetzt ueberall neun.
* Diagrammzahl: CLAUDE.md zaehlt 32 Kapiteldiagramme, README 33 - der
  Unterschied ist titelseite.svg. Das steht jetzt dabei.

Nebenbei am Upload-Skript: rsync bekommt --checksum. Der Bau schreibt alle 42
Seiten bei jedem Lauf neu, auch ohne inhaltliche Aenderung; nach Zeitstempel
und Groesse waeren das jedes Mal rund 19 MB sinnlose Uebertragung. Gemessen
nach einem Neubau ohne Aenderung: 0 statt 151 Dateien.

Website unveraendert - kein Upload noetig, der Probelauf meldet 0 zu
uebertragen und 0 zu loeschen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 19:13:34 +02:00

220 lines
12 KiB
Markdown

# CLAUDE.md
Kontext zum Repository *Optimierte Entscheidungsfindung mit Python*, Version 04.
## Vor jeder Arbeit zuerst lesen
* **`PLAN.md`** — das Ziel und der Weg dorthin: alle sechs Phasen, die Zielstruktur, der
Kapitel-Baukasten, die dreizehn verbindlichen Regeln, die Verifikationsschritte. Ändert
sich nur bei einem Kurswechsel.
* **`PROGRESS.md`** — der erreichte Stand: eine Checkliste zur Wiederaufnahme, die
Referenzwerte zum Gegenprüfen, was erledigt ist, welche Funde es unterwegs gab und **was
der konkret nächste Schritt ist**. Wird nach jedem Arbeitsschritt fortgeschrieben.
Ohne diese beiden Dateien fehlt der Kontext, um sinnvoll weiterzuarbeiten — die Kapiteltexte
allein verraten weder den Stand noch die Konventionen. Alle sechs Phasen sind abgeschlossen;
`PROGRESS.md` Abschnitt 7 nennt, was bewusst offen geblieben ist und warum.
---
## Struktur
Dieses Verzeichnis ist die Wurzel; alle Skripte leiten ihre Pfade daraus ab
(`BASIS = dirname(dirname(__file__))` bzw. `dirname(HIER)`).
```
Operations_Research_mit_Python_Version_04/ Quelle: 37 Kapiteldateien + Build-Skripte
bilder_04/ Quelle: Diagramme + erzeuge_*.py-Generatoren
web_04/ Quelle: site.css, site.js, icons.svg,
plotly.min.js, katex/ - die statischen
Bestandteile der Website
Operations_Research_mit_Python_Version_04.md generiert: Gesamtdokument
Operations_Research_mit_Python_Version_04.pdf generiert: PDF (xelatex)
OR_HTML_04/ generiert: Mehrseiten-Website
Operations_Research_mit_Python_Version_04_Programme/ generiert: Beispielprogramme
Notebooks_04/ generiert: ein .ipynb je Kapitel
Kritik_und_Verbesserungsvorschlaege/ Quelle: Rezension, Verbesserungsvorschläge,
NEUER_TITEL.md (Vorlage des Titelblatts)
pyproject.toml Quelle: Abhängigkeiten in Gruppen
Dockerfile, .dockerignore Quelle: Kurs-Image (Programme + JupyterLab)
LICENSE, LICENSE-TEXT.md MIT fuer Code, CC BY-SA 4.0 fuer den Text
.gitattributes LF im Repository, egal auf welchem System
.env.beispiel Vorlage; die echte .env (Ziel des Uploads)
ist bewusst NICHT im Repository
pandoc-defaults-basis.yaml, pandoc/, pandoc-defaults-buch.yaml PDF-Konfiguration
```
Nur die als **Quelle** markierten Verzeichnisse werden von Hand bearbeitet. Seit dem
Umzug nach `web_04/` stimmt dieser Satz auch: Vorher lagen `site.css`, `site.js`,
`icons.svg` und `katex/` mitten im erzeugten `OR_HTML_04/`. `--check` bewacht das jetzt
(`pruefe_assets()`), und der Bau bricht ab, wenn jemand die Kopie statt der Quelle
bearbeitet hat.
Dieses Verzeichnis ist seit dem 08.09.2026 ein **eigenes Git-Repository** (`main`). Das
übergeordnete `OR_mit_Python/` ist nur noch das Archiv der Historie bis zur Trennung und
verwaltet aktiv allein `Version_03`; es trägt `Version_04/` in seiner `.gitignore`. Commits
gehören ab jetzt hierher.
## Build
```bash
cd Version_04
python3 Operations_Research_mit_Python_Version_04/build_version_04.py --check # nur prüfen
python3 Operations_Research_mit_Python_Version_04/build_version_04.py --pdf --html
python3 Operations_Research_mit_Python_Version_04/extract_programme_04.py
python3 Operations_Research_mit_Python_Version_04/veroeffentliche_04.py # hochladen
```
`veroeffentliche_04.py` lädt `OR_HTML_04/` per `rsync --delete` nach <https://jamulix.de/OR/>
**nicht** per `scp`, denn das überschreibt nur und löscht nie; eine aus dem Buch entfernte
Seite bliebe sonst dauerhaft online. Vorher prüft es, ob eine Quelldatei neuer ist als die
gebaute Website (die Website hing einmal wochenlang einen Bau zurück, ohne dass es auffiel),
hinterher ruft es vierzehn Adressen per HTTP ab. Bei mehr als 30 Löschungen bricht es ab —
die Liste ist dann anzusehen, bevor `--loeschgrenze N` sie freigibt. Ziel in `.env`
(nicht im Repository, Vorlage `.env.beispiel`); Anmeldung per SSH-Schlüssel, kein Passwort.
`--check` prüft mehr als die Struktur: fehlende Codezäune, Links auf nicht existierende
Dateien und **harte Kapitel-/Abschnitts-/Aufgabennummern im Quelltext**. Es meldet Datei und
Zeile. Der Suchausdruck erlaubt beliebigen Zwischenraum — der letzte gefundene Fall war ein
`Handrechnung\n12.1` über zwei Zeilen, an dem jede zeilenweise Suche vorbeiläuft.
`build_version_04.py` fasst nicht notwendige harte Zeilenumbrüche innerhalb von Absätzen
zusammen (`reflow_markdown()`) — viele Markdown-Renderer stellen einen einzelnen Umbruch
sonst fälschlich als sichtbaren Umbruch dar.
---
## Die wichtigsten Konventionen
**Keine abgeleiteten Zahlen im Quelltext.** Das ist die Regel, an der dieses Projekt am
häufigsten gescheitert ist — **neun** Mal an verschiedenen Stellen verletzt gefunden
(Anhang A, 66 Programm-Docstrings, die Übersichtstabellen der Anhänge, `CLAUDE.md` selbst,
eine Tabellenzelle, die 98 Lösungsmarken des Anhangs, neun Denkfehler-Verweise, sieben
„Übung N.N"-Verweise in alter Zählung, zuletzt die Dateiübersicht des Leser-Wegweisers).
Seit dem letzten Fund trägt **keine** Marke mehr eine handgeschriebene Nummer, und `--check`
bewacht jede Familie. Konkret:
* Überschriften: `# Kapitel: <Titel> {#kap:<label>}`, `## Titel {#sec:<label>}` — die Nummer
vergibt der Build (`resolve_numbering()`, `nummeriere_abschnitte()`).
* Aufgaben, Handrechnungen, Abbildungen, Micro-Quiz: `**Aufgabe ⭐ — Titel.**`,
`> **✏️ Handrechnung: Titel**`, `![Bildunterschrift](bilder_04/…)`,
`> **❓ Micro-Quiz: Titel**` — Nummern von `nummeriere_marken()`.
* **Lösungen in Anhang A: `**{loesung} — Titel.**`** Der Präfix kommt nicht aus
`# Anhang A:`, sondern aus dem Kapitel des umgebenden `{#sec:loesungen-<X>}`-Abschnitts —
die Zuordnung `sec:loesungen-<X>` ↔ `kap:<X>` gilt für alle 23. `--check` zählt zusätzlich
ab, dass es je Kapitel so viele Lösungen wie Aufgaben gibt.
* **Der Denkfehler bekommt bewusst gar keine Nummer.** Es gibt je Kapitel genau einen, unter
einer bereits nummerierten Überschrift. Verwiesen wird auf
`{ref:sec:<kapitel>-denkfehler}`.
* Im Fließtext `{ref:<label>}`, nie eine Literalzahl.
* **Die Dateiübersicht in `Operations_Research_mit_Python_Version_04/README.md`** steht
zwischen `<!-- KAPITELTABELLE:ANFANG -->` und `<!-- KAPITELTABELLE:ENDE -->` und wird von
`schreibe_kapiteltabelle()` aus derselben Struktur erzeugt wie die Nummern im Buch. Nicht
von Hand bearbeiten — `--check` meldet einen veralteten Block, ein Lauf ohne `--check`
zieht ihn nach.
* In Code-Kommentaren und Docstrings der Kapitel**name** („Kapitel Metaheuristiken:"), nie
die Nummer. Auch nicht im Dateinamen.
**Querverweise für die Website** laufen über `baue_seiten_registry()` /
`resolve_numbering_seite()`, nicht über `resolve_numbering()` — nur dort entsteht aus einem
seitenübergreifenden Verweis `andere-seite.html#anker`.
**Stichwortregister:** `{idx:Begriff}` bzw. `{idx:Oberbegriff!Unterbegriff}`. Vor einem neuen
Begriff prüfen, ob er schon existiert:
`grep -ohE '\{idx:[^}]+\}' Operations_Research_mit_Python_Version_04/*.md | sort -u` — sonst
entstehen zwei Registereinträge für dasselbe Konzept. `texindy` sortiert mit dem deutschen
`din5007`-Modul (Umlaute wie im Telefonbuch); das generische `-L german` bricht ab.
**Programme sind ein Artefakt, keine zweite Quelle.** Änderungen gehören in den
Kapitel-Codeblock, danach `extract_programme_04.py`. Ein Codeblock gilt als vollständiges
Programm, wenn er mit `#!/usr/bin/env python3`, einer Leerzeile und `# Name.py` beginnt.
Auch das `README.md` im Programme-Verzeichnis wird erzeugt — aus der Vorlage
`README_Programme.md`; `requirements.txt` ist die einzige dort von Hand gepflegte Datei.
**`or_kern.py` ist der gemeinsame Unterbau** aller Programme (Domänenmodell, `SolverStatus`,
`Loesung`-DTO, Abnahmeprüfung). Abgedruckt im Kapitel Praxisfallen.
**`ortools` und `highspy` lassen sich nicht im selben Prozess importieren** (beide bringen
eine eigene HiGHS-Kopie mit). Deshalb lädt `or_kern.py` Solverbibliotheken erst in der
aufrufenden Funktion, und Programme, die beide brauchen, starten getrennte Prozesse (Muster:
`Ein_System_Vier_Ansaetze.py`, `Solverwechsel_CPSAT_HiGHS.py`). `cvxpy` zieht ein
installiertes `highspy` bei der Solver-Erkennung selbst mit hinein — der Konflikt entsteht
also auch indirekt.
**Neue Kapiteldatei** ⇒ in die `DATEIEN`-Liste in `build_version_04.py`.
**Neues Programm** ⇒ drei Stellen: Kapitelkopf („Programme:"), Vorwort
(„Verzeichnis der Beispielprogramme"), bei neuer Abhängigkeit `requirements.txt` **und**
`pyproject.toml` (dort in die passende Gruppe, nicht pauschal in die Grundausstattung).
Die vollständigen **dreizehn Regeln** stehen in `PLAN.md` Abschnitt 9 — darunter, dass jede
abgedruckte Ausgabe aus einem echten Lauf stammt und dass `PROGRESS.md` in denselben Commit
gehört wie die Arbeit, die sie beschreibt.
---
## Diagramme
`bilder_04/erzeuge_*.py` erzeugen 18 der 32 Kapiteldiagramme, jeweils **aus derselben
Instanz wie das zugehörige Buchprogramm**. (Das 33. SVG im Verzeichnis ist `titelseite.svg`,
die der Build bei jedem Lauf selbst schreibt — deshalb nennt `README.md` 33 und diese Datei
32.) Das ist kein Selbstzweck: Von zehn nachgebauten Bildern förderten
sieben einen Fehler zutage — dreimal ein Modell, das im Buch gar nicht vorkommt, einmal
widersprüchliche Zahlen zwischen Bild und Text, einmal ein gekipptes Vorzeichen. Wer ein
Diagramm anfasst, vergleicht es zuerst mit dem Modell des Kapitels.
Die übrigen 14 sind schematisch (Kästen, Pfeile, beschriftete Formeln) und bekommen bewusst
keinen Generator — dort kann nichts driften. Das Kriterium steht in `PROGRESS.md`
Abschnitt 6c.
Konventionen der Generatoren: `plt.rcParams["svg.hashsalt"] = "or-mit-python-v04"` und
`metadata={"Date": None}` für byteidentische Läufe — **beides, sonst trägt das SVG einen
Zeitstempel und wechselnde `clip-path`-IDs**. Geschrieben wird **nur SVG**; PNG-Zweitfassungen
gab es einmal, sie wurden nirgends referenziert; stammt die Instanz aus einem
Zufallsstrom, wird die Ziehungsreihenfolge des Buchprogramms nachgespielt.
## Plotly-Figuren
`{plotly:name}` bindet `bilder_04/plotly/<name>.html` in die Kapitelseite ein. Das Fragment
wird als Rohblock ` ```{=html} ` ausgegeben — **nicht** als blankes HTML: Plotlys Fragment ist
eine einzige lange Zeile mit eingebetteten Leerzeichenketten, aus der Pandoc sonst einen
Codeblock macht. Genau daran waren alle vier Figuren kaputt, bis es die Schlussabnahme fand.
Im PDF steht stattdessen ein Hinweis auf die Website.
---
## Veroeffentlichung
Das Buch steht online auf <https://jamulix.de/OR/>, das Repository oeffentlich auf
<https://kitux.de/forgejo/dschlueter/operations_research>.
Die Website hat vier Übersichtsseiten: `index.html` (Landing), `programme.html`,
`notebooks.html` und `stichwortverzeichnis.html`. Die beiden Downloadverzeichnisse
`programme/` und `Notebooks_04/` bekommen je eine `index.html`, die auf ihre Übersicht
weiterleitet — sonst antwortet der Webserver mit 403, weil Directory-Listing abgeschaltet
ist.
**Nach jedem `--pdf --html` gehoert `veroeffentliche_04.py` hinterher** — sonst laeuft die
veroeffentlichte Fassung dem Repository hinterher, und genau das ist einmal wochenlang
unbemerkt geblieben. Von Hand muss dabei nichts mehr geprueft werden: Das Skript weigert
sich, einen veralteten Bau hochzuladen, und ruft anschliessend vierzehn Adressen ab.
Der erste Lauf am 08.09.2026 uebertrug 213 Dateien und loeschte 35 — 33 verwaiste PNGs, ein
leeres `__pycache__/` und die geteilte Anhangseite. Alle 35 haette ein `scp`-Upload stehen
gelassen.
**`COLAB_BASIS_URL` ist bewusst leer.** Colab oeffnet Notebooks nur aus GitHub, Google Drive
oder einem Upload; die URL-Form `colab.research.google.com/github/...` ist fest auf GitHub
verdrahtet und kann eine selbstgehostete Git-Instanz nicht lesen. Statt eines toten Knopfes
bekommt jedes Kapitel einen Download-Link auf sein Notebook. Wer spaeter nach GitHub
spiegelt, traegt dort **einen** Wert ein.
Wer ohne eigene Installation rechnen will, nimmt das Kurs-Image:
`docker run --rm -p 127.0.0.1:8888:8888 or-mit-python` startet JupyterLab mit allen
25 Notebooks. Ohne Portfreigabe und mit einem Programmnamen als Argument fuehrt dasselbe
Image ein einzelnes Programm aus.
---
## Sprache
Kommentare, Ausgaben und Fließtext sind durchgängig **Deutsch** — diesen Stil beibehalten.