Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
Agents
norjamille-kasan/t-wheels
claude-code
cursor
actions
controllers
crud
form-requests
arch-tests

Every UI change keeps the layout still. Nothing may flash, jump, clip or reflow on first load, navigation, polling, or after an action.

Install
php artisan boost:add-skill norjamille-kasan/t-wheels

Save this content to: AGENTS.md

<laravel-boost-guidelines>
=== .ai/app.actions rules ===

# App/Actions guidelines

- This application uses the Action pattern and prefers for much logic to live in reusable and composable Action classes.
- Actions live in `app/Actions`, they are named based on what they do, with no suffix.
- Actions will be called from many different places: jobs, commands, HTTP requests, API requests, MCP requests, and more.
- Create dedicated Action classes for business logic with a single `handle()` method.
- Inject dependencies via constructor using private properties.
- Create new actions with `php artisan make:action "{name}" --no-interaction`
- Wrap complex operations in `DB::transaction()` within actions when multiple models are involved.
- Some actions won't require dependencies via `__construct` and they can use just the `handle()` method.

<!-- Example action class -->
```php
<?php

declare(strict_types=1);

namespace App\Actions;

final readonly class CreateOrder
{
    public function __construct(private OrderService $orders)
    {
        //
    }

    public function handle(User $user, string $reference): bool
    {
        return $this->orders->place($user, $reference);
    }
}
```

=== .ai/app.http.controllers rules ===

# App/Http/Controllers guidelines

- Controllers are CRUD by design: their only public methods are `index`, `create`, `store`, `show`, `edit`, `update`, and `destroy`.
- Never add a custom method (`approve`, `pin`, `hold`, `export`, `fulfil`). Instead, find the resource being created, updated, or deleted, and give it its own controller:
  - Approving an order → `OrderApprovalController@store`. Rejecting it → `OrderRejectionController@store`.
  - Pinning an order → `OrderPinController@update`. Unpinning it → `OrderPinController@destroy`.
  - Holding an order → `OrderHoldController@store`. Releasing it → `OrderHoldController@destroy`.
  - Approving the selected orders in the fulfilment queue → `FulfilmentController@update`.
- When an endpoint really does not fit CRUD, that is the sign to use an invokable controller with a single `__invoke()` method, named after the thing, minimally: `ExportController`, not `PersonalDataExportController` or `ExportPersonalDataController`.
- Controller names are nouns, never verbs: `OrderStarController`, not `StarOrderController`.
- Controllers stay thin: resolve the form request, call one action (or query), and return a response.
- Controllers never authorize or validate anything: no `abort()`, `abort_if()`, `abort_unless()`, `throw_if()`, `throw_unless()`, `$request->validate()`, `Validator::make()`, or `ValidationException`. Every check lives in the form request of that endpoint.
- `tests/Unit/ArchTest.php` enforces this.

<!-- Example controller -->
```php
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Actions\CreateOrderStar;
use App\Actions\DeleteOrderStar;
use App\Http\Requests\CreateOrderStarRequest;
use App\Http\Requests\DeleteOrderStarRequest;
use App\Models\Order;
use App\Models\User;
use Illuminate\Container\Attributes\CurrentUser;
use Illuminate\Http\RedirectResponse;

final readonly class OrderStarController
{
    public function store(CreateOrderStarRequest $request, #[CurrentUser] User $user, Order $order, CreateOrderStar $action): RedirectResponse
    {
        $action->handle($user, $order);

        return back();
    }

public function destroy(DeleteOrderStarRequest $request, #[CurrentUser] User $user, Order $order, DeleteOrderStar $action): RedirectResponse
    {
        $action->handle($user, $order);

        return back();
    }
}
```

=== .ai/app.http.requests rules ===

# App/Http/Requests guidelines

