Create, modify, review, or test the Vendra User package in packages/vendra-user. Use for User, authentication, tenant membership, media and optional tags, user actions/services/rules, migrations, factories, seeders, policies, Filament resources, configuration, translations, and package wiring. OAuth social login belongs to the separate vendra-socialite add-on.
php artisan boost:add-skill misaf/vendra
Save this content to: .claude/skills/vendra-user-development/SKILL.md
# Vendra User
## Workflow
- Inspect `composer.json`, sibling files, and existing tests before changing the package.
- Use Laravel Boost `application-info` and `search-docs` before code changes.
- Apply `laravel-best-practices` to Laravel PHP and `pest-testing` whenever tests change.
- Apply `tailwindcss-development` only when changing Blade markup or Tailwind classes.
- Keep changes inside this package's boundary and preserve its public contracts.
- Add or update focused Pest coverage, then run `php artisan test --compact --testsuite=vendra-user` and `composer stan`.
## Translatable Persistence
- Making a persisted model field translatable is an explicit domain choice unless this package already requires it.
- Every field listed in a model's `$translatable` array must definitely use a JSON database column. Keep its model traits/casts, factories, validation, Filament locale UI, API serialization, and tests translation-aware.
- A field not listed in `$translatable` must use the appropriate scalar database type and must not use Spatie Translatable, translatable slug traits, locale switchers, translated callbacks, or translation-shaped array data.
## Vendra Transitive API Policy
- Treat a Vendra dependency intentionally exposed through the public API of a directly required Vendra platform package as part of the supported public contract of that package.
- Do not add a redundant direct Composer requirement solely because source code imports a type from that exposed dependency.
- Apply this only to Vendra platform packages listed under `require`; never extend it to `require-dev`, `suggest`, incidental implementation dependencies, or third-party packages. Removing or replacing an exposed dependency is a breaking change; keep `self.version` alignment across the Vendra package graph.
- Register every table whose migration calls `TenantSchema::addTenantColumn()` with `TenantTableRegistry` in this package's service provider, preserving configured table names and connections, so `vendra-tenant:enable {tenant}` can retrofit schemas migrated before tenancy was enabled.
## Module Boundary
Treat `packages/vendra-user` as the source of user domain behavior and Filament admin UI.
- Use namespace `Misaf\VendraUser`.
- Keep domain models, factories, seeders, policies, observers, console commands, Filament classes, config, migrations, translations, and tests inside this module.
- Do not place user domain code in the host app unless the host app is only integrating the module.
- Keep cross-module dependencies explicit in `composer.json`; do not introduce a dependency without approval.
- Tag-consuming models must use `Misaf\VendraSupport\Capabilities\HasOptionalTags` as the single source of their `tags()` relationship and pivot metadata. Keep the package tag-agnostic: define a stable package-owned tag type, use `TagIntegration` for availability and UI integration, never import the concrete Vendra Tagger model/provider or define the relationship through Spatie `HasTags`, and list Tagger only under Composer `suggest`.
## Domain Model Standards
Follow the existing `User` model patterns for new user entities. Social login (the `SocialiteUser` model, `SocialiteRegistrar`, and filament-socialite wiring) lives in the separate `misaf/vendra-socialite` package; do not reintroduce it or a `dutchcodingcompany/filament-socialite` dependency here.
- Use `declare(strict_types=1)`, final classes, typed method signatures, and PHPDoc generics for relationships.
- Follow Laravel comment style: document with PHPDoc (array shapes, generics, `@see`) and reserve inline comments for genuinely complex logic. Match the surrounding file's density and do not add comments that restate the code.
- Prefer only the Laravel attributes already used by the affected sibling model; do not add model attributes merely because another package uses them.
- The `User` model owns Filament tenant membership (`HasTenants`, `teams()` / `tenants()`) but resolves the tenant model through support-layer `BelongsToTenant`. Never reference the concrete `Misaf\VendraTenant` provider.
- Administrator membership changes use the add/promote/demote/remove/account-enabled actions and `Support\TenantAdministratorGuard`, which locks the tenant boundary and protects the final enabled administrator. Email/password changes use the package actions so validation, hashing, and token rotation stay out of panels.
- Admin user resource deletion uses `DeleteUserAction`; role removal uses guarded demotion inside the page save transaction. Count enabled administrator role holders even without a membership pivot. Bulk deletion preserves one administrator and reports the refusal; blocked role edits roll back all changes.
- Two-factor authentication is Filament's authenticator-app provider: `User` implements `HasAppAuthentication` and `HasAppAuthenticationRecovery` through Filament's `InteractsWithAppAuthentication*` traits, which cast `app_authentication_secret` and `app_authentication_recovery_codes` encrypted and hide them. A panel opts in with `->multiFactorAuthentication(AppAuthentication::make()->recoverable())`; the admin panel registers no provider and never challenges. Removing a lost authenticator goes through `ResetUserAppAuthenticationAction`, never a hand-written column update; tests create a user who already has one with `UserFactory::withAppAuthentication()`.
- Validate credentials in forms and commands through `Support\UserRules` (`username()`, `email()`, `password()`, `unique($column, $tenantId, $ignoreUserId)`, `exists($column, $tenantId)`). Username length constants drive form inputs; `password()` delegates strength to the application `Password::default()` policy, and `Support\PasswordGenerator::generate()` reads that same policy so a generated credential always passes it — never hand-roll `Str::password()` for a user password. Keep required/optional, confirmation, and scoped uniqueness at the caller, never a hand-built `Rule::unique(User::class, …)` or `Rule::exists(User::class, …)`. Soft-deleted users release their values, matching the `users_active_username_unique` and `users_active_email_unique` indexes.
- Derive all tenant awareness from the support layer (`TenantAwareness`, `BelongsToTenant`) and let the trait handle ordinary model creation.
- Reuse only the traits and conventions present on the affected sibling model; do not infer translations, media, slugs, sorting, or soft deletes from another package.
## Filament Standards
Keep every resource that declares a `$cluster`, including its complete supporting tree, under `src/Filament/Clusters/Resources/` with the matching `Misaf\VendraUser\Filament\Clusters\Resources` namespace and plugin discovery path. Resources without a cluster belong under `src/Filament/Resources/`.
- Register module UI through the module `Plugin` and `ServiceProvider`; do not manually wire resources in unrelated panel providers.
- Keep resource classes thin. Delegate form schemas to `Schemas/*Form.php` and table configuration to `Tables/*Table.php`.
- Use Filament v5 namespaces: form fields from `Filament\Forms\Components`, layout from `Filament\Schemas\Components`, table columns from `Filament\Tables\Columns`, filters from `Filament\Tables\Filters`, actions from `Filament\Actions`, and icons from `Filament\Support\Icons\Heroicon`.
- Use this module's translation keys (`vendra-user::attributes`, `vendra-user::navigation`) for labels, breadcrumbs, and navigation.
- Keep `UserResource` in `CustomersCluster`, ungrouped and ordered through `NavigationPriority`. User-domain add-ons such as User Profile share the cluster with their own priority.
- Register `Filament\Pages\Auth\EditProfile` as every panel's profile page (`->profile(EditProfile::class)`): users have no `name`, and it changes the password through `UpdateUserPasswordAction`. Never use Filament's default profile page.
- Provide separate singular and plural resource labels in `en`, `de`, and `fa`: model labels use the singular key, while navigation and plural model labels use the plural key. Keep navigation labels at 24 characters or fewer.
- Prevent N+1 issues in tables and relation managers with eager loading, `withCount`, or computed state based on loaded relationships.
- Use public media visibility only when public access is actually required.
## Permissions And Navigation
Use policy enums and policies as the permission source.
- Add enum cases for every resource action the panel exposes.
- Keep policy method names aligned with Filament actions: `viewAny`, `view`, `create`, `update`, `delete`, `deleteAny`, `restore`, `restoreAny`, `forceDelete`, `forceDeleteAny`, `replicate`, and `reorder` as applicable.
- Update `PermissionPolicySeeder` when new permissions are introduced.
- Keep navigation labels and groups configurable through the module `Plugin` and `config/vendra-user.php`. Do not add a `tenant_aware` config value; tenant awareness derives from the bound `TenantResolver`.
## Data And Localization
Migrations, factories, seeders, and translation files are part of the contract.
- Use package migrations in `database/migrations`, with stubs only when the install flow expects publishing.
- Use factories under `database/factories` and seeders under `database/seeders`. Keep them tenant-safe: import no concrete tenant provider and set no `tenant_id` directly; let `BelongsToTenant` assign it from the current tenant so they work with tenancy on or off.
- Keep demo fixtures deterministic and tenant-safe.
- Update all supported locales together and keep translation keys sorted.
- Preserve translation key parity tests when adding labels or attributes.
## Testing And Verification
Prefer focused Pest tests in the module.
- Keep tests purposeful and prevent unnecessary ones: cover behavior, contracts, and edge cases — not framework internals or trivially typed code. Do not duplicate coverage a focused test already proves, and do not add throwaway verification scripts (or `tinker`) when a test fits.
- Keep architecture coverage proving the module does not use Vendra Tagger or Spatie Tags directly, and test the typed relationship through the Support resolver.
- Add or update unit tests for model contracts, policy permission coverage, resolver-derived tenant awareness, navigation/config behavior, and translation parity.
- Keep Pest architecture tests in `tests/ArchTest.php`: the `php`, `security`, and `laravel` presets, plus `arch()->expect('Misaf\VendraUser')->not->toUse('Misaf\VendraTenant')`.
- Add feature or Livewire tests when changing Filament behavior with meaningful user-visible effects.
- Run checks from the host app: `php artisan test --compact --testsuite=vendra-user` and `composer stan`.
- If PHP files changed, run Pint for the touched code: `vendor/bin/pint --dirty --format agent` from the host app.
- Demo seeders declare their factory dependencies in `FACTORIES`; the shared base uses bundled fixtures when any declared factory is unavailable, including standalone Composer installs. Keep factories in development autoloading.
composer.json, sibling files, and existing tests before changing the package.application-info and search-docs before code changes.laravel-best-practices to Laravel PHP and pest-testing whenever tests change.tailwindcss-development only when changing Blade markup or Tailwind classes.php artisan test --compact --testsuite=vendra-user and composer stan.$translatable array must definitely use a JSON database column. Keep its model traits/casts, factories, validation, Filament locale UI, API serialization, and tests translation-aware.$translatable must use the appropriate scalar database type and must not use Spatie Translatable, translatable slug traits, locale switchers, translated callbacks, or translation-shaped array data.Treat a Vendra dependency intentionally exposed through the public API of a directly required Vendra platform package as part of the supported public contract of that package.
Do not add a redundant direct Composer requirement solely because source code imports a type from that exposed dependency.
Apply this only to Vendra platform packages listed under require; never extend it to require-dev, suggest, incidental implementation dependencies, or third-party packages. Removing or replacing an exposed dependency is a breaking change; keep self.version alignment across the Vendra package graph.
Register every table whose migration calls TenantSchema::addTenantColumn() with TenantTableRegistry in this package's service provider, preserving configured table names and connections, so vendra-tenant:enable {tenant} can retrofit schemas migrated before tenancy was enabled.
Treat packages/vendra-user as the source of user domain behavior and Filament admin UI.
Misaf\VendraUser.composer.json; do not introduce a dependency without approval.Misaf\VendraSupport\Capabilities\HasOptionalTags as the single source of their tags() relationship and pivot metadata. Keep the package tag-agnostic: define a stable package-owned tag type, use TagIntegration for availability and UI integration, never import the concrete Vendra Tagger model/provider or define the relationship through Spatie HasTags, and list Tagger only under Composer suggest.Follow the existing User model patterns for new user entities. Social login (the SocialiteUser model, SocialiteRegistrar, and filament-socialite wiring) lives in the separate misaf/vendra-socialite package; do not reintroduce it or a dutchcodingcompany/filament-socialite dependency here.
declare(strict_types=1), final classes, typed method signatures, and PHPDoc generics for relationships.@see) and reserve inline comments for genuinely complex logic. Match the surrounding file's density and do not add comments that restate the code.User model owns Filament tenant membership (HasTenants, teams() / tenants()) but resolves the tenant model through support-layer BelongsToTenant. Never reference the concrete Misaf\VendraTenant provider.Support\TenantAdministratorGuard, which locks the tenant boundary and protects the final enabled administrator. Email/password changes use the package actions so validation, hashing, and token rotation stay out of panels.DeleteUserAction; role removal uses guarded demotion inside the page save transaction. Count enabled administrator role holders even without a membership pivot. Bulk deletion preserves one administrator and reports the refusal; blocked role edits roll back all changes.User implements HasAppAuthentication and HasAppAuthenticationRecovery through Filament's InteractsWithAppAuthentication* traits, which cast app_authentication_secret and app_authentication_recovery_codes encrypted and hide them. A panel opts in with ->multiFactorAuthentication(AppAuthentication::make()->recoverable()); the admin panel registers no provider and never challenges. Removing a lost authenticator goes through ResetUserAppAuthenticationAction, never a hand-written column update; tests create a user who already has one with UserFactory::withAppAuthentication().Support\UserRules (username(), email(), password(), unique($column, $tenantId, $ignoreUserId), exists($column, $tenantId)). Username length constants drive form inputs; password() delegates strength to the application Password::default() policy, and Support\PasswordGenerator::generate() reads that same policy so a generated credential always passes it — never hand-roll Str::password() for a user password. Keep required/optional, confirmation, and scoped uniqueness at the caller, never a hand-built Rule::unique(User::class, …) or Rule::exists(User::class, …). Soft-deleted users release their values, matching the users_active_username_unique and users_active_email_unique indexes.TenantAwareness, BelongsToTenant) and let the trait handle ordinary model creation.Keep every resource that declares a $cluster, including its complete supporting tree, under src/Filament/Clusters/Resources/ with the matching Misaf\VendraUser\Filament\Clusters\Resources namespace and plugin discovery path. Resources without a cluster belong under src/Filament/Resources/.
Plugin and ServiceProvider; do not manually wire resources in unrelated panel providers.Schemas/*Form.php and table configuration to Tables/*Table.php.Filament\Forms\Components, layout from Filament\Schemas\Components, table columns from Filament\Tables\Columns, filters from Filament\Tables\Filters, actions from Filament\Actions, and icons from Filament\Support\Icons\Heroicon.vendra-user::attributes, vendra-user::navigation) for labels, breadcrumbs, and navigation.UserResource in CustomersCluster, ungrouped and ordered through NavigationPriority. User-domain add-ons such as User Profile share the cluster with their own priority.Filament\Pages\Auth\EditProfile as every panel's profile page (->profile(EditProfile::class)): users have no name, and it changes the password through UpdateUserPasswordAction. Never use Filament's default profile page.en, de, and fa: model labels use the singular key, while navigation and plural model labels use the plural key. Keep navigation labels at 24 characters or fewer.withCount, or computed state based on loaded relationships.Use policy enums and policies as the permission source.
viewAny, view, create, update, delete, deleteAny, restore, restoreAny, forceDelete, forceDeleteAny, replicate, and reorder as applicable.PermissionPolicySeeder when new permissions are introduced.Plugin and config/vendra-user.php. Do not add a tenant_aware config value; tenant awareness derives from the bound TenantResolver.Migrations, factories, seeders, and translation files are part of the contract.
database/migrations, with stubs only when the install flow expects publishing.database/factories and seeders under database/seeders. Keep them tenant-safe: import no concrete tenant provider and set no tenant_id directly; let BelongsToTenant assign it from the current tenant so they work with tenancy on or off.Prefer focused Pest tests in the module.
Keep tests purposeful and prevent unnecessary ones: cover behavior, contracts, and edge cases — not framework internals or trivially typed code. Do not duplicate coverage a focused test already proves, and do not add throwaway verification scripts (or tinker) when a test fits.
Keep architecture coverage proving the module does not use Vendra Tagger or Spatie Tags directly, and test the typed relationship through the Support resolver.
Add or update unit tests for model contracts, policy permission coverage, resolver-derived tenant awareness, navigation/config behavior, and translation parity.
Keep Pest architecture tests in tests/ArchTest.php: the php, security, and laravel presets, plus arch()->expect('Misaf\VendraUser')->not->toUse('Misaf\VendraTenant').
Add feature or Livewire tests when changing Filament behavior with meaningful user-visible effects.
Run checks from the host app: php artisan test --compact --testsuite=vendra-user and composer stan.
If PHP files changed, run Pint for the touched code: vendor/bin/pint --dirty --format agent from the host app.
Demo seeders declare their factory dependencies in FACTORIES; the shared base uses bundled fixtures when any declared factory is unavailable, including standalone Composer installs. Keep factories in development autoloading.
How can I help you explore Laravel packages today?