CluPilotCloud/docs/handoffs/2026-07-25-clupilot-state-h...

10 KiB

Handoff — CluPilot (Stand 2026-07-25)

Für: frische Claude-Code-Session Art: Zustands-/Fortsetzungs-Handoff Zuerst lesen: dieses Dokument komplett, dann git log --oneline -15.


0. TL;DR

CluPilot = Managed-Nextcloud-Cloud-Business (fertige, betreute Firmen-Cloud). Laravel-App auf einer lokalen Dev-VM. Gebaut & gepusht sind bereits: öffentliche Landingpage, helles Kundenportal (Login/2FA + Dashboard + 6 Reiter) und eine dunkle Admin-/Operator-Konsole (/admin, 6 Sektionen) — alles mit Chart.js, 48 Pest-Tests grün, jeder Commit Codex-(R15)-clean, überall 0 Konsolenfehler.

Nächster großer Schritt: die Provisionierungs-Engine v1.0 (aus Bestellung → laufende Nextcloud-Instanz). Design-Spec liegt beim Nutzer (nicht im Repo). Superpowers-Plugin (TDD) ist installiert.


1. Umgebung / Fakten

  • VM: Debian, User nexxo (uid/gid 1000), IP 10.10.90.185, nur Docker (kein PHP/Composer/Node auf dem Host → alles im Container, R8).
  • Projekt: /home/nexxo/clupilot (eigenes Git-Repo).
  • Remote: https://git.bave.dev/boban/CluPilotCloud.git. Token liegt in .env (GIT_ACCESS_TOKEN, gitignored). Push: Token nur zur Push-Zeit inline injizieren, danach git config branch.<b>.remote origin und .git/config auf Token prüfen. Nie .env/Token committen.
  • Aktueller Branch: feat/portal-design (alles hier, gepusht). main existiert lokal ohne Commits — Merge/PR steht aus.
  • Docker sudo-frei (nexxo in docker-Gruppe). sudo bräuchte Passwort → vermeiden.

2. Stack

Laravel 13.8, Livewire 3 (klassenbasiert, kein Volt — R2), Tailwind v3 (bewusst von v4 zurückgebaut, Nutzer-Entscheid), Vite, Laravel Fortify (Login + TOTP-2FA), Laravel Reverb, MariaDB 11.4, Redis, Chart.js 4, phpseclib, wire-elements/modal, Pest 4 (Tests). Fonts: IBM Plex self-hosted via @fontsource (R14, kein CDN).

3. Docker / Befehle (alles im Container, R8)

Helper clupilot (global via ~/.local/bin, + cp-* Aliases):

clupilot up | down | restart [svc] | rebuild | ps | logs [svc] | health
clupilot artisan … | composer … | npm … | tinker | migrate | shell

Roh, falls nötig (User 1000, HOME für Composer-/npm-Cache):

docker compose run --rm --no-deps -u 1000:1000 -e HOME=/tmp app <cmd>
docker compose exec -T -u 1000:1000 -e HOME=/tmp app php artisan …

Services: app (php-fpm+nginx+vite via supervisor — EIN Container), reverb, queue, scheduler, mariadb, redis. App auf :80, Vite HMR :5173 (VITE_HMR_HOST=10.10.90.185).

4. Was gebaut ist

Öffentlich

  • /Landingpage (resources/views/landing.blade.php) — Marketing, self-contained CSS/JS in @verbatim, System-Fonts, absichtlich außerhalb des App-Token-Systems. Login-Link → route('login').
  • /legal/{impressum,datenschutz,agb,status}Platzhalter (legal.blade.php). ⚠️ Echter Rechtstext fehlt (Nutzer muss liefern — gesetzl. Pflicht vor Launch).