- Never use policies or gates: no `app/Policies`, no `Gate::define()`, no `can:` middleware, no `$user->can()`, no `$this->authorize()`.
- Authorization and validation only ever happen in form requests: authorization in `authorize()`, validation in `rules()`. Never in controllers, actions, or models. This includes `index` and `show` endpoints.
- Controllers and actions never check anything themselves: no `abort()`, `abort_if()`, `abort_unless()`, `throw_if()`, `throw_unless()`, `Validator::make()`, `$request->validate()`, or `ValidationException`.
- Create a dedicated form request for each endpoint that checks anything, and never share one between endpoints. If the endpoint has none yet, create one.
- Name it after the controller method it serves, CRUD by design: `index` → `List…Request`, `show` → `Show…Request`, `store` → `Create…Request`, `update` → `Update…Request`, `destroy` → `Delete…Request`. For example `OrderPinController@update` → `UpdateOrderPinRequest`, `OrderPinController@destroy` → `DeleteOrderPinRequest`, `OrderApprovalController@store` → `CreateOrderApprovalRequest`.
- When hiding something should look like it does not exist, return `Response::denyAsNotFound()` from `authorize()` instead of `false`.
- Put input interpretation in form request methods (`$request->tab()`, `$request->parent()`), and complex checks in `App\Rules` validation rules.
- `tests/Unit/ArchTest.php` enforces this.

<!-- Example form request -->
```php
<?php

declare(strict_types=1);

namespace App\Http\Requests;

use App\Models\Order;
use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Foundation\Http\FormRequest;

final class CreateOrderNoteRequest extends FormRequest
{
    public function authorize(): Response
    {
        $user = $this->user();
        assert($user instanceof User);

        $order = $this->route('order');
        assert($order instanceof Order);

        return $order->isVisibleTo($user) ? Response::allow() : Response::denyAsNotFound();
    }

    /**
     * @return array{}
     */
    public function rules(): array
    {
        return [];
    }
}
```

=== .ai/general rules ===

# General Guidelines

- Don't include any superfluous PHP Annotations, except ones that start with `@` for typing variables.

=== .ai/resources.js rules ===

# Frontend UI stability

Every UI change keeps the layout still. Nothing may flash, jump, clip or reflow on first load, navigation, polling, or after an action.

## Loading

- A skeleton occupies exactly the loaded content's box: same height, line count and wrapping at every breakpoint, including the narrow aside. It is visible in light and dark mode, popovers included.
- Delay skeletons by about 300ms so fast responses show nothing. Show one only while data is genuinely missing, never while refetching data you already have.
- Shared props that persist across pages (aside, nav counts) use `Inertia::defer(...)->once()`, so client visits don't refetch them and flash.
- Polling and refresh buttons use partial reloads (`only: [...]`) of the page's own props. Never reload the whole page, and keep showing the old data until the new data arrives.

## Changing content

- Numbers, counts and countdowns use `tabular-nums` in fixed-width slots.
- Buttons whose label changes ("Place order", "Placing…") get a `min-w`. Disabled, loading and enabled states are the same size.
- Conditional UI (banners, "@x is reviewing", inline confirms, errors) reserves its space or overlays. An inline confirm fits in the slot of the control it replaces.
- Removed list rows collapse with the existing order-deletion animation, keeping scroll position and focus.
- Right after an action, the control under the cursor must not flip to a destructive state, e.g. "Place order" → "Cancel order" before the pointer leaves.

## Overflow

- Dialogs and popovers are capped to the viewport (`dvh`) and scroll inside, with a sticky header and footer when actions would scroll away. No double scrollbars and no page scroll through the overlay. Scroll lock must not shift the page: keep `scrollbar-gutter: stable`.
- Long usernames, URLs, reasons, queue names and messages use `min-w-0` with `truncate` or `line-clamp`, and the full text in `title`.
- Auto-growing textareas are `overflow-x-hidden`. Inputs use at least 16px text below `md` so iOS doesn't zoom.
- On mobile, toasts sit above the bottom nav.

## Verify before calling it done

