php artisan boost:add-skill tey/starter
Save this content to: AGENTS.md
# Starter
Guidance for contributors and coding agents working in this repository.
- Preserve the tenant, ownership, grant and stale-write regression tests.
- Focused checks explicitly opt into synthetic SQLite memory. Public requests must never seed fixtures, bypass auth, or use default credentials or keys.
- No external services, production data or provider side effects in tests.
- Fail closed, and restore tenant, permission and settings context in `finally` blocks.
- Keep capabilities, entitlements, membership and admission distinct.
- State missing Inertia, protocol, lifecycle or platform coverage explicitly rather than implying it.
- Run local tests with `composer test` (`pest --tia`, impacted tests only) or `vendor/bin/pest --tia …`; `--no-tia` only for browser specs. A bare local `pest` refuses to run; a non-empty CI, or a set PARATEST or PEST_TIA, bypasses the guard (an empty `CI=` does not). TIA diffs against `main`, or `TIA_DEFAULT_BRANCH` when set.
- Never edit a file a kit feature owns (`database/migrations/kit/**`, the kit's pages): CI checks them against `kit:add`.
## Gates
```sh
composer install --no-interaction
vendor/bin/pint --test
vendor/bin/pest
composer fixture:check
npm ci && npm run typecheck && npm run build && npm test
composer test:browser
sh bin/privacy-check.sh
```
The PHP suites run on SQLite memory. `TEST_DB_CONNECTION=pgsql` with `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME` and `DB_PASSWORD` set for the run puts `pest` and `composer fixture:check` on PostgreSQL, as CI does; the database is wiped. See `docs/testing.md` for a local Herd example.
`vendor/bin/pest --parallel` also works: on PostgreSQL each worker gets its own disposable database, `{DB_DATABASE}_w{token}`, created on first use, and its own storage root. `vendor/bin/pest`, `vendor/bin/phpunit` and `vendor/bin/pest --tia` read the root `phpunit.xml`.
## Conventions
- Styling is Tailwind utilities only, with tokens in `@theme`. No hand-written CSS rules, `<style>` blocks or appearance `style=` bindings; a genuine exception goes in the guard's allow-list with a reason.
- Commit as Jasper Tey <[email protected]>. Commits and PRs carry no attribution lines or trailers.
- Work on a branch and open a draft pull request. Never push to `main`, merge, tag or release.
- Every PR names its issue with `Closes #N`.
- Never commit private project names, internal tooling references or local paths. `bin/privacy-check.sh` runs in CI.
- No hand-written CSS rules: styling is Tailwind utilities on the markup. The one documented exception is `resources/css/kit.css`, the kit's own base stylesheet (the pointer cursor on clickable things and not-allowed on disabled ones, with a `kit-cursor-native` opt-out on `<html>`); an app does not add rules beside it.
<!-- tey/standards:patterns:start -->
## Patterns (generated by `php artisan standards:install`; do not edit)
Before implementing a page, action, MCP tool, setting, notification, job or integration, call bakery.find (offline: `php artisan standards:pattern "<intent or path>"`).
**Traverse before you build.** Answer the questions until you reach a leaf (or a feature plan), then scaffold with its make command. Stamp hand-written new files with `// standards-pattern: <leaf-id>`, or `gap` when no leaf fits. Never hand-roll a kit primitive. Name the pattern in the commit. `php artisan standards:check --changed` must pass before commit and push.
| Leaf | Status | Building… | Make | Checks |
| ---- | ------ | --------- | ---- | ------ |
| `ui-page.index` | ready | A CRUDDY index controller passes a typed ViewModel to resources/js/pages/<Area>/Index/Page.vue. | `php artisan standards:make page --flavor=index <Name>` | 20, 21, descriptors, js-imports |
| `ui-page.show` | ready | A show method passes one record's ViewModel to resources/js/pages/<Area>/Show/Page.vue; dates render relative to the viewer's timezone, never by hand. | `php artisan standards:make page --flavor=show <Name>` | 20, 21, descriptors, js-imports |
| `ui-page.form` | ready | create/store or edit/update methods, a FormRequest, an Action for the write and a toast on the redirect. | `php artisan standards:make page --flavor=form <Name>` | 20, 21, toast, actions, descriptors |
| `ui-page.add-button` | ready | Add, New and Create buttons are kit Buttons carrying the Plus icon. | `php artisan standards:make page --flavor=index <Name>` | ui plus |
| `ui-page.external-link` | ready | target=_blank links carry rel=noopener and the external-link icon. | `php artisan standards:make page --flavor=show <Name>` | ui new-tab |
| `ui-page.action-menu` | ready | Action menus run actions only. Navigation goes in links or tabs, never in an action menu, and nothing hand-rolls a kit primitive. | `php artisan standards:make page --flavor=show <Name>` | descriptors, js-imports |
| `ui-page.upload` | ready | File inputs only come from the kit dropzone component inside a form page. | `php artisan standards:make page --flavor=form <Name>` | ui dropzone |
| `ui-page.data-table` | gap | Sorting, filters, row actions with confirmation and reordering through the kit DataTable and toolbar, not a hand-rolled table. | — | js-imports |
| `ui-page.crud-resource` | gap | One generator for a record type's controller, request, ViewModel and index, form and show pages. | — | 20, 21 |
| `ui-page.workbench` | gap | A bespoke workflow page beyond CRUD still gets route, controller, typed ViewModel, page and test from one skeleton. | — | 20, 21 |
| `ui-page.dashboard-widget` | gap | Register a widget with its order and placement; never fork the kit dashboard page. | — | |
| `ui-page.auth-override` | gap | Extend kit auth screens through their slots and link configuration instead of an app-owned copy. | — | |
| `ui-page.public-legal` | gap | A sessionless public shell with config-driven product and legal links and markdown content. | — | |
| `ui-page.exhibit` | gap | A per-visitor demo exhibit with capacity limits and a prune command. | — | |
| `ui-page.signed-guest` | gap | A token-bound page for an unauthenticated recipient, served no-store, recording their decisions. | — | |
| `ui-page.error` | gap | A branded Inertia error page for 4xx and 5xx responses. | — | |
| `ui-page.filament` | gap | Use native Filament fields and relations by default; a custom field states its reason. | — | |
| `ui-page.kiosk` | gap | A kiosk-style public entry flow. | — | |
| `ui-page.activity-feed` | draft | Publish activity through the activity-stream service's verb guide and authoriser, and render its feed; never a hand-built activity table. | — | |
| `data-actions.action` | ready | One principal-first Action owns the write. It checks lock_version against stale writes, which also stops concurrent edits, and is shared by controllers, jobs and MCP tools. | `php artisan standards:make action --flavor=plain <Name>` | actions, 21 |
| `data-actions.tenant-model` | gap | A model with composite tenancy foreign keys, a lock_version stale-write guard and its policy. | — | |
| `data-actions.constraints` | gap | Backed enums and database check constraints for a domain model's states. | — | |
| `data-actions.ledger` | gap | An append-only ledger or receipts table guarded by database triggers and idempotency keys. | — | |
| `data-actions.document` | gap | A frozen document rendered to PDF through one renderer contract. | — (Bakery: `php artisan mod:butter/pdf-reports <Name>`) | |
| `data-actions.api-op` | gap | An OpenAPI entry, controller and MCP mirror for each public, versioned API operation. | — | |
| `data-actions.file-import` | gap | A queued spreadsheet, CSV or XML import with an upload workspace and processing history. | — | |
| `data-actions.report` | gap | Monthly aggregation and performance report classes with export. | — | |
| `data-actions.media-library` | draft | Per-record files and photos through spatie/laravel-medialibrary as the kit wires it. Follow the kit's avatar upload (SessionUi Avatars and StoreAvatar) and the medialibrary Boost skill. There is no scaffold yet. | — | 19, ui dropzone |
| `mcp.read` | ready | A kit CapabilityTool with a | `php artisan standards:make capability-tool --flavor=read <Name>` | 55 |
| `mcp.write` | ready | A CapabilityTool whose call() runs the same Action as the UI (destructive in its | `php artisan standards:make capability-tool --flavor=write <Name>` (Bakery: `php artisan mod:butter/mcp-parity <Name>`) | 55, actions |
| `mcp.queued` | gap | Slow or external work answers with a run token the assistant reads back. | — | 55 |
| `mcp.agent-docs` | gap | A capability manifest, generated llms.txt and an architecture test that keeps routes and MCP tools in parity. | — | 55 |
| `mcp.module-placement` | gap | Where MCP tools, resources and servers live inside a module. | — | |
| `settings.form` | ready | An edit/update controller under the settings route group renders resources/js/pages/settings/<Section>/Page.vue in the settings layout. | `php artisan standards:make settings-section --flavor=form <Name>` | 20, 21, toast, actions |
| `settings.toggles` | ready | Each toggle saves on change through one update endpoint and confirms with a toast. | `php artisan standards:make settings-section --flavor=toggles <Name>` | 20, 21, toast |
| `settings.user-state` | gap | Timezone, avatar and remembered UI state stored per person, outside a settings page. | — | |
| `settings.workspace` | ready | A settings section per workspace group from the settings-section generator, guarded by a permission (rule 32) and audited. | `php artisan standards:make settings-section --flavor=form <Name>` | 32 |
| `settings.house` | gap | A typed settings class with its settings migration for a single-tenant installation. | — | |
| `settings.config` | gap | A config key read from env, with a boot-time guard that fails closed when it is missing. | — | |
| `settings.capability-toggle` | gap | A per-workspace capability in the capabilities registry, kept distinct from entitlements; name the key and its gate points. | — | 32 |
| `settings.not-a-setting` | gap | Not a setting. Build it under access-tenancy as an entitlement or a role permission. | — | |
| `settings.record-attribute` | gap | Not a setting. Add a field or option to the domain record through data-actions. | — | |
| `mail-notifications.notification` | ready | A Notification with the configured mail composer; never a Mailable or the Mail facade. | `php artisan standards:make notification --flavor=plain <Name>` | mail |
| `mail-notifications.document-delivery` | gap | Send a document or signed link with an attachment threshold, a send record and a test-send variant. | — | |
| `mail-notifications.digest` | gap | Many users in a digest. | — | |
| `mail-notifications.custom-channel` | gap | A custom notification channel for SMS or an agent, with held and quiet-hours gating. | — | |
| `mail-notifications.branded-layout` | ready | Every notification renders through the configured mail composer's branded layout, with the header, footer and preheader set once; no per-mail Blade copies. | `php artisan standards:make notification --flavor=plain <Name>` | mail |
| `background.job` | ready | A queued job that calls the Action doing the work, so the logic is shared and the job stays thin. | `php artisan standards:make job --flavor=plain <Name>` | actions |
| `background.schedule` | gap | A scheduled job registered with an overlap guard and a retention sweep. The evidence is weak; few cited changes clearly fit. | — | |
| `background.command` | gap | An artisan command that is idempotent, supports --dry-run and refuses to run in production unless forced. | — | |
| `access-tenancy.signed-link` | gap | A hashed, expiring, revocable token link that records who used it. | — | |
| `access-tenancy.capability` | ready | A policy authorizes the record; the operation asks it, never a role check in code. | `php artisan standards:make policy --flavor=plain <Name>` | 32 |
| `access-tenancy.entitlements` | gap | Access granted by purchase or plan, kept distinct from capabilities and roles. | — | |
| `access-tenancy.membership` | gap | Workspace membership. | — | |
| `access-tenancy.auth-hardening` | gap | Login rate limiters and local-only sign-in defaults. | — | |
| `access-tenancy.host-middleware` | gap | Host-split routing and security-headers middleware. | — | |
| `access-tenancy.permissions` | ready | Register the permission with Registry::permission() and run php artisan kit:permissions:sync; check it through a policy, never a role name in code. | `php artisan standards:make policy --flavor=plain <Name>` | 32, descriptors |
| `integrations.monitoring` | gap | A token-gated Nightwatch install with request sampling at 0.1, off in tests, checked by preflight. Queue dashboards and debug tooling belong here too. | — | |
| `integrations.billing` | gap | Guarded Stripe Checkout, a signed webhook, a shared-account guard and the catalogue as code. | — | |
| `integrations.ai` | gap | An AI agent call with metering, a timeout and a per-workspace key. | — | |
| `integrations.outbound` | gap | Use the official SDK behind an app contract with a fake for tests; verify inbound webhooks by signature. | — | |
| `integrations.webhooks` | gap | Receive signed webhooks without an official SDK. | — | |
| `integrations.accounting` | gap | OAuth to an accounting package, invoice generation and entity mapping sync. | — | |
| `integrations.search` | gap | A full-text search index with rebuild and sync. | — | |
<!-- tey/standards:patterns:end -->
===
<laravel-boost-guidelines>
=== .ai/starter rules ===
# Starter
- Run `php artisan standards:check` before pushing; it must exit 0. Find the pattern for new work with `php artisan standards:pattern "<intent>"` (offline `bakery.find`).
- Kit-owned files under `resources/js/components/Kit` and the kit's server classes are not edited here; fix them in the kit.
- Pages render server-provided descriptors and capability flags; they never check roles or permissions themselves.
- Tests run with `--tia` locally (`--no-tia` for browser specs); see `tests/Pest.php`.
=== 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](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.
=== 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.
=== inertiaui/modal/core rules ===
## Inertia Modal
- `inertiaui/modal` opens any Laravel route in a Modal or Slideover without changing existing routes or controllers. Supports React and Vue.
- Always activate the `inertia-modal-development` skill when working with modal routes, `ModalLink` components, `Modal` components, the `Inertia::modal()` method, modal configuration, or any code that uses `@inertiaui/modal-vue` or `@inertiaui/modal-react`.
=== spatie/laravel-medialibrary/core rules ===
## Media Library
- `spatie/laravel-medialibrary` associates files with Eloquent models, with support for collections, conversions, and responsive images.
- Always activate the `medialibrary-development` skill when working with media uploads, conversions, collections, responsive images, or any code that uses the `HasMedia` interface or `InteractsWithMedia` trait.
=== tey/mod/core rules ===
## Mod
This application uses `tey/mod` for file placement and discovery. Before placing a file, run `php artisan mod:list --json` or `mod-inventory` and read the active layout, file types, templates and scaffolds. Prefer the matching `mod:*` generator with explicit placement. Preview with `--dry-run --json`; inspect files, warnings and `would_write` before writing. Load `mod-development` for options and the package API.
Call `mod-survey` without arguments for structure, references and recurring sets. Call `mod-fit` with an uninstalled layout or recipe manifest for ownership, conflicts, variances, outside touches and code configuration. Both read only `base_path()` (plus local Git metadata for survey), classify vendor references, exclude secrets/runtime data and symlinks, and make no writes or network calls. The skill documents inputs, privacy and limits. Load `mod-learning-patterns` to learn this app's conventions and propose a layout and scaffold recipes for human review, checking them with `mod-fit`. Never adopt proposals automatically.
</laravel-boost-guidelines>
Guidance for contributors and coding agents working in this repository.
finally blocks.composer test (pest --tia, impacted tests only) or vendor/bin/pest --tia …; --no-tia only for browser specs. A bare local pest refuses to run; a non-empty CI, or a set PARATEST or PEST_TIA, bypasses the guard (an empty CI= does not). TIA diffs against main, or TIA_DEFAULT_BRANCH when set.database/migrations/kit/**, the kit's pages): CI checks them against kit:add.composer install --no-interaction
vendor/bin/pint --test
vendor/bin/pest
composer fixture:check
npm ci && npm run typecheck && npm run build && npm test
composer test:browser
sh bin/privacy-check.sh
The PHP suites run on SQLite memory. TEST_DB_CONNECTION=pgsql with DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME and DB_PASSWORD set for the run puts pest and composer fixture:check on PostgreSQL, as CI does; the database is wiped. See docs/testing.md for a local Herd example.
vendor/bin/pest --parallel also works: on PostgreSQL each worker gets its own disposable database, {DB_DATABASE}_w{token}, created on first use, and its own storage root. vendor/bin/pest, vendor/bin/phpunit and vendor/bin/pest --tia read the root phpunit.xml.
@theme. No hand-written CSS rules, <style> blocks or appearance style= bindings; a genuine exception goes in the guard's allow-list with a reason.main, merge, tag or release.Closes #N.bin/privacy-check.sh runs in CI.resources/css/kit.css, the kit's own base stylesheet (the pointer cursor on clickable things and not-allowed on disabled ones, with a kit-cursor-native opt-out on <html>); an app does not add rules beside it.php artisan standards:install; do not edit)Before implementing a page, action, MCP tool, setting, notification, job or integration, call bakery.find (offline: php artisan standards:pattern "<intent or path>").
Traverse before you build. Answer the questions until you reach a leaf (or a feature plan), then scaffold with its make command. Stamp hand-written new files with // standards-pattern: <leaf-id>, or gap when no leaf fits. Never hand-roll a kit primitive. Name the pattern in the commit. php artisan standards:check --changed must pass before commit and push.
| Leaf | Status | Building… | Make | Checks |
|---|---|---|---|---|
ui-page.index |
ready | A CRUDDY index controller passes a typed ViewModel to resources/js/pages//Index/Page.vue. | php artisan standards:make page --flavor=index <Name> |
20, 21, descriptors, js-imports |
ui-page.show |
ready | A show method passes one record's ViewModel to resources/js/pages//Show/Page.vue; dates render relative to the viewer's timezone, never by hand. | php artisan standards:make page --flavor=show <Name> |
20, 21, descriptors, js-imports |
ui-page.form |
ready | create/store or edit/update methods, a FormRequest, an Action for the write and a toast on the redirect. | php artisan standards:make page --flavor=form <Name> |
20, 21, toast, actions, descriptors |
ui-page.add-button |
ready | Add, New and Create buttons are kit Buttons carrying the Plus icon. | php artisan standards:make page --flavor=index <Name> |
ui plus |
ui-page.external-link |
ready | target=_blank links carry rel=noopener and the external-link icon. | php artisan standards:make page --flavor=show <Name> |
ui new-tab |
ui-page.action-menu |
ready | Action menus run actions only. Navigation goes in links or tabs, never in an action menu, and nothing hand-rolls a kit primitive. | php artisan standards:make page --flavor=show <Name> |
descriptors, js-imports |
ui-page.upload |
ready | File inputs only come from the kit dropzone component inside a form page. | php artisan standards:make page --flavor=form <Name> |
ui dropzone |
ui-page.data-table |
gap | Sorting, filters, row actions with confirmation and reordering through the kit DataTable and toolbar, not a hand-rolled table. | — | js-imports |
ui-page.crud-resource |
gap | One generator for a record type's controller, request, ViewModel and index, form and show pages. | — | 20, 21 |
ui-page.workbench |
gap | A bespoke workflow page beyond CRUD still gets route, controller, typed ViewModel, page and test from one skeleton. | — | 20, 21 |
ui-page.dashboard-widget |
gap | Register a widget with its order and placement; never fork the kit dashboard page. | — | |
ui-page.auth-override |
gap | Extend kit auth screens through their slots and link configuration instead of an app-owned copy. | — | |
ui-page.public-legal |
gap | A sessionless public shell with config-driven product and legal links and markdown content. | — | |
ui-page.exhibit |
gap | A per-visitor demo exhibit with capacity limits and a prune command. | — | |
ui-page.signed-guest |
gap | A token-bound page for an unauthenticated recipient, served no-store, recording their decisions. | — | |
ui-page.error |
gap | A branded Inertia error page for 4xx and 5xx responses. | — | |
ui-page.filament |
gap | Use native Filament fields and relations by default; a custom field states its reason. | — | |
ui-page.kiosk |
gap | A kiosk-style public entry flow. | — | |
ui-page.activity-feed |
draft | Publish activity through the activity-stream service's verb guide and authoriser, and render its feed; never a hand-built activity table. | — | |
data-actions.action |
ready | One principal-first Action owns the write. It checks lock_version against stale writes, which also stops concurrent edits, and is shared by controllers, jobs and MCP tools. | php artisan standards:make action --flavor=plain <Name> |
actions, 21 |
data-actions.tenant-model |
gap | A model with composite tenancy foreign keys, a lock_version stale-write guard and its policy. | — | |
data-actions.constraints |
gap | Backed enums and database check constraints for a domain model's states. | — | |
data-actions.ledger |
gap | An append-only ledger or receipts table guarded by database triggers and idempotency keys. | — | |
data-actions.document |
gap | A frozen document rendered to PDF through one renderer contract. | — (Bakery: php artisan mod:butter/pdf-reports <Name>) |
|
data-actions.api-op |
gap | An OpenAPI entry, controller and MCP mirror for each public, versioned API operation. | — | |
data-actions.file-import |
gap | A queued spreadsheet, CSV or XML import with an upload workspace and processing history. | — | |
data-actions.report |
gap | Monthly aggregation and performance report classes with export. | — | |
data-actions.media-library |
draft | Per-record files and photos through spatie/laravel-medialibrary as the kit wires it. Follow the kit's avatar upload (SessionUi Avatars and StoreAvatar) and the medialibrary Boost skill. There is no scaffold yet. | — | 19, ui dropzone |
mcp.read |
ready | A kit CapabilityTool with a | php artisan standards:make capability-tool --flavor=read <Name> |
55 |
mcp.write |
ready | A CapabilityTool whose call() runs the same Action as the UI (destructive in its | php artisan standards:make capability-tool --flavor=write <Name> (Bakery: php artisan mod:butter/mcp-parity <Name>) |
55, actions |
mcp.queued |
gap | Slow or external work answers with a run token the assistant reads back. | — | 55 |
mcp.agent-docs |
gap | A capability manifest, generated llms.txt and an architecture test that keeps routes and MCP tools in parity. | — | 55 |
mcp.module-placement |
gap | Where MCP tools, resources and servers live inside a module. | — | |
settings.form |
ready | An edit/update controller under the settings route group renders resources/js/pages/settings//Page.vue in the settings layout. | php artisan standards:make settings-section --flavor=form <Name> |
20, 21, toast, actions |
settings.toggles |
ready | Each toggle saves on change through one update endpoint and confirms with a toast. | php artisan standards:make settings-section --flavor=toggles <Name> |
20, 21, toast |
settings.user-state |
gap | Timezone, avatar and remembered UI state stored per person, outside a settings page. | — | |
settings.workspace |
ready | A settings section per workspace group from the settings-section generator, guarded by a permission (rule 32) and audited. | php artisan standards:make settings-section --flavor=form <Name> |
32 |
settings.house |
gap | A typed settings class with its settings migration for a single-tenant installation. | — | |
settings.config |
gap | A config key read from env, with a boot-time guard that fails closed when it is missing. | — | |
settings.capability-toggle |
gap | A per-workspace capability in the capabilities registry, kept distinct from entitlements; name the key and its gate points. | — | 32 |
settings.not-a-setting |
gap | Not a setting. Build it under access-tenancy as an entitlement or a role permission. | — | |
settings.record-attribute |
gap | Not a setting. Add a field or option to the domain record through data-actions. | — | |
mail-notifications.notification |
ready | A Notification with the configured mail composer; never a Mailable or the Mail facade. | php artisan standards:make notification --flavor=plain <Name> |
|
mail-notifications.document-delivery |
gap | Send a document or signed link with an attachment threshold, a send record and a test-send variant. | — | |
mail-notifications.digest |
gap | Many users in a digest. | — | |
mail-notifications.custom-channel |
gap | A custom notification channel for SMS or an agent, with held and quiet-hours gating. | — | |
mail-notifications.branded-layout |
ready | Every notification renders through the configured mail composer's branded layout, with the header, footer and preheader set once; no per-mail Blade copies. | php artisan standards:make notification --flavor=plain <Name> |
|
background.job |
ready | A queued job that calls the Action doing the work, so the logic is shared and the job stays thin. | php artisan standards:make job --flavor=plain <Name> |
actions |
background.schedule |
gap | A scheduled job registered with an overlap guard and a retention sweep. The evidence is weak; few cited changes clearly fit. | — | |
background.command |
gap | An artisan command that is idempotent, supports --dry-run and refuses to run in production unless forced. | — | |
access-tenancy.signed-link |
gap | A hashed, expiring, revocable token link that records who used it. | — | |
access-tenancy.capability |
ready | A policy authorizes the record; the operation asks it, never a role check in code. | php artisan standards:make policy --flavor=plain <Name> |
32 |
access-tenancy.entitlements |
gap | Access granted by purchase or plan, kept distinct from capabilities and roles. | — | |
access-tenancy.membership |
gap | Workspace membership. | — | |
access-tenancy.auth-hardening |
gap | Login rate limiters and local-only sign-in defaults. | — | |
access-tenancy.host-middleware |
gap | Host-split routing and security-headers middleware. | — | |
access-tenancy.permissions |
ready | Register the permission with Registry::permission() and run php artisan kit:permissions:sync; check it through a policy, never a role name in code. | php artisan standards:make policy --flavor=plain <Name> |
32, descriptors |
integrations.monitoring |
gap | A token-gated Nightwatch install with request sampling at 0.1, off in tests, checked by preflight. Queue dashboards and debug tooling belong here too. | — | |
integrations.billing |
gap | Guarded Stripe Checkout, a signed webhook, a shared-account guard and the catalogue as code. | — | |
integrations.ai |
gap | An AI agent call with metering, a timeout and a per-workspace key. | — | |
integrations.outbound |
gap | Use the official SDK behind an app contract with a fake for tests; verify inbound webhooks by signature. | — | |
integrations.webhooks |
gap | Receive signed webhooks without an official SDK. | — | |
integrations.accounting |
gap | OAuth to an accounting package, invoice generation and entity mapping sync. | — | |
integrations.search |
gap | A full-text search index with rebuild and sync. | — |
===
php artisan standards:check before pushing; it must exit 0. Find the pattern for new work with php artisan standards:pattern "<intent>" (offline bakery.find).resources/js/components/Kit and the kit's server classes are not edited here; fix them in the kit.--tia locally (--no-tia for browser specs); see tests/Pest.php.=== foundation rules ===
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:
composer show --direct to list direct dependencies with versions, or composer show <vendor/package> for a single package.package.json for the installed versions.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.
isRegisteredForDiscounts, not discount().npm run build or ask the user to run npm run dev or composer run dev.=== boost rules ===
database-query to run read-only queries against the database instead of writing raw SQL in tinker.database-schema to inspect table structure before writing migrations or models.get-absolute-url to resolve the correct scheme, domain, and port for project URLs. Always use this before sharing a URL with the user.browser-logs to read browser logs, errors, and exceptions. Only recent logs are useful, ignore old entries.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.packages array to scope results when you know which packages are relevant.['rate limiting', 'routing rate limiting', 'routing']. Expect the most relevant results first.test resource table, not filament 4 test resource table.rate limit matches both "rate" AND "limit"."quoted phrases" for exact position matching: "infinite scroll" requires adjacent words in order.middleware "rate limit".queries=["authentication", "middleware"]..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.php artisan route:list). Use php artisan list to discover available commands and php artisan [command] --help to check parameters.php artisan route:list. Filter with: --method=GET, --name=users, --path=api, --except-vendor, --only-vendor.php artisan config:show app.name, php artisan config:show database.default. Or read config files directly from the config/ directory.php artisan tinker --execute 'Your::code();'
php artisan tinker --execute 'User::where("active", true)->count();'=== php rules ===
public function __construct(public GitHub $github) { }. Do not leave empty zero-parameter __construct() methods unless the constructor is private.function isAccessible(User $user, ?string $path = null): boolFavoritePerson, BestLake, Monthly.=== deployments rules ===
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 ===
resources/js/pages (unless specified in vite.config.js). Use Inertia::render() for server-side routing instead of Blade views.search-docs tool for version-specific Inertia documentation and updated code examples.inertia-vue-development when working with Inertia Vue client-side patterns.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.Inertia::lazy() / LazyProp has been removed. Use Inertia::optional() instead.Inertia::optional(), Inertia::defer(), Inertia::merge()) work inside nested arrays with dot-notation paths.@inertiajs/vite - no separate Node.js server needed during development.invalid is now httpException, exception is now networkError.router.cancel() replaced by router.cancelAll().future configuration namespace has been removed - all v2 future options are now always enabled.=== laravel/core rules ===
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.php artisan make:class.--no-interaction to all Artisan commands to ensure they work without user input. You should also pass the correct --options to ensure correct behavior.php artisan make:model --help to check the available options.route() function.$this->faker->word() or fake()->randomDigit(). Follow existing conventions whether to use $this->faker or fake().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.=== pint/core rules ===
vendor/bin/pint --dirty --format agent before finalizing changes to ensure your code matches the project's expected style.vendor/bin/pint --test --format agent, simply run vendor/bin/pint --format agent to fix any formatting issues.=== pest/core rules ===
php artisan make:test --pest {name}.{name}. Use SomeFeatureTest, not Feature/SomeFeatureTest.testing-best-practices skill for guidance on coverage, naming, structure, dependency isolation, and review.--filter=testName to php artisan test --compact.vendor/bin/pest to call the test runner directly. It accepts the same file path and --filter=testName arguments.php artisan test --compact.=== inertia-vue/core rules ===
Vue components must have a single root element.
inertia-vue-development when working with Inertia Vue client-side patterns.=== inertiaui/modal/core rules ===
inertiaui/modal opens any Laravel route in a Modal or Slideover without changing existing routes or controllers. Supports React and Vue.inertia-modal-development skill when working with modal routes, ModalLink components, Modal components, the Inertia::modal() method, modal configuration, or any code that uses @inertiaui/modal-vue or @inertiaui/modal-react.=== spatie/laravel-medialibrary/core rules ===
spatie/laravel-medialibrary associates files with Eloquent models, with support for collections, conversions, and responsive images.medialibrary-development skill when working with media uploads, conversions, collections, responsive images, or any code that uses the HasMedia interface or InteractsWithMedia trait.=== tey/mod/core rules ===
This application uses tey/mod for file placement and discovery. Before placing a file, run php artisan mod:list --json or mod-inventory and read the active layout, file types, templates and scaffolds. Prefer the matching mod:* generator with explicit placement. Preview with --dry-run --json; inspect files, warnings and would_write before writing. Load mod-development for options and the package API.
Call mod-survey without arguments for structure, references and recurring sets. Call mod-fit with an uninstalled layout or recipe manifest for ownership, conflicts, variances, outside touches and code configuration. Both read only base_path() (plus local Git metadata for survey), classify vendor references, exclude secrets/runtime data and symlinks, and make no writes or network calls. The skill documents inputs, privacy and limits. Load mod-learning-patterns to learn this app's conventions and propose a layout and scaffold recipes for human review, checking them with mod-fit. Never adopt proposals automatically.
How can I help you explore Laravel packages today?