Files
tessera-ctl/.planning/quick/260914-ku1-zwei-auslieferungskanaele-beta-auf-main-/260914-ku1-VERIFICATION.md
T
schalli 5c42c558c4
Tessera CI/CD / Lint & Type Check (push) Successful in 45s
Tessera CI/CD / Tests (push) Successful in 56s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m52s
docs(quick-260914-ku1): Kanaele und Versionsstempel abgeschlossen und verifiziert 8/8 — Zusammenfassung, Verifikation, Aktenstand
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
2026-09-14 16:01:13 +02:00

13 KiB

phase, verified, status, score, covered_files, covered_digest, behavior_unverified, overrides_applied
phase verified status score covered_files covered_digest behavior_unverified overrides_applied
quick-260914-ku1 2026-09-14T13:59:19Z passed 8/8 must-haves verified
.gitea/scripts/publish-images.sh
.gitea/workflows/ci.yml
.planning/quick/260914-ku1-zwei-auslieferungskanaele-beta-auf-main-/260914-ku1-PLAN.md
.planning/quick/260914-ku1-zwei-auslieferungskanaele-beta-auf-main-/260914-ku1-SUMMARY.md
apps/api/Dockerfile
apps/api/src/health/app-version.ts
apps/api/src/health/health.controller.spec.ts
apps/api/src/health/health.controller.ts
apps/api/src/main.ts
apps/web/Dockerfile
apps/web/src/components/layout/app-version-badge.test.tsx
apps/web/src/components/layout/app-version-badge.tsx
apps/web/src/components/layout/sidebar.test.tsx
apps/web/src/components/layout/sidebar.tsx
apps/web/src/lib/app-version.test.ts
apps/web/src/lib/app-version.ts
apps/web/src/messages/de.json
apps/web/src/messages/en.json
docker-compose.prod.yml
docs/anleitung-betrieb.md
docs/ci-cd-setup.md
packages/shared/src/index.ts
v1:sha256:3cc012949ab9484c9c9821c7dea15392439adc9224bd2f9e3025991c0eba6062 0 0

Quick-Task 260914-ku1: Zwei Auslieferungskanaele (Beta/Live) — Verifikationsbericht

Ziel: Zwei Auslieferungskanaele (main -> beta+latest; Tag v* -> live+vX.Y.Z; Zweig live ohne Tag nur geprueft), Versionsstempel per Build-Args in beide Container-Abbilder, GET /health/version, Versionsabzeichen in der Seitenleiste, IMAGE_TAG in docker-compose.prod.yml, Publish-Skript mit --print-plan, Betriebshandbuch Kapitel „Zwei Kanaele" inkl. Hotfix-Rezept und Live-Server-Einrichtung, ci-cd-setup.md aktualisiert, gepusht, echter CI-Lauf gruen.

Verifiziert: 2026-09-14T13:59:19Z Status: passed Re-Verifikation: Nein — Erstverifikation

Alle Pruefungen wurden selbst erneut ausgefuehrt (nicht aus dem SUMMARY uebernommen). Wo die eigene Messung von der SUMMARY-Behauptung abweicht, ist das unten vermerkt — es gab keine Abweichung.

Pruefung 1 — Commits, Diff-Umfang, unangetastete Dateien