- Check in a browser at 1440×900, 1366×650 and 390×844, in light and dark.
- Measure layout shift (`PerformanceObserver` with `layout-shift`) for first load, navigation, a polling cycle and every action you added. The target is 0.
- Compare side by side with `production` using the same demo data. Record before/after clips for any fix to motion or loading.

=== .ai/resources.js.http rules ===

# Frontend HTTP requests

- Every call to an API route goes through `useHttp()` from `@inertiajs/vue3`. Never `fetch()`, never `XMLHttpRequest`, and never an HTTP library: no `axios`, no `ky`, no `got`, no `superagent`. Axios is not a dependency of this app and must not become one.
- Pass a Wayfinder route to the hook, never a hand-written URL string: the route object to `submit()`, or its `.url(...)` to `get()`, `post()`, `put()`, `patch()` and `delete()`.
- The payload lives on the hook instance, not in the call: `const form = useHttp({ name: '' })`, then `form.name = 'x'` and `await form.put(update.url(id))`.
- Read state off the hook instead of tracking it by hand: `processing`, `errors`, `hasErrors`, `response`, `wasSuccessful`, `recentlySuccessful`, `progress`, `isDirty`. `cancel()` aborts, `optimistic()` (or the `optimistic` option) updates the UI before the response lands, `withPrecognition()` validates a field as the user types.
- `useHttp` is for JSON endpoints and performs no Inertia visit: the page does not change. Page transitions and form submissions that should navigate keep using `router` and `useForm`.
- `resources/js/composables/useTwoFactorAuth.ts` is the reference implementation in this app.

<!-- Example useHttp calls -->
```ts
import { useHttp } from '@inertiajs/vue3';
import { show, update } from '@/actions/App/Http/Controllers/OrderController';

const loadOrder = async (id: number): Promise<Order> => {
    const http = useHttp();

    // http.errors / http.hasErrors carry any failure.
    return (await http.submit(show(id))) as Order;
};

const renameOrder = async (id: number, name: string): Promise<void> => {
    const form = useHttp({ name: '' });
    form.name = name;

    await form.put(update.url(id));
};
```

=== 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 `bun run build` or ask the user to run `bun 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](https://cloud.laravel.com/), which is the fastest way to deploy and scale production Laravel applications.
- Activate the `deploying-to-cloud` skill whenever deploying to Laravel Cloud, configuring Cloud environments or resources, using the Cloud CLI, or troubleshooting Cloud deployments.

=== inertia-laravel/core rules ===

# Inertia

- Inertia creates fully client-side rendered SPAs without modern SPA complexity, leveraging existing server-side patterns.
- Components live in `resources/js/pages` (unless specified in `vite.config.js`). Use `Inertia::render()` for server-side routing instead of Blade views.
- ALWAYS use `search-docs` tool for version-specific Inertia documentation and updated code examples.
- IMPORTANT: Activate `inertia-vue-development` when working with Inertia Vue client-side patterns.

# Inertia v3

- Use all Inertia features from v1, v2, and v3. Check the documentation before making changes to ensure the correct approach.
- New v3 features: standalone HTTP requests (`useHttp` hook), optimistic updates with automatic rollback, layout props (`useLayoutProps` hook), instant visits, simplified SSR via `@inertiajs/vite` plugin, custom exception handling for error pages.
- Carried over from v2: deferred props, infinite scroll, merging props, polling, prefetching, once props, flash data.
- When using deferred props, add an empty state with a pulsing or animated skeleton.
- Axios has been removed. Use the built-in XHR client with interceptors, or install Axios separately if needed.
- `Inertia::lazy()` / `LazyProp` has been removed. Use `Inertia::optional()` instead.
- Prop types (`Inertia::optional()`, `Inertia::defer()`, `Inertia::merge()`) work inside nested arrays with dot-notation paths.
- SSR works automatically in Vite dev mode with `@inertiajs/vite` - no separate Node.js server needed during development.
- Event renames: `invalid` is now `httpException`, `exception` is now `networkError`.
- `router.cancel()` replaced by `router.cancelAll()`.
- The `future` configuration namespace has been removed - all v2 future options are now always enabled.

=== 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.

=== wayfinder/core rules ===

# Laravel Wayfinder

Use Wayfinder to generate TypeScript functions for Laravel routes. Import from `@/actions/` (controllers) or `@/routes/` (named routes).

=== 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`.

