diff --git a/.agents/skills/laravel-best-practices/SKILL.md b/.agents/skills/laravel-best-practices/SKILL.md index 99018f3..aca32c9 100644 --- a/.agents/skills/laravel-best-practices/SKILL.md +++ b/.agents/skills/laravel-best-practices/SKILL.md @@ -94,7 +94,7 @@ Check sibling files, related controllers, models, or tests for established patte ### 9. Queue & Job Patterns → `rules/queue-jobs.md` - `retry_after` must exceed job `timeout`; use exponential backoff `[1, 5, 10]` -- `ShouldBeUnique` to prevent duplicates; `WithoutOverlapping::untilProcessing()` for concurrency +- `ShouldBeUnique` to prevent duplicates; `ShouldBeUniqueUntilProcessing` for early lock release - Always implement `failed()`; with `retryUntil()`, set `$tries = 0` - `RateLimited` middleware for external API calls; `Bus::batch()` for related jobs - Horizon for complex multi-queue scenarios diff --git a/.agents/skills/laravel-best-practices/rules/architecture.md b/.agents/skills/laravel-best-practices/rules/architecture.md index 1650564..6112a63 100644 --- a/.agents/skills/laravel-best-practices/rules/architecture.md +++ b/.agents/skills/laravel-best-practices/rules/architecture.md @@ -82,7 +82,7 @@ $this->app->bind(PaymentGateway::class, StripeGateway::class); ## Default Sort by Descending -When no explicit order is specified, sort by `id` or `created_at` descending. Explicit ordering prevents cross-database inconsistencies between MySQL and Postgres. +When no explicit order is specified, sort by `id` or `created_at` descending. Without an explicit `ORDER BY`, row order is undefined. Incorrect: ```php diff --git a/.agents/skills/laravel-best-practices/rules/caching.md b/.agents/skills/laravel-best-practices/rules/caching.md index eb3ef3e..e65146d 100644 --- a/.agents/skills/laravel-best-practices/rules/caching.md +++ b/.agents/skills/laravel-best-practices/rules/caching.md @@ -2,7 +2,7 @@ ## Use `Cache::remember()` Instead of Manual Get/Put -Atomic pattern prevents race conditions and removes boilerplate. +Cleaner cache-aside pattern that removes boilerplate. use `Cache::lock()` for race conditions. Incorrect: ```php diff --git a/.agents/skills/laravel-best-practices/rules/config.md b/.agents/skills/laravel-best-practices/rules/config.md index 8fd8f53..193155d 100644 --- a/.agents/skills/laravel-best-practices/rules/config.md +++ b/.agents/skills/laravel-best-practices/rules/config.md @@ -2,7 +2,7 @@ ## `env()` Only in Config Files -Direct `env()` calls return `null` when config is cached. +Direct `env()` calls may return `null` when config is cached. Incorrect: ```php diff --git a/.agents/skills/laravel-best-practices/rules/events-notifications.md b/.agents/skills/laravel-best-practices/rules/events-notifications.md index bc43f19..47fcf32 100644 --- a/.agents/skills/laravel-best-practices/rules/events-notifications.md +++ b/.agents/skills/laravel-best-practices/rules/events-notifications.md @@ -29,7 +29,11 @@ class InvoicePaid extends Notification implements ShouldQueue ## Use `afterCommit()` on Notifications in Transactions -Same race condition as events — the queued notification job may run before the transaction commits. +Same race condition as events — call `afterCommit()` to delay dispatch until the transaction commits. + +```php +$user->notify((new InvoicePaid($invoice))->afterCommit()); +``` ## Route Notification Channels to Dedicated Queues diff --git a/.agents/skills/laravel-best-practices/rules/http-client.md b/.agents/skills/laravel-best-practices/rules/http-client.md index 0a7876e..fd37ddb 100644 --- a/.agents/skills/laravel-best-practices/rules/http-client.md +++ b/.agents/skills/laravel-best-practices/rules/http-client.md @@ -52,7 +52,7 @@ $response = Http::retry([100, 500, 1000]) Only retry on specific errors: ```php -$response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) { +$response = Http::retry(3, 100, function (Throwable $exception, PendingRequest $request) { return $exception instanceof ConnectionException || ($exception instanceof RequestException && $exception->response->serverError()); })->post('https://api.example.com/data'); diff --git a/.agents/skills/laravel-best-practices/rules/mail.md b/.agents/skills/laravel-best-practices/rules/mail.md index c7f6796..2435d9c 100644 --- a/.agents/skills/laravel-best-practices/rules/mail.md +++ b/.agents/skills/laravel-best-practices/rules/mail.md @@ -10,7 +10,7 @@ A queued mailable dispatched inside a transaction may process before the commit. ## Use `assertQueued()` Not `assertSent()` for Queued Mailables -`Mail::assertSent()` only catches synchronous mail. Queued mailables silently pass `assertSent`, giving false confidence. +`Mail::assertSent()` only catches synchronous mail. Queued mailables fail `assertSent` with a "Did you mean to use assertQueued()?" hint. Incorrect: `Mail::assertSent(OrderShipped::class);` when mailable implements `ShouldQueue`. diff --git a/.agents/skills/laravel-best-practices/rules/queue-jobs.md b/.agents/skills/laravel-best-practices/rules/queue-jobs.md index d4575aa..f7aa548 100644 --- a/.agents/skills/laravel-best-practices/rules/queue-jobs.md +++ b/.agents/skills/laravel-best-practices/rules/queue-jobs.md @@ -106,25 +106,23 @@ When using time-based retry limits, set `$tries = 0` to avoid premature failure. ```php public $tries = 0; -public function retryUntil(): DateTime +public function retryUntil(): \DateTimeInterface { return now()->addHours(4); } ``` -## Use `WithoutOverlapping::untilProcessing()` +## Use `ShouldBeUniqueUntilProcessing` for Early Lock Release -Prevents concurrent execution while allowing new instances to queue. +`ShouldBeUnique` holds the lock until the job completes. `ShouldBeUniqueUntilProcessing` releases it when processing starts, allowing new instances to queue. ```php -public function middleware(): array +class UpdateSearchIndex implements ShouldQueue, ShouldBeUniqueUntilProcessing { - return [new WithoutOverlapping($this->product->id)->untilProcessing()]; + // Lock releases when processing begins, not when it finishes } ``` -Without `untilProcessing()`, the lock extends through queue wait time. With it, the lock releases when processing starts. - ## Use Horizon for Complex Queue Scenarios Use Laravel Horizon when you need monitoring, auto-scaling, failure tracking, or multiple queues with different priorities. diff --git a/.agents/skills/laravel-best-practices/rules/routing.md b/.agents/skills/laravel-best-practices/rules/routing.md index e288375..977d136 100644 --- a/.agents/skills/laravel-best-practices/rules/routing.md +++ b/.agents/skills/laravel-best-practices/rules/routing.md @@ -36,7 +36,8 @@ Use `Route::resource()` or `apiResource()` for RESTful endpoints. ```php Route::resource('posts', PostController::class); -Route::apiResource('api/posts', Api\PostController::class); +// In routes/api.php — the /api prefix is applied automatically +Route::apiResource('posts', Api\PostController::class); ``` ## Keep Controllers Thin diff --git a/.agents/skills/laravel-best-practices/rules/security.md b/.agents/skills/laravel-best-practices/rules/security.md index 524d47e..909ff91 100644 --- a/.agents/skills/laravel-best-practices/rules/security.md +++ b/.agents/skills/laravel-best-practices/rules/security.md @@ -32,7 +32,7 @@ Use policies or gates in controllers. Never skip authorization. Incorrect: ```php -public function update(Request $request, Post $post) +public function update(UpdatePostRequest $request, Post $post) { $post->update($request->validated()); } @@ -90,7 +90,7 @@ Correct: ## CSRF Protection -Include `@csrf` in all POST/PUT/DELETE Blade forms. Not needed in Inertia. +Include `@csrf` in all POST/PUT/DELETE Blade forms. In Inertia apps, the `@csrf` directive is automatically applied. Incorrect: ```blade @@ -121,7 +121,7 @@ Route::post('/login', LoginController::class)->middleware('throttle:login'); ## Validate File Uploads -Validate MIME type, extension, and size. Never trust client-provided filenames. +Validate extension, MIME type, and size. The `mimes` rule checks extensions; use `mimetypes` for actual MIME type validation. Never trust client-provided filenames. ```php public function rules(): array diff --git a/.agents/skills/laravel-best-practices/rules/style.md b/.agents/skills/laravel-best-practices/rules/style.md index db689bf..67af989 100644 Binary files a/.agents/skills/laravel-best-practices/rules/style.md and b/.agents/skills/laravel-best-practices/rules/style.md differ diff --git a/.agents/skills/laravel-best-practices/rules/testing.md b/.agents/skills/laravel-best-practices/rules/testing.md index d39cc3e..287b083 100644 --- a/.agents/skills/laravel-best-practices/rules/testing.md +++ b/.agents/skills/laravel-best-practices/rules/testing.md @@ -2,7 +2,7 @@ ## Use `LazilyRefreshDatabase` Over `RefreshDatabase` -`RefreshDatabase` runs all migrations every test run even when the schema hasn't changed. `LazilyRefreshDatabase` only migrates when needed, significantly speeding up large suites. +`RefreshDatabase` migrates once per process and wraps each test in a rolled-back transaction. `LazilyRefreshDatabase` skips even that first migration if the schema is already up to date. ## Use Model Assertions Over Raw Database Assertions diff --git a/.agents/skills/wayfinder-development/SKILL.md b/.agents/skills/wayfinder-development/SKILL.md index fc352e1..0b30645 100644 --- a/.agents/skills/wayfinder-development/SKILL.md +++ b/.agents/skills/wayfinder-development/SKILL.md @@ -1,6 +1,6 @@ --- name: wayfinder-development -description: "Activates whenever referencing backend routes in frontend components. Use when importing from @/actions or @/routes, calling Laravel routes from TypeScript, or working with Wayfinder route functions." +description: "Use this skill for Laravel Wayfinder which auto-generates typed functions for Laravel controllers and routes. ALWAYS use this skill when frontend code needs to call backend routes or controller actions. Trigger when: connecting any React/Vue/Svelte/Inertia frontend to Laravel controllers, routes, building end-to-end features with both frontend and backend, wiring up forms or links to backend endpoints, fixing route-related TypeScript errors, importing from @/actions or @/routes, or running wayfinder:generate. Use Wayfinder route functions instead of hardcoded URLs. Covers: wayfinder() vite plugin, .url()/.get()/.post()/.form(), query params, route model binding, tree-shaking. Do not use for backend-only task" license: MIT metadata: author: laravel diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 619061f..77da3f5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -36,9 +36,6 @@ jobs: - name: Run Pint run: composer lint - - name: Format Frontend - run: npm run format - - name: Lint Frontend run: npm run lint diff --git a/AGENTS.md b/AGENTS.md index e492855..db0b49c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,12 +32,11 @@ This application is a Laravel application and its main Laravel ecosystems packag This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck. +- `fortify-development` — ACTIVATE when the user works on authentication in Laravel. This includes login, registration, password reset, email verification, two-factor authentication (2FA/TOTP/QR codes/recovery codes), profile updates, password confirmation, or any auth-related routes and controllers. Activate when the user mentions Fortify, auth, authentication, login, register, signup, forgot password, verify email, 2FA, or references app/Actions/Fortify/, CreateNewUser, UpdateUserProfileInformation, FortifyServiceProvider, config/fortify.php, or auth guards. Fortify is the frontend-agnostic authentication backend for Laravel that registers all auth routes and controllers. Also activate when building SPA or headless authentication, customizing login redirects, overriding response contracts like LoginResponse, or configuring login throttling. Do NOT activate for Laravel Passport (OAuth2 API tokens), Socialite (OAuth social login), or non-auth Laravel features. - `laravel-best-practices` — Apply this skill whenever writing, reviewing, or refactoring Laravel PHP code. This includes creating or modifying controllers, models, migrations, form requests, policies, jobs, scheduled commands, service classes, and Eloquent queries. Triggers for N+1 and query performance issues, caching strategies, authorization and security patterns, validation, error handling, queue and job configuration, route definitions, and architectural decisions. Also use for Laravel code reviews and refactoring existing Laravel code to follow best practices. Covers any task involving Laravel backend PHP code patterns. -- `wayfinder-development` — Activates whenever referencing backend routes in frontend components. Use when importing from @/actions or @/routes, calling Laravel routes from TypeScript, or working with Wayfinder route functions. +- `wayfinder-development` — Use this skill for Laravel Wayfinder which auto-generates typed functions for Laravel controllers and routes. ALWAYS use this skill when frontend code needs to call backend routes or controller actions. Trigger when: connecting any React/Vue/Svelte/Inertia frontend to Laravel controllers, routes, building end-to-end features with both frontend and backend, wiring up forms or links to backend endpoints, fixing route-related TypeScript errors, importing from @/actions or @/routes, or running wayfinder:generate. Use Wayfinder route functions instead of hardcoded URLs. Covers: wayfinder() vite plugin, .url()/.get()/.post()/.form(), query params, route model binding, tree-shaking. Do not use for backend-only task - `inertia-vue-development` — Develops Inertia.js v2 Vue client-side applications. Activates when creating Vue pages, forms, or navigation; using ,