Files
SealShare/CLAUDE.md
T
Andreas Reinhold / reiniandClaude Opus 5.5 63bae2d1a6 Update dependencies
Composer: Laravel 13.35, Livewire 4.4.7, Octane 2.21, Fortify 1.41,
Boost 2.10.3 and Pest 5.3.1, with the packages they bring (51 in all,
brick/math to 1.0). league/commonmark 2.10.3 closes a high and a medium
advisory. npm: Vite 8.3.4 and Playwright 1.64; npm audit fix updates
source-map-js past a high advisory. Both audits are now clean.

Boost's update rewrote its guidelines in CLAUDE.md and four skills.

The Gitea workflows move to the current majors of checkout, setup-node
and the Docker actions, which run on Node 24 instead of the end-of-life
Node 20. QEMU stays pinned to 9.2.2: 10.2 still segfaults on the arm64
runner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:28:45 +02:00

21 KiB
Raw Permalink Blame History

=== foundation rules ===

Laravel Boost Guidelines

Foundational Context

This application is a Laravel application running on PHP 8.5. Always use the APIs that match the installed major version of each package — do not assume a version.

Before relying on a package's API, confirm its installed version:

  • PHP packages: run composer show --direct to list direct dependencies with versions, or composer show <vendor/package> for a single package.
  • JS packages: check package.json for the installed versions.

Skills Activation

