S07: Teilgebiet 01 Iteration B (Iterationen B1, B1.5, B2) durchgezogen. Neue Datei build/build-reference-docx.py baut templates/reference.docx programmatisch aus Pandocs Default-Reference (Python-Stdlib only, kein pip; pandoc --print-default-data-file zur Laufzeit, ZIP entpacken, ElementTree-XML-Anpassungen, repacken). B1: Theme major+minor und alle direkten Schrift-Refs in styles.xml auf Calibri umgestellt (Code-Schriften wie Consolas bleiben), Tabellen-Default-Stil mit tblBorders=none auf allen Sides. B1.5: Body-DocDefault 11 pt, Heading 1/2/3 auf 15/13/12 pt analog PDF. B2: header1.xml (Default ab Seite 2 mit Name links und Lebenslauf rechts), header2.xml (leer fuer Seite 1 via titlePg), footer1.xml (rechts Seite n / m mit PAGE/NUMPAGES-Feldern, doppelt referenziert als default und first damit Seite 1 trotz titlePg den Footer hat). Page-Setup explizit in sectPr: A4 mit 2.2 cm oben/unten und 2.5 cm links/rechts analog PDF, Tab-Stop am rechten Textrand 9072 dxa. Beziehungen mit dynamisch naechster freier rId in document.xml.rels, Content-Types-Overrides in [Content_Types].xml, sectPr regex-ersetzt idempotent. Sandbox-End-to-End mit Pandoc 2.9 verifiziert (sectPr und Header/Footer im generierten DOCX vorhanden). Auf Thomas System: DOCX visuell bestaetigt. teilgebiete/01-lebenslauf.md um vollstaendigen Iteration-B-Block ergaenzt, Naechste-Schritte-Liste auf B3, B4, C, D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit Hinweisen zur reference-docx-Pipeline (manuell vor build.ps1 aufrufen, nicht von Hand in Word editieren) und zur Edit-Tool-Truncation auf dem NTFS-Mount. Build-UX-Fix in build.ps1 mit 3-Sekunden-Pause pro fehlgeschlagenem Schritt war ebenfalls Teil dieser Session.

This commit is contained in:
tlg
2026-04-26 13:29:31 +02:00
parent b9c5c08a69
commit 3cec98d9d9
9 changed files with 464 additions and 21 deletions

View File

@@ -1,2 +1,2 @@
S07
PDF-Build-Fehler endgueltig behoben. S06-Hotfix (array, calc, providecommand real) loeste das Problem nicht; nach Rebuild kam unveraendert "! LaTeX Error: No counter 'none' defined." Eigentliche Ursache: Pandoc 3.x emittiert fuer unnummerierte Tabellen direkt vor begin{longtable} die Zeile def LTcaptype none, ohne den Counter none zu definieren. Pandocs eigene Default-Vorlage definiert ihn (commit d835461 in Pandoc 3.8.2.1), Custom-Templates muessen das selbst tun (siehe Pandoc-Issue 11201). Fix: eine Zeile newcounter none direkt nach providecommand real im Tabellen-Block des Templates. Sandbox-Reproduktion exakter Fehlertext ohne Fix, sauberes PDF mit Fix. Auf Thomas' System: PDF wird erzeugt, Ausbildungs-Layout im PDF visuell bestaetigt. Iteration A fuer Teilgebiet 01 damit inhaltlich abgeschlossen. Build-UX-Fix: build.ps1 ergaenzt um Start-Sleep -Seconds 3 nach jedem fehlschlagenden Build-Schritt (Pflichtdatei-Check, PDF-Build, DOCX-Build), damit die rote Fehlerzeile lesbar bleibt bevor das PowerShell-Fenster zugeht. teilgebiete/01-lebenslauf.md um beide Fixes ergaenzt (zweistufige Hotfix-Geschichte, PDF-Bestaetigung, UX-Fix). agent-prompt.md Aktueller-Stand-Abschnitt fuer S08 fortgeschrieben mit verbleibender Iterationsreihenfolge B-C-D, Hinweise auf Pandoc-Versionsunterschied praeziser formuliert.
Teilgebiet 01 Iteration B (Iterationen B1, B1.5, B2) durchgezogen. Neue Datei build/build-reference-docx.py baut templates/reference.docx programmatisch aus Pandocs Default-Reference (Python-Stdlib only, kein pip; pandoc --print-default-data-file zur Laufzeit, ZIP entpacken, ElementTree-XML-Anpassungen, repacken). B1: Theme major+minor und alle direkten Schrift-Refs in styles.xml auf Calibri umgestellt (Code-Schriften wie Consolas bleiben), Tabellen-Default-Stil mit tblBorders=none auf allen Sides. B1.5: Body-DocDefault 11 pt, Heading 1/2/3 auf 15/13/12 pt analog PDF. B2: header1.xml (Default ab Seite 2 mit Name links und Lebenslauf rechts), header2.xml (leer fuer Seite 1 via titlePg), footer1.xml (rechts Seite n / m mit PAGE/NUMPAGES-Feldern, doppelt referenziert als default und first damit Seite 1 trotz titlePg den Footer hat). Page-Setup explizit in sectPr: A4 mit 2.2 cm oben/unten und 2.5 cm links/rechts analog PDF, Tab-Stop am rechten Textrand 9072 dxa. Beziehungen mit dynamisch naechster freier rId in document.xml.rels, Content-Types-Overrides in [Content_Types].xml, sectPr regex-ersetzt idempotent. Sandbox-End-to-End mit Pandoc 2.9 verifiziert (sectPr und Header/Footer im generierten DOCX vorhanden). Auf Thomas System: DOCX visuell bestaetigt. teilgebiete/01-lebenslauf.md um vollstaendigen Iteration-B-Block ergaenzt, Naechste-Schritte-Liste auf B3, B4, C, D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit Hinweisen zur reference-docx-Pipeline (manuell vor build.ps1 aufrufen, nicht von Hand in Word editieren) und zur Edit-Tool-Truncation auf dem NTFS-Mount. Build-UX-Fix in build.ps1 mit 3-Sekunden-Pause pro fehlgeschlagenem Schritt war ebenfalls Teil dieser Session.

View File