Befehl Erwartet Gemessen
git log --oneline 1cd4212..HEAD 3 Commits ea6aa99, 9731501, cdb571c — 3 Commits
git diff --stat 6c19451 -- . ':!.planning' (letzte Zeile) 20 files changed 20 files changed, 851 insertions(+), 49 deletions(-)
git diff --name-only 6c19451 -- '.env*' apps/api/prisma/schema.prisma apps/api/prisma/migrations package.json pnpm-lock.yaml biome.json apps/web/src/components/layout/sidebar-footer.tsx leer leer (keine Ausgabe)
git status --porcelain (vor jeder Aenderung durch die Verifikation) nur die zwei erwarteten offenen Dateien M .planning/STATE.md, ?? .../260914-ku1-SUMMARY.md — beides die dem Orchestrator gehoerenden, unberuehrt gelassenen Dateien
git fetch -q && git status -sb | head -1 kein [ahead ## main...origin/main

Status: ✓ VERIFIED

Pruefung 2 — Testsuiten und Typpruefung

Befehl Erwartet Gemessen
cd apps/api && npx vitest run 65 Dateien / 1060 Tests Test Files 65 passed (65) / Tests 1060 passed (1060)
cd apps/web && npx vitest run 40 Dateien / 243 Tests Test Files 40 passed (40) / Tests 243 passed (243)
pnpm -C packages/shared exec tsc --noEmit Exit 0 Exit 0
pnpm -C apps/api exec tsc --noEmit Exit 0 Exit 0
pnpm -C apps/web exec tsc --noEmit Exit 0 Exit 0

Status: ✓ VERIFIED

Pruefung 3 — Versionsstempel im Code (API und Web)

Quelldateien gelesen (nicht nur gegrept):

  • apps/api/src/health/health.controller.ts: getVersion() delegiert an getAppVersion() aus ./app-version, @Public() und @Get('version') gesetzt, kein Zugriff mehr auf npm_package_version. Kommentar begruendet T-KU1-03.
  • apps/api/src/health/app-version.ts: getAppVersion() liest process.env.APP_VERSION/APP_CHANNEL/APP_COMMIT/APP_BUILD_TIME mit ||-Vorgaben dev/dev//, name: 'tessera'. formatAppVersionLine() baut Tessera API <version> (<channel>) <commit>, ohne Commit ohne Leerzeichen am Ende.
  • apps/api/src/health/health.controller.spec.ts: 6 Tests wie im Plan beschrieben (check(), Vorgaben, Durchreichen, Compose-Leerstring-Semantik, formatAppVersionLine, @Public()-Metadatenpruefung per Reflect.getMetadata). Alle 6 gruen (siehe Pruefung 2).
  • apps/api/src/main.ts: console.log(formatAppVersionLine()) direkt nach der bestehenden Port-Zeile (Zeile 35, nach Zeile 34).
  • apps/web/src/lib/app-version.ts: appVersion liest process.env.NEXT_PUBLIC_APP_VERSION/_CHANNEL/_COMMIT je mit vollem Literalnamen (keine Destrukturierung, kein process.env[name]); normalizeChannel faellt bei unbekanntem Kanal auf 'dev' zurueck; loadApiVersion() memoisiert ein Modul-Promise gegen ${API_URL}/health/version mit credentials: 'include', still bei Fehler (.catch(() => null)).
  • apps/web/src/components/layout/app-version-badge.tsx: data-testid="app-version", Text ${appVersion.version} · ${t('channel.'+channel)}, title aus Commit <commit> und API <version> (<channel>), undefined wenn beides fehlt.
  • apps/web/src/components/layout/sidebar.tsx: Badge-Block liegt NACH dem Einklapp-Block (der hidden md:block traegt) und OHNE dieses Attribut — dadurch auch in der mobilen Schublade sichtbar; !isCollapsed blendet ihn eingeklappt aus (Zeilen ~201-206).
  • apps/web/src/messages/de.json / en.json: sidebar.channel = { beta: "Beta", live: "Live", dev: "Entwicklung"/"Development" } — in beiden Dateien vorhanden, per Node geprueft.
  • sidebar.test.tsx: Modul-Mock fuer AppVersionBadge und ein sechster Test renders the version badge below the navigation, der screen.getByTestId('app-version-badge') prueft.

Status: ✓ VERIFIED

Pruefung 4 — Dockerfile-Falsifizierung (unabhaengig wiederholt)

Beide Dockerfiles gelesen: globales ARG APP_VERSION=dev (und die drei weiteren) vor dem ersten FROM; im Web-builder ARG-Wiederholung + ENV NEXT_PUBLIC_APP_* unmittelbar VOR RUN pnpm --filter=@tessera/web build; im runner beider Images ARG-Wiederholung + ENV APP_VERSION=... nach ENV NODE_ENV=production.

Eigener Bau (nicht aus dem SUMMARY uebernommen):

docker build -f apps/api/Dockerfile --build-arg APP_VERSION=v7.7.7-verify --build-arg APP_CHANNEL=live -t tessera-api-verify:tmp .
docker run --rm --entrypoint node tessera-api-verify:tmp -e 'console.log(process.env.APP_VERSION, process.env.APP_CHANNEL)'
-> v7.7.7-verify live

Erwartung erfuellt. Danach docker rmi tessera-api-verify:tmp ausgefuehrt — kein Rueckstand (docker images | grep -c tessera-api-verify -> 0).

Status: ✓ VERIFIED

Pruefung 5 — CI-Workflow-Struktur und Publish-Skript

.gitea/workflows/ci.yml per js-yaml geparst:

branches=["main","live"] tags=["v*"] jobs=quality,test,publish fetchDepth=0 script=true needs=test

.gitea/scripts/publish-images.sh --print-plan in drei Lagen (eigene Ausfuehrung):

GITHUB_REF Ergebnis
refs/heads/main Kanal beta, Push-Zeilen fuer web:beta, web:latest, api:beta, api:latest (main-Fall enthaelt BEIDE, beta und latest)
refs/heads/live „Kein Veroeffentlichungs-Anlass ... nichts zu tun." — keine push-Zeile
refs/tags/v1.0.0 Kanal live, Push-Zeilen fuer web:live, web:v1.0.0, api:live, api:v1.0.0

Status: ✓ VERIFIED

Pruefung 6 — CI-gebaute Abbilder und echter Gitea-Lauf

docker images | grep tessera-ctl zeigt localhost:3002/schalli/tessera-ctl/{api,web}:{beta,latest} (zusaetzlich git.vicolab.de/... als Fetch-Alias und lokale tessera-ctl-{api,web}:latest aus fruehreren lokalen Bauten — nicht Teil dieser Pruefung).

docker run --rm --entrypoint node localhost:3002/schalli/tessera-ctl/api:beta -e 'console.log(process.env.APP_VERSION, process.env.APP_CHANNEL, process.env.APP_COMMIT)'
-> ea6aa99 beta ea6aa99

docker run --rm --entrypoint sh localhost:3002/schalli/tessera-ctl/web:beta -c 'grep -rl "ea6aa99" apps/web/.next/static | wc -l'
-> 1

ea6aa99 ist der zuletzt gepushte Commit (siehe Pruefung 1) — Uebereinstimmung.

Gitea-API (GET /api/v1/repos/schalli/tessera-ctl/actions/runs?limit=5, Token aus git remote get-url --push origin, nicht ausgegeben):

297 ea6aa995b27de1dfa9b557c06df9fa3bbcdd8ea3 completed success push
296 6c19451be9a25feb227af075076a839a968c5366 completed success push
...

Lauf 297 entspricht head_sha = ea6aa99..., status: completed, conclusion: success — deckt sich mit der SUMMARY-Angabe.

Status: ✓ VERIFIED

Pruefung 7 — docker-compose.prod.yml / IMAGE_TAG

docker compose -f docker-compose.prod.yml config --images
-> git.vicolab.de/schalli/tessera-ctl/web:beta, .../api:beta, postgres:16-alpine

IMAGE_TAG=live docker compose -f docker-compose.prod.yml config --images
-> .../api:live, postgres:16-alpine, .../web:live

Ohne Variable Vorgabe beta, mit IMAGE_TAG=live live fuer beide Images. Kein :latest mehr in der Datei (per grep unabhaengig bestaetigt: 0 Treffer).

Status: ✓ VERIFIED

Pruefung 8 — Betriebshandbuch und ci-cd-setup.md

docs/anleitung-betrieb.md, Abschnitt ## 9. Zwei Kanäle: Live und Beta (Zeile 355) gelesen (nicht nur gegreppt): erklaert den Kanalbegriff, die eine .env-Zeile je Server (IMAGE_TAG=beta/IMAGE_TAG=live), die Server-Compose-Anpassung, pull + up -d --force-recreate, das Freigabe-Rezept, den vollstaendigen Hotfix-Ablauf mit der fett gesetzten Regel „Keine Datenbankänderung als Hotfix" samt Alltagssprache-Begruendung (Migrations-Zeitstempel-Reihenfolge), die drei Erkennungswege (Oberflaeche/curl/Log) und die Einrichtung des neuen Live-Servers (eigene Secrets, eigene leere Datenbank, APP_URL, Reihenfolge Erstfreigabe vor erstem Pull). Echte Umlaute durchgaengig (Zwei Kanäle, änderungen, möglicherweise, größeren etc.) — Ton der Datei gehalten, keine ASCII-Umschrift in diesem Kapitel.

docs/ci-cd-setup.md: Abschnitt „Gitea Secrets" beschreibt REGISTRY_TOKEN und den Login (kein „keine Secrets" mehr); Abschnitt „Pipeline-Ueberblick" beschreibt Trigger (main/live/Tags v*), Etiketten-Tabelle, Build-Args-Tabelle, laengere Web-Bauzeit und markiert D-13 als ueberholt. grep -c "Kein Registry-Push\|build-deploy" -> 0.

