Anlass war die Frage, ob `llms.md` ein guter Name ist. Zwei Befunde:
1. Der Zweck der Konvention ist ein anderer, als D43 annahm. llmstxt.org
über die eigene Datei: „a markdown file that provides brief background
information and guidance, along with links to markdown files providing
more detailed information“ — ein Index, kein Inhalt. Der 211-Zeilen-
Leitfaden ist genau eine jener verlinkten Dateien; `llms.md` ist damit
der richtige Name, es fehlte der Wegweiser davor.
2. `llms.md` kam auf der stabilen Instanz falsch kodiert an: Apache kennt
`.md` nicht und sendet GAR KEINEN Content-Type, der Browser rät
windows-1252. Gemessen: characterSet=windows-1252, aus „notation —
guide“ wurde „notation â€" guide“, 31 Zeilen betroffen. GitHub Pages
liefert dieselbe Datei korrekt als text/markdown; charset=utf-8 aus.
- frontend/public/llms.txt: Index nach der Konvention (Titel, Blockquote,
Notation in Kurzform, ## Docs, ## Optional). Rein ASCII — er ist die
Datei, die ein fremder Agent ungefragt abruft, und soll auch dort
ankommen, wo ein Server die Kodierung verschweigt. Alle 5 Links: 200.
- scripts/prod.htaccess: AddType für .md/.txt/.werkbaum, von
deploy-prod.sh als .htaccess gespiegelt. Nicht in public/ — dort landete
es wirkungslos im Pages-Artefakt. Rückweg bei 500 steht in der Datei.
- Beide Deploy-Wege kopieren llms.txt mit.
SPEC §13 + D43-Nachtrag 2 (mit Richtigstellung der D43-Annahme);
Plan: #not.llms.index [x]. 243 Tests grün, Plan 157 Knoten, 0 Warnungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
.md ist die zum Inhalt ehrliche Endung (es IST Markdown); Link rückt zur
Werkzeug-Ecke des Footers. Pipelines, SPEC §13, CLAUDE.md und Plan
nachgezogen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Englische Markdown-Kurzfassung der Notation (Syntax + Semantik: Gates,
Status, Ränge, Extraktionsreihenfolge, Abhängigkeits- und
Beschreibungsregeln, Schreib-Faustregeln, vollständiges Beispiel), damit
Agenten Werkbaum lesen und schreiben können. Name und Ort folgen der
llms.txt-Konvention; Quelle in frontend/public/ (Dev-Server und dist/
gratis), Pages-Workflow und deploy-prod.sh kopieren sie mit je einer
Zeile, Footer verlinkt den Dateinamen (keine i18n nötig, Tooltip DE·EN).
Die SPEC bleibt normativ — Hausregel jetzt: SPEC zuerst, dann Code, dann
llms.txt nachziehen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der Rename example-werkbaum.werkbaum → werkbaum.werkbaum (82f34b2) hatte
das Skript nicht erreicht — der Deploy brach mit „Plan nicht gefunden"
ab. Dazu die Statusbox-Erkennung um das =-Gate und die Faltmarken >/<
erweitert (SPEC §1), damit auch `- > [x] …`-Zeilen befördert werden.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der Umfang war schon richtig — genau eine Datei, kein Glob —, aber als bloße
Zusage zu schwach. `[x]` steht im Repo an Stellen, wo eine Beförderung falsch
bis unsinnig wäre, und der Schaden wäre still:
- Die Legende („Agenda") zeigt `[x] fertig` als ANSCHAUUNGSMATERIAL für die
Notation (index.html, chip('fertig','[x]') in app.js). Daraus würde
„[^] fertig" — Unsinn, den beim Durchsehen eines Diffs niemand bemerkt.
- Das mitgelieferte „Example"-Dokument (INITIAL in app.js) und die übrigen
docs/examples/*.werkbaum sind erfunden.
- SPEC §10 (kanonisches Beispiel, zugleich Test-Fixture) und die Checkboxen in
docs/TASKS.md.
Deshalb jetzt eine Laufzeitsicherung statt einer Absichtserklärung: Der Lauf
vergleicht `git status` vor und nach dem Schreiben und bricht ab, sobald mehr
als die Plandatei NEU geändert ist — Plandatei zurückgesetzt, nichts committet.
Der Vergleich ist gegen den Vorher-Zustand gebildet, damit anderweitig
schmutzige Dateien im Arbeitsbaum keinen Fehlalarm auslösen. Dazu ein
UMFANG-Absatz im Skriptkopf, der die drei Fallen benennt und ausdrücklich sagt:
nicht auf ein Muster erweitern.
Verifiziert im Wegwerf-Worktree: Normalfall befördert die zwei Knoten und
committet; ein absichtlich auf example-plan-0.werkbaum ausgeweiteter sed bricht
mit Exit 1 ab, listet beide betroffenen Dateien, setzt die Plandatei zurück und
committet nichts; danach läuft der Normalfall unverändert durch.
(Beim ersten Testlauf griff der Wächter scheinbar nicht — Ursache war der Test,
nicht das Skript: `git reset --hard` hatte die eingespielte Skriptfassung durch
die committete, noch ungesicherte ersetzt.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der mitgelieferte Werkbaum-Plan behauptete [^] für Funktionen, die nur auf der
automatisch deployten Pages-Instanz lagen, nicht auf werkbaum.javagil.de
(manueller Deploy, D16). Ausgerechnet das Dokument, das den Stand beschreiben
soll, war ungenau — und „Was ist neu?" (D28) meldete Dinge als live, die es
dort nicht waren.
Die Unterscheidung gibt es längst: SPEC §4 trennt [x] fertig von [^] in
Produktion. Der Plan hat sie für sich selbst nie benutzt. Konvention ab jetzt:
beim Mergen [x], der Deploy befördert — nur er weiß, wann die Aussage wahr wird.
- scripts/promote-shipped.sh schreibt Statusboxen am Zeilenanfang von [x] auf
[^] und hält das als eigenen Commit fest (-n zeigt nur, -y ohne Rückfrage).
Bricht ab, wenn die Plandatei uncommittete Änderungen hat; committet nur
diesen einen Pfad; pusht nicht.
- deploy-prod.sh ruft es als Schritt 0 auf (--no-promote schaltet es ab) und
warnt, wenn HEAD noch nicht auf origin liegt — der Footer-Versionslink zeigt
sonst auf einen Commit, den GitHub nicht kennt.
Warum ein Commit und kein Rewrite beim Bauen: Ein Rewrite macht GENAU EINE
Installation ehrlich; Pages untertriebe dauerhaft und die Neu-Anzeige wäre dort
für immer stumm. Der Commit wird von beiden Pipelines gesehen (Pages beim Push,
prod beim rsync), das Artefakt bleibt inhaltsgleich mit dem Repo — die
vorhandenen sed-Regeln (D16) fassen nur Pfade und Version an, Infrastruktur,
kein Rewrite dessen, was das Dokument aussagt. Außerdem Präzedenzfall D16:
VERSION per bewusstem Bump-Commit, „vollständig aus dem Repo reproduzierbar".
Einmalige Nachholung, exakt statt geschätzt: Der Footer der stabilen Instanz
verlinkt den deployten Commit (4061362); alles danach ist dort nicht drin. Es
sind GENAU ZWEI Knoten — „Optional nodes" (D29) und „Show what is new since
your last visit" (D28) —, nicht das Dutzend, das ich vorher grob geschätzt
hatte. Beide stehen jetzt auf [x] und leuchten beim nächsten Prod-Deploy als
neu auf. Eine Demotion [^]→[x] löst kein Falschleuchten aus: freshProdSet
meldet nur Knoten, die JETZT [^] sind (test-abgedeckt).
Umfang: nur example-werkbaum.werkbaum. Die übrigen Beispieldateien sind
erfunden und sagen nichts über ein Deployment aus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DEPLOY_TARGET wird jetzt aus der .env im Repo-Wurzelordner gelesen (die von der
bestehenden .gitignore-Regel ohnehin ignoriert wird; Vite liest sie nicht, da
dessen Env unter frontend/ liegt). scripts/deploy.env(.example) entfaellt; neue
Vorlage .env.example im Wurzelordner. Argument hat weiterhin Vorrang, ~ bleibt
fuer die Remote-Seite erhalten (kein source).
.gitignore: eigene scripts/deploy.env-Zeile zurueckgenommen, Kommentar an der
.env-Regel ergaenzt. README (de/en) entsprechend aktualisiert.
Lokal verifiziert: Fallback liest aus Wurzel-.env, .env ignoriert, .env.example
getrackt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ohne Ziel-Argument liest das Skript DEPLOY_TARGET aus scripts/deploy.env
(git-ignoriert; Vorlage scripts/deploy.env.example ist eingecheckt). Argument
hat weiterhin Vorrang.
Der Wert wird bewusst NICHT via `source` gelesen, weil bash bei `host:~/pfad`
die Tilde nach dem ':' lokal expandieren wuerde; stattdessen roh ausgelesen,
Whitespace/CR + umgebende Quotes gestrippt, das ~ bleibt fuer die Remote-Seite
erhalten. Lokal verifiziert: Fallback greift, ~ bleibt in unquoted/single-/
double-quoted erhalten (kein lokales $HOME), Quotes entfernt.
.gitignore: scripts/deploy.env ergaenzt (die vorhandene .env-Regel greift wegen
des abweichenden Dateinamens nicht). README (de/en) aktualisiert; Beispielpfad
auf die htdocs-ssl-Direktaufschaltung umgestellt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
rsync --filter='protect /.well-known/***' bewahrt am Ziel liegende
.well-known-Dateien (Let's-Encrypt-Challenge waehrend einer Erneuerung) vor dem
--delete, obwohl sie nicht in der Quelle liegen. Verhindert die Race Condition,
dass ein Deploy eine laufende Zertifikatserneuerung abraeumt. Bedient
Hostsharing die Challenge ausserhalb des Docroots, ist es ein No-op.
Lokal verifiziert: .well-known/acme-challenge/testtoken ueberlebt den Deploy,
eine sonstige Alt-Datei wird weiterhin per --delete entfernt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mktemp -d legt das Staging-Verzeichnis mit 0700 an; rsync -a uebertrug diesen
Modus auf das Ziel-Verzeichnis, sodass der Webserver es nicht betreten konnte
(Apache 403 "Server unable to read htaccess file, denying access to be safe" —
die .htaccess-Suche selbst ist normal, der Zugriff scheiterte an den Rechten).
Fix: rsync mit --chmod=D755,F644, unabhaengig von lokalen mktemp-/umask-Rechten.
Lokal verifiziert: Ziel wird 755, Dateien 644 (auch wenn das Ziel vorher 700 war).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neues Skript scripts/deploy-prod.sh baut das badge-freie Prod-Bundle
(npm run build:prod), stellt es lokal wie der Pages-Workflow zusammen
(LICENSE danebenlegen + ../LICENSE-Link geradeziehen, Footer-Version +
Commit-Link) und spiegelt es per rsync --delete ueber SSH in ein als Argument
uebergebenes Zielverzeichnis — am Ziel bleibt nichts Altes stehen.
- Pflichtargument: rsync-Ziel (z. B. user@host:~/doms/.../subs-ssl/werkbaum/).
- Ohne -y erst --dry-run-Vorschau + Rueckfrage (--delete ist destruktiv).
- Warnt bei unsauberem Arbeitsbaum (Commit-Link zeigt auf HEAD).
- node_modules-Guard: npm ci nur wenn noetig.
End-to-End gegen ein lokales Ziel verifiziert: --delete raeumt Alt-Datei und
Alt-Unterordner weg, deployte index.html enthaelt 0x 🚧/🔧 (nur die ungenutzte
CSS-Regel), LICENSE danebengelegt + Link auf "LICENSE", Version 1.0.x + Commit.
Doku: README (de/en) Abschnitt Prod-Installation, DECISIONS D16 (warum die
sed-Zusammenstellung bewusst in Workflow UND Skript liegt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>