Kundenportal (hell)layouts/portal.blade.php (Auth) + layouts/portal-app.blade.php (App-Shell)

  • Auth: /login, /two-factor-challenge (Fortify, views=false, Seiten als full-page Livewire — R1/R2).
  • /dashboard + Reiter /cloud /users /backups /invoices /support — je full-page Livewire (app/Livewire/*), Chart.js, Tabellen, lokalisiert DE/EN.

Admin-Konsole (dunkel)layouts/admin.blade.php (class="theme-admin"), gate is_admin

  • /admin + /admin/{customers,instances,hosts,provisioning,revenue} (app/Livewire/Admin/*). Middleware ['auth','admin'] (EnsureAdmin).

Design-System (Kern!)

  • Tokens als CSS-Variablen: resources/css/portal-tokens.css (hell) + resources/css/admin-tokens.css (.theme-admin überschreibt alle Tokens dunkel). Tailwind mappt Tokens → Utilities (tailwind.config.js). Nur Token-Utilities in Markup (R3), keine Hex.
  • Komponenten: resources/views/components/ui/ — button, input, checkbox, alert, card, badge, stat-tile, otp-input, progress-stepper, nav-item, icon (Lucide, keine Emoji im App-UI, R9), chart (Alpine-Island).
  • Chart-Island: PHP-Config-Array, Farben als "token:accent"/"token:accent/0.12" (in resources/js/app.js zur Laufzeit aus CSS-Vars aufgelöst, liest vom Element → theme-aware).

5. Verifikations-Workflow (jede Änderung)

  1. Tests: docker compose run --rm --no-deps -u 1000:1000 -e HOME=/tmp app ./vendor/bin/pest (aktuell 48 grün).
  2. R12 Browser (0 Konsolenfehler): Prod-Assets bauen → npm run build, supervisorctl stop vite + rm -f public/hot, dann Puppeteer:
    docker run --rm --network host -v /pfad/probe.js:/home/pptruser/probe.js \
      -w /home/pptruser ghcr.io/puppeteer/puppeteer:latest node probe.js
    
    Probe: page.on('console'/'pageerror'/'requestfailed'), HTTP 200 + 0 Fehler pro Seite; Login-Flow via #email/#password + submit. Danach docker compose restart app (Vite/public/hot zurück). (Probe-Skripte liegen NICHT im Repo — bei Bedarf neu schreiben.)
  3. R15 Codex-Review (Pflicht vor „fertig"):
    export CLAUDE_PLUGIN_ROOT=/home/nexxo/.claude/remote/plugins/e3a3a3c04ad124ae
    node "$CLAUDE_PLUGIN_ROOT/scripts/codex-companion.mjs" review "--scope working-tree --background"
    
    Ergebnis pollen: neuestes /tmp/codex-companion/*/jobs/review-*.log (awk '/Final output/{f=1}f'). /codex:review ist disable-model-invocation → Skript direkt aufrufen. Working-tree-Scope (nicht branch/base). Loop bis „no actionable regressions". Findings waren durchweg echt (a11y, i18n, Portabilität, Security) — ernst nehmen.

Dev-Logins (beide PW password): admin@clupilot.local (Admin, sieht /admin+/dashboard), kunde@clupilot.local (Kunde, /admin=403). Nach DB-Reset: clupilot artisan db:seed.

6. Gotchas (teuer gelernt — beachten!)

  • @js() wird NICHT kompiliert in @click/Attributen von Blade-Komponenten (<x-ui.button>) → landet literal, bricht Alpine. Lösung: @js() in x-data eines plain Elements, Buttons referenzieren die Variable.
  • Test-Isolation: env_file: .env in docker-compose wurde entfernt — es injizierte Dev-DB-Config als echte Env-Vars und überschrieb phpunit-force → Tests liefen gegen die Dev-MariaDB und wischten sie. Laravel liest .env von der Platte; nur Vite-Vars sind explizit gesetzt. Tests nutzen jetzt sqlite :memory: (phpunit.xml mit force="true").
  • CSRF in Tests: $this->post() sendet kein Token → 419. In Auth-Tests Session-Token setzen: withSession(['_token'=>'t'])->post(..., ['_token'=>'t', ...]).
  • Großes rohes HTML/CSS/JS in Blade → @verbatim … @endverbatim (sonst zerlegt Blade @keyframes/${}/{{ }}).
  • AA-Kontrast: #f97316 (accent) fällt für weißen/kleinen Text durch AA → Buttons/Links nutzen accent-active/accent-text (dunkleres Orange hell / helleres Orange dunkel). --accent nur Deko/Rahmen/Tints.
  • Locale: Fixture-Daten immer locale-aware (Carbon::isoFormat, Number::format/currency), sonst mischt EN deutsche Formate — Codex flaggt das zuverlässig.
  • Vite reload: docker compose restart liest env_file/Config nicht neu → docker compose up -d app (recreate) wenn Env/Compose geändert.

7. Offene Punkte

Prio Punkt
Hoch Echter Rechtstext (Impressum/Datenschutz/AGB) — Nutzer liefert; ich erfinde keine Rechtsangaben
Mittel Merge feat/portal-designmain (Gitea-PR)
Mittel Provisionierungs-Engine v1.0 (s. u.)
Niedrig Multi-Domain-Split www. / app. / admin.clupilot.com (Nutzer legt Domains an) — aktuell pfad-basiert
Niedrig v1.1-Auth: Passwort-Reset / „Passwort vergessen" (in Fortify bewusst deaktiviert)
Niedrig Breiteres Komponenten-Kit (Modal-Nutzung, Tabs, Toast-Komponente, Skeleton, Tooltip …) falls gebraucht

8. Nächster großer Block: Provisionierungs-Engine v1.0

Ziel: aus bezahlter Bestellung vollautomatisch eine laufende, gesicherte, überwachte Nextcloud-Instanz. Modell = DB-State-Machine + Tick-Orchestrator (kein Monolith-Job).

  • Design-Spec liegt beim Nutzer (war als „Engine-v1.0-Handoff" vorhanden, ist NICHT im Repo). Vor Baubeginn anfordern.
  • TDD (Superpowers test-driven-development ist ab dieser Session verfügbar). ProxmoxClient in Step-Tests mocken.
  • Grob: Migrations/Models (customers, orders, hosts, instances, provisioning_runs, provisioning_step_events, run_resources, …) → Orchestrator-Kern (Step-Interface, StepResult advance/retry/fail, AdvanceRunJob, Per-Run-Lock, Timeout, Tick im scheduler) → ProxmoxClient (NEU, REST/UPID-Polling) → 15 idempotente Kunden-Pipeline-Steps → Stripe-Webhook (Idempotency-Key) → Livewire-Live-Fortschritt via Reverb.
  • Nicht anfassen/vermischen: Alt-Domäne existiert hier nicht (frischer Build) — Engine ist additiv. Panel-Reverb/Queue/Scheduler sind da und wiederverwendbar.
  • Die Admin-/admin/provisioning-Seite ist aktuell Fixture-View → später an echte provisioning_runs/provisioning_step_events binden (live via Reverb).

9. Regeln (Kurzfassung — verbindlich)

R1/R2 full-page klassenbasierte Livewire als Routen (kein Volt, kein Page-Controller) · R3 nur @theme/Token-Utilities, kein Hex · R4 keine Inline-Styles außer Progress-width · R5 destruktiv → wire-elements/modal · R6 Ordner-Map · R7 responsive 375/768/1280, Touch ≥44px · R8 alles im Container · R9 UI-Copy DE, keine Emoji (Status via Farbe/Dots/Pills) · R10 Design-System wiederverwenden · R11 URLs per UUID (nicht Integer-PK) · R12 Browser-verifiziert HTTP 200 + 0 Konsolenfehler · R13 Routen-Pfade/Namen englisch · R14 Fonts self-hosted · R15 Codex-Review clean vor „fertig" · R16 alles lokalisiert DE+EN (identische Keys) · R17 Blade-@php-Block-Regeln · R18 Icon-Größe und Zeilenumbruch (siehe CLAUDE.md).

Volltext rules.md/CLAUDE.md hat der Nutzer (nicht im Repo). Bei Konflikt: STOP & fragen.

10. Session-Memory

Persistente Notizen unter ~/.claude/projects/-home-nexxo/memory/ (MEMORY.md + clupilot-project-state, clupilot-portal-progress, clupilot-r15-codex-review) — beim Start werden die relevanten automatisch eingespielt.