# Clusev — STRICT RULES > **Non-negotiable.** Generated from `handoff.md` §4. These are the user's explicit, > mandatory rules. If any instruction (mine, yours, or a future agent's) conflicts with a > rule here, **STOP and ask** — do not "work around" it. Keep this file open while coding. > > *Stack note (2026-06-11): built on **Laravel 13** (handoff said 12; updated by user decision). The rules below are version-agnostic.* --- ## Quick gate — run through this before every commit - [ ] **R1/R2** — Page is a **full-page, class-based Livewire component** mapped directly in `routes/web.php`. - [ ] **R2** — Livewire **class** in `app/Livewire/…` **and** a matching **separate Blade view** in `resources/views/livewire/…`. **No Volt.** - [ ] **R3** — No hard-coded hex/rgb in markup — only `@theme` token utilities (`bg-surface`, `text-accent`, …). - [ ] **R4** — No inline `style="…"` **except** a progress bar's `width`. - [ ] **R5** — Destructive/confirm action runs through a **wire-elements/modal**, never Alpine `confirm()`. - [ ] **R6** — Files placed exactly per the §5 folder map. - [ ] **R7** — Verified at **375 / 768 / 1280 px**; touch targets ≥ 44px. - [ ] **R8** — Every PHP/Composer/Node/artisan command ran **inside the container**. - [ ] **R9** — UI copy is **German**, **no emoji**; status via color/dots/pills. - [ ] **R10** — Reused existing `@theme` tokens + Blade components (no ad-hoc colors/widgets). - [ ] **R11** — Records exposed in URLs are addressed by **UUID**, never the integer PK. - [ ] **R12** — every touched page loads in a **browser** at **HTTP 200** with **zero console errors** (the **loaded** state of `wire:init` pages, not just the skeleton). - [ ] **Secrets** — `git status` shows **no** `.env*`, token, key, or home-dir dotfile staged. --- ## R1 — Pages are full-page Livewire components, mapped directly as routes **Why.** One page = one component = one route. No controller indirection, no orphan Blade views. Keeps routing, state, and view co-located and predictable. ✅ **Correct** ```php // routes/web.php use App\Livewire\Dashboard; use App\Livewire\Servers\Show; Route::get('/', Dashboard::class)->name('dashboard'); Route::get('/servers/{server}', Show::class)->name('servers.show'); ``` ❌ **Forbidden** ```php // Controller returning a Blade view for a PAGE Route::get('/', [DashboardController::class, 'index']); class DashboardController { public function index() { return view('dashboard'); // ❌ pages do not belong to controllers } } ``` --- ## R2 — Class-based Livewire only. Volt is FORBIDDEN. **Why.** Class + separate view is explicit, testable, and conventional. Volt single-file components hide logic in the view and are banned for this project. ✅ **Correct** — `php artisan make:livewire Servers/Show` (creates class **and** view) ```php // app/Livewire/Servers/Show.php namespace App\Livewire\Servers; use App\Models\Server; use Livewire\Component; class Show extends Component { public Server $server; public function render() { return view('livewire.servers.show'); // ← separate Blade file } } ``` ```blade {{-- resources/views/livewire/servers/show.blade.php --}}
{{ $server->hostname }}
``` ❌ **Forbidden** ```blade {{-- Volt single-file component — NEVER. Do not run `php artisan make:volt`. --}} 0]); ?>
{{ $count }}
``` --- ## R3 — All colors / design tokens live in `resources/css/app.css` under `@theme` **Why.** Single source of truth for the visual system (Tailwind v4 CSS-first, no `tailwind.config.js`). Markup references **tokens**, never raw colors — so a token change restyles the whole app. ✅ **Correct** ```css /* resources/css/app.css */ @import "tailwindcss"; @theme { --color-surface: #0F1318; --color-accent: #FF6B2C; --color-online: #35D07F; --color-line: rgba(255,255,255,.10); } ``` ```blade
CPU online
soft accent tint
{{-- opacity modifier, not a separate token --}}
``` ❌ **Forbidden** ```blade
{{-- ❌ raw hex in markup --}} ``` --- ## R4 — Inline styles are FORBIDDEN (one exception) **Why.** Inline styles bypass the token system and Tailwind. The **only** legitimate dynamic value that can't be a utility class is a progress bar's percentage width. ✅ **Correct** — the ONE allowed inline style ```blade
``` ❌ **Forbidden** ```blade
{{-- ❌ use utilities: mt-3 flex text-accent --}} ``` --- ## R5 — Destructive / confirm actions use a Modal, never an Alpine dialog **Why.** Destructive actions (delete server, remove SSH key, stop a service) must be deliberate, styled, and auditable. Use **wire-elements/modal** components — never native `confirm()` or an `x-data` popup. ✅ **Correct** ```blade {{-- trigger: open the modal component (exact payload per installed wire-elements/modal version) --}} ``` ```php // app/Livewire/Modals/ConfirmDelete.php (a real Livewire modal class) namespace App\Livewire\Modals; use App\Models\Server; use LivewireUI\Modal\ModalComponent; // wire-elements/modal base class class ConfirmDelete extends ModalComponent { public Server $server; public function delete(): void { $this->server->delete(); // audit-logged in the domain layer $this->closeModalWithEvents([/* refresh listeners */]); } public function render() { return view('livewire.modals.confirm-delete'); } } ``` ❌ **Forbidden** ```blade {{-- ❌ no native confirm(), no Alpine confirm popups for destructive actions --}} ``` --- ## R6 — Clean, conventional folder structure **Why.** Predictable structure = fast ramp-up, no duplication, no "where does this go?". Follow the §5 map in `CLAUDE.md` exactly. ✅ **Correct** — component, view, model, SSH wrapper each in their canonical place ``` app/Livewire/Servers/Show.php resources/views/livewire/servers/show.blade.php app/Models/Server.php app/Support/Ssh/SshClient.php ``` ❌ **Forbidden** ``` app/Http/Controllers/ServerController.php {{-- ❌ pages aren't controllers (R1) --}} app/Livewire/show.blade.php {{-- ❌ view in the class namespace --}} app/ServerSshHelperStuff.php {{-- ❌ unstructured dumping ground --}} ``` --- ## R7 — Fully responsive: mobile + tablet + desktop **Why.** Operators use phones and tablets in the field. Every screen must adapt — not just shrink. ✅ **Correct** ```blade {{-- KPI grid reflows 1 → 2 → 4; sidebar is drawer on small, fixed on large --}}
{{-- desktop sidebar --}} {{-- ≥44px touch --}} ``` ❌ **Forbidden** ```blade
{{-- ❌ desktop-only; breaks on mobile/tablet --}}