docs: prerender-snapshot spec-klarstellungen + implementation-plan

spec:
- deploy upload-reihenfolge: drei-phasen-flow (assets → html → delete)
  mit präziser beschreibung statt flag-kombi-raten
- migrations-schritt 4 + 5 entkoppelt: 4 liefert snapshot primär mit
  runtime-fallback für nostr-first-posts, 5 entfernt fallback erst
  nach stabilem cutover
- exclude-glob im delete-pass für extern verwaltete files

plan (20 tasks, tdd):
- snapshot/ als deno-modul mit config, relays, dedup, plausibility,
  cover, extract, write — voll unit-getestet
- renderMarkdown auf isomorphic-dompurify
- sveltekit-route mit prerender=true, entries, og/twitter/json-ld/
  hreflang im head, snapshot-primary + runtime-fallback
- deploy-script auf lftp drei-phasen
- dokumentation in HANDOFF und CLAUDE.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jörg Lohrer 2026-04-21 17:20:25 +02:00
parent fd093dff5e
commit 11e406e5de
2 changed files with 2745 additions and 11 deletions

File diff suppressed because it is too large Load Diff

View File

@ -311,17 +311,27 @@ des SvelteKit-Builds sind.
**Upload-Reihenfolge (kritisch wegen Hash-benannten JS-Bundles):** **Upload-Reihenfolge (kritisch wegen Hash-benannten JS-Bundles):**
1. Zuerst **Assets** hochladen (`_app/immutable/**`, Bilder, CSS) — 1. Zuerst **Assets** hochladen (`_app/immutable/**`, Bilder, CSS) —
`lftp mirror` ohne `--delete`, nur Upload reine Upload-Phase ohne Server-seitiges Löschen. Neue Hash-Bundles
landen zusätzlich zu den alten auf dem Server.
2. Danach **HTML-Seiten** hochladen (`index.html`, `<slug>/index.html`, 2. Danach **HTML-Seiten** hochladen (`index.html`, `<slug>/index.html`,
`404.html`), ebenfalls ohne Delete `404.html`), ebenfalls ohne Löschen. Ab diesem Punkt zeigen die neuen
3. **Zum Schluss** `lftp mirror --delete --only-missing` auf das HTMLs auf ihre zugehörigen neuen Asset-Hashes — konsistent.
Top-Level, um obsolete Dateien zu entfernen (alte Hash-Bundles, 3. **Zum Schluss** ein separater **Delete-Pass**, der Server-Dateien
gelöschte Post-HTMLs) entfernt, die im aktuellen Build-Output nicht mehr existieren (alte
Hash-Bundles, gelöschte Post-HTMLs, veraltete Snapshot-JSONs). Nichts
wird in dieser Phase erneut hochgeladen. Konkrete `lftp`-Flag-Kombi
in der Planungsphase festzulegen — wichtig ist nur die
Phasen-Trennung: Upload zuerst, Delete zuletzt, kein paralleler
Mirror-Call.
Damit ist zu keinem Zeitpunkt ein inkonsistenter Zustand auf dem Server: Damit ist zu keinem Zeitpunkt ein inkonsistenter Zustand auf dem Server:
Neue HTMLs referenzieren stets bereits vorhandene Asset-Hashes; alte Neue HTMLs referenzieren stets bereits vorhandene Asset-Hashes; alte
Assets werden erst nach erfolgreichem Upload gelöscht. Assets werden erst nach erfolgreichem Upload gelöscht.
Von `--delete` ausgeschlossen bleiben außerhalb des SvelteKit-Builds
verwaltete Dateien (Hero-Bild, Favicons im Root, `.well-known/`,
Webspace-Spezifika) via `--exclude-glob`.
Kein weiteres Verhalten ändert sich. Kein weiteres Verhalten ändert sich.
## Mehrsprachigkeit ## Mehrsprachigkeit
@ -381,12 +391,20 @@ an keiner Stelle einen Big-Bang bildet:
Änderung an SPA. Rollback: Verzeichnis löschen. Änderung an SPA. Rollback: Verzeichnis löschen.
3. **Snapshot in CI einbauen.** GitHub-Actions-Schritt vor SvelteKit-Build. 3. **Snapshot in CI einbauen.** GitHub-Actions-Schritt vor SvelteKit-Build.
Rollback: Workflow-Schritt entfernen. Rollback: Workflow-Schritt entfernen.
4. **SvelteKit-Route auf Prerender umstellen.** `[...slug]/+page.ts` 4. **SvelteKit-Route auf Prerender umstellen, mit Laufzeit-Fallback.**
bekommt `prerender = true` + `entries()` + Load aus JSON. `[...slug]/+page.ts` bekommt `prerender = true` + `entries()` + Load
`+page.svelte` rendert `content_markdown` per `renderMarkdown()` zur aus JSON. `+page.svelte` rendert `content_markdown` per
Build-Zeit. Rollback: Commit revert, alte Runtime-Logik kommt zurück. `renderMarkdown()` zur Build-Zeit. Der bisherige Runtime-Relay-Fetch
5. **SPA-Relay-Fetch in Detail-Seite komplett abschalten.** Nur noch bleibt in diesem Schritt noch als Fallback bestehen — falls ein Slug
Snapshot-Content. Rollback: Commit revert. zur Build-Zeit nicht im Snapshot war (z.B. ganz frisch Nostr-first
publiziert), kann die SPA ihn über `adapter-static`-`fallback`
weiterhin rendern. Rollback: Commit revert, alte Runtime-Logik kommt
vollständig zurück.
5. **Runtime-Relay-Fetch der Detail-Seite entfernen.** Wenn Schritt 4
sich stabil zeigt, wird der Fallback-Code-Pfad abgebaut. Die
Detail-Seite lebt dann ausschließlich vom Snapshot. Neue Nostr-first-
Posts erscheinen erst nach dem nächsten Snapshot+Build-Lauf. Rollback:
Commit revert.
6. **Deploy-Script erweitern.** `lftp mirror --delete` mit 6. **Deploy-Script erweitern.** `lftp mirror --delete` mit
Upload-Reihenfolge. Rollback: Script revert — Site bleibt, nur Upload-Reihenfolge. Rollback: Script revert — Site bleibt, nur
Obsolete-Cleanup fehlt. Obsolete-Cleanup fehlt.