=== inertia-vue/core rules ===

# Inertia + Vue

Vue components must have a single root element.
- IMPORTANT: Activate `inertia-vue-development` when working with Inertia Vue client-side patterns.

</laravel-boost-guidelines>

App/Actions guidelines

  • This application uses the Action pattern and prefers for much logic to live in reusable and composable Action classes.
  • Actions live in app/Actions, they are named based on what they do, with no suffix.
  • Actions will be called from many different places: jobs, commands, HTTP requests, API requests, MCP requests, and more.
  • Create dedicated Action classes for business logic with a single handle() method.
  • Inject dependencies via constructor using private properties.
  • Create new actions with php artisan make:action "{name}" --no-interaction
  • Wrap complex operations in DB::transaction() within actions when multiple models are involved.
  • Some actions won't require dependencies via __construct and they can use just the handle() method.
<?php

declare(strict_types=1);

namespace App\Actions;

final readonly class CreateOrder
{
    public function __construct(private OrderService $orders)
    {
        //
    }

    public function handle(User $user, string $reference): bool
    {
        return $this->orders->place($user, $reference);
    }
}

=== .ai/app.http.controllers rules ===

App/Http/Controllers guidelines

  • Controllers are CRUD by design: their only public methods are index, create, store, show, edit, update, and destroy.
  • Never add a custom method (approve, pin, hold, export, fulfil). Instead, find the resource being created, updated, or deleted, and give it its own controller:
    • Approving an order → OrderApprovalController@store. Rejecting it → OrderRejectionController@store.
    • Pinning an order → OrderPinController@update. Unpinning it → OrderPinController@destroy.
    • Holding an order → OrderHoldController@store. Releasing it → OrderHoldController@destroy.
    • Approving the selected orders in the fulfilment queue → FulfilmentController@update.
  • When an endpoint really does not fit CRUD, that is the sign to use an invokable controller with a single __invoke() method, named after the thing, minimally: ExportController, not PersonalDataExportController or ExportPersonalDataController.
  • Controller names are nouns, never verbs: OrderStarController, not StarOrderController.
  • Controllers stay thin: resolve the form request, call one action (or query), and return a response.
  • Controllers never authorize or validate anything: no abort(), abort_if(), abort_unless(), throw_if(), throw_unless(), $request->validate(), Validator::make(), or ValidationException. Every check lives in the form request of that endpoint.
  • tests/Unit/ArchTest.php enforces this.
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Actions\CreateOrderStar;
use App\Actions\DeleteOrderStar;
use App\Http\Requests\CreateOrderStarRequest;
use App\Http\Requests\DeleteOrderStarRequest;
use App\Models\Order;
use App\Models\User;
use Illuminate\Container\Attributes\CurrentUser;
use Illuminate\Http\RedirectResponse;