@@ -88,14 +88,19 @@ Setze zwischen sinnvollen Zwischenständen Checkpoints (z.B. nach "Marketing.md
**Letzte Session:** S07 (2026-04-26)
**Was wurde gemacht:**
- **PDF-Build-Fehler endgültig behoben.** Der S06-Hotfix (array, calc, providecommand real) hat das Problem nicht beseitigt — nach Rebuild auf Thomas' System kam unverändert `! LaTeX Error: No counter 'none' defined.` Eigentliche Ursache: Pandoc 3.x emittiert für unnummerierte Tabellen direkt vor `\begin{longtable}` die Zeile `\def\LTcaptype{none}`, ohne den Counter `none` zu definieren. Sobald longtable intern `\refstepcounter{\LTcaptype}` aufruft, bricht LaTeX ab. Pandocs eigene Default-Vorlage definiert den Counter (commit d835461 in 3.8.2.1), Custom-Templates müssen das selbst tun. Siehe [Pandoc-Issue #11201](https://github.com/jgm/pandoc/issues/11201). Fix: eine Zeile `\newcounter{none}` direkt nach `\providecommand{\real}` im Tabellen-Block des Templates. In der Sandbox sauber reproduziert (gleicher Fehlertext ohne Fix, sauberes PDF mit Fix). Auf Thomas' System: PDF wird erzeugt, Ausbildungs-Layout im PDF visuell bestätigt.
- **Iteration A für Teilgebiet 01 damit inhaltlich abgeschlossen** (Tabellen-Variante mit zwei Spalten ca. 14 % / 80 %, rahmenlos im PDF, native Word-Tabelle im DOCX — Rahmen-Aus folgt in Iteration B).
- **Build-UX-Fix:** `build/build.ps1` ergänzt um `Start-Sleep -Seconds 3` nach jedem fehlschlagenden Build-Schritt (Pflichtdatei-Check, PDF-Build, DOCX-Build). Vorher schloss sich das PowerShell-Fenster bei Doppelklick auf das Skript so schnell, dass die rote Fehlerzeile nicht lesbar war. Bei mehreren Fehlern in einem Lauf akkumulieren sich die Pausen — gewollt.
- **PDF-Build-Fehler endgültig behoben.** Pandoc-3.x-`\def\LTcaptype{none}`-Bug ([Issue #11201](https://github.com/jgm/pandoc/issues/11201)). Fix: `\newcounter{none}` im Template, sandbox-reproduziert. PDF läuft auf Thomas' System, Ausbildungs-Layout visuell bestätigt. **Iteration A damit inhaltlich abgeschlossen.**
- **Build-UX-Fix:** `build/build.ps1` mit `Start-Sleep -Seconds 3` pro fehlschlagendem Schritt, damit das PowerShell-Fenster bei Fehler nicht zu schnell schließt.
- **Iteration B durchgezogen — `reference.docx` programmatisch via `build/build-reference-docx.py` (Python-Stdlib only, kein pip).** Holt Pandoc-Default-Reference per `pandoc --print-default-data-file`, entpackt die DOCX als ZIP, modifiziert XML mit ElementTree, repackt.
- **B1 — Schriften:** Theme `majorFont` und `minorFont` beide auf Calibri (Pandoc 3.x setzt Defaults auf Aptos Display / Aptos). Defensive Maßnahme: alle direkten `<w:rFonts>`-Referenzen außerhalb von Code-Schriften (Consolas, Courier, ...) auf Calibri.
- **B1 — Tabellen:** Stil `Table` mit `<w:tblBorders val="none">` auf allen Sides. Word-Editor zeigt weiterhin Tabellen-Anzeige-Hilfslinien (kein Druck-Rendering); Druckansicht und PDF-Export sind sauber rahmenlos.
- **B1.5 — Schriftgrößen analog PDF:** DocDefault Body 11 pt, Heading 1/2/3 auf 15/13/12 pt. DOCX schrumpft von 10 auf 9 Seiten.
- **B2 — Header, Footer, Page-Setup:** `header1.xml` (Default ab Seite 2: Name links, „Lebenslauf" rechts), `header2.xml` (leer für Seite 1 via `titlePg`), `footer1.xml` (rechts „Seite n / m" mit `PAGE`/`NUMPAGES`-Feldern, einmal als `default`, einmal als `first` referenziert, damit Seite 1 trotz titlePg den Footer hat). Page-Setup explizit: A4 mit 2.2 cm oben/unten, 2.5 cm links/rechts (analog PDF). Tab-Stop am rechten Textrand 9072 dxa = 16 cm. Beziehungen werden mit dynamisch ermittelter nächster freier `rId` registriert; Content-Types-Overrides ergänzt; sectPr regex-basiert ersetzt (idempotent gegen `<w:sectPr/>` und längere Varianten). Pandoc 2.9 und 3.x übernehmen die sectPr ins generierte DOCX (in der Sandbox end-to-end verifiziert). DOCX-Layout von Thomas visuell bestätigt: Seite 1 ohne Header und mit Footer, Seite 2 ff. Header und Footer wie gewünscht, Tab-Stops bündig am rechten Textrand.
**Nächste Aufgabe:** Teilgebiet 01 — drei verbleibende Iterationen in dieser Reihenfolge:
1. **B) `templates/reference.docx` in Word polieren** — Header/Footer setzen, Schriften auf Calibri vereinheitlichen, Listen-Schutz „Keep with next" und Widow-Control via Word-Stile, Tabellen-Stile so konfigurieren, dass die Ausbildungs-Tabelle ohne Rahmen rendert (Default-Tabellenstil oder benannter Stil).
2. **C) Foto-Einbindung** in cv.md mit Pandoc-Image-Syntax und Template-Anpassung für Position/Größe (z.B. oben rechts neben Name, ca. 3 cm).
3. **D) Hyphenation-Feintuning für PDF** — kurze Wortteile am Zeilenanfang mit höherer Penalty oder gezielten `\hyphenation`-Ausnahmen reduzieren. Iterativ.
**Nächste Aufgabe:** Teilgebiet 01 — verbleibende Iterationen:
1. **B3 — Keep with next + Widow/Orphan-Control für DOCX.** Schusterjungen-Schutz analog `\widowpenalty`/`needspace` im PDF. Auf Stilebene `<w:keepNext/>` für Headings und `<w:widowControl w:val="true"/>` als DocDefault.
2. **B4 (optional) — Heading-Farben auf DesTEngS-Blau und/oder Trennlinien analog PDF.** Eher Kosmetik bei Vorlage für Consulting-Agenturen.
3. **C — Foto-Einbindung** in cv.md mit Pandoc-Image-Syntax und Template-Anpassung für Position/Größe (z.B. oben rechts neben Name, ca. 3 cm).
4. **D — Hyphenation-Feintuning für PDF** — kurze Wortteile am Zeilenanfang mit höherer Penalty oder `\hyphenation`-Ausnahmen reduzieren.
Nach D): Status von Teilgebiet 01 in `zentral-index.md` auf „abgeschlossen" setzen (R2-OK von Thomas). Anschließend nächstes Teilgebiet nach Priorität (laut Index Teilgebiet 02 „Zeugnis von ASMPT").
@@ -108,3 +113,5 @@ Nach D): Status von Teilgebiet 01 in `zentral-index.md` auf „abgeschlossen" se
- **Sandbox kann nichts an `.git/` schreiben** (NTFS-Permission-Issue): Lock-Files, korrupte Index — alles muss von PowerShell aus repariert werden.
- **`checkpoint.ps1` ist robust** gegen Anführungszeichen, Pipes, Whitespace-Anomalien und Index-Lock-Reste. `.checkpoint-pending.txt` darf ganz normal Sonderzeichen enthalten.
- **`build.ps1` pausiert bei Fehler 3 Sekunden pro fehlgeschlagenem Schritt.** Nicht überrascht sein, wenn ein fehlerhafter Lauf entsprechend länger braucht.
- **`build/build-reference-docx.py` muss VOR `build.ps1` manuell aufgerufen werden, wenn `templates/reference.docx` neu gebaut werden soll.** Das Skript ist nicht in `build.ps1` integriert (würde jeden Build verlangsamen und Pandoc-Default-Reference jedes Mal neu ziehen). Wenn jemand die `reference.docx` von Hand in Word editiert, gehen die Änderungen beim nächsten Skript-Lauf verloren — Stile gehören also ins Skript, nicht in Word.
- **Edit-Tool kann Dateien beim Schreiben über den NTFS-Mount truncatieren** (mehrfach in S07 erlebt am Python-Skript). `mcp__workspace__bash` mit `cat <<'EOF' > path` ist die zuverlässige Alternative für längere Dateien (>~150 Zeilen). Nach jedem Edit auf NTFS-Mount-Datei: `wc -l` und `tail -c` zur Verifikation.