Grep Erwartet Gemessen
^## 9\. Zwei Kanäle: Live und Beta in anleitung-betrieb.md 1 1
IMAGE_TAG in anleitung-betrieb.md >= 6 11
Keine Datenbankänderung als Hotfix 1 1
Kein Registry-Push|build-deploy in ci-cd-setup.md 0 0
[äöüÄÖÜß] in ci-cd-setup.md (ASCII-Konvention) 0 0

Status: ✓ VERIFIED

Anti-Pattern-Scan

Alle 13 durch die Fingerprint-Liste erfassten Code-/Config-Dateien auf TBD, FIXME, XXX, TODO, HACK, PLACEHOLDER, „not yet implemented" u. ae. geprueft — keine Treffer in einer der geaenderten Dateien.

Requirements-Abdeckung

QUICK-260914-KU1 ist eine Quick-Task-Anforderung ohne eigenen Eintrag in .planning/REQUIREMENTS.md (projektueblich fuer /gsd-quick-Auftraege, kein Roadmap-Phasenbezug) — kein verwaistes Requirement, da REQUIREMENTS.md keinen Phasen-Bezug fuer diesen Quick-Task erwartet.

Beobachtete Nebenpunkte (keine Gaps)

  • Zusaetzliche lokale Docker-Images (git.vicolab.de/...:latest, tessera-ctl-api:latest, tessera-ctl-web:latest) liegen auf dem Host aus fruehreren Bauten/Pulls — nicht Teil dieses Plans und nicht durch ihn verursacht.
  • sidebar-footer.tsx bleibt wie geplant unangetastet (toter Code, im SUMMARY als Nebenbefund vermerkt).
  • .env.prod.example bewusst ohne IMAGE_TAG-Zeile (Regel: keine .env*-Aenderungen) — im Handbuch als offener Punkt vermerkt.

