chore(app): TS app skeleton, docker compose and verify commands - #21
Conversation
The impact manifest lives in the PR description (plan before code, SYSTEM.md §4). Refs #3 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… leaks /api/health will be reachable without login, so a failed check must be named but never described (no hosts, users or driver messages). A check that hangs counts as failed after its time limit instead of blocking the endpoint. Toolchain pinned: TypeScript 6.0.3 because typescript-eslint does not accept TS 7 yet; ESLint 9 because eslint-plugin-react and jsx-a11y (via eslint-config-next) do not accept ESLint 10 yet. Refs #3 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Environment variables are an outside boundary: missing or malformed values must stop the start with a clear message. The message names the variables but never their values, because values are secrets and error messages end up in logs. New module `config` added to the map first. Refs #3 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Two roles per ADR-0001 D7: app_owner runs migrations, app_rw is the runtime role without superuser or BYPASSRLS, so RLS always applies. Passwords come from the environment (local-only defaults in compose), never from a migration. The S3 identity is rendered from env at start. Verified locally 2026-09-22 (Docker 28.4): roles exist with rolsuper=false, rolbypassrls=false; SeaweedFS 4.47 accepts the configured credentials and answers 403 to a wrong secret. Refs #3 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ÜbergabeStatus: WIP – übergeben an die Cloud-Implementierungs-Session (Nachtlauf 2026-09-22/23) |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
…ry rules Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
…y:changed, docs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
…ose logs on failure Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
… shim Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
- dependency-cruiser: features may not open DB connections; imports must resolve; alias fixture - health route logs config errors (variable names only) - setup retries only transient errors; migration guard also checks inherited roles, with a test - docs: api.md (health contract), roles outside Docker, verify:changed wording Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
Frischer Review (review-pr, unabhängiger Subagent) – Befunde und AuflösungReview lief read-only gegen den Branch-Stand; zwei Durchläufe (der erste fand einen Blocker im Compose-Pfad, der zweite prüfte den Fix). Auflösung in Commit
Offen für den Menschen: Rollen entstehen im Init-Skript statt in der Migration (Abweichung vom Akzeptanzkriterium, begründet) – Generated by Claude Code |
Warum
Fixes #3 · Teil von Epic #2. Basis ist
main(Fundament #20 ist gemergt). Alle folgenden Issue-PRs des Nachtlaufs stapeln auf diesem Branch.Arbeitsstand
docker compose upstartet Postgres, SeaweedFS,setup,webund einen No-op-worker;verify:changed/verify/verify:fullexistieren und laufen in der CI.pnpm verifylokal grün; CIcheck+compose-smokegrün aufdba107f.main.Was ist passiert (Klartext)
Das Projekt hat jetzt ein lauffähiges technisches Gerüst. Ein Befehl (
docker compose up) startet die Datenbank, den Dokumentenspeicher, einen einmaligen Einrichtungsschritt (Datenbankstruktur anlegen, privaten Speicherbereich anlegen), die Webanwendung und den Hintergrund-Worker. Unter/api/healthsieht man, ob Datenbank und Speicher erreichbar sind – ohne dabei Adressen oder Benutzernamen zu verraten.Die Datenbank kennt zwei Rollen: eine, die die Struktur ändern darf (nur beim Einrichten), und eine für den laufenden Betrieb, die die spätere Mandantentrennung (Row-Level Security) nie umgehen kann. Die Einrichtung bricht ab, wenn diese Rollen falsch konfiguriert sind.
Außerdem gibt es jetzt die Prüfbefehle, die jeden weiteren PR absichern, und eine automatische Architekturprüfung: Module dürfen nur über ihre öffentliche Eingangsdatei miteinander sprechen, und nur die Datenbankschicht darf die rohe Datenbankverbindung benutzen.
Infrastruktur-/CI-Änderung (braucht menschliche Freigabe): Die CI startet jetzt PostgreSQL und SeaweedFS über
compose.yaml, damit die Integrationstests gegen echte Dienste laufen, und ein neuer Jobcompose-smokebaut das Image und prüftdocker compose up→/api/health, aber nur wenn sich Dockerfile oder Compose-Datei ändern. Bestehende Prüfschritte wurden nicht verändert.Plan-Pflicht (SYSTEM.md §4)
Impact Manifest
src/app(Layout, Platzhalterseite,api/health, Composition Root_server),src/db,src/config,src/features/*/index.ts,storage,observability,jobs(No-op-Worker),compose.yaml,Dockerfile,docker/postgres/init/,.env.example, CI.GET /api/health; zwei DB-Rollen (app_owner,app_rw); erste Migration legt Schemaapp+ Rechte an.GET /api/health→200 {"status":"ok","checks":{"database":"ok","storage":"ok"}}; bei Ausfall503mit Namen der Prüfung (database,storage,config), ohne Fehlerdetails. Vertrag:docs/technical/api.md.0000_app_schema: prüft Rollen (kein Superuser, kein BYPASSRLS, keine geerbten Owner-/Bypass-Rollen, läuft alsapp_owner),CREATE SCHEMA app AUTHORIZATION app_owner,USAGEfürapp_rwohneCREATE, Default-Privileges (DML auf Tabellen, Sequenzen) fürapp_rw.@/, roher DB-Client, eigene DB-Verbindung im Feature werden abgelehnt); Compose-Smoke in CI.next16.3.6,typescript6.0.3 (TS 7 wegen typescript-eslint-Peer ausgeschlossen),eslint9.39.5,vitest5.0.1,drizzle-orm0.45.3,drizzle-kit0.31.11,pg8.23.0,dependency-cruiser18.4.0,@aws-sdk/client-s33.1138.0,zod4.6.5,tsx4.23.15 (MIT, Node ≥ 18); SeaweedFS 4.47 lehnt falsche S3-Secrets mit 403 ab (Integrationstest);eslint-config-next16 exportiert Flat-Config-Arrays; pnpm legt.bin/tsxals Shell-Shim an → im Imagenode …/tsx/dist/cli.mjs.compose-smokebewiesen.pnpm audit --audit-level highinverify+ Lockfile; Rollback per Revert-PR, lokaldocker compose down -v(docs/technical/operations.md).Abweichungen von den Akzeptanzkriterien (offengelegt)
decision-neededfür den Orchestrator (Morgenbericht).Neue Dependencies (Begründung)
@/nicht auf)Geändert
package.json,pnpm-lock.yaml,tsconfig.json,eslint.config.mjs,next.config.ts,vitest.config.ts,.dependency-cruiser.cjs,drizzle.config.ts: Toolchain und Prüfbefehle.src/app/: Layout, Platzhalterseite,api/health/route.ts,_server/runtime.ts(Pool + Storage-Client, lazy).src/db/: Client, Migrator, Schema-Stub, Migration0000_app_schema.sql.src/features/*/index.ts: Modul-Skelette laut Architekturkarte;storage(S3-Adapter: ping, ensureBucket),observability(Health).src/setup.ts(Deploy-Schritt: Migrationen + Bucket, Retry nur bei transienten Fehlern),src/worker.ts(No-op).compose.yaml(+setup,web,worker),Dockerfile,.dockerignore,.env.example,scripts/verify-changed.sh..github/workflows/ci.yml: Postgres + SeaweedFS vorpnpm verify; Jobcompose-smoke.tests/integration/*(Health, Migration, Rollen-Guard),tests/architecture/dependency-rules.test.ts+ Fixtures.docs/technical/operations.md,docs/technical/api.md, CHANGELOG.Nachweis (SYSTEM.md §11)
verify:changed: grünverify: grün – lokal (Node 24.21, PostgreSQL 17 + SeaweedFS 4.47): lint, typecheck, 14 Unit + 8 Integrationstests, depcruise 0 Verstöße,next build,pnpm audit --audit-level high(0 high); CIcheckgrün: https://github.com/Fluory/RequestFlow/actions/runs/35824279604verify:full/ E2E-Spec: nicht betroffen (entsprichtverify, bis feat(review): review fields beside their source, correct, approve or reject #8 den Playwright-Smoke bringt)docker compose up --build→curl /api/health→ 200 im CI-Jobcompose-smoke(grün, gleicher Run); lokal zusätzlich Standalone-Server →/api/health200,pnpm setup:deploy,pnpm workerstartet/stoppt sauber.Doku-Entscheidung (genau eine)
README.md(Getting started)docs/technical/operations.md(Start/Stopp/Rollback, Rollen außerhalb Docker),docs/technical/api.md(Health-Vertrag),AGENTS.md(Befehle)docs/technical/architecture.md(Modulstatus,app-Zeile, DB-Rollen)[Unreleased](sichtbares Feature oder Verhalten – im selben PR, nie „später")Entferntes oder Umbenanntes:
docs/+ README gegrept, Treffer bereinigt: Fundament-Hinweis in AGENTS.md und README-Status ersetztDateigrößen und neue Bausteine (SYSTEM.md §7)
Dateien über 500 Zeilen im Diff (Ausnahmen: generierter Code, Lockfiles, Fixtures, Migrationen, Schemas, Ressourcen, Doku, Konfiguration):
Über 800 Zeilen mit neuer Fachlogik oder über 1000 Zeilen (P1/P2): nicht betroffen
Neue Shared-Komponente, Utility-Datei, Adapter oder fachlicher Service:
storage(S3-Adapter) undobservability(Health) sind in der Karte vorgesehen und entstehen hier erstmalsSubagent-Einsätze
.claude/skills/review-pr/SKILL.md, read-only (2 Läufe).Risiken / offene Punkte
docker compose uplokal nicht ausführbar (Docker Hub 429) – Beweis über CI-Jobcompose-smoke(grün).🤖 Generated with Claude Code
https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1