docs(quick-260916-dcz): Aenderungsliste abgeschlossen, verifiziert 8/8 und im Browser bewiesen — Zusammenfassung, Verifikation, Aktenstand
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
This commit is contained in:
+187
@@ -0,0 +1,187 @@
|
||||
---
|
||||
phase: quick-260916-dcz
|
||||
plan: 01
|
||||
subsystem: web-portal, ci-cd, docs
|
||||
tags: [changelog, whats-new, gitea-release, build-time-embedding, i18n, handbuecher]
|
||||
status: complete
|
||||
requires: [quick-260914-ku1, quick-260916-bwo, quick-260916-dyv]
|
||||
provides:
|
||||
- CHANGELOG.md (Wurzel) in Alltagssprache mit Unveroeffentlicht + 1.0.0
|
||||
- Seite /changelog "Was ist neu" mit Kanalfilter (Server-Komponente)
|
||||
- Bauzeit-Einbettung env.TESSERA_CHANGELOG_MD (next.config.ts, Dockerfile, .dockerignore)
|
||||
- .gitea/scripts/publish-release.sh + CI-Schritt (Gitea-Release je Tag v*)
|
||||
- Release "Tessera 1.0.0" in Gitea (id 1)
|
||||
affects: [apps/web, .gitea, docs]
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Bauzeit-Einbettung einer Repo-Datei ueber next.config.ts env + Importdisziplin (nur Server-Seite importiert das Modul)"
|
||||
- "Release-Skript: Token nur ueber Header-Datei, JSON nur per jq --arg, idempotent GET/tags -> PATCH oder POST"
|
||||
key-files:
|
||||
created:
|
||||
- CHANGELOG.md
|
||||
- apps/web/src/lib/changelog.ts
|
||||
- apps/web/src/lib/changelog.test.ts
|
||||
- apps/web/src/app/(portal)/changelog/page.tsx
|
||||
- apps/web/src/app/(portal)/changelog/changelog-page.test.tsx
|
||||
- apps/web/src/components/changelog/changelog-view.tsx
|
||||
- .gitea/scripts/publish-release.sh
|
||||
modified:
|
||||
- .dockerignore
|
||||
- apps/web/Dockerfile
|
||||
- apps/web/next.config.ts
|
||||
- apps/web/src/components/layout/app-version-badge.tsx
|
||||
- apps/web/src/components/layout/app-version-badge.test.tsx
|
||||
- apps/web/src/messages/de.json
|
||||
- apps/web/src/messages/en.json
|
||||
- .gitea/workflows/ci.yml
|
||||
- docs/anleitung-betrieb.md
|
||||
- docs/anleitung-anwender.md
|
||||
- docs/anleitung-entwicklung.md
|
||||
- docs/ci-cd-setup.md
|
||||
decisions:
|
||||
- "Commits auf main: Projektstrategie branching_strategy none, Auftrag schreibt Push von main vor"
|
||||
- "Ein feat-Commit fuer Task 1 statt getrennter test/feat-Commits: der Plan schreibt genau drei Commits (feat/ci/docs) vor; RED-Nachweis liegt als TAP-Record vor (RED_EVIDENCE_OK)"
|
||||
- "filterChangelogForChannel liefert bei leerem Ergebnis '' statt '\\n' (Seite prueft ohnehin markdown.trim())"
|
||||
- "publish-release.sh gibt die API-Basis erst NACH der Tag-Entscheidung aus (Reihenfolge der Plan-Schritte 2 und 3); auf main erscheint daher nur 'nichts zu tun'"
|
||||
metrics:
|
||||
duration: "ca. 2 h 20 min (Start 09:13 UTC, Ende ca. 09:35 UTC + CI-Beobachtung bis 09:30 UTC lokal 11:30)"
|
||||
completed: 2026-09-16
|
||||
plan_head_before: 0db21627f51290f26ca049968d11d53b2394cd0f
|
||||
commits: 3
|
||||
actuals:
|
||||
tokens: 12659
|
||||
tasks: 3
|
||||
commits: 3
|
||||
---
|
||||
|
||||
# Quick 260916-dcz: Aenderungsliste (CHANGELOG.md) in Alltagssprache, Seite "Was ist neu", Gitea-Release je Tag — Summary
|
||||
|
||||
CHANGELOG.md im Wurzelverzeichnis (Unveroeffentlicht mit 8 Punkten aus bwo + dyv + dieser Seite, 1.0.0 mit 11 Punkten aus den Handbuechern), zur Bauzeit ueber `env.TESSERA_CHANGELOG_MD` ins Server-Bundle eingebettet und unter `/changelog` als Server-Seite mit Kanalfilter (Live ohne Unveroeffentlicht) ueber `MDEditor.Markdown` + `rehype-sanitize` gerendert; die Versionszeile ist ein Link; `publish-release.sh` legt beim Tag den Gitea-Release aus dem CHANGELOG-Abschnitt an (idempotent, Exit 1 ohne Abschnitt), Release `Tessera 1.0.0` rueckwirkend angelegt; vier Handbuecher nachgezogen; gepusht, CI 356 success.
|
||||
|
||||
## Aktenstand (git ist die Wahrheit)
|
||||
|
||||
`git status --porcelain` nach Task 3 (vor dem SUMMARY): leer.
|
||||
|
||||
`git log --oneline 0db2162..HEAD`:
|
||||
|
||||
```
|
||||
c5f4ade docs(quick-260916-dcz): Betriebshandbuch Kapitel 9 (Changelog-Schritt, Gitea-Release, Seite Was ist neu), Anwender-, Entwicklungs- und CI-Handbuch
|
||||
6940bd0 ci(quick-260916-dcz): Gitea-Release je Freigabe-Tag aus CHANGELOG.md (publish-release.sh, idempotent, --dry-run), Schritt in ci.yml
|
||||
ba06db9 feat(quick-260916-dcz): CHANGELOG.md, Seite "Was ist neu" mit Kanalfilter, Versionszeile als Link, Bauzeit-Einbettung
|
||||
```
|
||||
|
||||
`git status -sb | head -1` nach `git fetch -q`: `## main...origin/main` (nicht voraus, nicht zurueck). Push: `963fa36..c5f4ade main -> main` — die beiden Plan-Docs-Commits (df16f46, 0db2162) gingen wie erwartet mit.
|
||||
|
||||
`commits: 3` ist gemessen: `git rev-list --count 0db2162..HEAD` = 3.
|
||||
|
||||
## Task 1 — CHANGELOG.md, Bauzeit-Einbettung, Kanalfilter, Seite, Abzeichen-Link, i18n (ba06db9)
|
||||
|
||||
**RED (Schritt A), Befehl** `pnpm -C apps/web exec vitest run src/lib/changelog.test.ts "src/app/(portal)/changelog" src/components/layout/app-version-badge.test.tsx`, Exit 1:
|
||||
|
||||
```
|
||||
FAIL src/lib/changelog.test.ts [ src/lib/changelog.test.ts ]
|
||||
Error: Failed to resolve import "./changelog" from "src/lib/changelog.test.ts". Does the file exist?
|
||||
FAIL src/app/(portal)/changelog/changelog-page.test.tsx [ ... ]
|
||||
Error: Failed to resolve import "./page" from "src/app/(portal)/changelog/changelog-page.test.tsx". Does the file exist?
|
||||
FAIL ... app-version-badge.test.tsx > Test 5 (Link): die Versionszeile ist ein Link auf /changelog
|
||||
AssertionError: expected 'SPAN' to be 'A' // Object.is equality
|
||||
FAIL ... app-version-badge.test.tsx > Test 6 (aria-label): der Link traegt den Namen "Was ist neu"
|
||||
Test Files 3 failed (3)
|
||||
Tests 2 failed | 4 passed (6)
|
||||
```
|
||||
|
||||
RED-Nachweis nach #3770: TAP-Lauf (`--reporter=tap-flat`) der Badge-Datei als Record persistiert, `gsd_run check tdd-red-evidence` -> `RED_EVIDENCE_OK` (target_test_failed, Zieltest = Test 5 Link). Hinweis: Vitest-TAP hat keine node:test-Summenzeilen; `# tests 6 / # pass 4 / # fail 2` wurden aus den ok/not-ok-Zeilen gezaehlt und angehaengt (Format, kein Inhalt).
|
||||
|
||||
**GREEN (Schritt H):** Zielsuite `3 passed (3)` / `19 passed (19)`; gesamte Web-Suite `Test Files 49 passed (49)` / `Tests 309 passed (309)` (Plan-Soll 49/309, erfuellt); `tsc --noEmit` web Exit 0.
|
||||
|
||||
**Falsifizierung (a):** `if (isEmpty || channel === 'live')` kurzzeitig auf `if (isEmpty)` gesetzt -> `Tests 2 failed | 8 passed (10)`: Test 1 (live) UND Test 8 (CRLF, ebenfalls live) rot; Datei danach byte-identisch zurueckgesetzt (diff leer).
|
||||
|
||||
**Lokaler `next build`:** Exit 0 in 89 s; `/changelog` als `ƒ (Dynamic)` 1.84 kB; Marker `2026-09-15`: `apps/web/.next/server` = 1 Datei, `apps/web/.next/static` = 0.
|
||||
|
||||
**Verify-Gates Task 1 (alle erfuellt):** CL_EXISTS=0; `## Unveröffentlicht` 1; `## 1.0.0 – 2026-09-15` 1; `### ` 3; Punkte unter 1.0.0 = 11 (Soll 6-12); Punkte unter Unveroeffentlicht = 8 (Soll 7-9); `!CHANGELOG.md` 1; `COPY CHANGELOG.md ./` 1; TESSERA_CHANGELOG_MD in next.config.ts 3; `process.env.TESSERA_CHANGELOG_MD` in changelog.ts 1; `export function filterChangelogForChannel` 1; Importe von `@/lib/changelog` ausserhalb page.tsx = 0; `MDEditor.Markdown` 2; `rehypeSanitize` 2; page.tsx `use client` 0; `href="/changelog"` 1; `aria-label={t('whatsNew')}` 1; `"whatsNew"` de 1; `"unreleasedHint"` en 1; umlaut-dictionary.ts / package.json / pnpm-lock.yaml: UNTOUCHED=0 (unangetastet); TSC_web=0.
|
||||
|
||||
Umlaut-Waechter: alle neuen de.json-Texte bestehen (`aktuelle`, `Tessera` sind gelistet; keine neuen ae/oe/ue/ss-Woerter), `umlaut-dictionary.ts` unangetastet.
|
||||
|
||||
## Task 2 — Release-Skript, CI-Schritt, Docker-Falsifizierung, Release v1.0.0 (6940bd0)
|
||||
|
||||
Precondition: Gitea `{"version":"1.26.2"}`, jq `/bin/jq`, docker `/bin/docker`, Push-URL enthaelt localhost:3002 — erfuellt.
|
||||
|
||||
**Dry-run-Ausgaben:**
|
||||
|
||||
1. `--dry-run --tag v1.0.0` -> Exit 0; erste Zeile `Gitea-API: http://localhost:3002/api/v1 Repo: schalli/tessera-ctl Tag: v1.0.0`, dann POST-/PATCH-Ziele und das JSON; `.name` = `Tessera 1.0.0`, Body 2227 Zeichen, erste Zeile `### Neu`, letzte Zeile `- Versionsanzeige unten in der Seitenleiste ...`.
|
||||
2. `--dry-run --tag v9.9.9` -> Exit 1, stderr: `CHANGELOG.md hat keinen Abschnitt fuer Version 9.9.9 (erwartet eine Zeile '## 9.9.9 – <Datum>'). Kein Release ohne Text.` (Falsifizierung b).
|
||||
3. `GITHUB_REF=refs/heads/main ... --dry-run` -> Exit 0, `Kein Freigabe-Tag (nur refs/tags/v*): nichts zu tun.`
|
||||
4. `GITHUB_REF=refs/tags/v1.0.0 GITHUB_SERVER_URL=https://git.vicolab.de ... --dry-run` -> erste Zeile `Gitea-API: https://git.vicolab.de/api/v1 Repo: schalli/tessera-ctl Tag: v1.0.0`.
|
||||
|
||||
**Docker-Falsifizierung (c):** `docker build -t tessera-web-dcz-test --build-arg APP_VERSION=v9.9.9-test --build-arg APP_CHANNEL=live -f apps/web/Dockerfile .` -> Exit 0 in 103 s; Kontext `transferring context: 518.75MB 4.7s`; Schicht `#18 [builder 6/7] COPY CHANGELOG.md ./` (die Datei kam also trotz `*.md` durch die Ausnahme in den Kontext). Im Abbild: `grep -rl "2026-09-15" /app/apps/web/.next/server | wc -l` = **1**, `.../.next/static` = **0**. Abbild danach mit `docker rmi` entfernt.
|
||||
|
||||
**Echter Lauf v1.0.0 (Token nur in Variablen, nie ausgegeben):** vorher `GET .../releases` -> `0` Releases. Lauf 1: `Release v1.0.0 angelegt (id 1)`, Exit 0. Pruefung `GET .../releases/tags/v1.0.0`: `{"id":1,"tag_name":"v1.0.0","name":"Tessera 1.0.0","draft":false,"prerelease":false,"body_len":2227}`, Body beginnt mit `### Neu`. Lauf 2 (identischer Aufruf): `Release v1.0.0 aktualisiert (id 1)`, Exit 0; `jq length` auf `.../releases` = **1** (idempotent, kein Duplikat).
|
||||
|
||||
**Verify-Gates Task 2:** EXEC=0; Shebang 1; `set -eu` 1; `set -x` 0; Echo-Token-Zaehler 0; `jq -n --arg` 2; `header @` 3; `releases/tags/` 1; `PATCH` 3; DRY_OK=0; DRY_MISSING=1 mit `9.9.9` in stderr; „nichts zu tun“ 1; Name-grep 1; `publish-release.sh` in ci.yml 1; `secrets.REGISTRY_TOKEN` 2.
|
||||
|
||||
**Messwiderspruch (beide Werte, nicht angepasst):** Das Plan-Gate `grep -c "GITEA_TOKEN: \${{ secrets.REGISTRY_TOKEN }}" .gitea/workflows/ci.yml` liefert auf diesem Host **0**, weil `grep` hier `ugrep 7.8.4` ist und `$` mitten im Muster als Anker wirkt (Gegenprobe: dasselbe Muster auf eine Echo-Zeile mit exakt diesem Text liefert ebenfalls 0). `grep -cF 'GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }}'` und `grep -c 'GITEA_TOKEN: [$]{{ secrets.REGISTRY_TOKEN }}'` liefern **1**; die Zeile steht woertlich in ci.yml (Zeilen 70-72).
|
||||
|
||||
Zwei Korrekturen VOR dem Commit (kein Plan-Deviation, Skript war noch ungetestet): (1) dash-`echo` interpretiert `\n` im jq-JSON -> `printf '%s\n'`; (2) die Fehlermeldung bei fehlendem Token nannte den Variablennamen in einer echo-Zeile und haette das Echo-Token-Gate ausgeloest -> umformuliert („Kein Zugriffstoken in der Umgebung gesetzt“).
|
||||
|
||||
YAML-Pruefung: PyYAML nicht vorhanden (`No module named 'yaml'`); strukturelle grep-Pruefung wie im Plan vorgesehen, und der CI-Lauf hat die Datei geparst und ausgefuehrt.
|
||||
|
||||
## Task 3 — Handbuecher, Push, CI-Beobachtung (c5f4ade)
|
||||
|
||||
Precondition: Gitea 1.26.2, `gitea-runner` laeuft (1).
|
||||
|
||||
**Handbuch-Gates:** betrieb `CHANGELOG.md` 2 (>=2), `Was ist neu` 1 (>=1), `Release` 4 (>=2); anwender `## Was ist neu` 1, `Was ist neu` 4 (>=2); entwicklung `Änderungsliste` 1, `TESSERA_CHANGELOG_MD` 1; ci-cd-setup `publish-release.sh` 2 (>=2), `repository: write` 1, echte Umlaute 0. Abschnitt „Dashboard“ im Anwenderhandbuch: kein Diff (0 geaenderte Zeilen mit „Dashboard“, Aenderung nur Inhaltsverzeichnis, Satz in „Aufbau der Oberflaeche“ und neuer Abschnitt vor den Stolpersteinen).
|
||||
|
||||
**Baseline:** Web `Test Files 49 passed (49)` / `Tests 309 passed (309)` (zweimal gemessen, nach Task 1 und nach Task 3); API `Test Files 67 passed (67)` / `Tests 1078 passed (1078)`; `tsc --noEmit`: packages/shared 0, apps/api 0, apps/web 0; `pnpm install --frozen-lockfile` Exit 0; `git diff --stat 963fa36 -- . ':!.planning'` -> `19 files changed, 843 insertions(+), 17 deletions(-)`; Unantastbare (`.env*`, Compose, pnpm-lock.yaml, package.json beider Apps, prisma, apps/api/Dockerfile, umlaut-dictionary.ts): U_EMPTY=0 (unveraendert).
|
||||
|
||||
**CI-Lauf:** id **356**, head_sha c5f4ade, `started_at 2026-09-16T11:25:15+02:00`, `completed_at 2026-09-16T11:29:57+02:00` (4 min 42 s), `conclusion: success`. Jobs: 1023 Lint & Type Check success, 1024 Tests success, 1025 Build & Publish Images success. Job-Log 1025 (`/actions/jobs/1025/logs`, HTTP 200) zeigt den Release-Schritt:
|
||||
|
||||
```
|
||||
::group::Run sh .gitea/scripts/publish-release.sh
|
||||
Kein Freigabe-Tag (nur refs/tags/v*): nichts zu tun.
|
||||
```
|
||||
|
||||
Die Zeile `Gitea-API: https://git.vicolab.de/api/v1 ...` erscheint auf `main` NICHT — das Skript beendet sich laut Plan-Schritt 2 (Tag-Entscheidung) vor Plan-Schritt 3 (API-Basis ausgeben). Die Aufloesung ueber `GITHUB_SERVER_URL` ist lokal bewiesen (dry-run 4 oben); der CI-Beweis der Aufloesung kommt mit dem naechsten Tag (siehe offen).
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
**1. [Rule 1 - Bug] dash-`echo` haette das dry-run-JSON zerlegt** — Found during Task 2, Schritt C1 (jq konnte das dry-run-JSON nicht parsen: „control characters must be escaped“); Fix `printf '%s\n'` statt `echo`; Datei publish-release.sh; im Commit 6940bd0 enthalten.
|
||||
|
||||
**2. Reihenfolge Tag-Entscheidung vor API-Ausgabe** — der Plan-Text in key_links erwartet die `Gitea-API:`-Zeile im main-Lauf, die Action-Schritte 2/3 legen die Reihenfolge anders fest; ich habe die Action-Schritte umgesetzt. Ergebnis im CI-Log: nur „nichts zu tun“. Kein Gate verletzt.
|
||||
|
||||
**3. Leeres Ergebnis** — `filterChangelogForChannel` liefert `''` statt `'\n'`, wenn kein Abschnitt uebrig bleibt; die Seite prueft `markdown.trim()`, Test 3 der Seite deckt den Fall.
|
||||
|
||||
Sonst: Plan wie geschrieben ausgefuehrt. Keine neuen Pakete, keine Auth-Gates.
|
||||
|
||||
## Beobachtungen ausserhalb des Umfangs (nicht behoben)
|
||||
|
||||
- `pnpm exec biome check ...` bricht mit „Biome exited because the configuration resulted in errors“ ab (Konfiguration braucht `biome migrate`) — vorbestehend, nicht durch diesen Auftrag verursacht; Lint im CI ist ohnehin ein Leerlauf (WINDOWS #35).
|
||||
|
||||
## Threat Flags
|
||||
|
||||
Keine neue Oberflaeche ausserhalb des `<threat_model>`: T-DCZ-01 (rehypeSanitize 2 Treffer), T-DCZ-02 (Import-Zaehler 0, Docker static 0), T-DCZ-03 (middleware.ts nicht angefasst), T-DCZ-04 (set -x 0, Echo-Token 0, header @ 3, Token nie ausgegeben), T-DCZ-05 (jq --arg), T-DCZ-06 (v9.9.9 Exit 1, Release-Anzahl 1), T-DCZ-07 (API-Basis-Aufloesung lokal bewiesen), T-DCZ-09 (genau `!CHANGELOG.md`), T-DCZ-SC (frozen-lockfile 0) — alle Gates gruen.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
Keine.
|
||||
|
||||
## Was bewusst offen bleibt
|
||||
|
||||
- **CI-Beweis des Release-Wegs:** Der POST/PATCH-Weg lief nur lokal gegen `localhost:3002` (v1.0.0). Der erste echte Tag-Lauf im CI (`refs/tags/v1.0.1` oder `v1.1.0`) beweist die API-Aufloesung `https://git.vicolab.de/api/v1` aus dem Job-Container und die Token-Berechtigung von `secrets.REGISTRY_TOKEN` fuer Releases (gemessen zur Planungszeit: `write:repository`).
|
||||
- **Browser-Gegenprobe** (siehe naechster Abschnitt) — vom Orchestrator durchzufuehren; kein Container wurde gestartet oder neu gebaut.
|
||||
- Der Abschnitt „Fuer den Changelog“ entfaellt: die Punkte von bwo, dyv und diesem Auftrag stehen bereits in CHANGELOG.md unter Unveroeffentlicht.
|
||||
|
||||
## Fuer den Verifizierer/Orchestrator (Browser-Nachweis)
|
||||
|
||||
1. Web-Container neu bauen (der Changelog kommt zur BAUZEIT ins Bundle; `up` allein reicht nicht): `docker compose up -d --build --force-recreate web`. Lokal ist der Kanal `dev` (kein `APP_CHANNEL`-Build-Arg).
|
||||
2. Anmelden, unten in der Seitenleiste auf die Versionszeile (`dev · Entwicklung`, Link mit aria-label „Was ist neu“) klicken -> URL `/changelog`, Seitentitel „Was ist neu“, Vorspann.
|
||||
3. Erwartung auf `dev`: gelber Hinweis („Die Punkte unter „Noch nicht freigegeben“ sind in dieser Beta bereits enthalten ...“, `data-testid="changelog-unreleased-hint"`), darunter die gerenderte Liste mit Ueberschrift „Noch nicht freigegeben (Beta)“ (8 Punkte) und „1.0.0 – 2026-09-15“ (11 Punkte); H1 „Änderungen an Tessera“ und Vorspann der Datei fehlen (die Seite hat ihren eigenen Titel).
|
||||
4. Sanitized Rendering: das Markdown wird ueber `MDEditor.Markdown` mit `rehype-sanitize` gerendert (`data-testid="changelog-markdown"`); Dunkelmodus-Umschalter oben rechts wechselt `data-color-mode`.
|
||||
5. Ohne Anmeldung `/changelog` aufrufen -> Umleitung auf `/login` (bestehende Middleware).
|
||||
6. `live` lokal simulieren: `docker build --build-arg APP_CHANNEL=live -f apps/web/Dockerfile .` und den Container damit starten — dann fehlt der Abschnitt „Noch nicht freigegeben“ samt Hinweis komplett; alternativ genuegt der Unit-Beweis (Test 1 und 8 in changelog.test.ts, Test 1 in changelog-page.test.tsx decken den live-Pfad; Falsifizierung (a) oben).
|
||||
7. Gitea: Repository -> Releases zeigt „Tessera 1.0.0“ zum Tag v1.0.0 mit dem 1.0.0-Abschnitt als Text.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- Dateien: CHANGELOG.md, apps/web/src/lib/changelog.ts, changelog.test.ts, (portal)/changelog/page.tsx, changelog-page.test.tsx, components/changelog/changelog-view.tsx, .gitea/scripts/publish-release.sh — FOUND (19 Dateien im Diff gegen 963fa36).
|
||||
- Commits ba06db9, 6940bd0, c5f4ade — FOUND in `git log`, gepusht (origin/main == main).
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
---
|
||||
phase: quick-260916-dcz
|
||||
verified: 2026-09-16T11:40:00Z
|
||||
status: passed
|
||||
score: 8/8 must-haves verified
|
||||
covered_files: [".dockerignore", ".gitea/scripts/publish-release.sh", ".gitea/workflows/ci.yml", ".planning/quick/260916-dcz-aenderungsliste-changelog-md-in-alltagss/260916-dcz-PLAN.md", ".planning/quick/260916-dcz-aenderungsliste-changelog-md-in-alltagss/260916-dcz-SUMMARY.md", ".planning/quick/260916-dcz-aenderungsliste-changelog-md-in-alltagss/deferred-items.md", "CHANGELOG.md", "apps/web/Dockerfile", "apps/web/next.config.ts", "apps/web/src/app/(portal)/changelog/changelog-page.test.tsx", "apps/web/src/app/(portal)/changelog/page.tsx", "apps/web/src/components/changelog/changelog-view.tsx", "apps/web/src/components/layout/app-version-badge.test.tsx", "apps/web/src/components/layout/app-version-badge.tsx", "apps/web/src/lib/changelog.test.ts", "apps/web/src/lib/changelog.ts", "apps/web/src/messages/de.json", "apps/web/src/messages/en.json", "docs/anleitung-anwender.md", "docs/anleitung-betrieb.md", "docs/anleitung-entwicklung.md", "docs/ci-cd-setup.md"]
|
||||
covered_digest: "v1:sha256:45c924c03cc6ffda72be090cf17a9877899dbcde20eddf2a28b4c558b4cecb1f"
|
||||
behavior_unverified: 0
|
||||
overrides_applied: 0
|
||||
---
|
||||
|
||||
# Quick-Task 260916-dcz: Aenderungsliste (CHANGELOG.md), Seite "Was ist neu", Gitea-Release je Tag — Verifikationsbericht
|
||||
|
||||
**Auftragsziel:** `CHANGELOG.md` in Alltagssprache (Unveroeffentlicht + 1.0.0), Seite "Was ist neu" unter `/changelog` (Server-Komponente, Bauzeit-Einbettung, Kanalfilter, MDEditor.Markdown + rehypeSanitize), Dockerfile/`.dockerignore`-Anpassung, Release-Skript `publish-release.sh` + ci.yml-Schritt, rueckwirkender Gitea-Release v1.0.0, vier Handbuecher.
|
||||
|
||||
**Verifiziert:** 2026-09-16, 11:15-11:45 UTC (lokal 13:15-13:45)
|
||||
**Status:** passed
|
||||
**Re-Verifikation:** Nein — Erstverifikation
|
||||
|
||||
Zusaetzlich im Umfang dieser Verifikation (per Auftrag): der vom Orchestrator angehaengte Commit `c3d8e16` (fehlende i18n-Schluessel Kalenderquellen-Formular + ein "Behoben"-Eintrag in `CHANGELOG.md`) — geprueft nur darauf, ob er ein Gate dieses Plans bricht. Tut er nicht (siehe unten).
|
||||
|
||||
## Zielerreichung
|
||||
|
||||
### Beobachtbare Wahrheiten
|
||||
|
||||
| # | Wahrheit | Status | Beleg |
|
||||
|---|----------|--------|-------|
|
||||
| 1 | `CHANGELOG.md` liegt im Wurzelverzeichnis, deutsch, echte Umlaute, Alltagssprache, Keep-a-Changelog-Form | VERIFIZIERT | `cat CHANGELOG.md`: H1 "# Änderungen an Tessera", Vorspann, `## Unveröffentlicht` mit `### Neu` (2), `### Geändert` (6), `### Behoben` (1, aus c3d8e16), `## 1.0.0 – 2026-09-15` mit `### Neu` (11 Punkte, Bereich 6-12 erfuellt). Keine Dateinamen (`grep -nE '\.tsx|\.ts[^a-z]'` leer), keine Commit-Kuerzel (`grep -nE '\b[0-9a-f]{7,40}\b'` leer). |
|
||||
| 2 | Versionszeile ist Link zu `/changelog`, Seite rendert CHANGELOG als Markdown, Middleware schuetzt die Route | VERIFIZIERT | `app-version-badge.tsx`: `<Link href="/changelog" aria-label={t('whatsNew')} data-testid="app-version" title={title}>`; `page.tsx`: async Server-Komponente ohne `'use client'`; `middleware.ts` `publicRoutes = ['/login', '/reset-password']` — `/changelog` ist geschuetzt. |
|
||||
| 3 | Kanalregel `filterChangelogForChannel` als reine Funktion, live entfernt Unveroeffentlicht, beta/dev zeigen "Noch nicht freigegeben (Beta)", leerer Abschnitt immer ausgeblendet | VERIFIZIERT | Quellcode geprueft (`apps/web/src/lib/changelog.ts`); Falsifizierung selbst durchgefuehrt: `if (isEmpty || channel === 'live')` -> `if (isEmpty)` gesetzt, `vitest run changelog.test.ts` -> `2 failed \| 8 passed (10)` (Test 1 live, Test 8 CRLF/live rot wie von SUMMARY behauptet); danach `git checkout --` und `git status --porcelain -- apps/` leer bestaetigt. |
|
||||
| 4 | Bauzeit-Einbettung via `next.config.ts` `env.TESSERA_CHANGELOG_MD`, Server-Bundle-only, Docker COPY + `.dockerignore`-Ausnahme | VERIFIZIERT | `next.config.ts`: `readChangelog()` liest `path.resolve(__dirname, '../../CHANGELOG.md')`, `env: { TESSERA_CHANGELOG_MD: readChangelog() }`; `.dockerignore` Zeile 7 `*.md`, Zeile 10 `!CHANGELOG.md` (danach); `apps/web/Dockerfile` Zeile 27 `COPY CHANGELOG.md ./` in der builder-Stufe nach `COPY tsconfig.base.json ./`. Docker-Beweis selbst nicht neu gebaut (Environment verbietet Container-Build), SUMMARY-Beleg (Exit 0, `.next/server`=1 Treffer, `.next/static`=0) als plausibel bewertet, da Quellcode und Importdisziplin (`grep -rl "@/lib/changelog'" apps/web/src` nur in `page.tsx`/Tests) die Behauptung stuetzen. |
|
||||
| 5 | `publish-release.sh`: awk-Schnitt, jq, API-Basis aus GITHUB_*, POST/PATCH, `--dry-run`, Exit != 0 ohne Abschnitt, Token nie ausgegeben | VERIFIZIERT | Selbst ausgefuehrt: `--dry-run --tag v1.0.0` -> Exit 0, JSON mit `.name="Tessera 1.0.0"`; `--dry-run --tag v9.9.9` -> Exit 1, Meldung "CHANGELOG.md hat keinen Abschnitt fuer Version 9.9.9". Token nur in `printf ... > "$HDR"`, kein `echo`/`set -x` von `$GITEA_TOKEN`. |
|
||||
| 6 | `ci.yml`-Schritt ruft `publish-release.sh` mit `GITEA_TOKEN` aus `secrets.REGISTRY_TOKEN` in `env` auf, rueckwirkender Release v1.0.0 existiert | VERIFIZIERT | `.gitea/workflows/ci.yml` Zeilen 71-74: Schritt nach dem Abbild-Schritt, `env: GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }}`, `run: sh .gitea/scripts/publish-release.sh` — kein Echo, kein Argument. Gitea-API (read-only, Token aus Push-URL, nicht ausgegeben): `GET /releases` -> genau 1 Release, `tag_name=v1.0.0`, `name="Tessera 1.0.0"`, `body` 2227 Zeichen, beginnt mit `### Neu`. |
|
||||
| 7 | Handbuecher (Betrieb Kap. 9, Anwender, Entwicklung, CI-Setup) dokumentieren den neuen Ablauf | VERIFIZIERT | `anleitung-betrieb.md`: Schritt "Änderungsliste abschließen" vor dem Tag, automatischer Gitea-Release beschrieben, "Was ist neu" als vierter Weg. `anleitung-anwender.md`: `## Was ist neu` (Zeile 173) + Inhaltsverzeichnis-Eintrag. `anleitung-entwicklung.md`: Regel "jede Änderung sofort ... unter Unveröffentlicht". `docs/ci-cd-setup.md`: `publish-release.sh` (2 Treffer), `repository: write` genannt; ASCII-Umschrift konsistent mit Bestandsdatei. |
|
||||
| 8 | Baseline am Ende: Web 49/309, API 67/1078, tsc 0, `pnpm install --frozen-lockfile` 0, 19 Dateien im Diff, verbotene Pfade unangetastet, CI-Lauf success, `git push` synchron | VERIFIZIERT | Selbst gemessen: Web `Test Files 49 passed (49)` / `Tests 309 passed (309)`; API `Test Files 67 passed (67)` / `Tests 1078 passed (1078)`; `tsc --noEmit` Exit 0 in web/api/shared; `pnpm install --frozen-lockfile` Exit 0; `git diff --stat 963fa36 c3d8e16~1 -- . ':!.planning'` -> 19 Dateien; vollstaendiger Dateibaum-Vergleich `963fa36..c3d8e16` zeigt keine der verbotenen Pfade (Compose, pnpm-lock.yaml, package.json, prisma, api-Dockerfile, umlaut-dictionary.ts, `.env*`) — nur die 19 erwarteten Dateien; CI-Lauf 356 (c5f4ade) `status=success` (alle 3 Jobs), CI-Lauf fuer c3d8e16 (Task-IDs 771/772/773, `run 357`) bei erster Messung "running" (Build & Publish Images), nach Poll bis ~2 Min: alle 3 Jobs `success`; `git fetch && git status -sb` -> `## main...origin/main`. |
|
||||
|
||||
**Score:** 8/8 Wahrheiten verifiziert (0 present-behavior-unverified)
|
||||
|
||||
### Erforderliche Artefakte
|
||||
|
||||
| Artefakt | Erwartet | Status | Details |
|
||||
|----------|----------|--------|---------|
|
||||
| `CHANGELOG.md` | H1, Vorspann, Unveroeffentlicht + 1.0.0, echte Umlaute | VERIFIZIERT | Inhalt gelesen und geprueft, siehe Wahrheit 1 |
|
||||
| `.dockerignore` | `!CHANGELOG.md` nach `*.md` | VERIFIZIERT | Zeile 7 `*.md`, Zeile 10 `!CHANGELOG.md` |
|
||||
| `apps/web/Dockerfile` | `COPY CHANGELOG.md ./` in builder-Stufe | VERIFIZIERT | Zeile 27, nach `COPY tsconfig.base.json ./` |
|
||||
| `apps/web/next.config.ts` | `readChangelog()` + `env.TESSERA_CHANGELOG_MD` | VERIFIZIERT | Quellcode gelesen |
|
||||
| `apps/web/src/lib/changelog.ts` | `filterChangelogForChannel`, `UNRELEASED_HEADING`, `changelogMarkdown` | VERIFIZIERT | Quellcode gelesen, Falsifizierung bestanden |
|
||||
| `apps/web/src/lib/changelog.test.ts` | 10 Tests | VERIFIZIERT | `vitest run` 10/10 gruen |
|
||||
| `apps/web/src/app/(portal)/changelog/page.tsx` | async Server-Komponente | VERIFIZIERT | Quellcode gelesen, kein `'use client'` |
|
||||
| `apps/web/src/app/(portal)/changelog/changelog-page.test.tsx` | 3 Tests | VERIFIZIERT | in Gesamtsuite (309) enthalten |
|
||||
| `apps/web/src/components/changelog/changelog-view.tsx` | `MDEditor.Markdown` + `rehypeSanitize` | VERIFIZIERT | Quellcode gelesen |
|
||||
| `apps/web/src/components/layout/app-version-badge.tsx` | Link auf `/changelog` | VERIFIZIERT | Quellcode gelesen |
|
||||
| `apps/web/src/messages/de.json` + `en.json` | `sidebar.whatsNew`, Namensraum `changelog` | VERIFIZIERT | JSON geparst, alle Schluessel vorhanden |
|
||||
| `.gitea/scripts/publish-release.sh` | ausfuehrbar, POSIX sh | VERIFIZIERT | `sh .gitea/scripts/publish-release.sh` lief ohne chmod-Fehler |
|
||||
| `.gitea/workflows/ci.yml` | vierter Schritt im Job `publish` | VERIFIZIERT | Zeilen 71-74 |
|
||||
| Handbuecher (4 Dateien) | Abschnitte wie in Wahrheiten | VERIFIZIERT | siehe Wahrheit 7 |
|
||||
|
||||
### Key-Link-Verifikation
|
||||
|
||||
| Von | Nach | Via | Status | Details |
|
||||
|-----|------|-----|--------|---------|
|
||||
| `next.config.ts` | `changelog.ts` | `env.TESSERA_CHANGELOG_MD` -> `process.env.TESSERA_CHANGELOG_MD` (voller Literalname) | WIRED | Literalname in beiden Dateien identisch, Test 9/10 in changelog.test.ts bestehen |
|
||||
| `changelog.ts` | `page.tsx` | einziger Import ausserhalb Tests | WIRED | `grep -rl "@/lib/changelog'" apps/web/src` liefert nur `page.tsx` und Testdateien |
|
||||
| `page.tsx` | `changelog-view.tsx` | Prop `markdown` | WIRED | `<ChangelogView markdown={markdown} />` |
|
||||
| `app-version-badge.tsx` | `/changelog` | `next/link` `Link href` | WIRED | Quellcode gelesen |
|
||||
| `middleware.ts` | `/changelog` | implizit (nicht in `publicRoutes`) | WIRED | `publicRoutes` enthaelt `/changelog` nicht |
|
||||
| `ci.yml` (`publish`-Job) | `publish-release.sh` | `env.GITEA_TOKEN` + `run: sh ...` | WIRED | Skript lief im CI-Lauf 356 und 357 mit "nichts zu tun" (main, kein Tag) |
|
||||
| `publish-release.sh` | Gitea-API | `GITEA_API`/`GITHUB_API_URL`/`GITHUB_SERVER_URL`/Fallback | WIRED (lokal bewiesen) | Rueckwirkender Lauf `v1.0.0` gegen `localhost:3002` erzeugte den Release; Aufloesung im CI selbst noch ungeprueft, da noch kein neuer Tag seit diesem Auftrag gepusht wurde (siehe "Angenommene Risiken") |
|
||||
|
||||
### Verhaltens-Stichproben
|
||||
|
||||
| Verhalten | Befehl | Ergebnis | Status |
|
||||
|-----------|--------|----------|--------|
|
||||
| Kanalfilter live entfernt Unveroeffentlicht | `vitest run src/lib/changelog.test.ts` (Original) | 10/10 gruen | PASS |
|
||||
| Falsifizierung: Funktion ohne live-Zweig | `sed -i` Aenderung + `vitest run` | 2 failed / 8 passed | PASS (rot wie erwartet) |
|
||||
| Release-Skript ohne Abschnitt | `--dry-run --tag v9.9.9` | Exit 1, Meldung korrekt | PASS |
|
||||
| Release-Skript mit Abschnitt | `--dry-run --tag v1.0.0` | Exit 0, JSON korrekt | PASS |
|
||||
| Gitea-Release existiert | `GET /repos/schalli/tessera-ctl/releases` | 1 Release, v1.0.0, "Tessera 1.0.0" | PASS |
|
||||
| Web-Testsuite | `npx vitest run` (apps/web) | 49 Dateien / 309 Tests gruen | PASS |
|
||||
| API-Testsuite | `npx vitest run` (apps/api) | 67 Dateien / 1078 Tests gruen | PASS |
|
||||
| TypeScript | `npx tsc --noEmit` (web/api/shared) | Exit 0 je | PASS |
|
||||
| Lockfile | `pnpm install --frozen-lockfile` | Exit 0 | PASS |
|
||||
|
||||
### Requirements Coverage
|
||||
|
||||
Kein Eintrag `QUICK-260916-DCZ` in `.planning/REQUIREMENTS.md` gefunden — bei Quick-Tasks ueblich (keine formale Requirements-Zuordnung). Kein verwaistes Requirement identifiziert.
|
||||
|
||||
### Anti-Pattern-Scan
|
||||
|
||||
Alle 13 vom Plan genannten Code-/Skript-Dateien auf `TBD|FIXME|XXX|TODO|HACK|PLACEHOLDER|not yet implemented|coming soon` geprueft — keine Treffer.
|
||||
|
||||
### Probe-Ausfuehrung
|
||||
|
||||
Kein dediziertes `scripts/*/tests/probe-*.sh`-Muster im Umfang dieses Auftrags; die Falsifizierungen (a) und (b) wurden als Ad-hoc-Proben unter "Verhaltens-Stichproben" durchgefuehrt.
|
||||
|
||||
## Vom Orchestrator im Browser zu pruefen
|
||||
|
||||
Der Executor hat im SUMMARY (Abschnitt "Fuer den Verifizierer/Orchestrator") sieben Browser-Schritte dokumentiert; keiner davon wurde in dieser Verifikation ausgefuehrt (Umgebung untersagt Container-Neubau/-Start und Browser-Nutzung). Zur Erledigung durch den Orchestrator:
|
||||
|
||||
1. `docker compose up -d --build --force-recreate web` (Bauzeit-Einbettung erfordert Neubau).
|
||||
2. Anmelden, Versionszeile unten links anklicken -> `/changelog`, Titel "Was ist neu".
|
||||
3. Auf lokalem Kanal `dev`: gelber Hinweis + "Noch nicht freigegeben (Beta)" (9 Punkte inkl. Behoben-Eintrag) + "1.0.0 – 2026-09-15" (11 Punkte); H1/Vorspann der Datei fehlen.
|
||||
4. Sanitized Rendering pruefen (`data-testid="changelog-markdown"`), Dunkelmodus-Umschalter wechselt `data-color-mode`.
|
||||
5. Ohne Anmeldung `/changelog` -> Umleitung `/login`.
|
||||
6. Optional: `live`-Kanal lokal simulieren (Build-Arg `APP_CHANNEL=live`) -> Abschnitt "Noch nicht freigegeben" fehlt komplett.
|
||||
7. Gitea-Oberflaeche: Releases zeigt "Tessera 1.0.0".
|
||||
|
||||
Dies ist **keine** `human_needed`-Klassifizierung fuer den Gesamtbericht — der Auftrag weist diese Pruefung explizit dem Orchestrator zu, nicht dem Verifizierer, und alle automatisiert pruefbaren Wahrheiten sind bereits VERIFIZIERT.
|
||||
|
||||
## Angenommene Risiken
|
||||
|
||||
- **Docker-Falsifizierung (c) nicht selbst nachgebaut:** Die Umgebung dieser Verifikation untersagt Container-Builds. Ich habe die SUMMARY-Behauptung (`docker build` Exit 0, Marker nur in `.next/server`, nicht in `.next/static`) nicht durch einen eigenen Bau nachvollzogen, sondern anhand des Quellcodes (Importdisziplin, COPY-Zeile, `.dockerignore`-Ausnahme) als plausibel und konsistent mit dem Verhalten des `next build`-Laufs bewertet, den ich indirekt ueber die identischen Server-/Static-Pruefungen im Code nachvollziehen kann. Der Browser-/Docker-Nachweis bleibt formal beim Orchestrator (siehe oben).
|
||||
- **CI-Beweis der Gitea-API-Aufloesung im Job-Container:** Der reale POST/PATCH-Weg lief nur lokal gegen `localhost:3002`. Seit diesem Auftrag wurde kein neuer Freigabe-Tag gepusht, daher zeigt keiner der beiden beobachteten CI-Laeufe (356, 357) den Tag-Pfad — beide liefen auf `main` und meldeten "nichts zu tun", wie vom Skript-Design (Tag-Entscheidung vor API-Ausgabe) auch erwartet. Dies ist ein vom Executor selbst benanntes offenes Risiko ("Was bewusst offen bleibt"), keine Luecke dieses Auftrags — die lokale Falsifizierung (b) und der rueckwirkende Release-Lauf decken die Skriptlogik ab.
|
||||
- **Extra-Commit `c3d8e16`:** Ausserhalb des urspruenglichen Plans, aber nachweislich harmlos fuer alle Gates dieses Plans (19-Dateien-Zaehlung, verbotene Pfade, Testzahlen, CHANGELOG-Struktur) — CI-Lauf fuer diesen Commit ist inzwischen ebenfalls gruen (alle 3 Jobs `success`).
|
||||
- **`ci.yml`-Schritt hat kein `if:` auf Tag-Refs:** Die Gate-Beschreibung "nur bei Tag-Refs" wird durch interne Skriptlogik (`GITHUB_REF`-Pruefung, Exit 0 mit "nichts zu tun") erreicht, nicht durch eine Job-/Step-Bedingung in YAML. Das entspricht der im Plan dokumentierten Design-Entscheidung (Skript entscheidet wie `publish-images.sh`) und wurde durch zwei reale CI-Laeufe auf `main` bestaetigt — kein Gap, nur zur Transparenz vermerkt.
|
||||
|
||||
---
|
||||
|
||||
_Verifiziert: 2026-09-16T11:45:00Z_
|
||||
_Verifizierer: Claude (gsd-verifier)_
|
||||
|
||||
## Nachtrag des Orchestrators — Browser-Check durchgefuehrt (2026-09-16, 09:37Z)
|
||||
|
||||
Lokales Web-Abbild aus `c3d8e16` gebaut, Playwright MCP, Anmeldung als lokaler Admin. Versionsabzeichen ist ein Link (`<a href="/changelog" aria-label="Was ist neu">`). `/changelog`: H1 "Was ist neu", H2 "Noch nicht freigegeben (Beta)" mit H3 Neu/Geaendert/Behoben, H2 "1.0.0 – 2026-09-15" mit H3 Neu; 20 Listenpunkte; Hinweistext zur Beta sichtbar (Kanal `dev` verhaelt sich wie beta). Kalender-Einstellungen -> Kalenderquelle hinzufuegen: Beschriftungen "Name *", "Typ *", "Adresse (URL) *", "Benutzername", "Passwort", "Farbe", Knoepfe "Speichern / Verbindung testen / Abbrechen" — keine Schluesselnamen mehr (c3d8e16). CI-Lauf fuer c3d8e16 success.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Deferred Items (quick-260916-dcz)
|
||||
|
||||
- Biome-Konfiguration vorbestehend fehlerhaft: `pnpm exec biome check` bricht mit Konfigurationsfehler ab (braucht `biome migrate`). Nicht durch diesen Auftrag verursacht; Lint im CI ist ein Leerlauf (WINDOWS #35).
|
||||
Reference in New Issue
Block a user