Angenommene Risiken

  • T-KU1-03 (GET /health/version bleibt @Public(), gibt Version/Kanal/Commit/Bauzeit ohne Anmeldung preis): als "accept" im Threat-Register des Plans geflaggt, durch Spec-Test 6 gepinnt. Bei externem Verkauf von Tessera muesste das revidiert werden (im Plan bereits als Folgeaenderung genannt). Die Verifikation bestaetigt nur, dass die Entscheidung wie dokumentiert umgesetzt und getestet ist — keine eigene Bewertung des Risikos selbst.
  • T-KU1-04 (Tag-Push v* als alleiniger Freigabe-Hebel, kein Tag-/Branch-Schutz in Gitea): als "accept" geflaggt, weil aktuell nur ein Konto Schreibrecht hat. Diese Verifikation hat KEINE Gitea-Repository-Einstellungen aendern koennen/muessen (ausserhalb der Erlaubnisliste) und bestaetigt nur, dass die Empfehlung im Handbuch/ci-cd-setup steht.
  • live-Zweig und Tag v1.0.0 sind bewusst nicht angelegt — laut Plan Folgearbeit nach dem noch ausstehenden „Fehler-melden-Knopf"-Quick-Task. Das bedeutet: der Live-Kanal ist bislang nur durch die drei --print-plan-Simulationen und den lokalen Docker-Falsifizierungslauf bewiesen, NICHT durch einen echten CI-Lauf mit refs/tags/v* (der echte CI-Lauf in Pruefung 6 deckt nur den Beta-Pfad ab). Das ist im Rahmen des Plans ausdruecklich so vorgesehen (Output-Abschnitt: "Zweig live und Tag v1.0.0 werden NICHT in diesem Plan angelegt") und daher kein Gap, aber ein offener Punkt fuer die naechste Freigabe.
  • Zusaetzliche, vom Runner gebaute lokale Images auf dem Host (git.vicolab.de/...:latest etc.) wurden nicht aufgeraeumt — sie stammen nicht aus dieser Verifikation und wurden nicht entfernt, um den Host-Zustand nicht ueber das Mandat hinaus zu veraendern.

Verifiziert: 2026-09-14T13:59:19Z Verifier: Claude (gsd-verifier)