Optimierung: konsistenz, Skills/Commands, Dateihoheit, _print_pages

just konsistenz und Hook-Test; 12 Skills/12 Commands; AGENTS §5–6.
Manuskript, Bilder und Haufen 1 nicht in diesem Commit.
This commit is contained in:
grok-bot committed 2026-10-29 23:01:28 +01:00
1 parent d81ee6d638
commit 8a92268572
24 files changed
+859 -106

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
---
description: Eine Abbildung mit Grok Imagine erzeugen oder nachziehen (nur mit Auftrag für genau diese ID)
argument-hint: "<I-ID> [Befund]"
---
# Abbildung
Der Aufruf ist der Auftrag für genau diese ID. Lade `.grok/skills/abbildung/SKILL.md` und folge ihm.
Vorher: `.grok/skills/bildpruefung/SKILL.md` lesen, wenn ein Prüfbericht existiert (`audits/bildpruefung-*.md`); dessen Befund für die ID gilt.
Nur die eine Bilddatei ersetzen (gleicher Name), Manuskript, Captions und Marker nicht ändern.
+7
View File
@@ -0,0 +1,7 @@
---
description: Alle oder einzelne Abbildungen nur berichtend prüfen (Grok 4.7, Effort low)
argument-hint: "[alle|I-ID …]"
---
# Bildprüfung
Lade `.grok/skills/bildpruefung/SKILL.md` und folge ihm. Ohne Argument: alle Marker im Manuskript. Nur berichten, nichts ändern.
+6
View File
@@ -0,0 +1,6 @@
---
description: KDP-Vorabprüfung (Träger, STALE, QA, Seiten ↔ Umschlag, offene AEs), nur prüfend
---
# KDP-Vorab
Lade `.grok/skills/kdp-vorab/SKILL.md` und folge ihm. Kein Upload, keine Edits, Bauen nur mit Auftrag.
+7
View File
@@ -0,0 +1,7 @@
---
description: Konsistenz-Check (nur lesend) – Kapitel, Wörter, Bilder, Stichtag, Auflage, Seiten/Umschlag
argument-hint: "[--formate|--strict]"
---
# Konsistenz
Lade `.grok/skills/konsistenz/SKILL.md`. Führe `just konsistenz` aus, ein übergebenes Argument (`--formate`, `--strict`) anhängen, und werte nach dem Skill aus. Keine Edits.
+36 -3
View File
@@ -1,6 +1,8 @@
# Project-scoped Grok config for „Die KI hinter dem Vorhang“.
# Only [permission] / [mcp_servers] / [plugins] are honored here.
# No secrets. User MCP lives in ~/.grok/config.toml.
# Dateihoheit (AGENTS.md): research/, assets/, Bilder, Prompts, Marketing nur mit Auftrag → ask.
# History-Schutz: kein Force-Push, kein reset --hard, kein --no-verify, Stash tabu → deny.
[permission]
# Normal book workflow
@@ -21,10 +23,15 @@ allow = [
"Bash(grok inspect*)",
"Read",
"Grep",
"Bash(git tag)",
"Bash(git tag -l*)",
"Bash(git rev-list*)",
"Bash(git merge-base*)",
"Bash(python3 scripts/konsistenz_check.py*)",
"Bash(sh scripts/test_hook.sh)",
"Edit(manuskript/**)",
"Edit(research/**)",
"Edit(abbildungen/**)",
"Edit(assets/**)",
"Edit(HANDOFF.md)",
"Edit(audits/**)",
]
# Hard stops
@@ -36,6 +43,24 @@ deny = [
"Bash(git reset --hard*)",
"Bash(git clean -fd*)",
"Bash(git clean -f*)",
"Bash(git push --force-with-lease*)",
"Bash(git push * --force*)",
"Bash(git push * -f*)",
"Bash(git commit --no-verify*)",
"Bash(git commit * --no-verify*)",
"Bash(git commit --amend*)",
"Bash(git rebase*)",
"Bash(git filter-branch*)",
"Bash(git stash)",
"Bash(git stash push*)",
"Bash(git stash pop*)",
"Bash(git stash drop*)",
"Bash(git stash clear*)",
"Bash(git stash apply*)",
"Bash(git branch -D*)",
"Bash(git tag -d*)",
"Bash(git checkout -- .*)",
"Bash(git restore .*)",
]
# Confirm before shared or destructive-ish actions
@@ -48,4 +73,12 @@ ask = [
"Edit(justfile)",
"Edit(.grok/config.toml)",
"Edit(devbox.json)",
"Edit(research/**)",
"Edit(assets/**)",
"Edit(abbildungen.md)",
"Edit(abbildungen-korrekturen.md)",
"Edit(prompts/**)",
"Edit(marketing/**)",
"Edit(AGENTS.md)",
"Edit(.grok/**)",
]
+17 -4
View File
@@ -1,5 +1,6 @@
#!/bin/sh
# PreToolUse safety net: block clearly destructive shell patterns.
# Greift auch im yolo-Modus. Regeln: AGENTS.md § Git und yolo-Grenzen.
# Fail-open on parse errors is avoided by always emitting a decision JSON.
INPUT=$(cat)
@@ -15,11 +16,23 @@ if [ -z "$CMD" ]; then
exit 0
fi
# Destructive / history-rewriting patterns
if printf '%s' "$CMD" | grep -qE '(rm[[:space:]]+-rf[[:space:]]+/|git[[:space:]]+push[[:space:]]+(-f|--force)|git[[:space:]]+reset[[:space:]]+--hard|git[[:space:]]+clean[[:space:]]+-f|mkfs\.|dd[[:space:]]+if=)'; then
printf '%s\n' '{"decision":"deny","reason":"Blocked destructive git/shell pattern (protect-pipeline hook). Use a safer alternative or run manually."}'
deny() {
printf '{"decision":"deny","reason":"protect-pipeline: %s. Sicherere Alternative nutzen oder Richie fragen."}\n' "$1"
exit 2
fi
}
S='[[:space:]]'
# Shell-Zerstörung
printf '%s' "$CMD" | grep -qE "rm$S+-rf$S+/|mkfs\.|dd$S+if=" && deny "destruktiver Shell-Befehl"
# History umschreiben / Arbeit verwerfen
printf '%s' "$CMD" | grep -qE "git$S+push.*($S-f($S|$)|$S--force|$S\+[^[:space:]])" && deny "Force-Push"
printf '%s' "$CMD" | grep -qE "git$S+reset$S+--hard" && deny "reset --hard"
printf '%s' "$CMD" | grep -qE "git$S+clean$S+-[a-zA-Z]*f" && deny "git clean -f"
printf '%s' "$CMD" | grep -qE -- "--no-verify" && deny "--no-verify"
printf '%s' "$CMD" | grep -qE "git$S+(rebase|filter-branch)|git$S+commit.*--amend" && deny "History umschreiben"
printf '%s' "$CMD" | grep -qE "git$S+stash($S+(push|pop|drop|clear|apply|save)|$S*($|[;&|]))" && deny "Stash ist tabu"
printf '%s' "$CMD" | grep -qE "git$S+(branch$S+-D|tag$S+-d|branch$S+--delete|tag$S+--delete)" && deny "Branch/Tag löschen"
printf '%s' "$CMD" | grep -qE "git$S+(checkout$S+(--$S+)?\.($S|$)|restore$S+(--staged$S+)?\.($S|$))" && deny "Arbeitsbaum pauschal verwerfen"
printf '%s\n' '{"decision":"allow"}'
exit 0
+12 -10
View File
@@ -24,13 +24,15 @@ Print: `/pdf-print` bzw. `.grok/skills/pdf-print/SKILL.md`
| `just qa-print` | `scripts/check_pdf.py --mode print` | nach `just pdf-print` |
| `just all-print` | pdf-print + qa-print | Release-nahe Prüfung Print |
| `just clean` | build/ und dist/*.pdf löschen | Vor sauberem Neu-Build |
| `just konsistenz` | `scripts/konsistenz_check.py`, nur lesend | Vor Prüfläufen, nach jedem Build, vor HANDOFF |
| `just kdp-cover` | Umschlag mit Rücken aus `_print_pages` | Nach Print-Seitenänderung, nur mit Auftrag |
eBook und Print sind **parallele** Outputs.
## Pipeline-Architektur
```
manuskript/*.md (12 Kapitel + Prolog + Epilog + Glossar + Literatur + Autor + Kolophon)
manuskript/*.md (10 Kapitel + Prolog + Epilog + Glossar + Literatur + Autor + Kolophon)
│
▼ scripts/assemble_book.py
build/book.md (zusammengeführt mit Part-Pages)
@@ -38,7 +40,7 @@ build/book.md (zusammengeführt mit Part-Pages)
├─ eBook ──────────────────────────────────────────────────────────
│ scripts/prepare_ebook_assets.py → build/ebook-assets/assets/… (RGB, ≤240 dpi)
│ pandoc + nexttoken-book.tex + nexttoken-colors.tex (A4, Farbe)
│ → dist/die-ki-hinter-dem-vorhang.pdf (~170 Seiten)
│ → dist/die-ki-hinter-dem-vorhang.pdf
│
└─ Print / KDP ────────────────────────────────────────────────────
scripts/prepare_print_assets.py → build/print-assets/assets/… (Gray)
@@ -73,7 +75,7 @@ just pdf-print
| `scripts/prepare_print_assets.py` | Gray-Stage MD-Pfad-Spiegel |
**KDP-Spec (Interior):** Trim 6×9 in · White Paper · kein Bleed · inner **0,65"** · outer **0,60"** · top **0,70"** · bottom **1,00"** · headsep **0,12"** · footskip **0,30"** (Header-Ink ≈ 0,45″ · Footer-Ink ≈ 0,65″) · Titelseite Cover-Safe-Frame **0,30"** · Figuren Gin **0,87** · Body **Charter 10 pt** · linestretch **1,22** · Box 9 pt. Optional: `just pdf-print-optimize`.
**Cover-PDF** (Bleed/Spine) ist **nicht** Teil dieser Pipeline.
**Cover-PDF** (Bleed/Spine): `just kdp-cover`, Seitenzahl aus `_print_pages`. Ändert sich die Print-Seitenzahl, werden `_print_pages` und Umschlag gemeinsam nachgezogen (nur mit Auftrag).
## Build-Regeln
@@ -85,7 +87,7 @@ just pdf-print
### Nach dem Build
- `just qa` läuft automatisch — QA-Bericht lesen, nicht blind akzeptieren.
- QA-Prüfungen:
- **Seitenzahl:** Sollte bei vollständigem Manuskript ~172 Seiten sein.
- **Seitenzahl:** messen (`pdfinfo`, `just konsistenz`), nicht gegen eine Doku-Zahl prüfen.
- **Grafiken:** Illustrationen eingebettet? Keine Platzhalter mehr?
- **Fonts:** Charter, DejaVu Sans, DejaVu Sans Mono alle eingebettet?
- **Formale Seiten:** Impressum, Kolophon, Inhaltsverzeichnis korrekt?
@@ -104,7 +106,7 @@ just pdf-print
| Pfad | Rolle | Bearbeiten? |
|------|-------|------------|
| `manuskript/*.md` | Quell-Manuskripte (inkl. Autor + Kolophon) | ✅ Ja (Schreiben/Editieren) |
| `manuskript/*.md` | Quell-Manuskripte (inkl. Autor + Kolophon) | ✅ Mit Auftrag (Dateihoheit, `AGENTS.md`) |
| `pdf/templates/nexttoken-book.tex` | KOMA-Script Pandoc-Template (eBook + Print-Switch) | ⚠️ Nur bei explizitem Auftrag |
| `pdf/styles/nexttoken-colors.tex` | Farbpalette eBook | ⚠️ Nur bei Design-Änderungen |
| `pdf/print/*` | Layout/Meta KDP Interior | ⚠️ Nur bei Print-Auftrag |
@@ -116,9 +118,9 @@ just pdf-print
| `scripts/check_pdf.py` | QA eBook + Print (`--mode`) | ⚠️ nur mit Auftrag |
| `scripts/prepare_print_assets.py` | Grayscale-Assets für Print | ⚠️ nur mit Auftrag |
| `scripts/prepare_ebook_assets.py` | RGB-Downscale für A4-eBook (≤240 dpi) | ⚠️ nur mit Auftrag |
| `assets/cover/*.jpg` | Cover-Bild (+ optional `cover-print.jpg`) | ✅ Cover neu generieren |
| `assets/illustrations/**/*.jpg` | Buch-Illustrationen | ✅ Bilddateien ersetzen |
| `assets/parts/*` | Teil-Trenner | ✅ Trenner-Grafiken ersetzen |
| `assets/cover/*.jpg` | Cover-Bild (+ optional `cover-print.jpg`) | ⚠️ Nur mit Auftrag |
| `assets/illustrations/**/*.jpg` | Buch-Illustrationen | ⚠️ Nur mit Auftrag (`/abbildung`) |
| `assets/parts/*` | Teil-Trenner | ⚠️ Nur mit Auftrag |
### Fehler: Print-Bilder bleiben RGB / eBook bleibt unskaliert
@@ -129,8 +131,8 @@ Filter zuerst `build/print-assets/assets/…`. Im eBook-Build zuerst
QA Print: `pdfimages -list` → alle `color=gray`. QA eBook: Figuren ≈ 240 dpi, nicht 330+.
### Smoke-Test nach Portierung
- Assembler: 18 Body-Dateien + 3 Part-Pages ergeben `build/book.md`.
- PDF eBook: `dist/die-ki-hinter-dem-vorhang.pdf` existiert und hat ~170 Seiten.
- Assembler: 16 Body-Dateien (`CHAPTER_ORDER`) + 3 Part-Pages ergeben `build/book.md`.
- PDF eBook: `dist/die-ki-hinter-dem-vorhang.pdf` existiert; Seitenzahl gemessen melden.
- PDF Print: `dist/die-ki-hinter-dem-vorhang-print.pdf` · Media-Box 432×648 · gray images.
- Cover und Illustrationen: `\IfFileExists`-Platzhalter durch echte Assets ersetzt.
- Keine kritischen LaTeX-Warnungen im Log.
+13
View File
@@ -0,0 +1,13 @@
# Sub-Agenten
Gilt für jeden Skill und Workflow, der Arbeit verteilt (faktencheck, lesepass, total-check, bildpruefung).
- **Textarbeit:** Sub-Agenten laufen mit `deepseek-v4_1-flash`. **Kein Effort-Parameter.** Die Modell-ID mit `grok models` bestätigen, die Setzweise aus `grok --help`. Flags nicht erfinden.
- **Bildprüfung:** Grok 4.7 mit Effort `low` (ID mit `grok models` bestätigen). Mehr Effort hilft beim Zählen und Erkennen nicht.
- Ist das Modell nicht wählbar: anhalten und melden. Kein Ausweichmodell. Der Orchestrator bleibt beim Startmodell.
- **Kontext minimal:** Jeder Sub-Agent bekommt nur seine Datei bzw. Zeilen, den passenden Regelauszug und die Ausgabeform. Keine Gesamtregeln, keine Vorberichte, kein ganzes Manuskript.
- **Mechanik per Skript, nicht per Modell:** Zählungen, Kandidatenlisten, Seiten, Bildformate → `just konsistenz`, `rg`, `wc -w`, `pdfinfo`.
- **Zweitprüfer:** frischer Sub-Agent ohne den Erstbefund für jeden P0/P1 und jede Faktenkorrektur. Sind beide uneinig: Status `unsicher`. Nicht mitteln, keine Spanne bilden.
- Sub-Agenten committen, mergen und pushen nie. Sie bekommen die Git-Grenzen aus `AGENTS.md` wörtlich.
- Rückgabe in Zeilen: `datei:zeile | Klasse | Befund | Quelle/Prüffrage | Vorschlag (ungeprüft)`.
- Der Workflow `vollpruefung.rhai` setzt kein Modell; gilt dort die Workflow-Vorgabe, steht das verwendete Modell im Protokollkopf.
+38
View File
@@ -0,0 +1,38 @@
---
name: bildpruefung
description: >
Prüft Buch-Abbildungen nur berichtend mit Grok 4.7 (Effort low): Wörter im
Bild abschreiben, Labels gegen Spec und Caption, Zählungen, Bildfehler,
Graustufen-Lesbarkeit, Format. Erzeugt keine Bilder und ändert nichts.
Nutzen bei: „Bilder prüfen“, „Bildprüfung“, „stimmen die Labels“, „Bild-QA
aller Abbildungen“, vor KDP-Upload, /bildpruefung. Nicht zum Erzeugen →
/abbildung.
---
# Bildprüfung (nur berichtend)
Ergebnis: `audits/bildpruefung-YYYY-MM-DD.md`. Keine Änderung an Bildern, Manuskript, Captions, `research/`, `prompts/`, Build. Kein `image_gen`.
## Modell
Bildprüfer und Zweitprüfer: **Grok 4.7, Effort `low`** (`.grok/rules/subagenten.md`). Fehlt das Modell oder lässt sich `low` nicht setzen: anhalten, melden, kein Ausweichmodell.
## Ablauf
1. **Bestand per Skript (Orchestrator, ohne Sub-Agent):** `just konsistenz --formate` → Marker, Dateien, ASSET-MAP, Formate. Abweichungen nur berichten.
2. **Graustufen:** vorhandene Print-Stage nutzen (`build/print-assets/…` nach `just print-assets`); sonst temporär außerhalb des Repos erzeugen. Nichts ins Repo schreiben.
3. **Erstprüfung:** Stapel zu 4–5 Bildern, je Stapel ein frischer Sub-Agent. Er bekommt je Bild nur: Bilddatei (+ Graustufen), Caption-Zeile aus dem Manuskript, Spec aus `abbildungen.md` bzw. `prompts/imagine/<ID>*.md`, die Kriterien U1–U13/C1–C5 aus `.grok/skills/abbildung/references/qa-checklist.md`.
4. **Zweitprüfung:** frischer Sub-Agent ohne Erstbefund, nur für P1 und `unsicher`, nur Felder 1 und 3. Weichen beide ab → `unsicher`. Der Orchestrator entscheidet nicht selbst.
## Felder je Bild (jede Zeile beantworten, nie „passt“)
1. WÖRTER: jedes sichtbare Wort wörtlich, auch Fehler. Keine Schrift: „keine“.
2. WORTPRÜFUNG: deutsch? richtig geschrieben? in Spec oder Caption? Abweichungen auflisten.
3. INHALT: geforderte Elemente zählen (Stufen, Wege, Zonen …), Reihenfolge und Richtung prüfen.
4. BILDFEHLER: verstümmelte Schrift, Prompt-Reste („16:9“), doppelte Labels, Extra-Elemente.
5. DRUCK: Labels in Graustufen lesbar? Ohne Graustufen-Fassung: „nicht geprüft“.
6. BEWERTUNG: OK / P2 (kosmetisch) / P1 (widerspricht Spec, Caption oder Kanon), ein Satz, Sicherheit `sicher`/`unsicher`.
## Bericht
Kopf (Datum, Modell-ID, Effort, Bestand je Quelle) · Tabelle aller Bilder (ID · Datei · Wörter · Zählung · Bewertung · Sicherheit · Zweitprüfer) · Formatabweichungen · priorisierte Nachzugliste (sichere P1, unsichere P1, P2) als Auftrag für `/abbildung` · neue AE-Fragen · Sub-Agenten (Rolle, Modell, Effort, Aufrufe).
+26
View File
@@ -0,0 +1,26 @@
---
name: kdp-vorab
description: >
KDP-Vorabprüfung vor einem Upload, nur prüfend: Stichtag, Auflage und
Impressum über alle Träger, STALE-Ausgaben, eBook- und Print-QA, Seiten ↔
_print_pages ↔ Rücken, Shop-Cover, offene AEs. Lädt nichts hoch, baut nur
mit Auftrag. Nutzen bei: „KDP-Check“, „bereit für KDP?“, „vor dem Upload“,
„Paperback prüfen“, „Rückenbreite“, /kdp-vorab.
---
# KDP-Vorabprüfung
Prüft, ob die Dateien zu `marketing/kdp-checkliste.md` passen. Kein Upload, kein Wizard, keine Edits. Bauen (`just pdf`, `just pdf-print`, `just epub`, `just kdp-cover`) nur, wenn der Auftrag es nennt; sonst STALE melden.
## Schritte
1. `just konsistenz` → Abschnitte 5 (Stichtag/Auflage) und 6 (Seiten/Umschlag).
2. **Gleichlauf Träger:** Impressum PDF (`pdf/front/impressum.tex`), EPUB-Impressum (`scripts/prepare_epub.py`), Kolophon, Vorwort-Datum, `marketing/kdp-produkttexte.md`. Jede Abweichung bei Stichtag oder Auflage ist eine AE-Frage, kein Fix.
3. **Ausgaben:** `dist/*.pdf`, `*.epub`, `kdp-cover-wrap.pdf`, `assets/cover/cover-kdp-shop.jpg` vorhanden und neuer als das Manuskript? Sonst STALE.
4. **QA lesen** (nur wenn Ausgaben aktuell): `just qa`, `just qa-print`, `just qa-epub`. FAIL, jede WARN, Seiten, MB.
5. **Seiten ↔ Umschlag:** gemessene Print-Seiten = `_print_pages`? Rücken = Seiten × 0,002252″ (weiß); mit dem KDP Cover Calculator abgleichen lassen (manueller Schritt für Richie). Weicht es ab: melden; setzen nur mit Auftrag, dann `_print_pages` und `just kdp-cover` im selben Commit.
6. **Offene AEs** aus `HANDOFF.md`, die Upload-relevant sind (Stichtag, Auflage, ISBN, Bilder).
## Ausgabe
Ampel je Block (Träger · Ausgaben · QA · Seiten/Umschlag · AEs), darunter nur die roten und gelben Punkte mit `Datei:Zeile`. Urteil genau eines: „uploadbereit“ (alles grün, keine offene Upload-AE) oder „nicht uploadbereit“ mit Liste. Manuelle Schritte (Previewer, Calculator, Author Proof, Preis, KI-Deklaration) nur aufzählen.
+32
View File
@@ -0,0 +1,32 @@
---
name: konsistenz
description: >
Misst die Meta-Zahlen des Buchs nur lesend per Skript: Kapitel, Wörter,
Abbildungen (Marker ↔ Dateien ↔ ASSET-MAP ↔ Prompts ↔ Workflow), Stichtag
und Auflage über alle Träger, Print-Seiten ↔ _print_pages ↔ Rücken, tote
Pfade in .grok, HANDOFF-Aufbau. Nutzen bei: „Konsistenz“, „stimmen die
Zahlen“, „wie viele Bilder/Wörter/Seiten“, vor Vollprüfung, nach Build,
vor HANDOFF, /konsistenz.
---
# Konsistenz-Check
Ein Skriptlauf, keine Sub-Agenten, keine Edits.
```bash
just konsistenz # Bericht
just konsistenz --formate # + Bildformat je Datei (Pillow)
just konsistenz --strict # Exit 1 bei ABWEICHUNG (für Gates)
```
## Auswerten
- Zahlen aus der Ausgabe übernehmen, nie aus README, Exposé oder alten Protokollen.
- **ABWEICHUNG, Meta** (README, AGENTS, `.grok`, Kapitelzahl, Skill-Liste, tote Pfade): nachziehen, wenn der Auftrag Doku-Pflege umfasst; sonst melden.
- **ABWEICHUNG, Inhalt** (Stichtag, Auflage, Marker ↔ Bilder, Seiten ↔ Umschlag): nicht beheben. Als AE-Frage formulieren (`AGENTS.md` § Entscheidungen): Stelle, Optionen, Empfehlung „ungeprüft“.
- „nicht vorhanden“ heißt: Datei fehlt in diesem Arbeitsbaum (z. B. Bilder oder `dist/` nicht gebaut). Kein Fehler des Skripts.
- INFO-Zeilen nur zählen, außer der Auftrag fragt danach.
## Ausgabe im Chat
Höchstens 10 Zeilen: Kapitel, Wörter (Vorwort–Epilog), Marker, Print-Seiten/`_print_pages`, Zahl ABWEICHUNG/INFO, die AE-Kandidaten.
+2 -2
View File
@@ -56,7 +56,7 @@ just qa
```
Die QA prüft:
- **Seitenzahl:** ~172 Seiten bei vollständigem Manuskript (±5 Seiten Toleranz).
- **Seitenzahl:** gemessen melden (`pdfinfo`, `just konsistenz`), keinen Sollwert aus der Doku übernehmen. Soft-Ziel in `scripts/check_pdf.py`.
- **Grafiken:** Illustrationen unter `assets/illustrations/` eingebettet (Mermaid-Diagramme sind Archiv, nicht im Buchtext).
- **Fonts:** Charter, DejaVu Sans, DejaVu Sans Mono eingebettet.
- **Formale Seiten:** Impressum, Kolophon, Inhaltsverzeichnis vorhanden.
@@ -68,7 +68,7 @@ Die QA prüft:
**Bei QA-Fehlern:**
- Fehlende Grafiken: Illustration-Pfad und `assets/illustrations/ASSET-MAP.md` prüfen, dann neu bauen.
- Fehlende Fonts: Fonts auf dem System installieren (`fc-list | grep Charter`).
- Seitenzahl-Abweichung: Auf gravierende Struktur-Änderungen prüfen (nicht bei ±5 Seiten).
- Seitenzahl-Abweichung: Delta zum letzten Bau im HANDOFF nennen; nur Struktursprünge untersuchen.
- Formale Seiten fehlen: `pdf/front/impressum.tex` und Backmatter in `manuskript/` (`ueber-den-autor.md`, `kolophon.md`) prüfen.
**Bei Warnungen:**
+9 -1
View File
@@ -88,6 +88,14 @@ just qa-print
Prüft u. a.: Existenz, Seitenzahl (Soft 180–320), Font-Embedding, **Page size ≈ 6×9 in**,
Gray-Stage, eingebettete Bilder gray.
### 3b. Seiten und Umschlag (Pflicht)
Seitenzahl, `_print_pages` und Rücken gehören zusammen:
1. `just konsistenz` → Abschnitt 6 zeigt gemessene Print-Seiten, `_print_pages` und Rücken.
2. Weicht `_print_pages` ab: melden. Setzen nur mit Auftrag; dann im selben Commit `_print_pages` im `justfile` ändern, `just kdp-cover` bauen und Datei, Seiten und Rücken (mm) berichten.
3. Seitenzahlen in `marketing/` und README nur mit Auftrag nachziehen.
### 4. Ergebnis melden
```text
@@ -130,4 +138,4 @@ Löscht `build/` (inkl. print-assets) und `dist/*.pdf`.
|-----------------|--------|
| `pdf-bauen` / `/pdf` | A4 eBook (Farbe) |
| **`pdf-print` / `/pdf-print`** | KDP Interior 6×9 B/W |
| KDP-**Cover** | separat (Bleed + Spine) — nicht dieser Skill |
| KDP-**Cover** | `just kdp-cover` (liest `_print_pages`), Vorabprüfung `/kdp-vorab` |
+6 -3
View File
@@ -16,7 +16,10 @@ Schneller Überblick ohne Lektorat und ohne Build. **Zuerst `HANDOFF.md`.** Nich
`HANDOFF.md` lesen: HEAD, Letzte Arbeit, Offen, Nicht still. Wenn die Datei > 60 Zeilen oder ein Changelog ist: das sagen, den Kopf nutzen, nicht still aufblasen.
2. **Manuskript-Inventar**
Liste `manuskript/*.md` (Prolog, Kap. 1–12, Epilog, Glossar, Literatur, Kolophon, Über den Autor). Keine gelöschten Review-Dateien erwarten.
Liste `manuskript/*.md` (Vorwort, Prolog, Kap. 1–10, Epilog, Glossar, Literatur, Über den Autor, Kolophon; Reihenfolge = `CHAPTER_ORDER` in `scripts/assemble_book.py`). Keine gelöschten Review-Dateien erwarten.
2b. **Zahlen messen statt abschreiben**
`just konsistenz` (nur lesend). Kapitel, Wörter, Marker, Stichtag, Auflage, `_print_pages` aus der Ausgabe übernehmen; ABWEICHUNG-Zeilen unter „Nächste Schritte“ als Frage nennen, nicht beheben.
3. **Faktencheck-Lage**
Lies Kurzfassung aus `research/faktencheck-v2/ABSCHLUSSBERICHT.md` (Executive Summary / Statistik). Nenne offene Kategorien: teilweise korrekt, stichtagsabhängig — keine erfundenen P0-Listen.
@@ -28,7 +31,7 @@ Schneller Überblick ohne Lektorat und ohne Build. **Zuerst `HANDOFF.md`.** Nich
Die Liste **Offen** aus `HANDOFF.md`, abgleichen mit Git/Faktencheck. Maximal 3–5 konkrete nächste Aktionen. Keine zweite Prioritätenliste erfinden.
6. **Skills-Hinweis**
Welche Slash-Commands als Nächstes sinnvoll sind (`/lesepass`, `/faktencheck`, `/lektorat`, `/pdf`).
Welche Slash-Commands als Nächstes sinnvoll sind (`/konsistenz`, `/lesepass`, `/faktencheck`, `/lektorat`, `/pdf`, `/kdp-vorab`).
## Ausgabeformat
@@ -60,6 +63,6 @@ Schneller Überblick ohne Lektorat und ohne Build. **Zuerst `HANDOFF.md`.** Nich
## Regeln
- Keine Manuskript-Edits in diesem Skill.
- `HANDOFF.md` nicht still verlängern.
- `HANDOFF.md` nicht still verlängern. Neu schreiben nur nach Vorlage `references/handoff-vorlage.md`.
- Keine erfundenen Review-Dateien (`review-kapitel-*-minimax.md` existieren nicht mehr).
- Wenn Abschlussbericht fehlt: das sagen und auf `research/` verweisen.
@@ -0,0 +1,32 @@
# HANDOFF — Die KI hinter dem Vorhang
<!-- Vorlage. ≤ 60 Zeilen. Kein Changelog (das ist git log). Zahlen aus `just konsistenz`, nicht abschreiben. -->
## Stand
**Datum:** YYYY-MM-DD · **HEAD:** `main` @ `<sha>` (= origin/main ja/nein) · **Backup-Tag:** `<tag>`
**Messung (`just konsistenz`):** Kapitel 10 · Wörter Vorwort–Epilog <n> · Marker <n> · eBook <n> S. · Print <n> S. · `_print_pages` <n> · Rücken <mm> mm
**Ausgaben:** eBook <Datum/STALE> · Print <Datum/STALE> · EPUB <Datum/STALE> · Umschlag <Datum/STALE>
## Letzte Arbeit
2–4 Sätze. Was, welcher Bericht (`audits/…`), welche Commits.
## AE-Tabelle
| AE | Frage (kurz) | Stelle | Status | Entscheidung · Datum | umgesetzt (Commit) |
|----|--------------|--------|--------|----------------------|--------------------|
| AE-01 | … | `datei:zeile` | offen / entschieden | A · YYYY-MM-DD | `<sha>` / – |
Nächste freie Nummer: AE-NN. Entschiedene AEs werden nicht neu geöffnet.
## Einstieg
Ein Block: der nächste Auftrag in 1–3 Zeilen, mit Befehl.
## Offene Reste (nur mit Auftrag)
- …
## Nicht still
- Entschiedene AEs nicht neu öffnen; neue Fragen als AE ab der nächsten freien Nummer.
- Manuskript, `research/`, Bilder nur mit ausdrücklichem Auftrag.
- `_print_pages` und Umschlag nur gemeinsam ändern.
- …
## Verweise
`git log`, `audits/`, `research/` (nur Pfade, kein zweiter Einstieg).
+3 -1
View File
@@ -27,12 +27,14 @@ Die Pause nach Welle 1 ist der Schnitt: die Datei hat die Abschnitte 1–4, 6, 7
## Stand
Zehn Kapitel. 13 Texteinheiten (Vorwort, Prolog, Kap. 1–10, Epilog). 9 Literaturblöcke. 48 ASSET-MAP-IDs. 4 Lesepass-Spuren. Formel `3T+L+B+11` = 107, plus bis zu 9 Skeptiker. Companion nur, wenn das Inventar in `HANDOFF.md` einen Schwesterpfad findet.
Zehn Kapitel. 13 Texteinheiten (Vorwort, Prolog, Kap. 1–10, Epilog). 9 Literaturblöcke. 48 ASSET-MAP-IDs im Workflow; das Manuskript hat derzeit weniger Marker (I-7b, I-7e ohne Marker, siehe `just konsistenz`). 4 Lesepass-Spuren. Formel `3T+L+B+11` = 107, plus bis zu 9 Skeptiker. Companion nur, wenn das Inventar in `HANDOFF.md` einen Schwesterpfad findet.
Die Liste steht im Workflow. Weicht die Zählung ab, steht das Delta in Abschnitt A. Das Roster nicht still auffüllen und nicht zusammenlegen. Gelöschte IDs (I-0b, I-4d, I-5d, I-9e, I-11a/b, I-E2) sind kein Fehlbestand.
Verträge: `references/agent-prompts.md`. Dieselben Felder sind die `output_schema` im Workflow. Ein Aufsatz fällt am Schema durch. Gerüst: `references/protokoll.md`. Der Schreiber im Workflow setzt nur diese eine Datei.
Vorher `just konsistenz` laufen lassen (Sekunden, keine Tokens). Dessen Zahlen gelten; weicht der Workflow ab, steht das Delta in Abschnitt A. Sub-Agenten-Modelle: `.grok/rules/subagenten.md`. Funde sind Claims: Umsetzen nur nach Freigabe, Ersatzsätze erst nach geöffneter Primärquelle (`AGENTS.md` § Entscheidungen).
## Nicht tun
| Ausrede | Regel |
@@ -9,7 +9,7 @@ Diese Datei ist die Feldliste. Der Prompt-Text, der wirklich läuft, steht im Wo
Gemeinsamer Kopf:
```
Projekt: /Volumes/SSD2TB/books/ki-hinterm-vorhang
Projekt: Repo-Root (aktuelles Arbeitsverzeichnis)
Buch: „Die KI hinter dem Vorhang“. Ton: demystifizierend, nüchtern, technisch korrekt.
Modus: READ-ONLY. Keine Datei anlegen, ändern, löschen. Kein Commit, kein Rebuild.
These: heutige Alltags-KI = Next-Token-Prediction. Modell ≠ System/Harness.
+3 -3
View File
@@ -233,7 +233,7 @@ fn self_check() {
}
fn kopf() {
let s = "Projekt: /Volumes/SSD2TB/books/ki-hinterm-vorhang\n";
let s = "Projekt: Repo-Root (aktuelles Arbeitsverzeichnis)\n";
s += "Buch: Die KI hinter dem Vorhang. Ton: demystifizierend, nuechtern, technisch korrekt.\n";
s += "Modus: READ-ONLY. Keine Datei anlegen, aendern, loeschen. Kein Commit, kein Rebuild.\n";
s += "These: heutige Alltags-KI ist Next-Token-Prediction. Modell ist nicht System/Harness.\n";
@@ -357,7 +357,7 @@ fn prompt_literatur(heading, keys) {
}
fn prompt_qa(spur) {
let s = "Projekt: /Volumes/SSD2TB/books/ki-hinterm-vorhang\n";
let s = "Projekt: Repo-Root (aktuelles Arbeitsverzeichnis)\n";
s += "Kein Rebuild. Kein just pdf, kein just pdf-print, kein just epub. Keine Datei aendern.\n";
s += "STALE = aelter als der letzte inhaltliche Manuskript-Commit, oder ein juengeres eBook liegt vor und Print oder EPUB hinken hinterher.\n";
if spur == "ebook" {
@@ -401,7 +401,7 @@ fn prompt_companion(repo) {
}
fn prompt_inventar() {
let s = "Projekt: /Volumes/SSD2TB/books/ki-hinterm-vorhang. Nur lesen. Keine Datei schreiben, kein Build, kein Commit.\n";
let s = "Projekt: Repo-Root (aktuelles Arbeitsverzeichnis). Nur lesen. Keine Datei schreiben, kein Build, kein Commit.\n";
s += "Nutze git status -sb, git log -1, read_file und grep. Nicht aus dem Gedaechtnis antworten.\n";
s += "Zaehle T = Vorwort, Prolog, Kapitel 1 bis 10, Epilog (Soll 13). ";
s += "L = ##-Bloecke in manuskript/literatur.md (Soll 9). ";
+33 -8
View File
@@ -37,7 +37,22 @@ Vage Wünsche → prüfbare Ziele. Bei Mehrschritt: Checkpoints nennen und verif
| Bau das PDF | `just pdf` + `just qa` grün |
| Print-PDF (KDP) | `just pdf-print` + `just qa-print` grün (6×9 Gray) |
| Faktencheck | Claims gegen `research/`; Unsicherheit markiert |
| Handoff | `HANDOFF.md` ≤ 60 Zeilen; SHA und „nicht nochmal“ stimmen; kein Changelog |
| Handoff | `HANDOFF.md` ≤ 60 Zeilen nach `.grok/skills/status/references/handoff-vorlage.md`; SHA, AE-Tabelle und „Nicht still“ stimmen; kein Changelog |
| Zahlen stimmen | `just konsistenz`: keine neue ABWEICHUNG; Inhaltliches → AE-Frage |
## 5. Dateihoheit, Entscheidungen, Korrekturen
- **Dateihoheit:** `manuskript/`, `pdf/front/`, `research/`, Bilder (`assets/`, `prompts/imagine/`) und `marketing/` nur mit ausdrücklichem Auftrag ändern, der Datei oder Stelle nennt. Sonst melden.
- **Keine Inhaltsentscheidungen.** Offene Fragen werden nummerierte **AE-Fragen** (AE-01 ff., Tabelle in `HANDOFF.md`): Stelle `datei:zeile`, Optionen, Diff-Skizze, Empfehlung „ungeprüft“. An der Stelle nichts ändern. **Entschiedene AEs werden nicht neu geöffnet**; lässt sich eine nicht wie beschlossen umsetzen: neue AE.
- **Korrekturen aus Audits sind ungeprüfte Claims.** Ersatzsätze aus Audits, Prüfprotokollen oder `research/` erst übernehmen, wenn die Primärquelle geöffnet ist (Fundstelle in Commit oder Bericht). Es gilt die neueste Prüfung mit geöffneter Primärquelle; widersprechen sich zwei solche Prüfungen: AE.
- **Lieber streichen als verneinen.** Keine Sätze darüber, was ein Kapitel nicht behauptet. Keine neuen Formel- oder Schablonensätze; bei Korrekturen vorher/nachher zählen (`rg`).
- **Keine Kompromiss-Spannen.** Zwei Quellen, zwei Zahlen: nicht mitteln, sondern Quelle und Stand (Version, Datum) festlegen und abhängige Rechnungen mitziehen. „Gibt es nicht“-Aussagen doppelt prüfen (zweite Quelle oder Zweitprüfer).
- **Gedankenexperimente** bleiben als solche erkennbar („Stellen Sie sich vor …“), nie als echtes Ereignis.
- **Sub-Agenten:** `.grok/rules/subagenten.md` (Text `deepseek-v4_1-flash` ohne Effort, Bilder Grok 4.7 `low`, minimaler Kontext).
## 6. Seiten und Umschlag
Print-Seitenzahl, `_print_pages` im `justfile` und Rücken (`just kdp-cover`) gehören zusammen. Nach jedem `just pdf-print`: `just konsistenz`. Weicht die gemessene Zahl von `_print_pages` ab: melden; mit Auftrag `_print_pages` setzen und `just kdp-cover` im selben Commit. Seitenzahlen nie in Doku festschreiben, messen. Nach Textänderungen gelten PDF, Print und EPUB als STALE; neu bauen nur mit Auftrag.
---
@@ -60,13 +75,15 @@ Detail-Regeln: `.grok/rules/schreibkonventionen.md` · Pipeline: `.grok/rules/ba
| Pfad | Rolle |
|------|--------|
| `HANDOFF.md` | Session-Arbeitszettel (kurz; siehe § Handoff) |
| `manuskript/*.md` | Kapitel-Quellen (editieren; Vorwort, Kap. 1–12 + Prolog/Epilog) |
| `research/` | Fakten, Timeline, Faktencheck-v2 — vor Behauptungen lesen |
| `manuskript/*.md` | Kapitel-Quellen (Vorwort, Prolog, Kap. 1–10, Epilog, Anhang; Reihenfolge `scripts/assemble_book.py`) — nur mit Auftrag |
| `research/` | Fakten, Timeline, Faktencheck-v2 — vor Behauptungen lesen; ändern nur mit Auftrag |
| `abbildungen.md` | Didaktischer Illustrations-Katalog |
| `assets/` | Illustrationen/Cover — über Skripte aktualisieren |
| `assets/` | Illustrationen/Cover — nur mit Auftrag (`/abbildung`) |
| `audits/` | Berichte von Prüf- und Optimierungsläufen |
| `pdf/**` | Template/Filter — nur auf expliziten Auftrag |
| `scripts/*` | Assembler, QA, Cover — ausführen, nicht umbauen |
| `justfile` | Build-Orchestrierung |
| `justfile` | Build-Orchestrierung; `_print_pages` = Seitenzahl für den Umschlag |
| `scripts/konsistenz_check.py` | Meta-Zahlen messen (`just konsistenz`), nur lesend |
---
@@ -80,6 +97,8 @@ just pdf-print # KDP Interior 6×9 Grayscale
just qa # eBook-QA
just qa-print # Print-QA (Page size, Gray-Assets)
just clean # build/ + dist/*.pdf (nachfragen)
just konsistenz # Zahlen messen (nur lesend)
just kdp-cover # Umschlag, Seiten aus _print_pages (nur mit Auftrag)
```
**Erfolg eBook:** `dist/die-ki-hinter-dem-vorhang.pdf` existiert, QA ohne Fehler.
@@ -101,7 +120,11 @@ TeX Live liegt **nicht** in Devbox (Host).
| `/total-check` | Buchweite Vollprüfung, max. Sub-Agenten, Protokoll |
| `/pdf [full\|draft\|sample]` | eBook-PDF bauen + QA |
| `/pdf-print [full\|assets-only\|qa-only]` | KDP Interior 6×9 + Print-QA |
| `/status` | Arbeitsstand: zuerst `HANDOFF.md`, dann Faktencheck-v2 / Git |
| `/status` | Arbeitsstand: zuerst `HANDOFF.md`, dann `just konsistenz`, Faktencheck-v2 / Git |
| `/konsistenz [--formate\|--strict]` | Kapitel, Wörter, Bilder, Stichtag, Auflage, Seiten/Umschlag messen |
| `/bildpruefung [alle\|I-ID]` | Abbildungen nur berichtend prüfen (Grok 4.7 low) |
| `/abbildung <I-ID>` | Eine Abbildung erzeugen/nachziehen (Imagine) — der Aufruf ist der Auftrag |
| `/kdp-vorab` | Upload-Reife prüfen, nichts hochladen |
---
@@ -144,7 +167,7 @@ Glossar: `manuskript/glossar.md`
`HANDOFF.md` ist der Startzettel für die nächste Session, **kein** Arbeitsprotokoll.
**Inhalt (in dieser Reihenfolge, eine Datei, ≤ 60 Zeilen):** Stand + HEAD; Letzte Arbeit (2–4 Sätze); ein Einstiegsblock; Offen nur mit Auftrag; Nicht still; Verweise (git, research), kein zweiter Einstieg.
**Inhalt (in dieser Reihenfolge, eine Datei, ≤ 60 Zeilen, Vorlage `.grok/skills/status/references/handoff-vorlage.md`):** Stand + HEAD + Messung (`just konsistenz`); Letzte Arbeit (2–4 Sätze); AE-Tabelle; ein Einstiegsblock; Offene Reste nur mit Auftrag; Nicht still; Verweise (git, research), kein zweiter Einstieg.
**Aktualisieren** nur an einer Phasengrenze oder wenn SHA / „nicht nochmal“ sonst falsch wäre. Nicht nach jedem kleinen Diff. Nicht weil eine Smartzone-Regel „Handoff + neue Session“ vorschlägt.
@@ -157,7 +180,9 @@ Glossar: `manuskript/glossar.md`
## Git
- Commit-Messages auf **Deutsch**, klar *was* am Manuskript/Build geändert wurde.
- Kein Force-Push, kein `reset --hard`, keine Secrets committen.
- Kein Force-Push (auch nicht `--force-with-lease`), kein `reset --hard`, kein `rebase`, kein `commit --amend`, nie `--no-verify`. Keinen Branch oder Tag löschen. Den Stash nicht anfassen. Der Hook `.grok/hooks/protect-pipeline.sh` blockt das auch im yolo-Modus; nach Hook-Änderungen `sh scripts/test_hook.sh` (Exit 0 = alle Fälle wie erwartet).
- **yolo-Läufe:** `git status --porcelain` nicht leer → anhalten, nichts committen oder verwerfen. Dann lokaler Backup-Tag `pre-<lauf>-YYYY-MM-DD` (existiert er: `-2`, nie überschreiben), eigener Branch, ein Commit je Schritt. Merge nur `--ff-only` nach `main`; `rework` nicht mergen. Push nur am Ende und nur bei grüner QA bzw. grünem Check.
- Keine Secrets committen.
- `git push` / `git commit` nur mit Freigabe (Permission-Ask).
- Keine PAT/API-Keys in Repo-Dateien (auch nicht in `.grok/config.toml`).
+28 -36
View File
@@ -2,48 +2,40 @@
Arbeitszettel für die nächste Session. Kein Changelog — das ist `git log`.
**Stand:** 2026-10-09
**HEAD:** `main` = `origin/main` nach Push. Dieser Commit: Querverweis-P1. Davor `bb4c666` Kap. 7+8 P1+P2. `rework` @ `935407e` — nicht mergen. Kein `lissabon/`.
## Stand
**Datum:** 2026-10-29 · **HEAD:** `main` (dieser Commit = Optimierungspaket, Parent `d81ee6d`) · **Backup-Tag:** –
**Messung (`just konsistenz`):** Kapitel 10 · Wörter Vorwort–Epilog 40868 · Marker 46 · eBook 148 S. · Print 264 S. · `_print_pages` 261 · Rücken 14,93 mm (261 S.)
**Ausgaben:** eBook 9. Okt. 2026 · Print 14. Aug. STALE · EPUB 31. Aug. STALE · Umschlag 11. Aug. STALE
**Letzte Arbeit:** Querverweis-P1 in Prolog, Glossar und Literatur: HELM → Kap. 10; TTC → Kap. 3, 5, 8; Prolog-Ampel vier Fragen inkl. wer prüft. Regel-2-Naht bleibt in `kapitel-08` bei den älteren Kap.-8-Resten (nicht mitgezogen). Daneben uncommitted: Kap.-9-P1, Kap.-10-P1, Kap.-6-/7-/8-Reste, Vorwort-Datum, Haufen 1.
## Letzte Arbeit
Optimierungspaket auf `main`: `just konsistenz`, Skills/Commands (12/12), Dateihoheit in `AGENTS.md`, `_print_pages` im justfile, Hook-Test 26/26. Manuskript unangetastet. AE-03 B+ bleibt `d81ee6d`. Scheiben und Haufen 1 liegen auf `wip-scheiben-haufen-2026-10-29` (nicht mergen).
## AE-Tabelle
| AE | Frage (kurz) | Stelle | Status | Entscheidung · Datum | umgesetzt (Commit) |
|----|--------------|--------|--------|----------------------|--------------------|
| AE-03 | I-7b/I-7e nach Kap.-7-Schnitt | Marker weg in `3a1611a`; Assets bis `d81ee6d` | entschieden | B+ · 2026-10-29 | `d81ee6d` |
Nächste freie Nummer: AE-04. Entschiedene AEs nicht neu öffnen. (AE-01/02/04 stehen im KR1-Auftrag, hier noch nicht in der Tabelle.)
## Einstieg
```
Lies HANDOFF.md. main = origin/main nach Push. Dieser Commit: Querverweis-P1 (prolog, glossar, literatur).
87379df, 3a1611a, ddea1f7, c3bacab, Kap.-7-P1+P2, Kap.-8-Spine (07.10.), Kap.-6-/7-/8-/9-/10-Satzpass (09.10.), Querverweis-P1 nicht nochmal. Kap.-9-P1, Kap.-10-P1 nicht zurück.
Gegenlese 07.10. = 83, chat-only. 22.09./06.10./07.10./09.10. nicht neu benoten, nicht in research/gegenlese-2026-09-22.md schreiben. Kein Schnitt aus der Note.
Sitze nach Schichten. A2A eigenes Protokoll. Summary Kap. 7: Hülle; Host an der Steckdose.
I-7b/e, I-8a–e, I-9a–d nicht neu nummerieren. Glossar Orchestrierung nicht angleichen.
Ampel-Logik, Schule/Verwaltung, Mitte 2026, Hybrid=dritte Betriebsart bleiben. JEPA-Kasten Kap. 9 Freeze.
Vorwort Z. 5 / Stand August 2026 / Kap. 6 Freeze. Haufen 1 nicht mitziehen.
Uncommitted, nicht mischen: Kap. 6 Persona-Cutoff; Kap. 7 Schema-Check (when=Datum); Kap. 8 chirurgisch (Medizin-H4, vierte Frage, AlphaFold, Weiche) plus Regel-2-Naht in derselben Datei; Kap. 9 P1; Kap. 10 P1. Vorwort.md/.tex August→Oktober = Freeze-Drift. Nur mit Auftrag committen oder verwerfen.
Bilder nicht regenerieren. Kein SHA-only-Commit. eBook/EPUB/Print nicht still bauen. rework nicht mergen.
eBook 9. Okt., 148 S. A4. Print 264 S. 6×9, 14. Aug., STALE. EPUB 31. Aug., STALE.
Nächster Schritt nur mit genanntem Auftrag (eine Scheibe committen oder verwerfen; Kap.-8-Katalog-Naht; Anker Kap. 1 Z. 27 / Prolog-Markus / Kap. 7 Inventar / Schema-Check; oder etwas anderes).
Lies HANDOFF.md. main = Optimierungspaket (Parent d81ee6d). AE-03 B+ nicht nochmal. I-7b/e nicht zurück; I-8a–e, I-9a–d nicht umnummerieren.
Nächster Auftrag: Korrekturrunde 1 auf leerem Porcelain (Tag pre-korrektur-1-2026-10-29, Branch korrektur-1). wip-scheiben-haufen-2026-10-29 und rework nicht mergen.
```
## Offen (nur mit Auftrag)
1. EPUB und Print STALE. eBook 148 S., 9. Okt.; nicht still bauen.
2. Working-tree-Scheiben je für sich: Kap. 9 P1; Kap. 10 P1; Kap. 8 Reste inkl. Regel-2-Naht. Nicht mit Haufen 1 mischen.
3. Kap. 7 Satzpass 09.10. (Kritik): P1 Z. 218; Host-Uhr „morgen“ Z. 53/67–80; Z. 302 zwei Blöcke; Prediktor-Weld Z. 95/224.
4. Kap. 6 Satzpass 09.10. (Kritik, Freeze): P1 Z. 71, Z. 75, Z. 31–33.
5. Kap. 8 Satzpass 09.10. (Kritik): Katalog-Naht nach HITL; Z. 18 „legt“; Drift-Satz; Z. 220 vs. Z. 290; RAG/Autoregression; Hybrid-Homonym auf Kap.-8-Seite.
6. Gegenlese-07.10.-Anker: Kap. 1 Z. 27, Prolog-Markus, Kap. 7 Inventar.
7. Haufen 1 lokal: JPGs, Imagine, REGEN-LOG, Research-Dumps, `_gen`, I-8d.png, Vorwort-Datum.
## Offene Reste (nur mit Auftrag)
- EPUB/Print/Umschlag STALE; `_print_pages` 261 ≠ Print 264 — nur gemeinsam mit `just kdp-cover` (AE-04 im KR1-Auftrag).
- Manuskript-Scheiben und Haufen 1: Branch `wip-scheiben-haufen-2026-10-29`, nicht auf main.
- Satzpass-Kritik 09.10. (nicht wiederholen): Kap. 7 P1 Z. 218, Host-Uhr, Z. 302, Prediktor-Weld; Kap. 6 Freeze P1 Z. 71/75/31–33; Kap. 8 Katalog-Naht, Drift, Hybrid-Homonym.
- Gegenlese-07.10.-Anker: Kap. 1 Z. 27, Prolog-Markus, Kap. 7 Inventar.
- Katalog-Reste: `abbildungen.md` Z. 6/18 noch „48“; Prompt-/Tier-READMEs listen I-7b/e.
## Nicht still
- AE-03 nicht neu öffnen; I-7b/e nicht restaurieren.
- Kap.-9-P1, Kap.-10-P1, Querverweis-P1, Kap.-7/8-P1+P2 (07.10.), Slop-P0 nicht zurück.
- JEPA-Kasten, Ampel-Logik, Hybrid=dritte Betriebsart, Mitte 2026, Vorwort-Stand August 2026, Kap. 6 Freeze.
- Glossar Orchestrierung nicht an Kap. 7 angleichen. Sitze nach Schichten. A2A eigenes Protokoll. Summary Kap. 7: Hülle; Host an der Steckdose.
- Bilder nicht regenerieren. eBook/EPUB/Print nicht still bauen. `rework` und `wip-scheiben-haufen-2026-10-29` nicht mergen. Haufen 1 nicht auf main. Gegenlese nicht neu benoten, nicht in `research/gegenlese-2026-09-22.md` schreiben.
- Kein SHA-only-Commit. HANDOFF mit der Arbeit mitziehen, der er gilt.
Kap.-9-P1 oder Kap.-10-P1 zurück. Querverweis-P1 zurück. Satzpässe 09.10. wiederholen. Kap.-7- oder Kap.-8-P1+P2 (07.10.) zurück. JEPA-Kasten umbauen. Vorwort auf Oktober ziehen. Gegenlese neu benoten oder in `research/gegenlese-2026-09-22.md` schreiben. Ampel-Logik umbauen, I-8e/d umnummerieren, Bilder regen. Glossar an Kap. 7/8 angleichen. Sitze vor die Schichten. A2A auf „offen“ zurück. AAIF-Namen ins Buch. Vorwort Z. 5 oder Stand August 2026 nachziehen. Kap. 6 oder Vorwort kürzen. eBook/EPUB/Print still bauen. rework mergen. Haufen 1 still mitziehen. Slop-P0 zurück.
## Wo der Rest liegt
| Was | Wo |
|-----|-----|
| HEAD Querverweis-P1 | dieser Commit; Parent `bb4c666` |
| Gegenlese 07.10. (83) | Chat; nicht in die 22.09.-Datei |
| Satzpässe 09.10. | Chat; Kap. 9/10 P1 im working tree |
| Kap. 8 Reste + Regel-2 | `kapitel-08-praxis-ampel.md` |
| Haufen 1 + Vorwort-Datum | working tree, nicht committen |
| eBook / Print / EPUB | 148 S., 9. Okt.; Print 264 S., 14. Aug.; EPUB 31. Aug. |
## Verweise
`git log`; `assets/illustrations/ASSET-MAP.md`; `research/`; Gegenlese 07.10. nur Chat.
+16 -32
View File
@@ -1,11 +1,10 @@
# Next Token
Sachbuch: *Next Token – Wie moderne KI funktioniert und wo sie an ihre Grenzen stößt*
Arbeitstitel im Manuskript/PDF: **Die KI hinter dem Vorhang**
Sachbuch: **Die KI hinter dem Vorhang** – *Was LLMs, Agenten und AGI wirklich können – und was nicht*
Arbeitstitel: *Next Token* (Exposé-Variante des Untertitels siehe `expose.md`)
**Status:** **Vollständiges Erstmanuskript (v1+)** – Kap. 1–12 + Prolog/Epilog + Glossar + Literatur + Autor/Kolophon.
**Manuskript-Kern:** ca. **60.000 Wörter** (inkl. Rahmen)
**Umfang:** Vorwort, Prolog, Kap. 1–10, Epilog + Glossar, Literatur, Über den Autor, Kolophon.
**Zahlen** (Wörter, Bilder, Seiten, Stichtag, Auflage) stehen hier bewusst nicht: `just konsistenz` misst sie. Arbeitsstand: `HANDOFF.md`.
---
@@ -13,8 +12,9 @@ Arbeitstitel im Manuskript/PDF: **Die KI hinter dem Vorhang**
| Teil | Datei | Status |
|------|-------|--------|
| Vorwort | `manuskript/vorwort.md` (+ `pdf/front/vorwort.tex`) | |
| Prolog | `manuskript/prolog.md` | v1 |
| Kap. 1–12 | `manuskript/kapitel-*.md` | v1+ (Kap. 11: AGI→ASI; Kap. 12: Marketing) |
| Kap. 1–10 | `manuskript/kapitel-01-…` bis `kapitel-10-marketing-wahrnehmung.md` | Reihenfolge: `scripts/assemble_book.py` |
| Epilog | `manuskript/epilog.md` | v1 |
| Glossar | `manuskript/glossar.md` | saubere Fassung |
| Literatur | `manuskript/literatur.md` | Empfehlungen |
@@ -22,19 +22,16 @@ Arbeitstitel im Manuskript/PDF: **Die KI hinter dem Vorhang**
### Research & Abbildungen
- `research/` – Timeline, JEPA, MCP, Quellen, **Faktencheck-v2** (`research/faktencheck-v2/`)
- `assets/illustrations/` – kanonische Buch-Illustrationen (ASSET-MAP: 48 Dateien, alle eingebettet)
- `assets/illustrations/` – kanonische Buch-Illustrationen (`ASSET-MAP.md`; Abgleich Marker ↔ Dateien: `just konsistenz`)
- `abbildungen.md` – didaktischer Abbildungs-Katalog
- `expose.md` – 1-Seiten-Exposé (Pitch; Zahlen können vom Manuskript abweichen)
- Agent-Setup: `AGENTS.md`, `.grok/` (Skills `/lektorat`, `/review`, `/faktencheck`, `/lesepass`, `/pdf`, `/status`)
- Agent-Setup: `AGENTS.md`, `.grok/` — Commands `/lektorat`, `/review`, `/faktencheck`, `/lesepass`, `/total-check`, `/pdf`, `/pdf-print`, `/status`, `/konsistenz`, `/bildpruefung`, `/abbildung`, `/kdp-vorab`; Regeln unter `.grok/rules/`
---
## Nächste sinnvolle Schritte (Lektorat)
## Nächste Schritte
1. Einheitlicher Lesepass (Wiederholungen, Kapitelbrücken) — `/lesepass`
2. Offene Präzisierungen aus `research/faktencheck-v2/` (teilweise korrekt / stichtagsabhängig)
3. Gezielter Faktencheck **Kap. 11** (AGI→ASI; in v2 noch nicht als eigenes Kapitel)
4. Stichtag-Boxen und Produktnamen aktualisieren
Stehen nur in `HANDOFF.md` (Einstieg, Offene Reste, AE-Tabelle). Keine zweite Liste hier.
---
@@ -46,7 +43,7 @@ Demystifizierend, nüchtern, technisch korrekt. Kein Hype, keine Angstmacherei,
## PDF erzeugen
Finale Ausgabe: **`dist/die-ki-hinter-dem-vorhang.pdf`** (~172 Seiten, ~16 MB).
Finale Ausgabe: **`dist/die-ki-hinter-dem-vorhang.pdf`** (Seiten und Größe: `just konsistenz`).
### Voraussetzungen
- macOS (getestet) oder Linux mit TeX Live
@@ -63,22 +60,9 @@ TeX Live liegt **nicht** in Devbox (Host-Fallback).
# Optional
devbox shell
# Volles PDF (Assembler + Template + 4 Lua-Filter)
# Volles PDF (Assembler + Template + Lua-Filter)
just pdf
# oder ohne just:
python3 scripts/assemble_book.py
pandoc build/book.md \
--pdf-engine=lualatex \
--template=pdf/templates/nexttoken-book.tex \
-H pdf/styles/nexttoken-colors.tex \
--metadata-file=pdf/metadata.yaml \
--lua-filter=pdf/filters/boxes.lua \
--lua-filter=pdf/filters/figures.lua \
--lua-filter=pdf/filters/illustrations.lua \
--lua-filter=pdf/filters/partpages.lua \
--resource-path=.:manuskript:assets \
-V lang=de-DE -V papersize=a4 -V colorlinks \
--toc -o dist/die-ki-hinter-dem-vorhang.pdf
# ohne just: die Befehle des Rezepts `pdf-styled` im justfile (dort vollständig, inkl. marks.lua, --lof, ebook-assets)
# QA
just qa # bzw. python3 scripts/check_pdf.py
@@ -99,7 +83,7 @@ just clean
| `pdf/templates/nexttoken-book.tex` | KOMA `scrbook` Pandoc-Template |
| `pdf/templates/nexttoken-preamble.tex` | Fonts, Kopfzeile, Boxen, Cover/Part |
| `pdf/styles/nexttoken-colors.tex` | Farbpalette |
| `pdf/filters/*.lua` | boxes, figures, illustrations, partpages |
| `pdf/filters/*.lua` | boxes, figures, illustrations, partpages, marks |
| `pdf/front/impressum.tex` | Impressum |
| `manuskript/vorwort.md`, `pdf/front/vorwort.tex` | Vorwort (nach Impressum, vor TOC) |
| `manuskript/ueber-den-autor.md`, `kolophon.md` | Autor + Kolophon (Backmatter) |
@@ -117,7 +101,7 @@ Trigger = Markdown-Header (`##`/`###`), kein Blockquote.
| `Häufiges Missverständnis`, `Überblick`/`Ueberblick`, `Stand der Technik` (H2/H3) | info |
### Smoke-Test
- Assembler: 18 Body-Dateien + 3 Part-Pages → `build/book.md` OK
- PDF: `dist/die-ki-hinter-dem-vorhang.pdf` (~172 Seiten)
- Assembler: 16 Body-Dateien (`CHAPTER_ORDER`) + 3 Part-Pages → `build/book.md` OK
- PDF: `dist/die-ki-hinter-dem-vorhang.pdf` existiert; Seiten gemessen (`just konsistenz`)
- Cover, Teil-Trenner und Illustrationen unter `assets/` vorhanden
- Warnung: einige Unicode-Kreis-Ziffern (①②…) fehlen in Charter
+12 -2
View File
@@ -9,7 +9,8 @@
# just epub → dist/die-ki-hinter-dem-vorhang.epub (Kindle/KDP)
# just qa-epub → structural QA for the EPUB only
# just kdp-shop-cover → assets/cover/cover-kdp-shop.jpg (eBook Shop 1600×2560)
# just kdp-cover → dist/kdp-cover-wrap.pdf (Print-Umschlag)
# just kdp-cover → dist/kdp-cover-wrap.pdf (Print-Umschlag, Seiten = _print_pages)
# just konsistenz → Konsistenz-Bericht (nur lesend, stdout)
default: pdf
@@ -20,6 +21,11 @@ _resource_path := "build/ebook-assets:.:manuskript:assets"
# build/print-assets/). First path wins → gray before RGB originals.
_print_resource_path := "build/print-assets:.:manuskript:assets"
# Print-Seitenzahl für den KDP-Umschlag (Rücken). Nur zusammen mit `just kdp-cover`
# ändern, nach `just pdf-print` gegen das gemessene PDF prüfen (`just konsistenz`).
# Wert 261 = bisheriger fester Wert in kdp-cover; ob 261 oder neu gemessen: AE-04.
_print_pages := "261"
assemble:
@python3 scripts/assemble_book.py
@@ -189,7 +195,11 @@ kdp-shop-cover:
# --- KDP Paperback cover wrap (front + spine + back) -------------------------
kdp-cover:
@python3 scripts/build_kdp_cover_wrap.py --pages 261 --paper white
@python3 scripts/build_kdp_cover_wrap.py --pages {{_print_pages}} --paper white
# Konsistenz-Check (nur lesend): Kapitel, Wörter, Bilder, Stichtag, Auflage, Seiten/Umschlag
konsistenz *ARGS:
@python3 scripts/konsistenz_check.py {{ARGS}}
clean:
@rm -rf build
+472
View File
@@ -0,0 +1,472 @@
#!/usr/bin/env python3
"""Konsistenz-Check für „Die KI hinter dem Vorhang“ – nur lesend.
Misst statt zu behaupten: Kapitel, Wörter, Abbildungen (Marker ↔ Dateien ↔
ASSET-MAP ↔ Prompts ↔ Workflow), Stichtag und Auflage über alle Träger,
Seiten/Umschlag (_print_pages ↔ Print-PDF ↔ Rücken), Meta-Angaben in
README/AGENTS/.grok, tote Pfade in .grok und den HANDOFF-Aufbau.
Schreibt nichts. Fehlende Dateien (Bilder, PDFs) erscheinen als
„nicht vorhanden“, nie als Absturz.
python3 scripts/konsistenz_check.py # Bericht, Exit 0
python3 scripts/konsistenz_check.py --strict # Exit 1 bei ABWEICHUNG
python3 scripts/konsistenz_check.py --root PFAD
"""
from __future__ import annotations
import argparse
import ast
import re
import shutil
import subprocess
import sys
from pathlib import Path
SPINE_WHITE = 0.002252 # Zoll je Seite, KDP-Richtwert weißes Papier
MONTHS = "Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember"
ID_RE = re.compile(r"\bI-(?:\d{1,2}[a-z]?|E\d)\b")
IMG_EXT = {".jpg", ".jpeg", ".png", ".webp", ".tif", ".tiff"}
findings: list[tuple[str, str]] = [] # (Stufe, Text)
def flag(level: str, text: str) -> None:
findings.append((level, text))
def rel(root: Path, p: Path) -> str:
try:
return str(p.relative_to(root))
except ValueError:
return str(p)
def read(p: Path) -> str | None:
try:
return p.read_text(encoding="utf-8", errors="replace")
except (FileNotFoundError, IsADirectoryError, PermissionError):
return None
def strip_comments(text: str) -> str:
return re.sub(r"<!--.*?-->", " ", text, flags=re.S)
def words(text: str) -> int:
return len(strip_comments(text).split())
def sort_key(fig: str) -> tuple:
m = re.match(r"I-(E)?(\d+)([a-z]?)", fig)
if not m:
return (99, 0, fig)
return (1 if m.group(1) else 0, int(m.group(2)), m.group(3))
def fmt_ids(ids) -> str:
ids = sorted(ids, key=sort_key)
return ", ".join(ids) if ids else "–"
# --------------------------------------------------------------------------
def chapter_order(root: Path) -> list[str]:
src = read(root / "scripts/assemble_book.py")
if src is None:
return []
m = re.search(r"^CHAPTER_ORDER\s*=\s*(\[.*?^\])", src, re.S | re.M)
if not m:
return []
try:
return [f for f, _kind in ast.literal_eval(m.group(1))]
except (ValueError, SyntaxError):
return []
def sec_kapitel(root: Path) -> int:
print("## 1 Kapitel und Wörter\n")
order = chapter_order(root)
man = root / "manuskript"
if not order:
flag("ABWEICHUNG", "CHAPTER_ORDER in scripts/assemble_book.py nicht lesbar")
chapters = [f for f in order if f.startswith("kapitel-")]
n = len(chapters)
print(f"- Body-Dateien im Assembler: {len(order)}, davon Kapitel: {n}, Teileseiten: siehe PART_BEFORE")
for f in order:
if not (man / f).exists():
flag("ABWEICHUNG", f"Assembler nennt manuskript/{f}, Datei nicht vorhanden")
on_disk = sorted(p.name for p in man.glob("kapitel-*.md")) if man.exists() else []
extra = [f for f in on_disk if f not in chapters]
if extra:
flag("INFO", f"Kapiteldateien außerhalb CHAPTER_ORDER: {', '.join(extra)}")
print("\n| Datei | Wörter |\n|---|---:|")
total_body = total_all = 0
seq = ["vorwort.md"] + order
for f in seq:
t = read(man / f)
if t is None:
print(f"| {f} | nicht vorhanden |")
continue
w = words(t)
print(f"| {f} | {w} |")
total_all += w
if f not in {"glossar.md", "literatur.md", "ueber-den-autor.md", "kolophon.md"}:
total_body += w
print(f"| **Vorwort bis Epilog** | **{total_body}** |")
print(f"| **mit Anhang** | **{total_all}** |\n")
print("_Zählung: Leerzeichen-getrennt, HTML-Kommentare entfernt (≈ `wc -w`)._\n")
return n
# --------------------------------------------------------------------------
META_FILES = ["README.md", "AGENTS.md", "HANDOFF.md", "manuskript/README.md", "manuskript/AGENTS.md"]
def meta_files(root: Path) -> list[Path]:
out = [root / f for f in META_FILES if (root / f).exists()]
g = root / ".grok"
if g.exists():
out += sorted(p for p in g.rglob("*") if p.suffix in {".md", ".rhai", ".toml"} and p.is_file())
return out
def sec_meta(root: Path, n_kap: int, n_skills: int) -> None:
print("## 3 Meta-Angaben in README, AGENTS, HANDOFF, .grok\n")
pats = [
(re.compile(r"Kap(?:\.|itel)\s*1\s*[–-]\s*(\d+)"), "Kapitelspanne"),
(re.compile(r"\b(\d+)\s+Kapitel\b"), "Kapitelzahl"),
(re.compile(r"\bKap(?:\.|itel)\s+(1[1-9])\b"), "Kapitel > Zahl"),
]
hits = 0
for p in meta_files(root):
t = read(p) or ""
for i, line in enumerate(t.splitlines(), 1):
if re.search(r"\b[Kk]ein(e|en)?\s+Kap|Alt-Soll|nicht mehr", line):
continue # Verneinungen und Alt-Hinweise sind keine Behauptung
for rx, what in pats:
for m in rx.finditer(line):
v = int(m.group(1))
if what == "Kapitel > Zahl" and v > n_kap:
flag("ABWEICHUNG", f"{rel(root, p)}:{i} nennt Kap. {v}, Assembler hat {n_kap} Kapitel")
hits += 1
elif what in {"Kapitelspanne", "Kapitelzahl"} and v != n_kap and v > 5:
flag("ABWEICHUNG", f"{rel(root, p)}:{i} „{m.group(0)}“, gemessen {n_kap}")
hits += 1
for m in re.finditer(r"~\s?(\d{2,3})\s*Seiten|(\d{2},\d{3}|\d{2}\.\d{3}|~\d{2}[.,]?\d{3})\s*Wörter", line):
flag("INFO", f"{rel(root, p)}:{i} Umfangsangabe „{m.group(0).strip()}“ – gegen Messung prüfen, nicht festschreiben")
readme = read(root / "README.md") or ""
m = re.search(r"Skills.*?\n((?:\|.*\n)+)", readme)
print(f"- Kapitel-/Umfangsangaben geprüft: {hits} Abweichung(en) bei der Kapitelzahl (Details unten).")
print(f"- Skills unter .grok/skills: {n_skills}\n")
# --------------------------------------------------------------------------
def sec_grok(root: Path) -> int:
print("## 2 .grok: Skills, Commands, Pfade\n")
g = root / ".grok"
if not g.exists():
print("- .grok nicht vorhanden\n")
return 0
skills = sorted(p.parent.name for p in (g / "skills").glob("*/SKILL.md"))
cmds = sorted(p.stem for p in (g / "commands").glob("*.md"))
print(f"- Skills ({len(skills)}): {', '.join(skills)}")
print(f"- Commands ({len(cmds)}): {', '.join('/' + c for c in cmds)}")
for s in skills:
t = read(g / "skills" / s / "SKILL.md") or ""
fm = t.split("---")[1] if t.startswith("---") else ""
if "description:" not in fm:
flag("ABWEICHUNG", f".grok/skills/{s}/SKILL.md ohne description (kein Auslöser)")
for c in set(re.findall(r"(?<![\w/.])/([a-z][a-z-]{2,})\b", fm)):
if c not in cmds and not (root / c).exists():
flag("ABWEICHUNG", f".grok/skills/{s}: Auslöser /{c} hat keine Command-Datei")
readme = read(root / "README.md") or ""
via_cmd = {}
for c in cmds:
for sk in re.findall(r"skills/([a-z-]+)/", read(g / "commands" / f"{c}.md") or ""):
via_cmd.setdefault(sk, set()).add(c)
for s in skills:
if s not in readme and not any(f"/{c}" in readme for c in via_cmd.get(s, ())):
flag("INFO", f"Skill {s} fehlt in README.md")
# tote Pfade: Backtick-Pfade mit bekanntem Top-Verzeichnis
tops = ("manuskript/", "research/", "assets/", "scripts/", "pdf/", "prompts/", "marketing/",
"audits/", ".grok/", "dist/", "build/")
gen = ("dist/", "build/", "audits/") # werden erzeugt
dead = 0
for p in [root / "AGENTS.md", root / "README.md"] + sorted(g.rglob("*.md")) + sorted(g.rglob("*.rhai")):
t = read(p) or ""
for i, line in enumerate(t.splitlines(), 1):
for m in re.finditer(r"`([^`\s]+)`", line):
path = m.group(1).rstrip(".,;:)")
if not path.startswith(tops) or any(ch in path for ch in "*{}<>$…|"):
continue
if path.startswith(gen) or re.search(r"X{2,}|YYYY|NN|\.\.\.", path):
continue
if not (root / path).exists():
lvl = "INFO" if path.startswith("assets/") else "ABWEICHUNG"
flag(lvl, f"{rel(root, p)}:{i} Pfad `{path}` nicht vorhanden")
dead += 1
abs_paths = []
for p in sorted(g.rglob("*")):
if p.is_file():
for i, line in enumerate((read(p) or "").splitlines(), 1):
if re.search(r"/(Volumes|Users|home)/", line):
abs_paths.append(f"{rel(root, p)}:{i}")
for a in abs_paths:
flag("ABWEICHUNG", f"{a} absoluter Rechnerpfad statt Repo-Root")
print(f"- Pfadverweise nicht vorhanden: {dead} (assets/-Pfade nur INFO, falls Bilder lokal fehlen)\n")
return len(skills)
# --------------------------------------------------------------------------
def image_info(p: Path) -> str:
try:
from PIL import Image # type: ignore
except ImportError:
return "Format nicht geprüft (Pillow fehlt)"
try:
with Image.open(p) as im:
return f"{im.width}×{im.height} {im.mode} {im.format}"
except Exception as exc: # noqa: BLE001
return f"nicht lesbar ({exc.__class__.__name__})"
def sec_bilder(root: Path, show_formats: bool) -> None:
print("## 4 Abbildungen\n")
man = root / "manuskript"
markers: dict[str, str] = {}
paths: dict[str, str] = {}
captions: dict[str, str] = {}
for p in sorted(man.glob("*.md")):
lines = (read(p) or "").splitlines()
for i, line in enumerate(lines, 1):
m = re.match(r">\s*\*\*Abbildung\s+(I-[0-9A-Za-z]+)", line)
if m:
captions[m.group(1)] = f"{p.name}:{i}"
m = re.search(r"<!--\s*illustration:\s*(\S+)\s*-->", line)
if m:
path = m.group(1)
fid = ID_RE.search(Path(path).name)
key = fid.group(0) if fid else Path(path).stem
markers[key] = f"{p.name}:{i}"
paths[key] = path
ids_marker = set(markers)
print(f"- Marker im Manuskript: {len(ids_marker)} · Captions mit ID: {len(captions)}")
if set(captions) != ids_marker:
flag("ABWEICHUNG", f"Caption ohne Marker: {fmt_ids(set(captions) - ids_marker)} · Marker ohne Caption: {fmt_ids(ids_marker - set(captions))}")
missing = [k for k, v in paths.items() if not (root / v).exists()]
illu = root / "assets/illustrations"
files = sorted(p for p in illu.rglob("*") if p.suffix.lower() in IMG_EXT) if illu.exists() else []
if not files:
print("- Bilddateien unter assets/illustrations/: nicht vorhanden (Archiv ohne Bilder?)")
flag("INFO", f"{len(missing)} Marker zeigen auf Bilddateien, die hier nicht vorhanden sind")
else:
print(f"- Bilddateien unter assets/illustrations/: {len(files)}")
for k in sorted(missing, key=sort_key):
flag("ABWEICHUNG", f"{k}: Marker {markers[k]} → `{paths[k]}` nicht vorhanden")
ids_files = {ID_RE.search(f.name).group(0) for f in files if ID_RE.search(f.name)}
orphans = ids_files - ids_marker
if orphans:
flag("INFO", f"Bilddateien ohne Marker: {fmt_ids(orphans)}")
amap = read(illu / "ASSET-MAP.md")
ids_map = set(ID_RE.findall(amap)) if amap else set()
prompts = root / "prompts/imagine"
ids_prompt = {ID_RE.search(p.name).group(0) for p in prompts.glob("*.md") if ID_RE.search(p.name)} if prompts.exists() else set()
rhai = read(root / ".grok/workflows/vollpruefung.rhai") or ""
m = re.search(r"let\s+figs\s*=\s*\[(.*?)\]", rhai, re.S)
ids_wf = set(ID_RE.findall(m.group(1))) if m else set()
print("\n| Quelle | Zahl | fehlt ggü. Manuskript | zusätzlich ggü. Manuskript |\n|---|---:|---|---|")
print(f"| Manuskript-Marker | {len(ids_marker)} | – | – |")
for name, s, present in [("assets/illustrations/ASSET-MAP.md", ids_map, amap is not None),
("prompts/imagine/", ids_prompt, prompts.exists()),
(".grok/workflows/vollpruefung.rhai figs", ids_wf, bool(m))]:
if not present:
print(f"| {name} | nicht vorhanden | – | – |")
continue
print(f"| {name} | {len(s)} | {fmt_ids(ids_marker - s)} | {fmt_ids(s - ids_marker)} |")
if s != ids_marker:
flag("ABWEICHUNG", f"{name}: {len(s)} IDs vs. {len(ids_marker)} Marker (fehlt {fmt_ids(ids_marker - s)}; zusätzlich {fmt_ids(s - ids_marker)})")
for f in ["README.md", "abbildungen.md", ".grok/skills/total-check/SKILL.md"]:
t = read(root / f) or ""
for i, line in enumerate(t.splitlines(), 1):
if "Alt-Soll" in line:
continue
for mm in re.finditer(r"\b(\d{2})\s+(?:Dateien|Abbildungen|IDs|Bilder|Figuren|Marker)\b", line):
if int(mm.group(1)) != len(ids_marker) and 20 <= int(mm.group(1)) <= 99:
flag("INFO", f"{f}:{i} nennt „{mm.group(0)}“, Manuskript hat {len(ids_marker)} Marker")
if show_formats and files:
print("\n| Datei | Format |\n|---|---|")
for f in files:
info = image_info(f)
print(f"| {rel(root, f)} | {info} |")
mw = re.match(r"(\d+)×", info)
if mw and not 1800 <= int(mw.group(1)) <= 2048:
flag("INFO", f"{rel(root, f)}: Breite {mw.group(1)} px außerhalb Kanon 1800–2048 (abbildung/references/anmutung.md)")
print()
# --------------------------------------------------------------------------
def sec_stand(root: Path) -> None:
print("## 5 Stichtag und Auflage\n")
cands = ["pdf/front/impressum.tex", "pdf/front/vorwort.tex", "manuskript/vorwort.md",
"manuskript/kolophon.md", "manuskript/ueber-den-autor.md", "scripts/prepare_epub.py",
"pdf/metadata.yaml", "justfile"]
cands += [rel(root, p) for p in sorted((root / "marketing").glob("*.md"))] if (root / "marketing").exists() else []
stand_rx = re.compile(rf"(?:Stand[^\n]{{0,40}}?|,\s*)\b((?:{MONTHS})\s+20\d\d)|Stand:\s*(20\d\d)\b")
aufl_rx = re.compile(r"(\d+)\.(?:~|\s)?Auflage(?:\s+20\d\d)?")
stands, aufls = {}, {}
print("| Datei:Zeile | Fund |\n|---|---|")
for f in cands:
t = read(root / f)
if t is None:
print(f"| {f} | nicht vorhanden |")
continue
for i, line in enumerate(t.splitlines(), 1):
for m in stand_rx.finditer(line):
v = m.group(1) or m.group(2)
stands.setdefault(v, []).append(f"{f}:{i}")
print(f"| {f}:{i} | Stichtag/Datum „{v}“ |")
for m in aufl_rx.finditer(line):
aufls.setdefault(m.group(1), []).append(f"{f}:{i}")
print(f"| {f}:{i} | „{m.group(0).replace('~', ' ')}“ |")
if len({v for v in stands if not re.fullmatch(r"20\d\d", v)}) > 1:
flag("ABWEICHUNG", "Stichtag uneinheitlich: " + " · ".join(f"{k} ({', '.join(v)})" for k, v in stands.items()))
if len(aufls) > 1:
flag("ABWEICHUNG", "Auflage uneinheitlich: " + " · ".join(f"{k}. ({', '.join(v)})" for k, v in aufls.items()))
print()
# --------------------------------------------------------------------------
def pdfinfo(p: Path) -> dict[str, str]:
if not shutil.which("pdfinfo"):
return {}
r = subprocess.run(["pdfinfo", str(p)], capture_output=True, text=True)
return {k.strip(): v.strip() for k, v in (l.split(":", 1) for l in r.stdout.splitlines() if ":" in l)}
def spine(pages: int) -> str:
inch = pages * SPINE_WHITE
return f"{inch:.4f} in = {inch * 25.4:.2f} mm"
def sec_seiten(root: Path) -> None:
print("## 6 Seiten, Print und Umschlag\n")
dist = root / "dist"
newest_src = max((p.stat().st_mtime for p in (root / "manuskript").glob("*.md")), default=0)
measured_print = None
print("| Ausgabe | Seiten | Format | MB | Stand ggü. Manuskript |\n|---|---:|---|---:|---|")
for name in ["die-ki-hinter-dem-vorhang.pdf", "die-ki-hinter-dem-vorhang-print.pdf",
"die-ki-hinter-dem-vorhang.epub", "kdp-cover-wrap.pdf"]:
p = dist / name
if not p.exists():
print(f"| dist/{name} | nicht vorhanden | – | – | – |")
continue
info = pdfinfo(p) if p.suffix == ".pdf" else {}
if p.suffix != ".pdf":
pages = "–"
elif not shutil.which("pdfinfo"):
pages = "pdfinfo fehlt"
else:
pages = info.get("Pages", "nicht lesbar")
size = re.sub(r"\s*pts.*", " pt", info.get("Page size", "–"))
stale = "älter (STALE?)" if p.stat().st_mtime < newest_src else "neuer"
print(f"| dist/{name} | {pages} | {size} | {p.stat().st_size / 1e6:.1f} | {stale} |")
if name.endswith("-print.pdf") and pages.isdigit():
measured_print = int(pages)
jf = read(root / "justfile") or ""
m = re.search(r"^_print_pages\s*:=\s*['\"]?(\d+)", jf, re.M)
jp = int(m.group(1)) if m else None
m2 = re.search(r"build_kdp_cover_wrap\.py[^\n]*--pages\s+(\S+)", jf)
cover_arg = m2.group(1) if m2 else None
src = read(root / "scripts/build_kdp_cover_wrap.py") or ""
m3 = re.search(r"^DEFAULT_PAGES\s*=\s*(\d+)", src, re.M)
print()
print(f"- justfile `_print_pages`: {jp if jp is not None else 'nicht vorhanden'}")
print(f"- justfile kdp-cover `--pages`: {cover_arg or 'nicht vorhanden'}")
print(f"- build_kdp_cover_wrap.py DEFAULT_PAGES: {m3.group(1) if m3 else 'nicht vorhanden'}")
print(f"- Print-PDF gemessen: {measured_print if measured_print else 'nicht vorhanden'}")
if jp is None and cover_arg and cover_arg.isdigit():
flag("ABWEICHUNG", f"justfile: Seitenzahl {cover_arg} fest in kdp-cover, keine Variable `_print_pages`")
if cover_arg and jp is not None and cover_arg not in {str(jp), "{{_print_pages}}"}:
flag("ABWEICHUNG", f"kdp-cover --pages {cover_arg} ≠ _print_pages {jp}")
ref = jp or (int(cover_arg) if cover_arg and cover_arg.isdigit() else None)
if ref:
print(f"- Rücken bei {ref} S. (weiß): {spine(ref)}")
if measured_print:
print(f"- Rücken bei gemessenen {measured_print} S.: {spine(measured_print)}")
if ref and ref != measured_print:
flag("ABWEICHUNG", f"Umschlag-Seitenzahl {ref} ≠ gemessenes Print-PDF {measured_print} → _print_pages und kdp-cover gemeinsam nachziehen")
# Seitenangaben in Doku
for f in ["README.md", "HANDOFF.md"] + ([rel(root, p) for p in sorted((root / "marketing").glob("*.md"))] if (root / "marketing").exists() else []):
t = read(root / f) or ""
for i, line in enumerate(t.splitlines(), 1):
if not re.search(r"[Pp]rint|Rücken|[Ss]pine|Umschlag|[Cc]over|--pages|Paperback", line):
continue
for mm in re.finditer(r"(?:--pages\s+|bei\s+|aktuell\s+|~\s?)(\d{3})\b(?:\s*S(?:\.|eiten))?", line):
v = int(mm.group(1))
if 100 <= v <= 400 and ref and v != ref:
flag("INFO", f"{f}:{i} Seitenangabe {v} (Referenz {ref})")
print()
# --------------------------------------------------------------------------
def sec_handoff(root: Path) -> None:
print("## 7 HANDOFF\n")
t = read(root / "HANDOFF.md")
if t is None:
print("- HANDOFF.md nicht vorhanden\n")
return
n = len(t.splitlines())
heads = [h.strip("# ").strip() for h in re.findall(r"^#{2,3} .+$", t, re.M)]
print(f"- Zeilen: {n} (Ziel ≤ 60) · Abschnitte: {', '.join(heads) or '–'}")
if n > 60:
flag("INFO", f"HANDOFF.md hat {n} Zeilen (Ziel ≤ 60)")
labels = heads + re.findall(r"^\*\*([^*]+?):?\*\*", t, re.M)
for need in ["Stand", "AE", "Offen", "Nicht still"]:
if not any(need.lower() in h.lower() for h in labels):
flag("INFO", f"HANDOFF.md: Abschnitt „{need}“ fehlt (Vorlage: .grok/skills/status/references/handoff-vorlage.md)")
m = re.search(r"(20\d\d-\d\d-\d\d)", t)
print(f"- erstes Datum im HANDOFF: {m.group(1) if m else 'keins'}\n")
# --------------------------------------------------------------------------
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--root", help="Repo-Root (Standard: aktuelles Verzeichnis, sonst Elternordner von scripts/)")
ap.add_argument("--strict", action="store_true", help="Exit 1 bei ABWEICHUNG")
ap.add_argument("--formate", action="store_true", help="Bildformate je Datei listen (Pillow)")
args = ap.parse_args()
if args.root:
root = Path(args.root).resolve()
elif (Path.cwd() / "manuskript").is_dir():
root = Path.cwd()
else:
root = Path(__file__).resolve().parent.parent
if not (root / "manuskript").is_dir():
print(f"Kein Buch-Repo unter {root} (manuskript/ fehlt)", file=sys.stderr)
return 2
print(f"# Konsistenz-Check – {root.name}\n")
print("_Nur lesend. Zahlen sind gemessen; „nicht vorhanden“ heißt: Datei fehlt in diesem Arbeitsbaum._\n")
n_kap = sec_kapitel(root)
n_sk = sec_grok(root)
sec_meta(root, n_kap, n_sk)
sec_bilder(root, args.formate)
sec_stand(root)
sec_seiten(root)
sec_handoff(root)
print("## Befunde\n")
order = {"ABWEICHUNG": 0, "INFO": 1}
seen = set()
for lvl, txt in sorted(findings, key=lambda x: order.get(x[0], 9)):
if (lvl, txt) in seen:
continue
seen.add((lvl, txt))
print(f"- **{lvl}** {txt}")
n_dev = sum(1 for l, _ in seen if l == "ABWEICHUNG")
n_info = sum(1 for l, _ in seen if l == "INFO")
print(f"\n**Summe:** {n_dev} ABWEICHUNG · {n_info} INFO")
print("_Abweichungen sind Befunde, keine Aufträge: Inhalt → AE-Frage, Meta → Doku nachziehen._")
return 1 if (args.strict and n_dev) else 0
if __name__ == "__main__":
raise SystemExit(main())
+39
View File
@@ -0,0 +1,39 @@
#!/bin/sh
# Prüft .grok/hooks/protect-pipeline.sh mit festen Fällen. Nur lesend.
# Aufruf aus dem Repo-Root: sh scripts/test_hook.sh → Exit 0 = alle Fälle wie erwartet.
H=.grok/hooks/protect-pipeline.sh
FAIL=0; N=0
check() { # $1 = erwartet (deny|allow), $2 = Befehl
N=$((N+1))
OUT=$(printf '{"tool_input":{"command":"%s"}}' "$2" | sh "$H" 2>/dev/null)
case "$OUT" in *'"deny"'*) GOT=deny ;; *) GOT=allow ;; esac
if [ "$GOT" = "$1" ]; then echo "ok $1 $2"; else echo "FEHLER erwartet $1, war $GOT: $2"; FAIL=$((FAIL+1)); fi
}
check deny "git push origin main -f"
check deny "git push -f origin main"
check deny "git push --force origin main"
check deny "git push --force-with-lease"
check deny "git push origin +main"
check deny "git reset --hard HEAD~1"
check deny "git clean -fd"
check deny "git commit --no-verify -m x"
check deny "git commit --amend"
check deny "git rebase main"
check deny "git filter-branch"
check deny "git stash"
check deny "git stash pop"
check deny "git stash drop"
check deny "git branch -D rework"
check deny "git tag -d pre-x"
check deny "git checkout -- ."
check deny "git restore ."
check deny "rm -rf /"
check allow "git push origin main"
check allow "git push --follow-tags origin main"
check allow "git stash list"
check allow "git restore manuskript/x.md"
check allow "git tag pre-optimierung-2026-10-29"
check allow "git merge --ff-only optimierung"
check allow "rm -rf build"
echo "$((N-FAIL))/$N wie erwartet"
[ "$FAIL" -eq 0 ]