final readonly class OrderStarController
{
    public function store(CreateOrderStarRequest $request, #[CurrentUser] User $user, Order $order, CreateOrderStar $action): RedirectResponse
    {
        $action->handle($user, $order);

        return back();
    }

public function destroy(DeleteOrderStarRequest $request, #[CurrentUser] User $user, Order $order, DeleteOrderStar $action): RedirectResponse
    {
        $action->handle($user, $order);

        return back();
    }
}

=== .ai/app.http.requests rules ===

App/Http/Requests guidelines

  • Never use policies or gates: no app/Policies, no Gate::define(), no can: middleware, no $user->can(), no $this->authorize().
  • Authorization and validation only ever happen in form requests: authorization in authorize(), validation in rules(). Never in controllers, actions, or models. This includes index and show endpoints.
  • Controllers and actions never check anything themselves: no abort(), abort_if(), abort_unless(), throw_if(), throw_unless(), Validator::make(), $request->validate(), or ValidationException.
  • Create a dedicated form request for each endpoint that checks anything, and never share one between endpoints. If the endpoint has none yet, create one.
  • Name it after the controller method it serves, CRUD by design: index → List…Request, show → Show…Request, store → Create…Request, update → Update…Request, destroy → Delete…Request. For example OrderPinController@update → UpdateOrderPinRequest, OrderPinController@destroy → DeleteOrderPinRequest, OrderApprovalController@store → CreateOrderApprovalRequest.
  • When hiding something should look like it does not exist, return Response::denyAsNotFound() from authorize() instead of false.
  • Put input interpretation in form request methods ($request->tab(), $request->parent()), and complex checks in App\Rules validation rules.
  • tests/Unit/ArchTest.php enforces this.
<?php

declare(strict_types=1);

namespace App\Http\Requests;

use App\Models\Order;
use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Foundation\Http\FormRequest;

final class CreateOrderNoteRequest extends FormRequest
{
    public function authorize(): Response
    {
        $user = $this->user();
        assert($user instanceof User);

        $order = $this->route('order');
        assert($order instanceof Order);

        return $order->isVisibleTo($user) ? Response::allow() : Response::denyAsNotFound();
    }

    /**
     * @return array{}
     */
    public function rules(): array
    {
        return [];
    }
}

=== .ai/general rules ===

General Guidelines

  • Don't include any superfluous PHP Annotations, except ones that start with @ for typing variables.

=== .ai/resources.js rules ===

Frontend UI stability

Every UI change keeps the layout still. Nothing may flash, jump, clip or reflow on first load, navigation, polling, or after an action.

Loading

  • A skeleton occupies exactly the loaded content's box: same height, line count and wrapping at every breakpoint, including the narrow aside. It is visible in light and dark mode, popovers included.
  • Delay skeletons by about 300ms so fast responses show nothing. Show one only while data is genuinely missing, never while refetching data you already have.
  • Shared props that persist across pages (aside, nav counts) use Inertia::defer(...)->once(), so client visits don't refetch them and flash.
  • Polling and refresh buttons use partial reloads (only: [...]) of the page's own props. Never reload the whole page, and keep showing the old data until the new data arrives.

Changing content

  • Numbers, counts and countdowns use tabular-nums in fixed-width slots.
  • Buttons whose label changes ("Place order", "Placing…") get a min-w. Disabled, loading and enabled states are the same size.
  • Conditional UI (banners, "@x is reviewing", inline confirms, errors) reserves its space or overlays. An inline confirm fits in the slot of the control it replaces.
  • Removed list rows collapse with the existing order-deletion animation, keeping scroll position and focus.
  • Right after an action, the control under the cursor must not flip to a destructive state, e.g. "Place order" → "Cancel order" before the pointer leaves.

Overflow

  • Dialogs and popovers are capped to the viewport (dvh) and scroll inside, with a sticky header and footer when actions would scroll away. No double scrollbars and no page scroll through the overlay. Scroll lock must not shift the page: keep scrollbar-gutter: stable.
  • Long usernames, URLs, reasons, queue names and messages use min-w-0 with truncate or line-clamp, and the full text in title.
  • Auto-growing textareas are overflow-x-hidden. Inputs use at least 16px text below md so iOS doesn't zoom.
  • On mobile, toasts sit above the bottom nav.

Verify before calling it done

  • Check in a browser at 1440×900, 1366×650 and 390×844, in light and dark.
  • Measure layout shift (PerformanceObserver with layout-shift) for first load, navigation, a polling cycle and every action you added. The target is 0.
  • Compare side by side with production using the same demo data. Record before/after clips for any fix to motion or loading.

=== .ai/resources.js.http rules ===

Frontend HTTP requests

  • Every call to an API route goes through useHttp() from @inertiajs/vue3. Never fetch(), never XMLHttpRequest, and never an HTTP library: no axios, no ky, no got, no superagent. Axios is not a dependency of this app and must not become one.
  • Pass a Wayfinder route to the hook, never a hand-written URL string: the route object to submit(), or its .url(...) to get(), post(), put(), patch() and delete().
  • The payload lives on the hook instance, not in the call: const form = useHttp({ name: '' }), then form.name = 'x' and await form.put(update.url(id)).
  • Read state off the hook instead of tracking it by hand: processing, errors, hasErrors, response, wasSuccessful, recentlySuccessful, progress, isDirty. cancel() aborts, optimistic() (or the optimistic option) updates the UI before the response lands, withPrecognition() validates a field as the user types.
  • useHttp is for JSON endpoints and performs no Inertia visit: the page does not change. Page transitions and form submissions that should navigate keep using router and useForm.
  • resources/js/composables/useTwoFactorAuth.ts is the reference implementation in this app.
import { useHttp } from '@inertiajs/vue3';
import { show, update } from '@/actions/App/Http/Controllers/OrderController';

const loadOrder = async (id: number): Promise<Order> => {
    const http = useHttp();

    // http.errors / http.hasErrors carry any failure.
    return (await http.submit(show(id))) as Order;
};

const renameOrder = async (id: number, name: string): Promise<void> => {
    const form = useHttp({ name: '' });
    form.name = name;

    await form.put(update.url(id));
};

=== 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 bun run build or ask the user to run bun 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.
  • Activate the deploying-to-cloud skill whenever deploying to Laravel Cloud, configuring Cloud environments or resources, using the Cloud CLI, or troubleshooting Cloud deployments.

=== inertia-laravel/core rules ===

Inertia

  • Inertia creates fully client-side rendered SPAs without modern SPA complexity, leveraging existing server-side patterns.
  • Components live in resources/js/pages (unless specified in vite.config.js). Use Inertia::render() for server-side routing instead of Blade views.
  • ALWAYS use search-docs tool for version-specific Inertia documentation and updated code examples.
  • IMPORTANT: Activate inertia-vue-development when working with Inertia Vue client-side patterns.

Inertia v3

  • Use all Inertia features from v1, v2, and v3. Check the documentation before making changes to ensure the correct approach.
  • New v3 features: standalone HTTP requests (useHttp hook), optimistic updates with automatic rollback, layout props (useLayoutProps hook), instant visits, simplified SSR via @inertiajs/vite plugin, custom exception handling for error pages.
  • Carried over from v2: deferred props, infinite scroll, merging props, polling, prefetching, once props, flash data.
  • When using deferred props, add an empty state with a pulsing or animated skeleton.
  • Axios has been removed. Use the built-in XHR client with interceptors, or install Axios separately if needed.
  • Inertia::lazy() / LazyProp has been removed. Use Inertia::optional() instead.
  • Prop types (Inertia::optional(), Inertia::defer(), Inertia::merge()) work inside nested arrays with dot-notation paths.
  • SSR works automatically in Vite dev mode with @inertiajs/vite - no separate Node.js server needed during development.
  • Event renames: invalid is now httpException, exception is now networkError.
  • router.cancel() replaced by router.cancelAll().
  • The future configuration namespace has been removed - all v2 future options are now always enabled.

=== 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.

=== wayfinder/core rules ===

Laravel Wayfinder

Use Wayfinder to generate TypeScript functions for Laravel routes. Import from @/actions/ (controllers) or @/routes/ (named routes).

=== 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.

=== inertia-vue/core rules ===

Inertia + Vue

Vue components must have a single root element.

  • IMPORTANT: Activate inertia-vue-development when working with Inertia Vue client-side patterns.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core