This project has domain-specific skills available in **/skills/**. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.

Conventions

  • You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
  • Use descriptive names for variables and methods. For example, isRegisteredForDiscounts, not discount().
  • Check for existing components to reuse before writing a new one.

Verification Scripts

  • Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important.

Application Structure & Architecture

  • Stick to existing directory structure; don't create new base folders without approval.
  • Do not change the application's dependencies without approval.

Frontend Bundling

  • If a frontend change doesn't show in the UI or you get a "Unable to locate file in Vite manifest" error, run npm run build or ask the user to run npm run dev or composer run dev.

Documentation Files

  • You must only create documentation files if explicitly requested by the user.

=== boost rules ===

Laravel Boost

Tools

  • Laravel Boost is an MCP server with tools designed specifically for this application. Prefer Boost tools over manual alternatives like shell commands or file reads.
  • Use database-query to run read-only queries against the database instead of writing raw SQL in tinker.
  • Use database-schema to inspect table structure before writing migrations or models.
  • Use get-absolute-url to resolve the correct scheme, domain, and port for project URLs. Always use this before sharing a URL with the user.
  • Use browser-logs to read browser logs, errors, and exceptions. Only recent logs are useful, ignore old entries.

Searching Documentation (IMPORTANT)

  • Use search-docs before changes that depend on Laravel ecosystem APIs, behavior, configuration, or version-specific syntax. Skip it for copy-only edits and other changes where package documentation is irrelevant. Reuse sufficient results already in context instead of searching again.
  • Pass a packages array to scope results when you know which packages are relevant.
  • Use multiple broad, topic-based queries: ['rate limiting', 'routing rate limiting', 'routing']. Expect the most relevant results first.
  • Do not add package names to queries because package info is already shared. Use test resource table, not filament 4 test resource table.

Search Syntax

  1. Use words for auto-stemmed AND logic: rate limit matches both "rate" AND "limit".
  2. Use "quoted phrases" for exact position matching: "infinite scroll" requires adjacent words in order.
  3. Combine words and phrases for mixed queries: middleware "rate limit".
  4. Use multiple queries for OR logic: queries=["authentication", "middleware"].

Project Rules

  • This project contains committed, area-grouped rules in .ai/rules when that directory exists, including path-scoped framework guidelines under .ai/rules/boost. Before you enter plan mode or create/edit any file, you MUST first: open @.ai/rules/index.md (it maps file globs to rule files), read every rule file whose globs cover the path(s) in scope, and run grep -rin 'keyword' .ai/rules to catch what a path match alone misses. Do not write code until you have read and are following every matching rule. If .ai/rules does not exist, continue without it.

Artisan

  • Run Artisan commands directly via the command line (e.g., php artisan route:list). Use php artisan list to discover available commands and php artisan [command] --help to check parameters.
  • Inspect routes with php artisan route:list. Filter with: --method=GET, --name=users, --path=api, --except-vendor, --only-vendor.
  • Read configuration values using dot notation: php artisan config:show app.name, php artisan config:show database.default. Or read config files directly from the config/ directory.

Tinker

  • Execute PHP in app context for debugging and testing code. Do not create models without user approval, prefer tests with factories instead. Prefer existing Artisan commands over custom tinker code.
  • Always use single quotes to prevent shell expansion: php artisan tinker --execute 'Your::code();'
    • Double quotes for PHP strings inside: php artisan tinker --execute 'User::where("active", true)->count();'

=== php rules ===

PHP

  • Always use curly braces for control structures, even for single-line bodies.
  • Use PHP 8 constructor property promotion: public function __construct(public GitHub $github) { }. Do not leave empty zero-parameter __construct() methods unless the constructor is private.
  • Use explicit return type declarations and type hints for all method parameters: function isAccessible(User $user, ?string $path = null): bool
  • Use TitleCase for Enum keys: FavoritePerson, BestLake, Monthly.
  • Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic.
  • Use array shape type definitions in PHPDoc blocks.

=== deployments rules ===

Deployment

  • Laravel can be deployed using Laravel Cloud, which is the fastest way to deploy and scale production Laravel applications.

=== tests rules ===

Test Enforcement

  • Add or update tests for behavior and logic changes when a test provides meaningful regression coverage.
  • Pure copy, styling, and layout-only changes do not require new or updated tests.
  • When test coverage applies, run the affected tests and ensure they pass.
  • Test the changed behavior and its important failure modes, but do not add tests beyond them.
  • Read the testing-best-practices skill before writing tests.

=== laravel/core rules ===

Do Things the Laravel Way

  • Use php artisan make: commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using php artisan list and check their parameters with php artisan [command] --help.
  • If you're creating a generic PHP class, use php artisan make:class.
  • Pass --no-interaction to all Artisan commands to ensure they work without user input. You should also pass the correct --options to ensure correct behavior.

Model Creation

  • When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using php artisan make:model --help to check the available options.

APIs & Eloquent Resources

  • For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.

URL Generation

  • When generating links to other pages, prefer named routes and the route() function.

Testing

  • When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
  • Faker: Use methods such as $this->faker->word() or fake()->randomDigit(). Follow existing conventions whether to use $this->faker or fake().
  • When creating tests, make use of php artisan make:test [options] {name} to create a feature test, and pass --unit to create a unit test. Most tests should be feature tests.

=== laravel-octane/core rules ===

Laravel Octane

This application uses Laravel Octane, a long-running PHP server. The application bootstraps once and handles many requests within the same process.

  • Never store request-specific state in singletons or static properties, because it can leak across requests.
  • Use config('octane.server') to detect the active driver (swoole, roadrunner, or frankenphp).
  • Prefer scoped bindings ($this->app->scoped()) over singletons for per-request services.

When working on Octane-specific features (concurrency, shared tables, memory, driver configuration, testing), invoke octane-development for detailed rules.

=== livewire/core rules ===

Livewire

  • Livewire allows you to build dynamic, reactive interfaces in PHP without writing JavaScript.
  • You can use Alpine.js for client-side interactions instead of JavaScript frameworks.
  • Keep state server-side so the UI reflects it. Validate and authorize in actions as you would in HTTP requests.

=== pint/core rules ===

Laravel Pint Code Formatter

  • If you have modified any PHP files, you must run vendor/bin/pint --dirty --format agent before finalizing changes to ensure your code matches the project's expected style.
  • Do not run vendor/bin/pint --test --format agent, simply run vendor/bin/pint --format agent to fix any formatting issues.

=== pest/core rules ===

Pest

  • This project uses Pest. Create tests with php artisan make:test --pest {name}.
  • Do not include the test suite directory in {name}. Use SomeFeatureTest, not Feature/SomeFeatureTest.
  • Read the testing-best-practices skill for guidance on coverage, naming, structure, dependency isolation, and review.
  • Do not delete tests or test files without approval. They are part of the application.

Running Tests

  • Run the narrowest set of tests that covers the change. Pass a file path or --filter=testName to php artisan test --compact.
  • Rerun a test after each change to it.
  • Run vendor/bin/pest to call the test runner directly. It accepts the same file path and --filter=testName arguments.
  • After the feature tests pass, ask the user to run the complete suite with php artisan test --compact.

=== nonameweb/livewire-material/core rules ===

Livewire Material

This application uses nonameweb/livewire-material: Material 3 Expressive components for Laravel and Livewire, in plain CSS. No utility classes — Tailwind, daisyUI or any other — belong here: a class your CSS does not declare does nothing, and DesignGuard fails it.

  • Components are anonymous Blade components, unprefixed unless config/livewire-material.php sets a prefix. Before writing or changing a view that uses them, activate the livewire-material-development skill for the props, slots and traps of each component.
  • The CSS entry imports foundation.css first, then the stylesheet of each component the views render (or all.css for all of them). A component whose stylesheet is not imported renders unstyled; DesignGuard::missingStylesheets() names each missing @import.
  • Every layout includes <x-theme-script /> in <head> before @vite. The colour scheme is generated with php artisan material:scheme — never edit resources/css/material-scheme.css by hand. With colour profiles (livewire-material.profiles), run it without a seed after changing them; the active profile comes from Scheme::resolveProfileUsing().
  • While the application runs locally, every token and component renders in the application's own scheme at /material (the showcase).
  • HTTP error pages and the Markdown mail theme come from the package. Change error wording by publishing --tag=livewire-material-errors; select the mail theme with MAIL_MARKDOWN_THEME=livewire-material::mail.theme.

=== nonameweb/livewire-material/material-3 rules ===

Material 3

Every view in this application is Material 3 Expressive (m3.material.io), through nonameweb/livewire-material. These rules decide what to write; the material-3-design skill carries the tables, the numbers and Google's source pages behind each one — activate it before designing a screen.

The library is plain CSS on M3's tokens. No utility classes — Tailwind, daisyUI or any other — belong here: a class your CSS does not declare does nothing, and DesignGuard fails it. A view is written three ways:

  • Components and their props: <x-button variant="filled">, and the layout components <x-row>, <x-stack>, <x-grid>, <x-feed>, <x-surface> and <x-pane>, whose gap and padding take a spacing token (space200) and whose hide-below, hide-from and stack-below take a window size class.
  • A fixed set of classes for text and interaction on plain elements: md-type-*, md-ink-*, md-text-*, md-truncate, md-tabular, md-visually-hidden, md-state-layer, md-focus-ring, md-touch-target and md-link.
  • The application's own CSS, named by the application, whose values are --md-sys-* custom properties.

Colour

  • A colour is always a role: md-ink-variant on text, var(--md-sys-color-outline-variant) in the application's CSS, color="error" on a component. Never a hex, a palette tone or an opacity.
  • Pair a role only with its on- partner: a primary fill takes on-primary text, a secondary-container fill takes on-secondary-container. That pair is the one whose contrast is guaranteed at every contrast level; mixing pairs (primary-container under on-surface) is not.
  • primary is the one key action on a screen (a filled button; the FAB in primary-container). secondary-container is the quiet fill (tonal buttons, selected navigation, selected chips). tertiary is a contrasting accent, used rarely. error, success, warning, info mean state and nothing else: the -container for a tinted panel, the role itself for its text and icon.
  • Ink is on-surface (md-ink); lower emphasis is on-surface-variant (md-ink-variant); decoration is outline (md-ink-quiet). Never dim ink with an opacity: 38% means disabled.
  • outline is a boundary that must be read (a text field, the edge of a target). outline-variant is a divider or a card edge (<x-divider>, <x-surface outlined>). Never outline on a divider.
  • Fixed and dim roles (primary-fixed, surface-dim, …) are for a colour that must not change with the theme; if unsure, don't. Inverse roles only on an inverse surface (the snackbar).
  • A link in running text is underlined (md-link, with md-ink-primary); colour alone signals nothing.
  • Contrast: 4.5:1 for text, 3:1 for large text, icons and grouped controls; disabled is exempt. Three contrast levels exist (<html data-contrast>: standard, medium, high) and every role changes with them — which is why only roles are allowed.

Surfaces and elevation

  • The page is surface. Panels separate by tone first: surface-container-lowest … surface-container-highest is a hierarchy of emphasis, not of height (<x-surface level="surface-container-high">). Navigation chrome is surface-container; a dialog, a menu, the search bar are surface-container-high; a modal sheet is surface-container-low; a filled card is surface-container-highest. A region keeps its role at every width.
  • Shadows (var(--md-sys-elevation-1) … -5) are for what floats or lifts: 1 for elevated cards, buttons and modal sheets; 2 for menus, the navigation bar, a scrolled app bar; 3 for the FAB, dialogs, pickers and search; one level more on hover; nothing rests above 3. Fewer shadows carry more meaning.
  • A scrim is scrim at 32%: color-mix(in srgb, var(--md-sys-color-scrim) 32%, transparent).

Shape

  • Corners come from the scale var(--md-sys-shape-corner-{none|xs|sm|md|lg|lg-increased|xl|xl-increased|xxl|full}), or <x-surface corner="md">; never a length of your own.
  • By family: full buttons, icon buttons, chips' avatars, badges, switches, sliders, the search bar, navigation indicators; xs text fields, menus, snackbars, plain tooltips; sm chips; md cards, rich tooltips; lg the FAB and a side sheet's inner corners; xl dialogs, bottom sheets, the search view, pickers, carousel items; xxl large hero containers.
  • Nested shapes: inner radius = outer radius − padding; never the same radius inside and out.
  • A press squares a round shape (the components do it; nothing morphs on hover). The 35 <x-shape>s are decoration, never meaning, used sparingly.

Type

  • Every text element carries one md-type-* class: display for hero figures and short marketing lines; headline for page and section titles; title for card, dialog and list-section titles; body for paragraphs (md-type-body-lg for reading); label inside components (buttons, chips, tabs, captions). In the application's CSS a style is font: var(--md-sys-typescale-body-md) with its -tracking; never a size, weight, line height or letter spacing of your own.
  • md-type-emphasized-* is opt-in: a selected item, a primary action, a headline, a badge — not decoration.
  • 40–60 characters per line; md-tabular on figures that change; text must scale to 200% without loss (containers grow, rows wrap, no fixed heights on text, no ellipsis without a way to read the rest).

Motion

  • Position, size and shape move on the spatial springs (they overshoot): transition: transform var(--md-sys-motion-spatial-default-duration) var(--md-sys-motion-spatial-default) — fast for small elements, slow for large ones. Colour and opacity move on the effects springs (--md-sys-motion-effects-*), which never overshoot. Always pair an easing with its duration.
  • Entering decelerates, a permanent exit accelerates, a temporary exit (a sheet, a drawer) takes the emphasized curve; exits are shorter than entrances.
  • Everything that moves goes through these tokens, so reduced motion makes it instant; a literal duration is a bug.

States and targets

  • Interactive elements carry md-state-layer md-focus-ring: hover 8%, focus 10%, pressed 10%, dragged 16% (data-md-dragged) of the content colour. Disabled is content at 38% and a container at 12% of on-surface (--md-sys-state-disabled-content-opacity, --md-sys-state-disabled-container-opacity, through color-mix()), with no state layer. Every state shows two indicators: colour plus a shape, an outline, an icon or a word.
  • Every target is at least 48×48px with 8px between targets (md-touch-target on anything drawn smaller); a denser layout is an opt-in prop, never a default.
  • Keyboard: Tab between components, arrows within one, Enter and Space activate, Escape dismisses; a dialog takes focus and gives it back to what opened it.

Layout and breakpoints

  • Widths are M3's window size classes and only those: compact below 600px (the default), medium 600, expanded 840, large 1200, extra-large 1600. A layout component takes them as props (<x-row stack-below="medium">, <x-stack hide-from="expanded">, <x-grid :columns="['compact' => 1, 'expanded' => 2]">); the application's CSS writes @media (width >= 840px); a script asks from() and upTo() from resources/js/breakpoints.js.
  • What changes per class: compact — navigation bar, one pane, full-screen dialogs, a bottom sheet for choices; medium — collapsed rail, one pane; expanded — rail (collapsible), two panes, menus and basic dialogs; large and extra-large — the rail expanded, two panes, a third only at extra-large as a side sheet. <x-scaffold> does this; content lives in panes (<x-pane>, <x-list-detail> for a list's second pane), never beside the rail by hand.
  • Margins are 16px below medium and 24px from it (<x-pane> draws them); spacing sits on the 4px grid as space25 … space900, as padding and gaps on the parent, with margins only between layout regions. A fixed pane is 360px (expanded) or 412px (large); a side sheet at most 400px.
  • Write logical properties (padding-inline-start, inset-inline-end, md-text-start); directional icons mirror in RTL; charts and media controls stay LTR. Keep controls inside the safe area (--material-safe-*).

Accessibility

  • Native elements first (<button>, <dialog>, <input>), then ARIA. One main, one banner, one contentinfo; every repeated nav labelled, without the word "navigation".
  • Headings in order from a single H1; the level is structure, the md-type-* class is appearance.
  • An icon-only control has an accessible name that does not include its role; decorative icons are hidden; an error is announced and tied to its field (aria-describedby); a toast uses a polite live region and never takes focus. A single-key shortcut needs a modifier or a focused component.

Icons

  • <x-icon name="home"> is a Material Symbol Rounded: filled means active or selected, optical="20" when drawn at 20px or less, one weight per group, the size and colour of the text beside it.

Don'ts

  • No icon in a snackbar; no disabled FAB (hide it); no horizontal radio rows; no hover morph on cards; no outline on dividers; no hex colours; no utility classes, and no breakpoint, radius, shadow, type size or duration off M3's scales; no segmented buttons, navigation drawer or bottom app bar — use <x-button-group connected>, the expanded rail and <x-toolbar>.