Der Snapshot liefert content_markdown, nicht content_html. Rendering
(marked + DOMPurify + highlight.js) passiert im SvelteKit-Prerender-
Schritt über das bereits existierende \$lib/render/markdown.ts —
keine Duplikation in Deno, kein gemeinsames Policy-Modul nötig.
Für Blaupausen-Nutzung ist rohes Markdown portabler: alternative
Renderer (Astro, Eleventy) bringen eigenen Markdown-Prozessor mit.
Konsequenz für Migration: Schritt 1 ist jetzt \"renderMarkdown
Node-kompatibel machen\" (isomorphic-dompurify) statt \"shared/
markdown-policy.ts ergänzen\".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nachschärfungen nach Review:
- content_html als primary im Snapshot-JSON (marked + DOMPurify +
highlight.js, gemeinsame Policy in shared/markdown-policy.ts)
- content_markdown bleibt daneben (Debug + alternative Renderer)
- translations[] um title ergänzt (SPA-Switcher ohne Relay-Fetch)
- published_at vs. created_at semantisch klar getrennt (OG vs. Update)
- cover_image.fallback_url mit dokumentiertem Nutzungsszenario
- Fallback-Politik für fehlende summary/image/published_at
- --allow-shrink-Flag und kind:5-gestützte automatische Override
- Upload-Reihenfolge für Hash-benannte Bundles (Assets → HTML → Delete)
- /tag/<name>/-Verhalten in Nicht-Zielen erwähnt
- Edge-Case „Repo-Post + alle Relay-Events gelöscht" in Fehlertabelle
- Migrations-Weg um shared/markdown-policy.ts als Schritt 1 erweitert
- pro Migrations-Schritt explizite Rollback-Strategie
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Design-Dokument für die dreistufige Pipeline
publish → snapshot → build+deploy, die Post-Detailseiten mit echten
OG-Metadaten und prerendered Content versorgt, ohne Runtime-Relay-Fetch
und ohne Node-/Go-Server auf dem Shared-Hosting.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- README: neue plan-referenzen (3x multilingual), repo-struktur auf
content/posts/<lang>/<slug>/, svelte-i18n + translations im app-tree
- STATUS: event-count 27 (26 de + 1 en), kurzfassung um mehrsprachigkeit,
multilingual-abschnitt in „erledigt" (pipeline, spa, i18n + bugfixes)
- HANDOFF: option D entfernt (erledigt), neuer abschnitt „wie man eine
übersetzung anlegt", frontmatter-template um a:-platzhalter,
deploy-target-stolperfalle verschärft, vr-post-pfad aktualisiert
- Multilingual-spec: status von „noch nicht implementiert" auf „umgesetzt"
+ anmerkung zum post-switcher (📖 DE | EN statt text-hinweis)
- CLAUDE.md neu: knapper einstieg für agent-sessions mit commit-konvention,
deploy-falle, zsh-globbing, forgejo-mirror-timing
- workflow-skill generalüberholt: post-cutover-stand, multilingual,
publish-pipeline, activeLocale, 73 pipeline-tests
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
statt text-hinweis "auch verfügbar in: ..." zeigt der post jetzt einen
kompakten switcher (📖 aktiver-code | anderer-code). klick auf den
anderen code setzt die ui-sprache global und navigiert zur sprach-
variante — alles konsistent.
language names raus (unused): displayLanguage + tests entfernt, da die
darstellung nun nur noch sprachcodes (DE/EN) zeigt. auch i18n-keys
lang.de/lang.en und post.also_available_in aufgeräumt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bei navigation zwischen slugs innerhalb der gleichen [...slug]-route
bleibt die komponente montiert — onMount feuert dann nicht mehr, und
der neue post lud erst nach manuellem reload. $effect auf dtag löst
das und rendert die neue view sofort.
race-condition-guard: currentDtag wird pro effect-lauf festgefroren;
stale responses werden verworfen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- neue sprach-variante content/posts/en/bible-selfies/ verweist auf dt. original
- dt. bibel-selfies verweist zurück auf bible-selfies
- plan 2/3: npm test → npm run test:unit (existing script name)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8 neue Posts aus direkt-auf-Nostr-erstellten Events (Habla/Yakihonne) ins
Repo geholt, mit sauberen ASCII-slugs statt der kaputten d-tags (Umlaute,
Emojis, Doppelpunkte, Trailing-Dashes). Alte Events per NIP-09 geloescht.
Pipeline-Erweiterungen:
- neuer subcommand "delete" publisht NIP-09 kind:5 events via stabilem
bunker-signer (nutzt CLIENT_SECRET_HEX-identitaet, keine re-pairings).
- frontmatter.lang + kind:30023 event tagt jetzt NIP-32 konform mit
["L","ISO-639-1"] + ["l","de","ISO-639-1"] (default: de).
- validate-post deno-task bekommt --allow-env (yaml-parser brauchts).
Vorbereitung fuer spaetere Mehrsprachigkeit: EN-Versionen koennen via
separate markdown-datei mit lang:en als eigenes event publiziert und
spaeter per a-tag-referenz zum DE-pendant verlinkt werden.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- nip-09-delete-event für duplikat 1744905463975 festgehalten
- open-todos ergänzt: kind:5-filter in spa, repo/nostr-konflikt-mgmt,
delete-subcommand in pipeline
- option 3 (menü+impressum+startseite) als design-todo klargestellt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
app.html nutzt __SITE_URL__ als platzhalter in og:url und canonical.
deploy-svelte.sh ersetzt ihn nach dem build pro ziel via sed:
- svelte → https://svelte.joerg-lohrer.de (default, bisheriger SVELTE_FTP_-pfad)
- staging → https://staging.joerg-lohrer.de (STAGING_FTP_-pfad, webroot joerglohrer26)
- prod → https://joerg-lohrer.de (STAGING_FTP_-pfad, cutover-ziel)
env-auslese aus .env.local nicht mehr via `source` (bricht bei
sonderzeichen im passwort), sondern via awk pro schlüssel. build wird
jetzt vom deploy-skript angestoßen, damit immer gegen den frischen
html-stand gebaut wird.
app/.env.example dokumentiert PUBLIC_SITE_URL (derzeit ungenutzt, da
der platzhalter-ansatz zuverlässiger ist als runtime-env für prerender).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
spa → main gemergt, github-actions-workflow manuell verifiziert
(run #1: signer ok, outbox ok, blossom-liste ok, mode=diff posts=0).
der 24-task-plan aus docs/superpowers/plans/2026-04-16-publish-pipeline.md
ist offiziell durch. pipeline läuft sowohl lokal als auch in ci,
auto-trigger bei content-push ist konfiguriert (aber noch nicht real
ausgelöst worden — beiläufig mitzunehmen, wenn mal ein post editiert
wird).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
docs/github-ci-setup.md dokumentiert:
- forgejo → github push-mirror
- die 4 github-repository-secrets (bunker-url, author-pubkey-hex,
bootstrap-relay, client-secret-hex) — letzteres identisch mit
.env.local für stabile amber-app-identität
- wie man sie rotiert
- migrations-pfad weg von github (woodpecker, cron)
status + handoff reflektieren: pipeline live, alle 18 posts publiziert,
91 bilder auf blossom, ci-setup steht, cutover als nächster schritt
möglich.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
validatePost akzeptiert jetzt auch string-dates im YYYY-MM-DD- oder
ISO-8601-format und coerced sie in ein Date-objekt in-place. vorher
schlug die validation für 13 von 18 altposts fehl, weil deren yaml
`date: "2023-02-26"` quoted war (hugo-konvention) und der yaml-parser
strings statt Date-instanzen liefert.
migration durchgelaufen (log in docs/publish-logs/): 18/18 success,
91 bilder auf beiden blossom-servern, 5 write-relays — bis auf
relay.damus.io, der bei 6 posts nicht auf OK antwortet (üblich bei
damus, rate-limiting). alle 7 multi-relay-posts haben weiter mindestens
4 acks (über MIN_RELAY_ACKS=2).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
probleme auf der realen amber-infrastruktur behoben:
1. ohne festen CLIENT_SECRET_HEX erzeugt applesauce bei jedem lauf einen
neuen client-pubkey. amber bindet permissions pro client-pubkey, also
sah jeder lauf wie eine neue unberechtigte app aus und bekam
"no permission" als auto-antwort.
→ CLIENT_SECRET_HEX in config + cli, SimpleSigner.fromKey durchgereicht.
2. applesauce wirft bei "already connected"/"no permission" unhandled
rejections, weil response-promises asynchron reagieren.
→ globaler unhandledrejection-handler, der diese benannten fehler
schluckt; connect() im try/catch mit open+force als fallback.
3. timeout auf bunker connect auf 60s erhöht (amber-pairing kann
menschliches tap dauern beim ersten mal).
einzel-post-publish live verifiziert:
- offenheit-das-wesentliche als kind:30023 publiziert
- alle 5 write-relays haben bestätigt
- bild auf beide blossom-server hochgeladen
- SPA rendert das bild von blossom.edufeed.org
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>