View File

@@ -0,0 +1,401 @@
#!/usr/bin/env python3
"""
build-reference-docx.py
=======================
Baut die templates/reference.docx fuer die Pandoc-DOCX-Pipeline aus der
Pandoc-Default-Reference, mit gezielten Anpassungen.
Iteration B1 + B1.5 + B2 (aktuell):
B1 - Theme-Schriften (majorFont und minorFont) beide auf Calibri.
B1 - Direkte Schriftnamen-Referenzen in styles.xml auf Calibri
(Code-Schriften wie Consolas bleiben).
B1 - Tabellen-Default-Stil "Table" mit tblBorders=none.
B1.5 - Body-DocDefault 11pt, Heading 1/2/3 auf 15/13/12 pt.
B2 - Header (Name links, "Lebenslauf" rechts) ab Seite 2; Seite 1 mit
leerem Header (titlePg-Mechanik). Footer (rechts: Seite n / m) auf
allen Seiten inkl. Seite 1 (footer-Ref fuer "first" zeigt auf den
gleichen Footer wie "default"). Page-Setup explizit: A4, Raender
analog PDF (top/bottom 2.2 cm, left/right 2.5 cm). Damit ist der
Tab-Stop deterministisch unabhaengig von Word-Locale-Defaults.
Geplant in Folge-Iterationen:
B3 - Heading-Stile mit "keep with next", Widow/Orphan-Control auf Stilebene
B4 - optional Heading-Farben auf DesTEngS-Blau analog PDF
Vorgehen:
1. Pandoc-Default-Reference per `pandoc --print-default-data-file
reference.docx` extrahieren.
2. Als ZIP entpacken.
3. Relevante XML-Dateien anpassen, neue Header/Footer-XMLs anlegen,
Beziehungen und ContentTypes ergaenzen, sectPr setzen.
4. Als neue ZIP-Datei (templates/reference.docx) speichern.
Voraussetzungen: nur Python-Stdlib + Pandoc im PATH.
"""
from __future__ import annotations
import re
import subprocess
import sys
import tempfile
import zipfile
from pathlib import Path
from xml.etree import ElementTree as ET
# --- Pfade -----------------------------------------------------------------
SCRIPT_DIR = Path(__file__).resolve().parent
BASE_DIR = SCRIPT_DIR.parent
TEMPLATES_DIR = BASE_DIR / "templates"
OUTPUT_FILE = TEMPLATES_DIR / "reference.docx"
# --- XML-Namespaces --------------------------------------------------------
NS = {
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
"a": "http://schemas.openxmlformats.org/drawingml/2006/main",
"r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
"rel": "http://schemas.openxmlformats.org/package/2006/relationships",
"ct": "http://schemas.openxmlformats.org/package/2006/content-types",
}
for prefix, uri in NS.items():
ET.register_namespace(prefix, uri)
ET.register_namespace("", NS["rel"])
W = "{%s}" % NS["w"]
A = "{%s}" % NS["a"]
# --- Konfiguration ---------------------------------------------------------
CODE_FONTS = {"consolas", "courier", "courier new", "liberation mono",
"monaco", "menlo", "fira mono", "fira code"}
TARGET_FONT = "Calibri"
SIZE_BODY = 22
SIZE_HEADING1 = 30
SIZE_HEADING2 = 26
SIZE_HEADING3 = 24
HEADING_SIZES = {"Heading1": SIZE_HEADING1,
"Heading2": SIZE_HEADING2,
"Heading3": SIZE_HEADING3}
# Page-Setup (in DXA, 1cm = 566.929 dxa; 1 inch = 1440 dxa)
# A4: 21.0 x 29.7 cm
PAGE_W = 11906 # A4 Breite
PAGE_H = 16838 # A4 Hoehe
MARGIN_TOP = 1247 # 2.2 cm
MARGIN_BOT = 1247 # 2.2 cm
MARGIN_LEFT = 1417 # 2.5 cm
MARGIN_RIGHT = 1417 # 2.5 cm
HEADER_POS = 720 # 1.27 cm vom oberen Seitenrand
FOOTER_POS = 720 # 1.27 cm vom unteren Seitenrand
# Tab-Stop am rechten Textrand: PAGE_W - LEFT - RIGHT = 9072 dxa = 16 cm
HEADER_RIGHT_TAB = PAGE_W - MARGIN_LEFT - MARGIN_RIGHT
HEADER_LEFT = "Dr.-Ing. Thomas Langer"
HEADER_RIGHT = "Lebenslauf"
# --- Hilfsfunktionen -------------------------------------------------------
def log(msg: str) -> None:
print(f"[build-reference-docx] {msg}", flush=True)
XML_DECL = b'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n'
def write_xml(tree: ET.ElementTree, dest: Path) -> None:
body = ET.tostring(tree.getroot(), encoding="utf-8")
dest.write_bytes(XML_DECL + body)
def write_xml_bytes(content: bytes, dest: Path) -> None:
dest.write_bytes(XML_DECL + content)
def fetch_pandoc_default(dest: Path) -> None:
log("Pandoc-Default-Reference extrahieren ...")
result = subprocess.run(
["pandoc", "--print-default-data-file", "reference.docx"],
capture_output=True, check=False,
)
if result.returncode != 0:
sys.stderr.write(result.stderr.decode("utf-8", errors="replace"))
raise SystemExit(f"pandoc liefert Exit-Code {result.returncode}")
dest.write_bytes(result.stdout)
log(f" -> {dest} ({dest.stat().st_size} Bytes)")
def unpack_docx(src: Path, dest_dir: Path) -> None:
with zipfile.ZipFile(src, "r") as z:
z.extractall(dest_dir)
def repack_docx(src_dir: Path, dest: Path) -> None:
files = []
for path in src_dir.rglob("*"):
if path.is_file():
arcname = path.relative_to(src_dir).as_posix()
files.append((path, arcname))
files.sort(key=lambda t: (0 if t[1] == "[Content_Types].xml" else 1, t[1]))
with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as z:
for path, arcname in files:
z.write(path, arcname)
def is_code_font(name: str) -> bool:
return (name or "").strip().lower() in CODE_FONTS
# --- B1: Schriften ---------------------------------------------------------
def set_theme_fonts_to_calibri(theme_xml: Path) -> None:
tree = ET.parse(theme_xml)
root = tree.getroot()
for kind in ("majorFont", "minorFont"):
font = root.find(f".//{A}{kind}")
if font is None:
raise RuntimeError(f"{kind}-Element nicht im Theme")
latin = font.find(f"{A}latin")
if latin is None:
raise RuntimeError(f"{kind}/latin-Element nicht gefunden")
old = latin.get("typeface")
latin.set("typeface", TARGET_FONT)
log(f" Theme {kind}/latin: {old!r} -> {TARGET_FONT!r}")
write_xml(tree, theme_xml)
def replace_direct_fonts_in_styles(styles_xml: Path) -> None:
tree = ET.parse(styles_xml)
root = tree.getroot()
changed = 0
skipped = 0
for rfonts in root.iter(f"{W}rFonts"):
for attr in (f"{W}ascii", f"{W}hAnsi", f"{W}cs", f"{W}eastAsia"):
val = rfonts.get(attr)
if val is None:
continue
if is_code_font(val):
skipped += 1
continue
if val != TARGET_FONT:
rfonts.set(attr, TARGET_FONT)
changed += 1
log(f" styles.xml: {changed} direkte Font-Attribute auf {TARGET_FONT!r}"
f" gesetzt (Code-Fonts unangetastet: {skipped})")
write_xml(tree, styles_xml)
# --- B1: Tabellen ----------------------------------------------------------
def set_table_borders_none(styles_xml: Path) -> None:
tree = ET.parse(styles_xml)
root = tree.getroot()
style = next((s for s in root.findall(f"{W}style")
if s.get(f"{W}styleId") == "Table"), None)
if style is None:
raise RuntimeError("Style 'Table' nicht in styles.xml")
tbl_pr = style.find(f"{W}tblPr") or ET.SubElement(style, f"{W}tblPr")
existing = tbl_pr.find(f"{W}tblBorders")
if existing is not None:
tbl_pr.remove(existing)
borders = ET.SubElement(tbl_pr, f"{W}tblBorders")
for side in ("top", "left", "bottom", "right", "insideH", "insideV"):
e = ET.SubElement(borders, f"{W}{side}")
e.set(f"{W}val", "none")
e.set(f"{W}sz", "0")
e.set(f"{W}space", "0")
e.set(f"{W}color", "auto")
log(" Style 'Table': tblBorders=none auf allen Sides")
write_xml(tree, styles_xml)
# --- B1.5: Schriftgroessen ------------------------------------------------
def set_default_body_size(styles_xml: Path) -> None:
tree = ET.parse(styles_xml)
root = tree.getroot()
docDefaults = root.find(f"{W}docDefaults") or ET.SubElement(root, f"{W}docDefaults")
rPrDefault = docDefaults.find(f"{W}rPrDefault") or ET.SubElement(docDefaults, f"{W}rPrDefault")
rPr = rPrDefault.find(f"{W}rPr") or ET.SubElement(rPrDefault, f"{W}rPr")
for tag in (f"{W}sz", f"{W}szCs"):
elem = rPr.find(tag) or ET.SubElement(rPr, tag)
elem.set(f"{W}val", str(SIZE_BODY))
log(f" DocDefault Body-Schriftgroesse: {SIZE_BODY/2} pt")
write_xml(tree, styles_xml)
def set_heading_sizes(styles_xml: Path) -> None:
tree = ET.parse(styles_xml)
root = tree.getroot()
for style in root.findall(f"{W}style"):
sid = style.get(f"{W}styleId")
if sid not in HEADING_SIZES:
continue
target = HEADING_SIZES[sid]
rPr = style.find(f"{W}rPr") or ET.SubElement(style, f"{W}rPr")
for tag in (f"{W}sz", f"{W}szCs"):
elem = rPr.find(tag) or ET.SubElement(rPr, tag)
elem.set(f"{W}val", str(target))
log(f" Stil {sid!r}: Schriftgroesse {target/2} pt")
write_xml(tree, styles_xml)
# --- B2: Header und Footer ------------------------------------------------
def header_default_xml() -> bytes:
return (
b'<w:hdr xmlns:w="' + NS["w"].encode() + b'">\n'
b' <w:p>\n'
b' <w:pPr>\n'
b' <w:tabs>\n'
b' <w:tab w:val="right" w:pos="' + str(HEADER_RIGHT_TAB).encode() + b'"/>\n'
b' </w:tabs>\n'
b' </w:pPr>\n'
b' <w:r><w:t xml:space="preserve">' + HEADER_LEFT.encode() + b'</w:t></w:r>\n'
b' <w:r><w:tab/><w:t xml:space="preserve">' + HEADER_RIGHT.encode() + b'</w:t></w:r>\n'
b' </w:p>\n'
b'</w:hdr>\n'
)
def header_first_blank_xml() -> bytes:
return (
b'<w:hdr xmlns:w="' + NS["w"].encode() + b'">\n'
b' <w:p/>\n'
b'</w:hdr>\n'
)
def footer_default_xml() -> bytes:
return (
b'<w:ftr xmlns:w="' + NS["w"].encode() + b'">\n'
b' <w:p>\n'
b' <w:pPr>\n'
b' <w:tabs>\n'
b' <w:tab w:val="right" w:pos="' + str(HEADER_RIGHT_TAB).encode() + b'"/>\n'
b' </w:tabs>\n'
b' </w:pPr>\n'
b' <w:r><w:tab/><w:t xml:space="preserve">Seite </w:t></w:r>\n'
b' <w:fldSimple w:instr="PAGE">\n'
b' <w:r><w:t>1</w:t></w:r>\n'
b' </w:fldSimple>\n'
b' <w:r><w:t xml:space="preserve"> / </w:t></w:r>\n'
b' <w:fldSimple w:instr="NUMPAGES">\n'
b' <w:r><w:t>1</w:t></w:r>\n'
b' </w:fldSimple>\n'
b' </w:p>\n'
b'</w:ftr>\n'
)
REL_HEADER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
REL_FOOTER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
CT_HEADER = "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"
CT_FOOTER = "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"
def next_free_rel_id(rels_xml: Path) -> int:
text = rels_xml.read_text(encoding="utf-8")
ids = [int(m.group(1)) for m in re.finditer(r'Id="rId(\d+)"', text)]
return (max(ids) + 1) if ids else 1
def add_relationship(rels_xml: Path, rid: str, rtype: str, target: str) -> None:
text = rels_xml.read_text(encoding="utf-8")
new_rel = f'<Relationship Type="{rtype}" Id="{rid}" Target="{target}" />'
if new_rel in text:
return
text = text.replace("</Relationships>", new_rel + "</Relationships>")
rels_xml.write_text(text, encoding="utf-8")
def add_content_type_override(ct_xml: Path, part_name: str, ct: str) -> None:
text = ct_xml.read_text(encoding="utf-8")
new_override = f'<Override PartName="{part_name}" ContentType="{ct}"/>'
if part_name in text:
return
text = text.replace("</Types>", new_override + "</Types>")
ct_xml.write_text(text, encoding="utf-8")
def update_sectpr_with_headers(document_xml: Path,
header_default_rid: str,
header_first_rid: str,
footer_default_rid: str) -> None:
"""Ersetzt sectPr durch Page-Setup + Header/Footer-Refs + titlePg.
Footer-Ref wird zweimal eingebaut (default und first), beide auf
den gleichen Footer — dann hat Seite 1 trotz titlePg den Footer."""
text = document_xml.read_text(encoding="utf-8")
new_sectpr = (
f'<w:sectPr>'
f'<w:headerReference w:type="default" r:id="{header_default_rid}"/>'
f'<w:headerReference w:type="first" r:id="{header_first_rid}"/>'
f'<w:footerReference w:type="default" r:id="{footer_default_rid}"/>'
f'<w:footerReference w:type="first" r:id="{footer_default_rid}"/>'
f'<w:pgSz w:w="{PAGE_W}" w:h="{PAGE_H}"/>'
f'<w:pgMar w:top="{MARGIN_TOP}" w:right="{MARGIN_RIGHT}"'
f' w:bottom="{MARGIN_BOT}" w:left="{MARGIN_LEFT}"'
f' w:header="{HEADER_POS}" w:footer="{FOOTER_POS}" w:gutter="0"/>'
f'<w:titlePg/>'
f'</w:sectPr>'
)
new_text, n = re.subn(
r'<w:sectPr\s*/>|<w:sectPr>.*?</w:sectPr>',
new_sectpr, text, flags=re.DOTALL,
)
if n == 0:
new_text = text.replace("</w:body>", new_sectpr + "</w:body>")
document_xml.write_text(new_text, encoding="utf-8")
log(f" document.xml sectPr: pgSz/pgMar (A4, 2.2/2.5cm Raender), Header"
f" default+first, Footer default+first auf gleicher rId, titlePg")
def add_header_footer(unpacked: Path) -> None:
word_dir = unpacked / "word"
rels_xml = word_dir / "_rels" / "document.xml.rels"
ct_xml = unpacked / "[Content_Types].xml"
doc_xml = word_dir / "document.xml"
write_xml_bytes(header_default_xml(), word_dir / "header1.xml")
write_xml_bytes(header_first_blank_xml(), word_dir / "header2.xml")
write_xml_bytes(footer_default_xml(), word_dir / "footer1.xml")
log(" word/header1.xml (default), header2.xml (first blank),"
" footer1.xml geschrieben")
next_id = next_free_rel_id(rels_xml)
rid_h_def, rid_h_first, rid_f_def = (f"rId{next_id+i}" for i in range(3))
add_relationship(rels_xml, rid_h_def, REL_HEADER, "header1.xml")
add_relationship(rels_xml, rid_h_first, REL_HEADER, "header2.xml")
add_relationship(rels_xml, rid_f_def, REL_FOOTER, "footer1.xml")
log(f" Beziehungen: {rid_h_def}=header1, {rid_h_first}=header2,"
f" {rid_f_def}=footer1")
add_content_type_override(ct_xml, "/word/header1.xml", CT_HEADER)
add_content_type_override(ct_xml, "/word/header2.xml", CT_HEADER)
add_content_type_override(ct_xml, "/word/footer1.xml", CT_FOOTER)
log(" [Content_Types].xml: Override-Eintraege fuer header1/2 und footer1")
update_sectpr_with_headers(doc_xml, rid_h_def, rid_h_first, rid_f_def)
# --- Hauptablauf -----------------------------------------------------------
def main() -> int:
log(f"Ziel: {OUTPUT_FILE}")
TEMPLATES_DIR.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix="refdocx-") as tmp:
tmp_dir = Path(tmp)
default_docx = tmp_dir / "pandoc-default.docx"
unpacked = tmp_dir / "unpacked"
fetch_pandoc_default(default_docx)
unpacked.mkdir()
unpack_docx(default_docx, unpacked)
theme_xml = unpacked / "word" / "theme" / "theme1.xml"
styles_xml = unpacked / "word" / "styles.xml"
log("Anpassung: Theme major+minor auf Calibri")
set_theme_fonts_to_calibri(theme_xml)
log("Anpassung: Direkte Font-Referenzen in styles.xml -> Calibri")
replace_direct_fonts_in_styles(styles_xml)
log("Anpassung: Tabellen-Default ohne Rahmen")
set_table_borders_none(styles_xml)
log("Anpassung: Body-Schriftgroesse 11 pt (DocDefault)")
set_default_body_size(styles_xml)
log("Anpassung: Heading-Schriftgroessen 15/13/12 pt")
set_heading_sizes(styles_xml)
log("Anpassung: Header und Footer einbauen (B2)")
add_header_footer(unpacked)
log("Repack als reference.docx")
repack_docx(unpacked, OUTPUT_FILE)
log(f" -> {OUTPUT_FILE} ({OUTPUT_FILE.stat().st_size} Bytes)")
log("Fertig.")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -727,7 +727,7 @@ endobj
'
endstream
endobj
155 0 obj
155 0 obj
<< /Subtype /CIDFontType0C /Filter /FlateDecode /Length 4888 >>
stream
xڝ9 TW<><57>4U<0F>)<29><>[<5B>6U
@@ -895,7 +895,7 @@ xref
endobj
xref
0 163
0000000000 65535 f
0000000000 65535 f
0000000020 00000 n
0000016167 00000 n
0000065136 00000 n

View File

@@ -1,4 +1,4 @@
===== Build gestartet: 2026-04-26 10:41:37 =====
===== Build gestartet: 2026-04-26 13:24:16 =====
Source: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\source\cv.md
Template-TEX: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\templates\template.tex
Reference: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\templates\reference.docx
@@ -9,5 +9,5 @@ Cmd: pandoc --from=markdown+smart --pdf-engine=lualatex --template=Q:\DesTEngS\P
PDF OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.pdf (68.2 KB)
--- Pandoc -> DOCX ---
Cmd: pandoc --from=markdown+smart --reference-doc=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\templates\reference.docx --resource-path=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\source --output=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.docx Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\source\cv.md
DOCX OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.docx (20.1 KB)
===== Build beendet: 2026-04-26 10:41:42, Exit-Code 0 =====
DOCX OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.docx (22 KB)
===== Build beendet: 2026-04-26 13:24:21, Exit-Code 0 =====

View File

@@ -43,3 +43,4 @@ Chronologisches Log aller Entscheidungen und Prozessereignisse.
2026-04-25 22:35 | S06 | Tooling-Fix: checkpoint.ps1 robust gegen problematische Eingabedaten gemacht. Commit-Message wird jetzt via Temp-Datei und git commit -F uebergeben statt via -m mit String, damit doppelte Anfuehrungszeichen im Summary nicht mehr das Argument zerlegen (Ursache des fehlgeschlagenen Hotfix-Commits zuvor). Pipe-Zeichen im Summary werden vorab abgelehnt, da sie mit dem Changelog-Format Timestamp Pipe Session Pipe Summary kollidieren. Whitespace und Zeilenumbrueche im Summary werden zu einem einzelnen Leerzeichen normalisiert. Pre-flight-Checks ergaenzt: Existenz von .git, verwaiste .git/index.lock mit klarer Anleitung melden, changelog.md muss vor dem Lauf clean sein. Atomarer Rollback bei Fehler im Hauptablauf: changelog.md wird auf Original-Stand zurueckgeschrieben und Index-Stagung von changelog.md per git restore --staged oder git reset HEAD zurueckgesetzt, sodass der naechste Lauf nicht doppelt anhaengt. Cleanup-Robustheit: Temp-Message-Datei wird in finally geloescht, das Loeschen der Pending-Datei ist nicht mehr fatal sondern nur eine Warnung.
2026-04-25 22:43 | S06 | Session-Abschluss S06. Teilgebiet 01 Iteration A inhaltlich umgesetzt: Ausbildung als 2-Spalten-Layout via Pandoc-Multiline-Tabelle ohne Header, Template um array, calc, booktabs, longtable und providecommand real ergänzt, alle Linienbreiten auf 0pt. DOCX-Layout von Thomas visuell bestätigt (nur Default-Tabellenrahmen offen, Rahmen-Aus in Iteration B). PDF-Build mit Hotfix steht noch aus — erste Aufgabe der nächsten Session ist build.ps1 laufen lassen und das Ausbildungs-Layout im PDF prüfen. Tooling-Fix in checkpoint.ps1 abgeschlossen (Quoting-Bug, Pipe-Validierung, Whitespace-Normalisierung, Pre-flight-Checks, atomarer Rollback bei Fehler). agent-prompt.md Aktueller-Stand-Abschnitt für S07 fortgeschrieben mit verbleibender Iterationsreihenfolge B-C-D, neuen Lehren aus S06 dokumentiert (Sandbox-Reads über NTFS-Mount können stale sein, Sandbox-Pandoc 2.9 vs Thomas' Pandoc 3.x Output-Unterschiede).
2026-04-26 10:58 | S07 | PDF-Build-Fehler endgueltig behoben. S06-Hotfix (array, calc, providecommand real) loeste das Problem nicht; nach Rebuild kam unveraendert "! LaTeX Error: No counter 'none' defined." Eigentliche Ursache: Pandoc 3.x emittiert fuer unnummerierte Tabellen direkt vor begin{longtable} die Zeile def LTcaptype none, ohne den Counter none zu definieren. Pandocs eigene Default-Vorlage definiert ihn (commit d835461 in Pandoc 3.8.2.1), Custom-Templates muessen das selbst tun (siehe Pandoc-Issue 11201). Fix: eine Zeile newcounter none direkt nach providecommand real im Tabellen-Block des Templates. Sandbox-Reproduktion exakter Fehlertext ohne Fix, sauberes PDF mit Fix. Auf Thomas' System: PDF wird erzeugt, Ausbildungs-Layout im PDF visuell bestaetigt. Iteration A fuer Teilgebiet 01 damit inhaltlich abgeschlossen. Build-UX-Fix: build.ps1 ergaenzt um Start-Sleep -Seconds 3 nach jedem fehlschlagenden Build-Schritt (Pflichtdatei-Check, PDF-Build, DOCX-Build), damit die rote Fehlerzeile lesbar bleibt bevor das PowerShell-Fenster zugeht. teilgebiete/01-lebenslauf.md um beide Fixes ergaenzt (zweistufige Hotfix-Geschichte, PDF-Bestaetigung, UX-Fix). agent-prompt.md Aktueller-Stand-Abschnitt fuer S08 fortgeschrieben mit verbleibender Iterationsreihenfolge B-C-D, Hinweise auf Pandoc-Versionsunterschied praeziser formuliert.
2026-04-26 13:29 | S07 | Teilgebiet 01 Iteration B (Iterationen B1, B1.5, B2) durchgezogen. Neue Datei build/build-reference-docx.py baut templates/reference.docx programmatisch aus Pandocs Default-Reference (Python-Stdlib only, kein pip; pandoc --print-default-data-file zur Laufzeit, ZIP entpacken, ElementTree-XML-Anpassungen, repacken). B1: Theme major+minor und alle direkten Schrift-Refs in styles.xml auf Calibri umgestellt (Code-Schriften wie Consolas bleiben), Tabellen-Default-Stil mit tblBorders=none auf allen Sides. B1.5: Body-DocDefault 11 pt, Heading 1/2/3 auf 15/13/12 pt analog PDF. B2: header1.xml (Default ab Seite 2 mit Name links und Lebenslauf rechts), header2.xml (leer fuer Seite 1 via titlePg), footer1.xml (rechts Seite n / m mit PAGE/NUMPAGES-Feldern, doppelt referenziert als default und first damit Seite 1 trotz titlePg den Footer hat). Page-Setup explizit in sectPr: A4 mit 2.2 cm oben/unten und 2.5 cm links/rechts analog PDF, Tab-Stop am rechten Textrand 9072 dxa. Beziehungen mit dynamisch naechster freier rId in document.xml.rels, Content-Types-Overrides in [Content_Types].xml, sectPr regex-ersetzt idempotent. Sandbox-End-to-End mit Pandoc 2.9 verifiziert (sectPr und Header/Footer im generierten DOCX vorhanden). Auf Thomas System: DOCX visuell bestaetigt. teilgebiete/01-lebenslauf.md um vollstaendigen Iteration-B-Block ergaenzt, Naechste-Schritte-Liste auf B3, B4, C, D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit Hinweisen zur reference-docx-Pipeline (manuell vor build.ps1 aufrufen, nicht von Hand in Word editieren) und zur Edit-Tool-Truncation auf dem NTFS-Mount. Build-UX-Fix in build.ps1 mit 3-Sekunden-Pause pro fehlgeschlagenem Schritt war ebenfalls Teil dieser Session.

View File

@@ -140,12 +140,45 @@ Die in S04 mit docx-js erstellte Version hatte strukturelle typographische Mäng
**Build-UX-Fix (S07):** `build/build.ps1` ergänzt um `Start-Sleep -Seconds 3` nach jedem fehlschlagenden Build-Schritt (Pflichtdatei-Check, PDF, DOCX). Bei Doppelklick auf `checkpoint.cmd`-artigen Aufruf schließt sich das PowerShell-Fenster sonst sofort und Fehlermeldungen sind nicht lesbar. Bei mehreren Fehlern in einem Lauf akkumulieren sich die Pausen — gewollt.
## Iteration B (S07) — `reference.docx` programmatisch bauen
**Ansatz:** Anstatt die `reference.docx` manuell in Word zu pflegen (nicht versionierbar, nicht reproduzierbar), wird sie durch ein Python-Skript `build/build-reference-docx.py` aus Pandocs Default-Reference erzeugt und gezielt angepasst. Nur Python-Stdlib (`zipfile`, `xml.etree.ElementTree`, `subprocess`, `re`) — keine pip-Abhängigkeit. Das Skript läuft unter Sandbox-Pandoc 2.9 und Thomas' Pandoc 3.x gleichermaßen, weil es die Pandoc-Default-Reference per `pandoc --print-default-data-file reference.docx` zur Laufzeit zieht. Manueller Aufruf vor jedem `build.ps1`, wenn Stile geändert wurden.
**B1 — Schriften und Tabellen:**
- Theme-Schriften `majorFont` und `minorFont` beide auf `Calibri` umgestellt (Pandoc 3.x setzt sie als Default auf `Aptos Display` und `Aptos`, Sandbox-Pandoc 2.9 auf `Calibri` und `Cambria`).
- Defensive Maßnahme: alle direkten Schriftnamen-Referenzen in `styles.xml` (z.B. `<w:rFonts w:ascii="..." />`) auf Calibri umgestellt, ausgenommen Code-Schriften (Consolas, Courier, ...). In der Pandoc-3.x-Variante kommt das mit 0 Treffern aus, in zukünftigen Pandoc-Versionen mit direkten Heading-Schriftreferenzen würde es greifen.
- Tabellen-Default-Stil `Table` bekommt explizite `<w:tblBorders>` mit `val="none"` auf allen Sides (`top`, `left`, `bottom`, `right`, `insideH`, `insideV`). Word-Editor zeigt die Default-„Tabellenbegrenzungen" weiterhin als Anzeige-Hilfe an (kein Druck-Rendering), Druckansicht und PDF-Export sind sauber rahmenlos.
**B1.5 — Schriftgrößen analog PDF:**
- DocDefault `<w:sz>` auf 22 (= 11 pt Body, analog `template.tex`).
- Heading 1/2/3 explizit auf 30/26/24 (= 15/13/12 pt). Damit ist die Heading-Hierarchie visuell ähnlich zum PDF, ohne den Word-Default-Sprung von 20 pt nach 12 pt.
- Effekt: DOCX schrumpft von 10 auf 9 Seiten (im PDF sind es 7).
**B2 — Header, Footer, Page-Setup:**
- `word/header1.xml` (Default ab Seite 2): links „Dr.-Ing. Thomas Langer", rechts „Lebenslauf" (Tab-Stop am rechten Textrand).
- `word/header2.xml` (erste Seite): leerer `<w:p/>` über `<w:titlePg/>` aktiviert.
- `word/footer1.xml`: rechtsbündig „Seite n / m" mit Word-Feldern `PAGE` und `NUMPAGES`. Wird über zwei `footerReference`-Einträge (`type="default"` und `type="first"`) auf alle Seiten inkl. Seite 1 angewendet — ohne den `type="first"`-Eintrag würde `titlePg` Seite 1 ohne Footer lassen.
- Page-Setup explizit in `<w:sectPr>`: A4 (`pgSz w:w="11906" w:h="16838"`), Ränder 2.2 cm oben/unten, 2.5 cm links/rechts (analog PDF). Damit ist der Tab-Stop an `9072 dxa` (= 16 cm Textbreite) deterministisch unabhängig von Word-Locale-Defaults; ohne explizites Page-Setup waren die Tab-Stops vorher etwa 5 mm zu weit links.
- Beziehungen werden in `word/_rels/document.xml.rels` mit dynamisch ermittelter nächster freier `rId` registriert; Content-Types-Overrides in `[Content_Types].xml` ergänzt; `<w:sectPr>` in `word/document.xml` regex-basiert ersetzt (idempotent gegenüber Pandoc-Defaults `<w:sectPr/>` und längeren Varianten). Pandoc übernimmt die letzte sectPr aus der reference.docx ins generierte DOCX — End-to-End-Test in der Sandbox bestätigt: alle Header/Footer-Refs, pgMar und titlePg sind im finalen DOCX vorhanden.
**Visuelle Bestätigung im Word (S07):**
- Body: Calibri 11 pt; Headings 1/2/3: Calibri 15/13/12 pt.
- Ausbildungs-Tabelle in Druckansicht und PDF-Export rahmenlos.
- Seite 1 ohne Header, mit Footer.
- Seite 2 ff. mit Header (Name links, „Lebenslauf" rechts) und Footer (Seite n / m).
- Tab-Stops „Lebenslauf" und Seitenzahl bündig am rechten Textrand.
## Nächste Schritte
1. **Iteration B — `templates/reference.docx` in Word polieren:** Header/Footer setzen, Schriften auf Calibri vereinheitlichen, Listen-SchutzKeep with next" und Widow-Control via Word-Stile, Stile `DefinitionTerm` und `Definition` für 2-Spalten-Verhalten konfigurieren (z.B. fester linker Einzug auf `Definition`, hängende Einrückung).
2. **Iteration C — Foto-Einbindung:** Portraitfoto in `source/cv.md` einbetten (Pandoc-Image-Syntax), Position und Größe im Template absichern (z.B. oben rechts neben Name, ca. 3 cm).
3. **Iteration DHyphenation-Feintuning für PDF:** Kurze Wortteile am Zeilenanfang mit höherer Penalty oder gezielten `\hyphenation`-Ausnahmen reduzieren. Iterativ.
4. Teilgebiet nach erfolgreichem Output und Freigabe durch Thomas abschließen (R2-OK von Thomas: Status auf „abgeschlossen" im zentral-index.md).
1. **Iteration B3Heading-Stile mitkeep with next" und Widow/Orphan-Control:** Schusterjungen-Schutz für DOCX analog zu `\widowpenalty`/`needspace` im PDF. Auf Stilebene über `<w:keepNext/>` für Headings und `<w:widowControl w:val="true"/>` als DocDefault.
2. **Iteration B4 (optional)** — Heading-Farben auf DesTEngS-Blau und/oder Trennlinien analog PDF, falls das DOCX optisch näher ans PDF heran soll. Bei Vorlage für Consulting-Agenturen, die das Layout ohnehin überschreiben, ist das aber eher Kosmetik.
3. **Iteration CFoto-Einbindung:** Portraitfoto in `source/cv.md` einbetten (Pandoc-Image-Syntax), Position und Größe im Template absichern (z.B. oben rechts neben Name, ca. 3 cm).
4. **Iteration D — Hyphenation-Feintuning für PDF:** Kurze Wortteile am Zeilenanfang mit höherer Penalty oder gezielten `\hyphenation`-Ausnahmen reduzieren. Iterativ.
5. Teilgebiet nach erfolgreichem Output und Freigabe durch Thomas abschließen (R2-OK von Thomas: Status auf „abgeschlossen" im zentral-index.md).
## Artefakte
@@ -153,10 +186,11 @@ Die in S04 mit docx-js erstellte Version hatte strukturelle typographische Mäng
- `artefakte/01-lebenslauf/source/cv.md`**Aktive Quelldatei** (aufbauend auf V10, Draft-Marker entfernt).
- `artefakte/01-lebenslauf/source/foto-wrba_2026_6782_1.jpg` — Portraitfoto (umbenannt, noch nicht in cv.md eingebunden).
- `artefakte/01-lebenslauf/templates/template.tex` — Pandoc-LaTeX-Template für LuaLaTeX (erstes Grundgerüst).
- `artefakte/01-lebenslauf/templates/reference.docx` — Pandoc-Default-Reference-Doc als Ausgangsbasis (Styles noch anzupassen).
- `artefakte/01-lebenslauf/build/build.ps1` — PowerShell-Build-Skript (PDF + DOCX).
- `artefakte/01-lebenslauf/output/` — erzeugte Ausgaben plus `build.log` (leer bis zum ersten erfolgreichen Build).
- `artefakte/01-lebenslauf/templates/template.tex` — Pandoc-LaTeX-Template für LuaLaTeX (Iteration A inkl. Pandoc-3.x-Hotfix `\newcounter{none}`).
- `artefakte/01-lebenslauf/templates/reference.docx` — Pandoc-Reference-Doc, **automatisch erzeugt** durch `build/build-reference-docx.py`. Nicht von Hand editieren — Änderungen würden beim nächsten Skript-Lauf überschrieben.
- `artefakte/01-lebenslauf/build/build-reference-docx.py` — Python-Skript zum Bauen der `reference.docx` (Iteration B1+B1.5+B2). Manuell aufrufen, wenn Stile geändert werden sollen, danach normalen `build.ps1` laufen.
- `artefakte/01-lebenslauf/build/build.ps1` — PowerShell-Build-Skript (PDF + DOCX) mit 3-Sekunden-Pause bei Fehler.
- `artefakte/01-lebenslauf/output/` — erzeugte Ausgaben plus `build.log`.
### Historische Entwürfe (unter `artefakte/01-lebenslauf/entwuerfe/`)