falzmarke
Briefe entstehen hier als Markdown-Datei mit YAML-Frontmatter und werden von einem Renderer gesetzt. Das Layout wird nie von Hand gesetzt — weder durch Positionsangaben im Text noch durch Leerzeilen zum Ausrichten.
Regel 0
Kein PDF ohne grünen check. Der Ablauf ist immer render → Prüfbericht lesen → Vorschau
zeigen. render ruft die Prüfung selbst auf und endet mit Code 2, wenn ein Maß nicht stimmt.
Dasselbe gilt für die E-Mail-Fassung: keine Nachricht ohne grünen verify --email. Auch
email ruft die Prüfung selbst auf und endet mit Code 2, wenn sie nicht besteht.
Ablauf
Umgebung sicherstellen (einmal je Sitzung):
python3 scripts/bootstrap.pySchlägt das fehl, fehlt der Netzwerkzugriff für
pip. Dann abbrechen und das sagen. Es gibt bewusst keinen Ersatz-Renderer: ein zweiter würde ein anderes Layout erzeugen.Profil wählen:
python3 scripts/falzmarke.py profilesGibt es mehrere und ist aus dem Gespräch nicht klar, welcher Absender gemeint ist, einmal nachfragen. Gibt es nur eines, dieses nehmen.
Angaben sammeln. Pflicht sind Empfänger, Datum und Betreff. Fehlende Angaben einmal gesammelt erfragen, nicht einzeln nacheinander. Bei Feldern siehe
references/frontmatter.md.Unterschrift: Trägt das Profil eine
signatur:, erscheint sie auf jedem Brief. Wird der Brief von jemand anderem gezeichnet (unterzeichner: i. A. …) oder soll er von Hand unterschrieben werden, gehörtsignatur: keineins Frontmatter — sonst steht die fremde Unterschrift darunter. Eine andere Unterschrift:signatur: <datei>neben dem Brief.Text formulieren nach
references/stil.md. Diese Datei vor dem Schreiben lesen — sie regelt Anrede, Betreffbildung und den Aufbau je Brieftyp.Datei schreiben als
briefe/JJJJ-MM-TT_<empfänger-slug>_<betreff-slug>.md. Die Markdown-Datei ist die Quelle der Wahrheit, nicht das PDF.Rendern und prüfen:
python3 scripts/falzmarke.py render briefe/2026-08-25_muster-gmbh_angebot.md --pngVorschau zeigen und das PDF bereitstellen. Änderungswünsche in der
.mdeinarbeiten und neu rendern — nie im PDF nachbessern.
Eine E-Mail statt eines Briefes
Wer „schreib eine E-Mail an …" sagt, bekommt dieselbe Datei mit typ: email im Frontmatter —
und an: statt empfaenger:. Die Felder stehen in references/frontmatter.md.
python3 scripts/falzmarke.py lint briefe/2026-08-27_muster-gmbh_angebot.md
python3 scripts/falzmarke.py email briefe/2026-08-27_muster-gmbh_angebot.md --html --oeffnen
Es entstehen .eml (die Nachricht) und mit --html eine Vorschau zum Öffnen im Browser. Diese
Vorschau ist das, was gezeigt wird — nicht die .eml, die ist für das Mailprogramm.
--oeffnen gehört im Gespräch dazu, sobald ein Mensch die Nachricht wirklich abschicken
will: Auf macOS legt es einen Entwurf im Mailprogramm an — Empfänger, Kopie, Betreff, Rumpf
und Anhänge, mit Senden-Knopf. Weglassen, wenn die Nachricht bloß erzeugt oder geprüft wird, und
immer weglassen bei Serien — dreißig Nachrichten wären dreißig Fenster.
Drei Sätze, die dabei nicht fehlen dürfen, weil sie gemessen sind:
- Der Entwurfsweg ist für Outlook für Mac belegt. Auf Windows, unter Linux und in Apple Mail
wird stattdessen die
.emlübergeben — und die erscheint dort als Lesefenster, nicht als Entwurf; der nächste Handgriff heißt dann „Weiterleiten". - Das Mailprogramm setzt seine eigene Konto-Signatur in den Entwurf. Trägt das Profil eine Signatur, steht sie zweimal darin. Wer das nicht will, fährt für diesen Weg ein Profil ohne Signatur.
- Geht gar nichts auf, ist die Datei trotzdem fertig; der Befehl endet mit 0 und nennt den Pfad.
falzmarke versendet nichts. Es gibt keinen Versandbefehl und keine Option, die sendet — auch im Steuerskript des Entwurfs steht keiner. Die Nachricht wird im Mailprogramm geöffnet und dort von einem Menschen abgeschickt. Warum das so bleibt, steht in ADR 0034; wo seit dem 08.09.2026 die Grenze verläuft — Entwurf ja, Senden nie — in ADR 0038. Wer nach einem Versand fragt, bekommt diese Auskunft, keinen Behelf.
Signatur und Logo
Die Signatur kommt aus dem Absender-Profil und steht unter jeder Nachricht. Trägt das Profil ein
email.logo, erscheint es darin. Drei Formen sind zulässig, und die Wahl folgt aus dem Wert: ein
Dateipfad wird als eigener Teil eingebettet und kommt immer an — das ist die Vorgabe für
falzmarke email; eine Adresse (https://…) steht nur im src, und Outlook wie Gmail
blockieren externe Bilder standardmäßig; eine Data-URI reist mit, vergrößert aber jede
Nachricht. Ein SVG ist in allen drei Formen ausgeschlossen. Einzelheiten in
references/frontmatter.md.
Was das Werkzeug dazu sagt, wird weitergegeben. Bei Adresse und Data-URI druckt email unter
der erzeugten Datei einen Satz, der benennt, was die Form beim Empfänger kostet — im MCP-Dienst
ist es das Feld logo.hinweis. Dieser Satz gehört in die Antwort; sonst hält jemand ein Logo für
zugestellt, das bei einem Teil der Empfänger ein leerer Kasten bleibt. Dasselbe gilt für die
Warnung email.logo_kontrast: Bei einer Adresse sagt sie ausdrücklich, dass nicht gemessen
wurde — Messen hieße Abrufen, und das tut falzmarke nicht.
Bringt das Profil eine fertige Signatur mit (email.signatur_html, Pfad neben dem Profil),
wird sie eingesetzt und ersetzt die gebaute — email.logo bleibt dann unbeachtet, das Logo
steckt schon darin. Sie durchläuft dieselbe Prüfung wie eigener Satz; fällt sie durch, endet der
Befehl mit einer Meldung, die Datei und Grund nennt, statt eine halbe Signatur zu setzen. Die
Textfassung dazu ist email.signatur_text.
Weitere Befehle
Fünf Befehle gehören nicht zu jedem Brief und stehen deshalb nicht im Ablauf oben. Zwei davon ändern, wie ein Vorgang anfängt.
serie — eine Vorlage plus eine Datenquelle ergibt n Briefe.
python3 scripts/falzmarke.py serie vorlage.md --daten empfaenger.csv --ziel briefe/ --benennen nachname
Die Vorlage ist ein gewöhnlicher Brief mit {{spalte}} an den Stellen, die aus den Daten kommen
— im Frontmatter wie im Text. Ein Wert wird nie zu Markup: Was in der Datenquelle steht, kommt
als Text an. Ein Datensatz, der nicht durchgeht, bricht diesen ab und nicht die Serie;
angehalten wird nur bei einem Fehler an der Vorlage oder der Datenquelle. --sammel legt
zusätzlich alle Briefe in eine Datei für den Druck.
einlesen — aus einem fertigen PDF ein falzmarke-Markdown zurücklesen.
python3 scripts/falzmarke.py einlesen alter-brief.pdf -o neu.md
Das Ergebnis ist ein Gerüst mit benannten Lücken, kein fertiger Brief. Ein Feld wird nur
gesetzt, wenn es belegbar ist; sonst steht es als Kommentar mit Begründung und, wo es einen
gibt, mit einem Kandidaten. Ein Kandidat ist kein Wert — er wird nicht eingesetzt, sondern
von dem entschieden, der den Brief liest. Wie viel erkannt wird, hängt am Raster: Ohne Falz- und
Lochmarken kommt der Text mit, und die Felder bleiben Lücken. profil ist immer eine Lücke,
auch bei einem Brief, den falzmarke selbst gesetzt hat. Der Befehl endet mit 0, auch wenn Lücken
bleiben — sie sind das erwartete Ergebnis, kein Fehler.
preview — PNG der ersten Seite. Prüft die Eingabe wie render und schreibt bei einem
Fehler kein Bild. Nachgemessen wird trotzdem nichts: Es entsteht kein PDF, also gibt es
keine Geometrie zu messen — eine Vorschau ist deshalb nie ein Beleg. Für das, was jemand
bekommt, bleibt render --png (Regel 0).
init — Frontmatter-Vorlage schreiben.
python3 scripts/falzmarke.py init brief.md --profil meinefirma --form B
Spart das Abtippen der Pflichtfelder. --empfaenger nimmt die Anschriftzeilen mit | getrennt.
mcp — als Dienst über stdio laufen, damit ein Agent render, lint und email als
Werkzeuge aufrufen kann statt über die Kommandozeile. Kein Befehl für den Brief selbst.
Grenzen
- Markdown-Teilmenge (
references/markdown.md): Absätze,**fett**,*kursiv*, Aufzählungen, nummerierte Listen, harter Umbruch (\am Zeilenende), Pipe-Tabellen. Alles andere bricht mit Zeilenangabe ab — Links, Bilder, Code und HTML immer. - Zwischenüberschriften (
#bis####), tiefere Aufzählungen, Blockzitate (>) und wortgetreue Auszüge (Backticks) brauchendialekt: "1.1"im Frontmatter. Für einen gewöhnlichen Brief nicht setzen: Der hat einen Betreff und keine Kapitel. Für Schriftsätze, Stellungnahmen und längere Behördenpost schon. Ohne das Feld gilt Fassung 1.0 — ein bestehender Brief ändert sich nie. - Anschrift: höchstens 6 Zeilen, keine Leerzeilen.
- Vermerke (Einschreiben, Persönlich): höchstens 3 Zeilen.
- Informationsblock: je Wert höchstens 32 Zeichen.
- Keine Bilder im Fließtext. Ein Logo gehört ins Profil, und eine erzeugte Nachricht trägt höchstens ein Bild.
Exit-Codes
| Code | Bedeutung |
|---|---|
| 0 | PDF geschrieben, alle Maße eingehalten |
| 1 | Eingabefehler — die Meldung nennt Feld und Zeile |
| 2 | Geometrieprüfung gescheitert; der Bericht nennt Soll, Ist und Toleranz |
| 3 | Umgebung unvollständig, scripts/bootstrap.py ausführen |
Weiterführende Dateien
references/frontmatter.md— alle Felder mit Beispielen. Bei Unsicherheit über ein Feld lesen. Ein Feld, das dort nicht steht, lehntlintab — es gibt keine stillen Zusatzfelder.references/markdown.md— was im Brieftext erlaubt ist. Vor dem ersten Brief lesen: Links, Bilder und HTML brechen ab; Überschriften, Zitate und Code nur ohnedialekt: "1.1". Ein Auszug wird nie typografisch geändert, und das Werkzeug bricht ihn nicht um; eine Zeile über 68 Zeichen bricht allerdings der Satz um oder sie läuft über —lintmeldet sie deshalb vorher. Der Typografie-Pass setzt geschützte Leerzeichen von selbst — von Hand eingefügte sind überflüssig.references/stil.md— vor dem Formulieren lesen.references/din5008.md— die Maßtabelle. Nur lesen, wenn nach Normdetails gefragt wird odercheckfehlschlägt.
Profil auf claude.ai
Dort überlebt kein Verzeichnis den nächsten Chat: Ein Profil unter
~/.config/falzmarke/profiles/ ist im zweiten Chat weg, und das Release-Asset enthält nur das
Beispiel. Zwei Wege, damit der Absender bleibt:
- Skill mit eingebackenem Profil — einmal am eigenen Rechner:
Dieses Zip statt des Release-Assets hochladen. Es enthält Absenderdaten und gehört nicht in ein öffentliches Repository.python3 scripts/falzmarke.py pack --profil meinefirma -o falzmarke-meinefirma.skill - Profil im Brief —
profil:nimmt statt eines Namens auch die Felder selbst. Dann trägt der Brief alles Nötige und funktioniert überall.
Hochgeladene Dateien sucht falzmarke auch unter /mnt/user-data/uploads; Brief und Profil lassen
sich also zusammen in einen Chat legen.
Wenn es bricht
| Exit | Was zu tun ist |
|---|---|
| 1 | Eingabefehler. Die Meldung nennt Feld und Zeile — dort korrigieren und erneut lint. |
| 2 | Verifikation. Den Bericht wörtlich weitergeben — er nennt bei einem Überlauf auch die Ursache: und was daran zu tun wäre. Diese eine Änderung an der Eingabe machen und erneut rendern. Sonst aufhören, nicht am Layout herumprobieren. |
| 3 | Umgebung. python3 scripts/bootstrap.py, danach abbrechen, wenn es wieder scheitert. |
| 4 | Renderer. Das ist ein Fehler im Werkzeug — als Issue melden, mit der .md. |
Was bei Exit 2 nicht geändert wird
Der Bericht nennt die Ursache, damit eine gezielte Änderung möglich wird — nicht, damit so
lange geändert wird, bis die Maße passen. Ein Brief, an dem jemand herumprobiert hat, bis
verify grün war, ist am Ende ein Brief, den der Absender nicht geschrieben hat: gekürzter
Betreff, gestrichener Absatz, weggelassene Empfängerzeile. Die Messung stimmt dann, und genau
deshalb fällt es niemandem auf.
Nie ohne Rückfrage geändert werden:
- Sachaussagen — Beträge, Fristen, Aktenzeichen, Namen, Daten. Ein gekürzter Betrag ist ein anderer Brief.
- Empfängerangaben. Eine gestrichene Zeile im Anschriftfeld kann heißen, dass der Brief nicht ankommt.
- Absätze im Fließtext. Kürzen ist Redigieren, und das entscheidet der Absender.
Vertretbar ist, was die Form betrifft und die Aussage nicht ändert — ein Umbruch in einer zu langen Codezeile, eine Tabelle mit zusammengefassten Spalten, der Hinweis, dass der Brief zweiseitig wird.
Eigenes Profil anlegen
python3 scripts/falzmarke.py init-profil meinefirma
Das legt eine ausgefüllte Vorlage unter ~/.config/falzmarke/profiles/meinefirma.yaml an.
Die Pfade in diesem Dokument sind relativ zum Skill-Ordner; auf claude.ai liegt er unter
/mnt/skills/user/falzmarke.
Dieser Ort überlebt Aktualisierungen des Skills — ein Profil innerhalb des Skill-Ordners tut
das nicht: wird der Skill ersetzt, sind die Absender weg und keiner der alten Briefe lässt sich
mehr setzen.
Gesucht wird in dieser Reihenfolge:
--profiles VERZEICHNISFALZMARKE_PROFILES(mehrere Pfade mit Doppelpunkt getrennt)./profiles/neben den Briefen — für Profile, die zu einem Vorgang gehören~/.config/falzmarke/profiles/— die eigenen Absender- die mitgelieferten Beispiele
Achtung bei YAML: Eine Zeile mit Doppelpunkt braucht Anführungszeichen, sonst liest YAML sie als Feld statt als Text:
- "Geschäftsführerin: Erika Muster"