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
tey/starter
claude-code
cursor
testing
tenancy
regression-tests
ci-gates
tailwind
privacy-check
git-workflow

Guidance for contributors and coding agents working in this repository.

Install
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>

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

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.

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//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
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. —

===

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

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo