Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
21 KiB
phase, plan, subsystem, tags, status, requires, provides, affects, actuals, plan_head_before, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, metrics
| phase | plan | subsystem | tags | status | requires | provides | affects | actuals | plan_head_before | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | metrics | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-260914-ku1 | 01 | infra |
|
complete |
|
|
|
|
1cd4212df0 |
|
|
|
|
|
|
|
Quick 260914-ku1 Plan 01: Zwei Auslieferungskanaele (Beta auf main, Live per Tag) mit Versionsstempel durch alle Schichten — Summary
Versionsstempel APP_VERSION/APP_CHANNEL/APP_COMMIT/APP_BUILD_TIME fliesst vom CI-Skript ueber Build-Args in beide Abbilder, aus GET /health/version und dem Startlog der API und als v<Version> · <Kanal> unten in der Seitenleiste; main veroeffentlicht beta+latest, ein Tag v* veroeffentlicht live+vX.Y.Z; docker-compose.prod.yml waehlt den Kanal ueber ${IMAGE_TAG:-beta}; das Betriebshandbuch erklaert Kanaele, Freigabe, Hotfix (ohne Datenbankaenderung) und den neuen Live-Server. Der echte CI-Weg ist einmal durchlaufen: Lauf 297 hat :beta-Abbilder mit ea6aa99 beta gebaut.
Ausgangslage und Bezugspunkt
Alle Gates gegen 6c19451 (Code unangetastet seit Planung; HEAD bei Start 1cd4212, Arbeitsbaum sauber, main == origin/main).
Baseline vor jeder Aenderung (erneut gemessen, identisch mit der Planung):
| Suite | Befehl | Ergebnis |
|---|---|---|
| API | pnpm -C apps/api exec vitest run |
Test Files 64 passed (64) / Tests 1054 passed (1054), Exit 0 |
| Web | pnpm -C apps/web exec vitest run |
Test Files 38 passed (38) / Tests 233 passed (233), Exit 0 |
Task 1 — Versionsstempel durch alle Schichten (TDD)
RED-Lauf (Schritt A, vor jedem Produktionscode)
pnpm -C apps/api exec vitest run src/health/health.controller.spec.ts (Exit 1):
Error: Cannot find module './app-version' imported from '.../apps/api/src/health/health.controller.spec.ts'
Test Files 1 failed (1)
Tests no tests
pnpm -C apps/web exec vitest run src/lib/app-version.test.ts src/components/layout/app-version-badge.test.tsx src/components/layout/sidebar.test.tsx (Exit 1):
❯ src/components/layout/app-version-badge.test.tsx (0 test) Failed to resolve import "./app-version-badge"
❯ src/lib/app-version.test.ts (0 test) Failed to resolve import "./app-version"
❯ src/components/layout/sidebar.test.tsx (6 tests | 1 failed)
× renders the version badge below the navigation Unable to find an element by: [data-testid="app-version-badge"]
Test Files 3 failed (3)
Tests 1 failed | 5 passed (6)
GREEN und Gate (Schritt B/C)
Verify-Block von Task 1, gemessen:
| Pruefung | Erwartet | Gemessen |
|---|---|---|
API-Spec health.controller.spec.ts |
Tests 6 passed (6) |
Tests 6 passed (6) |
| Web drei Specs | Test Files 3 passed (3) / Tests 15 passed (15) |
Test Files 3 passed (3) / Tests 15 passed (15) |
Plan-Checker-Zusatz: obige drei + umlaut-guard.spec.ts + tenderRadar-parity.spec.ts in EINEM Aufruf |
gruen | Test Files 5 passed (5) / Tests 21 passed (21) |
grep -c npm_package_version health.controller.ts |
0 | 0 |
grep -c "formatAppVersionLine()" main.ts |
1 | 1 |
grep -c process.env.NEXT_PUBLIC_APP_VERSION app-version.ts |
1 | 1 |
grep -c "<AppVersionBadge />" sidebar.tsx |
1 | 1 |
| Node-Zeile Uebersetzungen | Beta Live Entwicklung | Development |
Beta Live Entwicklung | Development |
tsc --noEmit shared / api / web |
0 / 0 / 0 | 0 / 0 / 0 |
| API-Suite voll | 65 / 1060 | Test Files 65 passed (65) / Tests 1060 passed (1060) |
| Web-Suite voll | 40 / 243 | Test Files 40 passed (40) / Tests 243 passed (243) |
Commit cdb571c — git show --stat HEAD zeigt 13 Dateien (471+/8-).
Task 2 — Build-Args, Veroeffentlichungs-Skript, CI-Trigger, IMAGE_TAG
Gates ohne Docker (Schritt F), gemessen
branches=["main","live"] tags=["v*"] jobs=quality,test,publish fetchDepth=0 script=true needs=test
main_plan=4 tag_plan=4 live_plan=0 stamp=1
compose_beta=2 compose_live=2 latest-in-compose=0
ARG APP_VERSION=dev: api 1 / web 1; NEXT_PUBLIC_APP_VERSION=$APP_VERSION: 1; ENV APP_VERSION=$APP_VERSION: web 1 / api 1; EXEC=0
git describe --tags --always -> cdb571c (kein Tag vorhanden, Bau vor dem ersten Tag scheitert nicht)
--print-plan in den drei Lagen (woertlich):
GITHUB_REF=refs/heads/main:
Tessera cdb571c (beta) cdb571c 2026-09-14T13:33:40Z -> Etiketten: beta latest
push localhost:3002/schalli/tessera-ctl/web:beta
push localhost:3002/schalli/tessera-ctl/web:latest
push localhost:3002/schalli/tessera-ctl/api:beta
push localhost:3002/schalli/tessera-ctl/api:latest
GITHUB_REF=refs/tags/v1.2.3:
Tessera cdb571c (live) cdb571c 2026-09-14T13:33:40Z -> Etiketten: live v1.2.3
push localhost:3002/schalli/tessera-ctl/web:live
push localhost:3002/schalli/tessera-ctl/web:v1.2.3
push localhost:3002/schalli/tessera-ctl/api:live
push localhost:3002/schalli/tessera-ctl/api:v1.2.3
GITHUB_REF=refs/heads/live:
Kein Veroeffentlichungs-Anlass fuer 'refs/heads/live' (nur main und Tags v*): nichts zu tun.
docker compose -f docker-compose.prod.yml config --images ohne Variable: web:beta, api:beta, postgres:16-alpine; mit IMAGE_TAG=live: api:live, postgres:16-alpine, web:live.
Falsifizierung Bauzeit-Einbettung (Schritt E)
Baudauer (warmer deps-Cache): api:args 111 s, web:args 129 s, api:noargs 113 s, web:noargs 99 s — zusammen 7 min 32 s, alle Exit 0.
| # | Befehl (Kurzform) | Erwartet | Gemessen |
|---|---|---|---|
| 1 | api:args node APP_VERSION APP_CHANNEL APP_COMMIT APP_BUILD_TIME |
v9.9.9-test live abc1234 2026-09-14T00:00:00Z |
v9.9.9-test live abc1234 2026-09-14T00:00:00Z |
| 2 | api:args node require("/app/apps/api/dist/health/app-version").formatAppVersionLine() |
Tessera API v9.9.9-test (live) abc1234 |
Tessera API v9.9.9-test (live) abc1234 |
| 3 | web:args sh grep -rl v9.9.9-test /app/apps/web/.next/static | wc -l |
>= 1 | 1 |
| 4 | web:args node APP_VERSION |
v9.9.9-test |
v9.9.9-test |
| 5 | api:noargs node APP_VERSION APP_CHANNEL / web:noargs node APP_VERSION |
dev dev / dev |
dev dev / dev |
| 6 | web:noargs Bundle-Grep auf v9.9.9-test |
0 |
0 |
| 6b | api:noargs dist-Zeile (Zusatz) |
Tessera API dev (dev) |
Tessera API dev (dev) |
Aufgeraeumt: docker rmi der vier tessera-ku1-*-Etiketten und tessera-web-plancheck:baseline (alle „Untagged", docker images zeigt keine mehr).
Commit 9731501 — 5 Dateien (115+/13-).
Task 3 — Handbuch, ci-cd-setup, Push, CI-Beobachtung
Precondition: curl localhost:3002/api/v1/version -> {"version":"1.26.2"}; docker ps | grep -c ^gitea-runner$ -> 1.
Gates, gemessen:
| Pruefung | Erwartet | Gemessen |
|---|---|---|
grep -c "^## 9. Zwei Kanäle: Live und Beta" |
1 | 1 |
grep -c IMAGE_TAG anleitung-betrieb.md |
>= 6 | 11 |
grep -c "Keine Datenbankänderung als Hotfix" |
1 | 1 |
grep -c "Kein Registry-Push|build-deploy" ci-cd-setup.md |
0 | 0 |
grep -c publish-images.sh ci-cd-setup.md |
>= 2 | 4 |
grep -c '[äöüÄÖÜß]' ci-cd-setup.md |
0 | 0 |
git diff --stat 6c19451 -- . ':!.planning' |
GIT_EXIT=0, 20 files changed |
GIT_EXIT=0, 20 files changed, 851 insertions(+), 49 deletions(-) |
| Unangetastet-Stichprobe (prisma, biome.json, .env*, package.json, lockfile) | U_EXIT=0, U_EMPTY=0 |
U_EXIT=0, U_EMPTY=0 |
git status -sb nach Push |
kein [ahead |
## main...origin/main |
Commit ea6aa99 — 2 Dateien (265+/28-). git push -> 6c19451..ea6aa99 main -> main.
CI-Lauf nach dem Push
| Feld | Wert |
|---|---|
| Lauf-ID | 297 (event push, ref main, head_sha = ea6aa99…) |
| status / conclusion | completed / success |
| started_at / completed_at | 2026-09-14T15:44:23+02:00 / 2026-09-14T15:49:41+02:00 — 5 min 18 s (Planung: 4-6 min mit Build-Args) |
api:beta node APP_VERSION APP_CHANNEL APP_COMMIT APP_BUILD_TIME |
ea6aa99 beta ea6aa99 2026-09-14T13:46:12Z (git rev-parse --short ea6aa99 = ea6aa99) |
web:beta node APP_VERSION APP_CHANNEL |
ea6aa99 beta |
web:beta Bundle-Grep auf ea6aa99 |
1 (Bauzeit-Einbettung ueber den echten CI-Weg) |
docker image inspect Created web:beta / web:latest |
beide 2026-09-14T15:47:54.520283239+02:00 (ein Bau, zwei Etiketten, nach dem Push) |
docker image inspect Created api:beta / api:latest |
beide 2026-09-14T15:48:40.191081238+02:00 |
Registry (Gitea-API /packages/schalli?type=container) |
tessera-ctl/web beta 15:47:59, latest 15:48:00; tessera-ctl/api beta und latest 15:49:34 |
Kein Fehlversuch, ein einziger Push, ein einziger Lauf.
Abschluss-Verifikation (nach Task 3)
- API
Test Files 65 passed (65)/Tests 1060 passed (1060), Exit 0 - Web
Test Files 40 passed (40)/Tests 243 passed (243), Exit 0 tsc --noEmit:TSC_packages/shared=0,TSC_apps/api=0,TSC_apps/web=0git fetch -q && git status -sb | head -1->## main...origin/maingit ls-remote --heads origin live | wc -l-> 0,git tag | wc -l-> 0 (Zweigliveund Tagv1.0.0wie geplant NICHT angelegt)
git status --porcelain (vor dem Schreiben dieses SUMMARY): leer.
git log --oneline 1cd4212..HEAD:
ea6aa99 docs(quick-260914-ku1): Betriebshandbuch — Zwei Kanäle Live und Beta, Freigabe, Hotfix ohne Datenbankänderung, neuer Live-Server; ci-cd-setup auf gemessenen Stand
9731501 ci(quick-260914-ku1): zwei Kanaele — main -> beta+latest, Tag v* -> live+vX.Y.Z, Versionsstempel als Build-Args in beide Dockerfiles, IMAGE_TAG in docker-compose.prod.yml
cdb571c feat(quick-260914-ku1): Versionsstempel — GET /health/version aus APP_*, VersionResponse, app-version.ts und Abzeichen v<Version> · <Kanal> in der Seitenleiste
commits: 3 gemessen aus git rev-list --count 1cd4212..HEAD; actuals.tokens = 166183 Zeichen ueber die 20 geaenderten Dateien / 4 = 41545 (der reine Diff waere 49515 Zeichen = 12378).
Deviations from Plan
Auto-fixed Issues
None — plan executed exactly as written. Alle Zahlen des Plans (65/1060, 40/243, 20 Dateien, 13/5/2 Dateien je Commit, Grep-Werte) wurden exakt getroffen; keine Erwartung musste angepasst werden.
Prozess-Abweichung (dokumentiert, keine Code-Abweichung)
Commits auf main. Die Executor-Vorschrift verlangt eigentlich einen Nicht-Standard-Zweig. Dieses Projekt arbeitet per branching_strategy: none seit jeher direkt auf main, der Auftrag verlangt ausdruecklich git push auf main, und das Kanalmodell dieses Plans definiert main = Beta — ein Seitenzweig haette den Plan nicht erfuellen koennen (die Pipeline haette nichts gebaut). Kein git update-ref, kein Force-Push, kein Eingriff in .planning/config.json.
Nebenbefunde
apps/web/src/components/layout/sidebar-footer.tsxist seitba02b25(2026-06-26) toter Code: wird nirgends gerendert, einziger Treffer ausserhalb der Datei ist der Mock insidebar.test.tsx. Unangetastet gelassen (nicht in der Erlaubnisliste); Kandidat fuer einen Aufraeum-Quick-Task..env.prod.examplebewusst nicht angefasst (Regel: keine.env*-Dateien). Die ZeileIMAGE_TAGsteht nur im Handbuch (Kapitel 3 Tabelle, Kapitel 9 mit ausdruecklichem Hinweis, dass die Vorlage sie noch nicht enthaelt).- Der Web-Bau laeuft in der CI jetzt bei jedem Lauf durch die builder-Stufe (NEXT_PUBLIC_APP_COMMIT aendert sich je Commit) — gemessen 5 min 18 s statt unter 2 min zuvor; im ci-cd-setup vermerkt.
- Handbuch Kapitel 6/7 nennen weiterhin
/opt/tessera/docker-compose.ymlfuer die Volume-Reparatur; Kapitel 9 nennt die tatsaechlich benutzte Datei/opt/tessera/docker-compose.prod.yml(.envsetztCOMPOSE_FILE). Die aelteren Stellen wurden nicht umgeschrieben (nicht Teil des Plans).
Threat Flags
Keine neue Angriffsflaeche ausserhalb des Threat-Registers des Plans: GET /health/version war bereits @Public (T-KU1-03, akzeptiert und per Spec-Test 6 gepinnt); das Skript kennt kein Secret; das Push-Token wurde in Task 3 nur in einer Shell-Variablen benutzt (Ausgabe der Push-URL im Log mit *** maskiert).
Known Stubs
Keine. Alle neuen Werte sind an echte Quellen gebunden (Umgebung, /health/version); ohne Build-Args greifen die bewussten Vorgaben dev.
Was bewusst offen bleibt
- Zweig
liveund Tagv1.0.0sind NICHT angelegt — Rezept steht im Handbuch Kapitel 9 („Erstfreigabe v1.0.0"); erfolgt nach dem Fehler-melden-Knopf (eigener Quick-Task, derapps/web/src/lib/app-version.tsimportiert). - Server nicht angefasst: alpha (
/opt/tessera/.envunddocker-compose.prod.yml) und der neue Live-Server werden vom User eingerichtet, siehe „Handgriffe" unten. Bis dahin zieht alpha weiter:latest= dasselbe Beta-Abbild. .env.prod.exampleohneIMAGE_TAG-Zeile (Regel.env*); ein spaeterer Quick-Task darf sie ergaenzen.latestbleibt als Alias vonbeta, bis alpha aufIMAGE_TAG=betaumgestellt ist; danach kann das Etikett aus dem Skript entfallen.- Tag-Schutz
v*und Branch-Schutzlivein Gitea (T-KU1-04) — heute nicht noetig (nurschallihat Schreibrecht), im ci-cd-setup als Empfehlung fuer den Fall weiterer Konten. - Human-Check (end-of-phase, nicht blockierend): lokal
docker compose up -d --build web apiund unten in der Seitenleistedev · Entwicklungsehen, TooltipAPI dev (dev); eingeklappt verschwindet die Zeile.
Handgriffe fuer den User
Aus dem Handbuch Kapitel 9 („Die eine Zeile je Server" und „Den neuen Live-Server einrichten"):
Auf alpha (Beta), einmalig:
- In
/opt/tessera/.envdie ZeileIMAGE_TAG=betaeintragen. - Sicherung der Compose-Datei:
cd /opt/tessera cp docker-compose.prod.yml docker-compose.prod.yml.bak.$(date +%Y%m%d) - In
/opt/tessera/docker-compose.prod.ymldie zweiimage:-Zeilen aendern (nur das Ende der Zeile):image: git.vicolab.de/schalli/tessera-ctl/web:${IMAGE_TAG:-beta} image: git.vicolab.de/schalli/tessera-ctl/api:${IMAGE_TAG:-beta} - Dann wie in Kapitel 4:
docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml up -d --force-recreate api web - Kontrolle: unten in der Seitenleiste steht
<Kurzkennung> · Beta;curl -s http://localhost:3001/health/versionzeigt"channel":"beta".
Auf dem neuen Live-Server (tessera.ctl.de): Kapitel 2 des Handbuchs vollstaendig, mit diesen Abweichungen:
IMAGE_TAG=livein der.env(Pflicht — ohne die Zeile zieht der Server die Beta).- Eigene, neu erzeugte Geheimnisse (
JWT_SECRET,TESSERA_ENCRYPTION_KEY,DB_PASSWORD, Admin-Passwort); nichts von alpha uebernehmen. - Eigene, leere Datenbank; die alpha-Datenbank wird NICHT kopiert (falls doch gewuenscht: Kapitel 6 UND derselbe
TESSERA_ENCRYPTION_KEY). APP_URL=https://tessera.ctl.de.- Der erste
pullholt:live— dieses Etikett gibt es erst nach der Erstfreigabe v1.0.0. Also erst freigeben (Claude:git checkout -b live main, Tagv1.0.0, Push), dann installieren; oder fuer einen Probelauf voruebergehendIMAGE_TAG=beta, danach aufliveumstellen undpull+up -d --force-recreate api webwiederholen.
Bei jeder Freigabe danach (User sagt „Version X freigeben", Claude pusht Zweig und Tag), auf dem Live-Server:
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d --force-recreate api web
Self-Check: PASSED
- Dateien vorhanden: alle 7 neu erstellten Dateien (
app-version.tsapi/web, beide Specs/Tests, Badge + Test,publish-images.sh) — FOUND. - Commits vorhanden:
cdb571c,9731501,ea6aa99— FOUND (git log --oneline 1cd4212..HEAD), alle auforigin/main.