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:
1 parent
d81ee6d638
commit
8a92268572
24 files changed
+859
-106
No files matched your search
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
@@ -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/**)",
|
||||
]
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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:**
|
||||
|
||||
@@ -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` |
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
|
||||
@@ -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). ";
|
||||
|
||||
@@ -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
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Executable
+472
@@ -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())
|
||||
Executable
+39
@@ -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 ]
|
||||
Reference in new issue
Block a user