diff --git a/.ai/guidelines/filament-v5-blueprint.md b/.ai/guidelines/filament-v5-blueprint.md new file mode 100644 index 0000000..a3a558a --- /dev/null +++ b/.ai/guidelines/filament-v5-blueprint.md @@ -0,0 +1,173 @@ +## Source of Truth +If any Filament behavior is uncertain, lookup the exact section in: +- docs/research/filament-v5-notes.md +and prefer that over guesses. + +# SECTION B — FILAMENT V5 BLUEPRINT (EXECUTABLE RULES) + +# Filament Blueprint (v5) + +## 1) Non-negotiables +- Filament v5 requires Livewire v4.0+. +- Laravel 11+: register panel providers in `bootstrap/providers.php` (never `bootstrap/app.php`). +- Global search hard rule: If a Resource should appear in Global Search, it must have an Edit or View page; otherwise it will return no results. +- Destructive actions must execute via `Action::make(...)->action(...)` and include `->requiresConfirmation()` (no exceptions). +- Prefer render hooks + CSS hook classes over publishing Filament internal views. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/actions/modals +- https://filamentphp.com/docs/5.x/advanced/render-hooks +- https://filamentphp.com/docs/5.x/styling/css-hooks + +## 2) Directory & naming conventions +- Default to Filament discovery conventions for Resources/Pages/Widgets unless you adopt modular architecture. +- Clusters: directory layout is recommended, not mandatory; functional behavior depends on `$cluster`. + +Sources: +- https://filamentphp.com/docs/5.x/navigation/clusters +- https://filamentphp.com/docs/5.x/advanced/modular-architecture + +## 3) Panel setup defaults +- Default to a single `/admin` panel unless multiple audiences/configs demand multiple panels. +- Verify provider registration (Laravel 11+: `bootstrap/providers.php`) when adding a panel. +- Use `path()` carefully; treat `path('')` as a high-risk change requiring route conflict review. +- Assets policy: + - Panel-only assets: register via panel config. + - Shared/plugin assets: register via `FilamentAsset::register()`. + - Deployment must include `php artisan filament:assets`. + +Sources: +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/advanced/assets + +## 4) Navigation & information architecture +- Use nav groups + sort order intentionally; apply conditional visibility for clarity, but enforce authorization separately. +- Use clusters to introduce hierarchy and sub-navigation when sidebar complexity grows. +- Treat cluster code structure as a recommendation (organizational benefit), not a required rule. +- User menu: + - Configure via `userMenuItems()` with Action objects. + - Never put destructive actions there without confirmation + authorization. + +Sources: +- https://filamentphp.com/docs/5.x/navigation/overview +- https://filamentphp.com/docs/5.x/navigation/clusters +- https://filamentphp.com/docs/5.x/navigation/user-menu + +## 5) Resource patterns +- Default to Resources for CRUD; use custom pages for non-CRUD tools/workflows. +- Global search: + - If a resource is intended for global search: ensure Edit/View page exists. + - Otherwise disable global search for that resource (don’t “expect it to work”). + - If global search renders relationship-backed details: eager-load via global search query override. + - For very large datasets: consider disabling term splitting (only when needed). + +Sources: +- https://filamentphp.com/docs/5.x/resources/overview +- https://filamentphp.com/docs/5.x/resources/global-search + +## 6) Page lifecycle & query rules +- Treat relationship-backed rendering in aggregate contexts (global search details, list summaries) as requiring eager loading. +- Prefer render hooks for layout injection; avoid publishing internal views. + +Sources: +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/advanced/render-hooks + +## 7) Infolists vs RelationManagers (decision tree) +- Interactive CRUD / attach / detach under owner record → RelationManager. +- Pick existing related record(s) inside owner form → Select / CheckboxList relationship fields. +- Inline CRUD inside owner form → Repeater. +- Default performance stance: RelationManagers stay lazy-loaded unless explicit UX justification exists. + +Sources: +- https://filamentphp.com/docs/5.x/resources/managing-relationships +- https://filamentphp.com/docs/5.x/infolists/overview + +## 8) Form patterns (validation, reactivity, state) +- Default: minimize server-driven reactivity; only use it when schema/visibility/requirements must change server-side. +- Prefer “on blur” semantics for chatty inputs when using reactive behavior (per docs patterns). +- Custom field views must obey state binding modifiers. + +Sources: +- https://filamentphp.com/docs/5.x/forms/overview +- https://filamentphp.com/docs/5.x/forms/custom-fields + +## 9) Table & action patterns +- Tables: always define a meaningful empty state (and empty-state actions where appropriate). +- Actions: + - Execution actions use `->action(...)`. + - Destructive actions add `->requiresConfirmation()`. + - Navigation-only actions should use `->url(...)`. + - UNVERIFIED: do not assert modal/confirmation behavior for URL-only actions unless verified. + +Sources: +- https://filamentphp.com/docs/5.x/tables/empty-state +- https://filamentphp.com/docs/5.x/actions/modals + +## 10) Authorization & security +- Enforce panel access in non-local environments as documented. +- UI visibility is not security; enforce policies/access checks in addition to hiding UI. +- Bulk operations: explicitly decide between “Any” policy methods vs per-record authorization. + +Sources: +- https://filamentphp.com/docs/5.x/users/overview +- https://filamentphp.com/docs/5.x/resources/deleting-records + +## 11) Notifications & UX feedback +- Default to explicit success/error notifications for user-triggered mutations that aren’t instantly obvious. +- Treat polling as a cost; set intervals intentionally where polling is used. + +Sources: +- https://filamentphp.com/docs/5.x/notifications/overview +- https://filamentphp.com/docs/5.x/widgets/stats-overview + +## 12) Performance defaults +- Heavy assets: prefer on-demand loading (`loadedOnRequest()` + `x-load-css` / `x-load-js`) for heavy dependencies. +- Styling overrides use CSS hook classes; layout injection uses render hooks; avoid view publishing. + +Sources: +- https://filamentphp.com/docs/5.x/advanced/assets +- https://filamentphp.com/docs/5.x/styling/css-hooks +- https://filamentphp.com/docs/5.x/advanced/render-hooks + +## 13) Testing requirements +- Test pages/relation managers/widgets as Livewire components. +- Test actions using Filament’s action testing guidance. +- Do not mount non-Livewire classes in Livewire tests. + +Sources: +- https://filamentphp.com/docs/5.x/testing/overview +- https://filamentphp.com/docs/5.x/testing/testing-actions + +## 14) Forbidden patterns +- Mixing Filament v3/v4 APIs into v5 code. +- Any mention of Livewire v3 for Filament v5. +- Registering panel providers in `bootstrap/app.php` on Laravel 11+. +- Destructive actions without `->requiresConfirmation()`. +- Shipping heavy assets globally when on-demand loading fits. +- Publishing Filament internal views as a default customization technique. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/actions/modals +- https://filamentphp.com/docs/5.x/advanced/assets + +## 15) Agent output contract +For any implementation request, the agent must explicitly state: +1) Livewire v4.0+ compliance. +2) Provider registration location (Laravel 11+: `bootstrap/providers.php`). +3) For each globally searchable resource: whether it has Edit/View page (or global search is disabled). +4) Which actions are destructive and how confirmation + authorization is handled. +5) Asset strategy: global vs on-demand and where `filament:assets` runs in deploy. +6) Testing plan: which pages/widgets/relation managers/actions are covered. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/advanced/assets +- https://filamentphp.com/docs/5.x/testing/testing-actions \ No newline at end of file diff --git a/.ai/guidelines/filament-v5-checklist.md b/.ai/guidelines/filament-v5-checklist.md new file mode 100644 index 0000000..a19366f --- /dev/null +++ b/.ai/guidelines/filament-v5-checklist.md @@ -0,0 +1,79 @@ +# SECTION C — AI REVIEW CHECKLIST (STRICT CHECKBOXES) + +## Version Safety +- [ ] Filament v5 explicitly targets Livewire v4.0+ (no Livewire v3 references anywhere). + - Source: https://filamentphp.com/docs/5.x/upgrade-guide — “Upgrading Livewire” +- [ ] All references are Filament `/docs/5.x/` only (no v3/v4 docs, no legacy APIs). +- [ ] Upgrade assumptions match the v5 upgrade guide requirements and steps. + - Source: https://filamentphp.com/docs/5.x/upgrade-guide — “New requirements” + +## Panel & Navigation +- [ ] Laravel 11+: panel providers are registered in `bootstrap/providers.php` (not `bootstrap/app.php`). + - Source: https://filamentphp.com/docs/5.x/panel-configuration — “Creating a new panel” +- [ ] Panel `path()` choices are intentional and do not conflict with existing routes (especially `path('')`). + - Source: https://filamentphp.com/docs/5.x/panel-configuration — “Changing the path” +- [ ] Cluster usage is correctly configured (discovery + `$cluster` assignments). + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Creating a cluster” +- [ ] Cluster semantics (sub-navigation + grouped navigation behavior) are understood and verified against the clusters docs. + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Introduction” +- [ ] Cluster directory structure is treated as recommended, not mandatory. + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Code structure recommendations for panels using clusters” +- [ ] User menu items are registered via `userMenuItems()` and permission-gated where needed. + - Source: https://filamentphp.com/docs/5.x/navigation/user-menu — “Introduction” + +## Resource Structure +- [ ] `$recordTitleAttribute` is set for any resource intended for global search. + - Source: https://filamentphp.com/docs/5.x/resources/overview — “Record titles” +- [ ] Hard rule enforced: every globally searchable resource has an Edit or View page; otherwise global search is disabled for it. + - Source: https://filamentphp.com/docs/5.x/resources/global-search — “Setting global search result titles” +- [ ] Relationship-backed global search details are eager-loaded via the global search query override. + - Source: https://filamentphp.com/docs/5.x/resources/global-search — “Adding extra details to global search results” + +## Infolists & Relations +- [ ] Each relationship uses the correct tool (RelationManager vs Select/CheckboxList vs Repeater) based on required interaction. + - Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Choosing the right tool for the job” +- [ ] RelationManagers remain lazy-loaded by default unless there’s an explicit UX justification. + - Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Disabling lazy loading” + +## Forms +- [ ] Server-driven reactivity is minimal; chatty inputs do not trigger network requests unnecessarily. + - Source: https://filamentphp.com/docs/5.x/forms/overview — “Reactive fields on blur” +- [ ] Custom field views obey state binding modifiers (no hardcoded `wire:model` without modifiers). + - Source: https://filamentphp.com/docs/5.x/forms/custom-fields — “Obeying state binding modifiers” + +## Tables & Actions +- [ ] Tables define a meaningful empty state (and empty-state actions where appropriate). + - Source: https://filamentphp.com/docs/5.x/tables/empty-state — “Adding empty state actions” +- [ ] All destructive actions execute via `->action(...)` and include `->requiresConfirmation()`. + - Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” +- [ ] No checklist rule assumes confirmation/modals for `->url(...)` actions unless verified in docs (UNVERIFIED behavior must not be asserted as fact). + - Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” + +## Authorization & Security +- [ ] Panel access is enforced for non-local environments as documented. + - Source: https://filamentphp.com/docs/5.x/users/overview — “Authorizing access to the panel” +- [ ] UI visibility is not treated as authorization; policies/access checks still enforce boundaries. +- [ ] Bulk operations intentionally choose between “Any” policy methods vs per-record authorization where required. + - Source: https://filamentphp.com/docs/5.x/resources/deleting-records — “Authorization” + +## UX & Notifications +- [ ] User-triggered mutations provide explicit success/error notifications when outcomes aren’t instantly obvious. + - Source: https://filamentphp.com/docs/5.x/notifications/overview — “Introduction” +- [ ] Polling (widgets/notifications) is configured intentionally (interval set or disabled) to control load. + - Source: https://filamentphp.com/docs/5.x/widgets/stats-overview — “Live updating stats (polling)” + +## Performance +- [ ] Heavy frontend assets are loaded on-demand using `loadedOnRequest()` + `x-load-css` / `x-load-js` where appropriate. + - Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading CSS” / “Lazy loading JavaScript” +- [ ] Styling overrides use CSS hook classes discovered via DevTools (no brittle selectors by default). + - Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Discovering hook classes” + +## Testing +- [ ] Livewire tests mount Filament pages/relation managers/widgets (Livewire components), not static resource classes. + - Source: https://filamentphp.com/docs/5.x/testing/overview — “What is a Livewire component when using Filament?” +- [ ] Actions that mutate data are covered using Filament’s action testing guidance. + - Source: https://filamentphp.com/docs/5.x/testing/testing-actions — “Testing actions” + +## Deployment / Ops +- [ ] `php artisan filament:assets` is included in the deployment process when using registered assets. + - Source: https://filamentphp.com/docs/5.x/advanced/assets — “The FilamentAsset facade” \ No newline at end of file diff --git a/.gemini/settings.json b/.gemini/settings.json index 7f50738..dfcb3db 100644 --- a/.gemini/settings.json +++ b/.gemini/settings.json @@ -1,5 +1,14 @@ { - "general": { - "previewFeatures": false - } + "general": { + "previewFeatures": false + }, + "mcpServers": { + "laravel-boost": { + "command": "vendor/bin/sail", + "args": [ + "artisan", + "boost:mcp" + ] + } + } } \ No newline at end of file diff --git a/.github/agents/copilot-instructions.md b/.github/agents/copilot-instructions.md index 310d6a9..f1ecfec 100644 --- a/.github/agents/copilot-instructions.md +++ b/.github/agents/copilot-instructions.md @@ -11,6 +11,7 @@ ## Active Technologies - PHP 8.4.x (Laravel 12) + Laravel 12, Filament v4, Livewire v3 (feat/047-inventory-foundations-nodes) - PostgreSQL (JSONB for `InventoryItem.meta_jsonb`) (feat/047-inventory-foundations-nodes) - PostgreSQL (JSONB in `operation_runs.context`, `operation_runs.summary_counts`) (056-remove-legacy-bulkops) +- PHP 8.4.15 (Laravel 12.47.0) + Filament v5.0.0, Livewire v4.0.1 (058-tenant-ui-polish) - PHP 8.4.15 (feat/005-bulk-operations) @@ -30,9 +31,9 @@ ## Code Style PHP 8.4.15: Follow standard conventions ## Recent Changes +- 058-tenant-ui-polish: Added PHP 8.4.15 (Laravel 12.47.0) + Filament v5.0.0, Livewire v4.0.1 +- 058-tenant-ui-polish: Added [if applicable, e.g., PostgreSQL, CoreData, files or N/A] - 056-remove-legacy-bulkops: Added PHP 8.4.x + Laravel 12, Filament v4, Livewire v3 -- feat/047-inventory-foundations-nodes: Added PHP 8.4.x (Laravel 12) + Laravel 12, Filament v4, Livewire v3 -- feat/042-inventory-dependencies-graph: Added PHP 8.4.x + Laravel 12, Filament v4, Livewire v3 diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..b0091a0 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,683 @@ + +=== .ai/filament-v5-blueprint rules === + +## Source of Truth +If any Filament behavior is uncertain, lookup the exact section in: +- docs/research/filament-v5-notes.md +and prefer that over guesses. + +# SECTION B — FILAMENT V5 BLUEPRINT (EXECUTABLE RULES) + +# Filament Blueprint (v5) + +## 1) Non-negotiables +- Filament v5 requires Livewire v4.0+. +- Laravel 11+: register panel providers in `bootstrap/providers.php` (never `bootstrap/app.php`). +- Global search hard rule: If a Resource should appear in Global Search, it must have an Edit or View page; otherwise it will return no results. +- Destructive actions must execute via `Action::make(...)->action(...)` and include `->requiresConfirmation()` (no exceptions). +- Prefer render hooks + CSS hook classes over publishing Filament internal views. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/actions/modals +- https://filamentphp.com/docs/5.x/advanced/render-hooks +- https://filamentphp.com/docs/5.x/styling/css-hooks + +## 2) Directory & naming conventions +- Default to Filament discovery conventions for Resources/Pages/Widgets unless you adopt modular architecture. +- Clusters: directory layout is recommended, not mandatory; functional behavior depends on `$cluster`. + +Sources: +- https://filamentphp.com/docs/5.x/navigation/clusters +- https://filamentphp.com/docs/5.x/advanced/modular-architecture + +## 3) Panel setup defaults +- Default to a single `/admin` panel unless multiple audiences/configs demand multiple panels. +- Verify provider registration (Laravel 11+: `bootstrap/providers.php`) when adding a panel. +- Use `path()` carefully; treat `path('')` as a high-risk change requiring route conflict review. +- Assets policy: + - Panel-only assets: register via panel config. + - Shared/plugin assets: register via `FilamentAsset::register()`. + - Deployment must include `php artisan filament:assets`. + +Sources: +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/advanced/assets + +## 4) Navigation & information architecture +- Use nav groups + sort order intentionally; apply conditional visibility for clarity, but enforce authorization separately. +- Use clusters to introduce hierarchy and sub-navigation when sidebar complexity grows. +- Treat cluster code structure as a recommendation (organizational benefit), not a required rule. +- User menu: + - Configure via `userMenuItems()` with Action objects. + - Never put destructive actions there without confirmation + authorization. + +Sources: +- https://filamentphp.com/docs/5.x/navigation/overview +- https://filamentphp.com/docs/5.x/navigation/clusters +- https://filamentphp.com/docs/5.x/navigation/user-menu + +## 5) Resource patterns +- Default to Resources for CRUD; use custom pages for non-CRUD tools/workflows. +- Global search: + - If a resource is intended for global search: ensure Edit/View page exists. + - Otherwise disable global search for that resource (don’t “expect it to work”). + - If global search renders relationship-backed details: eager-load via global search query override. + - For very large datasets: consider disabling term splitting (only when needed). + +Sources: +- https://filamentphp.com/docs/5.x/resources/overview +- https://filamentphp.com/docs/5.x/resources/global-search + +## 6) Page lifecycle & query rules +- Treat relationship-backed rendering in aggregate contexts (global search details, list summaries) as requiring eager loading. +- Prefer render hooks for layout injection; avoid publishing internal views. + +Sources: +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/advanced/render-hooks + +## 7) Infolists vs RelationManagers (decision tree) +- Interactive CRUD / attach / detach under owner record → RelationManager. +- Pick existing related record(s) inside owner form → Select / CheckboxList relationship fields. +- Inline CRUD inside owner form → Repeater. +- Default performance stance: RelationManagers stay lazy-loaded unless explicit UX justification exists. + +Sources: +- https://filamentphp.com/docs/5.x/resources/managing-relationships +- https://filamentphp.com/docs/5.x/infolists/overview + +## 8) Form patterns (validation, reactivity, state) +- Default: minimize server-driven reactivity; only use it when schema/visibility/requirements must change server-side. +- Prefer “on blur” semantics for chatty inputs when using reactive behavior (per docs patterns). +- Custom field views must obey state binding modifiers. + +Sources: +- https://filamentphp.com/docs/5.x/forms/overview +- https://filamentphp.com/docs/5.x/forms/custom-fields + +## 9) Table & action patterns +- Tables: always define a meaningful empty state (and empty-state actions where appropriate). +- Actions: + - Execution actions use `->action(...)`. + - Destructive actions add `->requiresConfirmation()`. + - Navigation-only actions should use `->url(...)`. + - UNVERIFIED: do not assert modal/confirmation behavior for URL-only actions unless verified. + +Sources: +- https://filamentphp.com/docs/5.x/tables/empty-state +- https://filamentphp.com/docs/5.x/actions/modals + +## 10) Authorization & security +- Enforce panel access in non-local environments as documented. +- UI visibility is not security; enforce policies/access checks in addition to hiding UI. +- Bulk operations: explicitly decide between “Any” policy methods vs per-record authorization. + +Sources: +- https://filamentphp.com/docs/5.x/users/overview +- https://filamentphp.com/docs/5.x/resources/deleting-records + +## 11) Notifications & UX feedback +- Default to explicit success/error notifications for user-triggered mutations that aren’t instantly obvious. +- Treat polling as a cost; set intervals intentionally where polling is used. + +Sources: +- https://filamentphp.com/docs/5.x/notifications/overview +- https://filamentphp.com/docs/5.x/widgets/stats-overview + +## 12) Performance defaults +- Heavy assets: prefer on-demand loading (`loadedOnRequest()` + `x-load-css` / `x-load-js`) for heavy dependencies. +- Styling overrides use CSS hook classes; layout injection uses render hooks; avoid view publishing. + +Sources: +- https://filamentphp.com/docs/5.x/advanced/assets +- https://filamentphp.com/docs/5.x/styling/css-hooks +- https://filamentphp.com/docs/5.x/advanced/render-hooks + +## 13) Testing requirements +- Test pages/relation managers/widgets as Livewire components. +- Test actions using Filament’s action testing guidance. +- Do not mount non-Livewire classes in Livewire tests. + +Sources: +- https://filamentphp.com/docs/5.x/testing/overview +- https://filamentphp.com/docs/5.x/testing/testing-actions + +## 14) Forbidden patterns +- Mixing Filament v3/v4 APIs into v5 code. +- Any mention of Livewire v3 for Filament v5. +- Registering panel providers in `bootstrap/app.php` on Laravel 11+. +- Destructive actions without `->requiresConfirmation()`. +- Shipping heavy assets globally when on-demand loading fits. +- Publishing Filament internal views as a default customization technique. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/actions/modals +- https://filamentphp.com/docs/5.x/advanced/assets + +## 15) Agent output contract +For any implementation request, the agent must explicitly state: +1) Livewire v4.0+ compliance. +2) Provider registration location (Laravel 11+: `bootstrap/providers.php`). +3) For each globally searchable resource: whether it has Edit/View page (or global search is disabled). +4) Which actions are destructive and how confirmation + authorization is handled. +5) Asset strategy: global vs on-demand and where `filament:assets` runs in deploy. +6) Testing plan: which pages/widgets/relation managers/actions are covered. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/advanced/assets +- https://filamentphp.com/docs/5.x/testing/testing-actions + + +=== .ai/filament-v5-checklist rules === + +# SECTION C — AI REVIEW CHECKLIST (STRICT CHECKBOXES) + +## Version Safety +- [ ] Filament v5 explicitly targets Livewire v4.0+ (no Livewire v3 references anywhere). + - Source: https://filamentphp.com/docs/5.x/upgrade-guide — “Upgrading Livewire” +- [ ] All references are Filament `/docs/5.x/` only (no v3/v4 docs, no legacy APIs). +- [ ] Upgrade assumptions match the v5 upgrade guide requirements and steps. + - Source: https://filamentphp.com/docs/5.x/upgrade-guide — “New requirements” + +## Panel & Navigation +- [ ] Laravel 11+: panel providers are registered in `bootstrap/providers.php` (not `bootstrap/app.php`). + - Source: https://filamentphp.com/docs/5.x/panel-configuration — “Creating a new panel” +- [ ] Panel `path()` choices are intentional and do not conflict with existing routes (especially `path('')`). + - Source: https://filamentphp.com/docs/5.x/panel-configuration — “Changing the path” +- [ ] Cluster usage is correctly configured (discovery + `$cluster` assignments). + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Creating a cluster” +- [ ] Cluster semantics (sub-navigation + grouped navigation behavior) are understood and verified against the clusters docs. + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Introduction” +- [ ] Cluster directory structure is treated as recommended, not mandatory. + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Code structure recommendations for panels using clusters” +- [ ] User menu items are registered via `userMenuItems()` and permission-gated where needed. + - Source: https://filamentphp.com/docs/5.x/navigation/user-menu — “Introduction” + +## Resource Structure +- [ ] `$recordTitleAttribute` is set for any resource intended for global search. + - Source: https://filamentphp.com/docs/5.x/resources/overview — “Record titles” +- [ ] Hard rule enforced: every globally searchable resource has an Edit or View page; otherwise global search is disabled for it. + - Source: https://filamentphp.com/docs/5.x/resources/global-search — “Setting global search result titles” +- [ ] Relationship-backed global search details are eager-loaded via the global search query override. + - Source: https://filamentphp.com/docs/5.x/resources/global-search — “Adding extra details to global search results” + +## Infolists & Relations +- [ ] Each relationship uses the correct tool (RelationManager vs Select/CheckboxList vs Repeater) based on required interaction. + - Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Choosing the right tool for the job” +- [ ] RelationManagers remain lazy-loaded by default unless there’s an explicit UX justification. + - Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Disabling lazy loading” + +## Forms +- [ ] Server-driven reactivity is minimal; chatty inputs do not trigger network requests unnecessarily. + - Source: https://filamentphp.com/docs/5.x/forms/overview — “Reactive fields on blur” +- [ ] Custom field views obey state binding modifiers (no hardcoded `wire:model` without modifiers). + - Source: https://filamentphp.com/docs/5.x/forms/custom-fields — “Obeying state binding modifiers” + +## Tables & Actions +- [ ] Tables define a meaningful empty state (and empty-state actions where appropriate). + - Source: https://filamentphp.com/docs/5.x/tables/empty-state — “Adding empty state actions” +- [ ] All destructive actions execute via `->action(...)` and include `->requiresConfirmation()`. + - Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” +- [ ] No checklist rule assumes confirmation/modals for `->url(...)` actions unless verified in docs (UNVERIFIED behavior must not be asserted as fact). + - Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” + +## Authorization & Security +- [ ] Panel access is enforced for non-local environments as documented. + - Source: https://filamentphp.com/docs/5.x/users/overview — “Authorizing access to the panel” +- [ ] UI visibility is not treated as authorization; policies/access checks still enforce boundaries. +- [ ] Bulk operations intentionally choose between “Any” policy methods vs per-record authorization where required. + - Source: https://filamentphp.com/docs/5.x/resources/deleting-records — “Authorization” + +## UX & Notifications +- [ ] User-triggered mutations provide explicit success/error notifications when outcomes aren’t instantly obvious. + - Source: https://filamentphp.com/docs/5.x/notifications/overview — “Introduction” +- [ ] Polling (widgets/notifications) is configured intentionally (interval set or disabled) to control load. + - Source: https://filamentphp.com/docs/5.x/widgets/stats-overview — “Live updating stats (polling)” + +## Performance +- [ ] Heavy frontend assets are loaded on-demand using `loadedOnRequest()` + `x-load-css` / `x-load-js` where appropriate. + - Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading CSS” / “Lazy loading JavaScript” +- [ ] Styling overrides use CSS hook classes discovered via DevTools (no brittle selectors by default). + - Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Discovering hook classes” + +## Testing +- [ ] Livewire tests mount Filament pages/relation managers/widgets (Livewire components), not static resource classes. + - Source: https://filamentphp.com/docs/5.x/testing/overview — “What is a Livewire component when using Filament?” +- [ ] Actions that mutate data are covered using Filament’s action testing guidance. + - Source: https://filamentphp.com/docs/5.x/testing/testing-actions — “Testing actions” + +## Deployment / Ops +- [ ] `php artisan filament:assets` is included in the deployment process when using registered assets. + - Source: https://filamentphp.com/docs/5.x/advanced/assets — “The FilamentAsset facade” + + +=== foundation rules === + +# Laravel Boost Guidelines + +The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications. + +## Foundational Context +This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. + +- php - 8.4.15 +- filament/filament (FILAMENT) - v5 +- laravel/framework (LARAVEL) - v12 +- laravel/prompts (PROMPTS) - v0 +- livewire/livewire (LIVEWIRE) - v4 +- laravel/mcp (MCP) - v0 +- laravel/pint (PINT) - v1 +- laravel/sail (SAIL) - v1 +- pestphp/pest (PEST) - v4 +- phpunit/phpunit (PHPUNIT) - v12 +- tailwindcss (TAILWINDCSS) - v4 + +## 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, 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 it works. 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 the user doesn't see a frontend change reflected in the UI, it could mean they need to run `vendor/bin/sail npm run build`, `vendor/bin/sail npm run dev`, or `vendor/bin/sail composer run dev`. Ask them. + +## Replies +- Be concise in your explanations - focus on what's important rather than explaining obvious details. + +## Documentation Files +- You must only create documentation files if explicitly requested by the user. + + +=== boost rules === + +## Laravel Boost +- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them. + +## Artisan +- Use the `list-artisan-commands` tool when you need to call an Artisan command to double check the available parameters. + +## URLs +- Whenever you share a project URL with the user you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain / IP, and port. + +## Tinker / Debugging +- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly. +- Use the `database-query` tool when you only need to read from the database. + +## Reading Browser Logs With the `browser-logs` Tool +- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost. +- Only recent browser logs will be useful - ignore old logs. + +## Searching Documentation (Critically Important) +- Boost comes with a powerful `search-docs` tool you should use before any other approaches. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation specific for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages. +- The 'search-docs' tool is perfect for all Laravel related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc. +- You must use this tool to search for Laravel-ecosystem documentation before falling back to other approaches. +- Search the documentation before making code changes to ensure we are taking the correct approach. +- Use multiple, broad, simple, topic based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`. +- Do not add package names to queries - package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`. + +### Available Search Syntax +- You can and should pass multiple queries at once. The most relevant results will be returned first. + +1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth' +2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit" +3. Quoted Phrases (Exact Position) - query="infinite scroll" - Words must be adjacent and in that order +4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit" +5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms + + +=== php rules === + +## PHP + +- Always use curly braces for control structures, even if it has one line. + +### Constructors +- Use PHP 8 constructor property promotion in `__construct()`. + - public function __construct(public GitHub $github) { } +- Do not allow empty `__construct()` methods with zero parameters. + +### Type Declarations +- Always use explicit return type declarations for methods and functions. +- Use appropriate PHP type hints for method parameters. + + +protected function isAccessible(User $user, ?string $path = null): bool +{ + ... +} + + +## Comments +- Prefer PHPDoc blocks over comments. Never use comments within the code itself unless there is something _very_ complex going on. + +## PHPDoc Blocks +- Add useful array shape type definitions for arrays when appropriate. + +## Enums +- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`. + + +=== sail rules === + +## Laravel Sail + +- This project runs inside Laravel Sail's Docker containers. You MUST execute all commands through Sail. +- Start services using `vendor/bin/sail up -d` and stop them with `vendor/bin/sail stop`. +- Open the application in the browser by running `vendor/bin/sail open`. +- Always prefix PHP, Artisan, Composer, and Node commands** with `vendor/bin/sail`. Examples: +- Run Artisan Commands: `vendor/bin/sail artisan migrate` +- Install Composer packages: `vendor/bin/sail composer install` +- Execute node commands: `vendor/bin/sail npm run dev` +- Execute PHP scripts: `vendor/bin/sail php [script]` +- View all available Sail commands by running `vendor/bin/sail` without arguments. + + +=== tests rules === + +## Test Enforcement + +- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass. +- Run the minimum number of tests needed to ensure code quality and speed. Use `vendor/bin/sail artisan test` with a specific filename or filter. + + +=== laravel/core rules === + +## Do Things the Laravel Way + +- Use `vendor/bin/sail artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool. +- If you're creating a generic PHP class, use `vendor/bin/sail 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. + +### Database +- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins. +- Use Eloquent models and relationships before suggesting raw database queries +- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them. +- Generate code that prevents N+1 query problems by using eager loading. +- Use Laravel's query builder for very complex database operations. + +### Model Creation +- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `vendor/bin/sail artisan make:model`. + +### 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. + +### Controllers & Validation +- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages. +- Check sibling Form Requests to see if the application uses array or string based validation rules. + +### Queues +- Use queued jobs for time-consuming operations with the `ShouldQueue` interface. + +### Authentication & Authorization +- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.). + +### URL Generation +- When generating links to other pages, prefer named routes and the `route()` function. + +### Configuration +- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`. + +### 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 `vendor/bin/sail artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests. + +### Vite Error +- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `vendor/bin/sail npm run build` or ask the user to run `vendor/bin/sail npm run dev` or `vendor/bin/sail composer run dev`. + + +=== laravel/v12 rules === + +## Laravel 12 + +- Use the `search-docs` tool to get version specific documentation. +- Since Laravel 11, Laravel has a new streamlined file structure which this project uses. + +### Laravel 12 Structure +- No middleware files in `app/Http/Middleware/`. +- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files. +- `bootstrap/providers.php` contains application specific service providers. +- **No app\Console\Kernel.php** - use `bootstrap/app.php` or `routes/console.php` for console configuration. +- **Commands auto-register** - files in `app/Console/Commands/` are automatically available and do not require manual registration. + +### Database +- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost. +- Laravel 11 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`. + +### Models +- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models. + + +=== livewire/core rules === + +## Livewire Core +- Use the `search-docs` tool to find exact version specific documentation for how to write Livewire & Livewire tests. +- Use the `vendor/bin/sail artisan make:livewire [Posts\CreatePost]` artisan command to create new components +- State should live on the server, with the UI reflecting it. +- All Livewire requests hit the Laravel backend, they're like regular HTTP requests. Always validate form data, and run authorization checks in Livewire actions. + +## Livewire Best Practices +- Livewire components require a single root element. +- Use `wire:loading` and `wire:dirty` for delightful loading states. +- Add `wire:key` in loops: + + ```blade + @foreach ($items as $item) +
+ {{ $item->name }} +
+ @endforeach + ``` + +- Prefer lifecycle hooks like `mount()`, `updatedFoo()` for initialization and reactive side effects: + + + public function mount(User $user) { $this->user = $user; } + public function updatedSearch() { $this->resetPage(); } + + + +## Testing Livewire + + + Livewire::test(Counter::class) + ->assertSet('count', 0) + ->call('increment') + ->assertSet('count', 1) + ->assertSee(1) + ->assertStatus(200); + + + + + $this->get('/posts/create') + ->assertSeeLivewire(CreatePost::class); + + + +=== pint/core rules === + +## Laravel Pint Code Formatter + +- You must run `vendor/bin/sail bin pint --dirty` before finalizing changes to ensure your code matches the project's expected style. +- Do not run `vendor/bin/sail bin pint --test`, simply run `vendor/bin/sail bin pint` to fix any formatting issues. + + +=== pest/core rules === + +## Pest +### Testing +- If you need to verify a feature is working, write or update a Unit / Feature test. + +### Pest Tests +- All tests must be written using Pest. Use `vendor/bin/sail artisan make:test --pest {name}`. +- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files - these are core to the application. +- Tests should test all of the happy paths, failure paths, and weird paths. +- Tests live in the `tests/Feature` and `tests/Unit` directories. +- Pest tests look and behave like this: + +it('is true', function () { + expect(true)->toBeTrue(); +}); + + +### Running Tests +- Run the minimal number of tests using an appropriate filter before finalizing code edits. +- To run all tests: `vendor/bin/sail artisan test`. +- To run all tests in a file: `vendor/bin/sail artisan test tests/Feature/ExampleTest.php`. +- To filter on a particular test name: `vendor/bin/sail artisan test --filter=testName` (recommended after making a change to a related file). +- When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing. + +### Pest Assertions +- When asserting status codes on a response, use the specific method like `assertForbidden` and `assertNotFound` instead of using `assertStatus(403)` or similar, e.g.: + +it('returns all', function () { + $response = $this->postJson('/api/docs', []); + + $response->assertSuccessful(); +}); + + +### Mocking +- Mocking can be very helpful when appropriate. +- When mocking, you can use the `Pest\Laravel\mock` Pest function, but always import it via `use function Pest\Laravel\mock;` before using it. Alternatively, you can use `$this->mock()` if existing tests do. +- You can also create partial mocks using the same import or self method. + +### Datasets +- Use datasets in Pest to simplify tests which have a lot of duplicated data. This is often the case when testing validation rules, so consider going with this solution when writing tests for validation rules. + + +it('has emails', function (string $email) { + expect($email)->not->toBeEmpty(); +})->with([ + 'james' => 'james@laravel.com', + 'taylor' => 'taylor@laravel.com', +]); + + + +=== pest/v4 rules === + +## Pest 4 + +- Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage. +- Browser testing is incredibly powerful and useful for this project. +- Browser tests should live in `tests/Browser/`. +- Use the `search-docs` tool for detailed guidance on utilizing these features. + +### Browser Testing +- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories within Pest v4 browser tests, as well as `RefreshDatabase` (when needed) to ensure a clean state for each test. +- Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test. +- If requested, test on multiple browsers (Chrome, Firefox, Safari). +- If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints). +- Switch color schemes (light/dark mode) when appropriate. +- Take screenshots or pause tests for debugging when appropriate. + +### Example Tests + + +it('may reset the password', function () { + Notification::fake(); + + $this->actingAs(User::factory()->create()); + + $page = visit('/sign-in'); // Visit on a real browser... + + $page->assertSee('Sign In') + ->assertNoJavascriptErrors() // or ->assertNoConsoleLogs() + ->click('Forgot Password?') + ->fill('email', 'nuno@laravel.com') + ->click('Send Reset Link') + ->assertSee('We have emailed your password reset link!') + + Notification::assertSent(ResetPassword::class); +}); + + + +$pages = visit(['/', '/about', '/contact']); + +$pages->assertNoJavascriptErrors()->assertNoConsoleLogs(); + + + +=== tailwindcss/core rules === + +## Tailwind Core + +- Use Tailwind CSS classes to style HTML, check and use existing tailwind conventions within the project before writing your own. +- Offer to extract repeated patterns into components that match the project's conventions (i.e. Blade, JSX, Vue, etc..) +- Think through class placement, order, priority, and defaults - remove redundant classes, add classes to parent or child carefully to limit repetition, group elements logically +- You can use the `search-docs` tool to get exact examples from the official documentation when needed. + +### Spacing +- When listing items, use gap utilities for spacing, don't use margins. + + +
+
Superior
+
Michigan
+
Erie
+
+
+ + +### Dark Mode +- If existing pages and components support dark mode, new pages and components must support dark mode in a similar way, typically using `dark:`. + + +=== tailwindcss/v4 rules === + +## Tailwind 4 + +- Always use Tailwind CSS v4 - do not use the deprecated utilities. +- `corePlugins` is not supported in Tailwind v4. +- In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed. + +@theme { + --color-brand: oklch(0.72 0.11 178); +} + + +- In Tailwind v4, you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives used in v3: + + + - @tailwind base; + - @tailwind components; + - @tailwind utilities; + + @import "tailwindcss"; + + + +### Replaced Utilities +- Tailwind v4 removed deprecated utilities. Do not use the deprecated option - use the replacement. +- Opacity values are still numeric. + +| Deprecated | Replacement | +|------------+--------------| +| bg-opacity-* | bg-black/* | +| text-opacity-* | text-black/* | +| border-opacity-* | border-black/* | +| divide-opacity-* | divide-black/* | +| ring-opacity-* | ring-black/* | +| placeholder-opacity-* | placeholder-black/* | +| flex-shrink-* | shrink-* | +| flex-grow-* | grow-* | +| overflow-ellipsis | text-ellipsis | +| decoration-slice | box-decoration-slice | +| decoration-clone | box-decoration-clone | +
diff --git a/Agents.md b/Agents.md index 2993b9d..2f6e883 100644 --- a/Agents.md +++ b/Agents.md @@ -386,6 +386,266 @@ ## Reference Materials === +=== .ai/filament-v5-blueprint rules === + +## Source of Truth +If any Filament behavior is uncertain, lookup the exact section in: +- docs/research/filament-v5-notes.md +and prefer that over guesses. + +# SECTION B — FILAMENT V5 BLUEPRINT (EXECUTABLE RULES) + +# Filament Blueprint (v5) + +## 1) Non-negotiables +- Filament v5 requires Livewire v4.0+. +- Laravel 11+: register panel providers in `bootstrap/providers.php` (never `bootstrap/app.php`). +- Global search hard rule: If a Resource should appear in Global Search, it must have an Edit or View page; otherwise it will return no results. +- Destructive actions must execute via `Action::make(...)->action(...)` and include `->requiresConfirmation()` (no exceptions). +- Prefer render hooks + CSS hook classes over publishing Filament internal views. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/actions/modals +- https://filamentphp.com/docs/5.x/advanced/render-hooks +- https://filamentphp.com/docs/5.x/styling/css-hooks + +## 2) Directory & naming conventions +- Default to Filament discovery conventions for Resources/Pages/Widgets unless you adopt modular architecture. +- Clusters: directory layout is recommended, not mandatory; functional behavior depends on `$cluster`. + +Sources: +- https://filamentphp.com/docs/5.x/navigation/clusters +- https://filamentphp.com/docs/5.x/advanced/modular-architecture + +## 3) Panel setup defaults +- Default to a single `/admin` panel unless multiple audiences/configs demand multiple panels. +- Verify provider registration (Laravel 11+: `bootstrap/providers.php`) when adding a panel. +- Use `path()` carefully; treat `path('')` as a high-risk change requiring route conflict review. +- Assets policy: + - Panel-only assets: register via panel config. + - Shared/plugin assets: register via `FilamentAsset::register()`. + - Deployment must include `php artisan filament:assets`. + +Sources: +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/advanced/assets + +## 4) Navigation & information architecture +- Use nav groups + sort order intentionally; apply conditional visibility for clarity, but enforce authorization separately. +- Use clusters to introduce hierarchy and sub-navigation when sidebar complexity grows. +- Treat cluster code structure as a recommendation (organizational benefit), not a required rule. +- User menu: + - Configure via `userMenuItems()` with Action objects. + - Never put destructive actions there without confirmation + authorization. + +Sources: +- https://filamentphp.com/docs/5.x/navigation/overview +- https://filamentphp.com/docs/5.x/navigation/clusters +- https://filamentphp.com/docs/5.x/navigation/user-menu + +## 5) Resource patterns +- Default to Resources for CRUD; use custom pages for non-CRUD tools/workflows. +- Global search: + - If a resource is intended for global search: ensure Edit/View page exists. + - Otherwise disable global search for that resource (don’t “expect it to work”). + - If global search renders relationship-backed details: eager-load via global search query override. + - For very large datasets: consider disabling term splitting (only when needed). + +Sources: +- https://filamentphp.com/docs/5.x/resources/overview +- https://filamentphp.com/docs/5.x/resources/global-search + +## 6) Page lifecycle & query rules +- Treat relationship-backed rendering in aggregate contexts (global search details, list summaries) as requiring eager loading. +- Prefer render hooks for layout injection; avoid publishing internal views. + +Sources: +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/advanced/render-hooks + +## 7) Infolists vs RelationManagers (decision tree) +- Interactive CRUD / attach / detach under owner record → RelationManager. +- Pick existing related record(s) inside owner form → Select / CheckboxList relationship fields. +- Inline CRUD inside owner form → Repeater. +- Default performance stance: RelationManagers stay lazy-loaded unless explicit UX justification exists. + +Sources: +- https://filamentphp.com/docs/5.x/resources/managing-relationships +- https://filamentphp.com/docs/5.x/infolists/overview + +## 8) Form patterns (validation, reactivity, state) +- Default: minimize server-driven reactivity; only use it when schema/visibility/requirements must change server-side. +- Prefer “on blur” semantics for chatty inputs when using reactive behavior (per docs patterns). +- Custom field views must obey state binding modifiers. + +Sources: +- https://filamentphp.com/docs/5.x/forms/overview +- https://filamentphp.com/docs/5.x/forms/custom-fields + +## 9) Table & action patterns +- Tables: always define a meaningful empty state (and empty-state actions where appropriate). +- Actions: + - Execution actions use `->action(...)`. + - Destructive actions add `->requiresConfirmation()`. + - Navigation-only actions should use `->url(...)`. + - UNVERIFIED: do not assert modal/confirmation behavior for URL-only actions unless verified. + +Sources: +- https://filamentphp.com/docs/5.x/tables/empty-state +- https://filamentphp.com/docs/5.x/actions/modals + +## 10) Authorization & security +- Enforce panel access in non-local environments as documented. +- UI visibility is not security; enforce policies/access checks in addition to hiding UI. +- Bulk operations: explicitly decide between “Any” policy methods vs per-record authorization. + +Sources: +- https://filamentphp.com/docs/5.x/users/overview +- https://filamentphp.com/docs/5.x/resources/deleting-records + +## 11) Notifications & UX feedback +- Default to explicit success/error notifications for user-triggered mutations that aren’t instantly obvious. +- Treat polling as a cost; set intervals intentionally where polling is used. + +Sources: +- https://filamentphp.com/docs/5.x/notifications/overview +- https://filamentphp.com/docs/5.x/widgets/stats-overview + +## 12) Performance defaults +- Heavy assets: prefer on-demand loading (`loadedOnRequest()` + `x-load-css` / `x-load-js`) for heavy dependencies. +- Styling overrides use CSS hook classes; layout injection uses render hooks; avoid view publishing. + +Sources: +- https://filamentphp.com/docs/5.x/advanced/assets +- https://filamentphp.com/docs/5.x/styling/css-hooks +- https://filamentphp.com/docs/5.x/advanced/render-hooks + +## 13) Testing requirements +- Test pages/relation managers/widgets as Livewire components. +- Test actions using Filament’s action testing guidance. +- Do not mount non-Livewire classes in Livewire tests. + +Sources: +- https://filamentphp.com/docs/5.x/testing/overview +- https://filamentphp.com/docs/5.x/testing/testing-actions + +## 14) Forbidden patterns +- Mixing Filament v3/v4 APIs into v5 code. +- Any mention of Livewire v3 for Filament v5. +- Registering panel providers in `bootstrap/app.php` on Laravel 11+. +- Destructive actions without `->requiresConfirmation()`. +- Shipping heavy assets globally when on-demand loading fits. +- Publishing Filament internal views as a default customization technique. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/actions/modals +- https://filamentphp.com/docs/5.x/advanced/assets + +## 15) Agent output contract +For any implementation request, the agent must explicitly state: +1) Livewire v4.0+ compliance. +2) Provider registration location (Laravel 11+: `bootstrap/providers.php`). +3) For each globally searchable resource: whether it has Edit/View page (or global search is disabled). +4) Which actions are destructive and how confirmation + authorization is handled. +5) Asset strategy: global vs on-demand and where `filament:assets` runs in deploy. +6) Testing plan: which pages/widgets/relation managers/actions are covered. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/advanced/assets +- https://filamentphp.com/docs/5.x/testing/testing-actions + + +=== .ai/filament-v5-checklist rules === + +# SECTION C — AI REVIEW CHECKLIST (STRICT CHECKBOXES) + +## Version Safety +- [ ] Filament v5 explicitly targets Livewire v4.0+ (no Livewire v3 references anywhere). + - Source: https://filamentphp.com/docs/5.x/upgrade-guide — “Upgrading Livewire” +- [ ] All references are Filament `/docs/5.x/` only (no v3/v4 docs, no legacy APIs). +- [ ] Upgrade assumptions match the v5 upgrade guide requirements and steps. + - Source: https://filamentphp.com/docs/5.x/upgrade-guide — “New requirements” + +## Panel & Navigation +- [ ] Laravel 11+: panel providers are registered in `bootstrap/providers.php` (not `bootstrap/app.php`). + - Source: https://filamentphp.com/docs/5.x/panel-configuration — “Creating a new panel” +- [ ] Panel `path()` choices are intentional and do not conflict with existing routes (especially `path('')`). + - Source: https://filamentphp.com/docs/5.x/panel-configuration — “Changing the path” +- [ ] Cluster usage is correctly configured (discovery + `$cluster` assignments). + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Creating a cluster” +- [ ] Cluster semantics (sub-navigation + grouped navigation behavior) are understood and verified against the clusters docs. + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Introduction” +- [ ] Cluster directory structure is treated as recommended, not mandatory. + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Code structure recommendations for panels using clusters” +- [ ] User menu items are registered via `userMenuItems()` and permission-gated where needed. + - Source: https://filamentphp.com/docs/5.x/navigation/user-menu — “Introduction” + +## Resource Structure +- [ ] `$recordTitleAttribute` is set for any resource intended for global search. + - Source: https://filamentphp.com/docs/5.x/resources/overview — “Record titles” +- [ ] Hard rule enforced: every globally searchable resource has an Edit or View page; otherwise global search is disabled for it. + - Source: https://filamentphp.com/docs/5.x/resources/global-search — “Setting global search result titles” +- [ ] Relationship-backed global search details are eager-loaded via the global search query override. + - Source: https://filamentphp.com/docs/5.x/resources/global-search — “Adding extra details to global search results” + +## Infolists & Relations +- [ ] Each relationship uses the correct tool (RelationManager vs Select/CheckboxList vs Repeater) based on required interaction. + - Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Choosing the right tool for the job” +- [ ] RelationManagers remain lazy-loaded by default unless there’s an explicit UX justification. + - Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Disabling lazy loading” + +## Forms +- [ ] Server-driven reactivity is minimal; chatty inputs do not trigger network requests unnecessarily. + - Source: https://filamentphp.com/docs/5.x/forms/overview — “Reactive fields on blur” +- [ ] Custom field views obey state binding modifiers (no hardcoded `wire:model` without modifiers). + - Source: https://filamentphp.com/docs/5.x/forms/custom-fields — “Obeying state binding modifiers” + +## Tables & Actions +- [ ] Tables define a meaningful empty state (and empty-state actions where appropriate). + - Source: https://filamentphp.com/docs/5.x/tables/empty-state — “Adding empty state actions” +- [ ] All destructive actions execute via `->action(...)` and include `->requiresConfirmation()`. + - Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” +- [ ] No checklist rule assumes confirmation/modals for `->url(...)` actions unless verified in docs (UNVERIFIED behavior must not be asserted as fact). + - Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” + +## Authorization & Security +- [ ] Panel access is enforced for non-local environments as documented. + - Source: https://filamentphp.com/docs/5.x/users/overview — “Authorizing access to the panel” +- [ ] UI visibility is not treated as authorization; policies/access checks still enforce boundaries. +- [ ] Bulk operations intentionally choose between “Any” policy methods vs per-record authorization where required. + - Source: https://filamentphp.com/docs/5.x/resources/deleting-records — “Authorization” + +## UX & Notifications +- [ ] User-triggered mutations provide explicit success/error notifications when outcomes aren’t instantly obvious. + - Source: https://filamentphp.com/docs/5.x/notifications/overview — “Introduction” +- [ ] Polling (widgets/notifications) is configured intentionally (interval set or disabled) to control load. + - Source: https://filamentphp.com/docs/5.x/widgets/stats-overview — “Live updating stats (polling)” + +## Performance +- [ ] Heavy frontend assets are loaded on-demand using `loadedOnRequest()` + `x-load-css` / `x-load-js` where appropriate. + - Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading CSS” / “Lazy loading JavaScript” +- [ ] Styling overrides use CSS hook classes discovered via DevTools (no brittle selectors by default). + - Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Discovering hook classes” + +## Testing +- [ ] Livewire tests mount Filament pages/relation managers/widgets (Livewire components), not static resource classes. + - Source: https://filamentphp.com/docs/5.x/testing/overview — “What is a Livewire component when using Filament?” +- [ ] Actions that mutate data are covered using Filament’s action testing guidance. + - Source: https://filamentphp.com/docs/5.x/testing/testing-actions — “Testing actions” + +## Deployment / Ops +- [ ] `php artisan filament:assets` is included in the deployment process when using registered assets. + - Source: https://filamentphp.com/docs/5.x/advanced/assets — “The FilamentAsset facade” + + === foundation rules === # Laravel Boost Guidelines @@ -396,10 +656,10 @@ ## Foundational Context This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. - php - 8.4.15 -- filament/filament (FILAMENT) - v4 +- filament/filament (FILAMENT) - v5 - laravel/framework (LARAVEL) - v12 - laravel/prompts (PROMPTS) - v0 -- livewire/livewire (LIVEWIRE) - v3 +- livewire/livewire (LIVEWIRE) - v4 - laravel/mcp (MCP) - v0 - laravel/pint (PINT) - v1 - laravel/sail (SAIL) - v1 @@ -411,7 +671,6 @@ ## 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, naming. - Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`. - Check for existing components to reuse before writing a new one. -- UI consistency: Prefer Filament components (``, infolist/table entries, etc.) over custom HTML/Tailwind for admin UI; only roll custom markup when Filament cannot express the UI. ## Verification Scripts - Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important. @@ -421,7 +680,7 @@ ## Application Structure & Architecture - Do not change the application's dependencies without approval. ## Frontend Bundling -- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them. +- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `vendor/bin/sail npm run build`, `vendor/bin/sail npm run dev`, or `vendor/bin/sail composer run dev`. Ask them. ## Replies - Be concise in your explanations - focus on what's important rather than explaining obvious details. @@ -499,20 +758,35 @@ ## Enums - Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`. +=== sail rules === + +## Laravel Sail + +- This project runs inside Laravel Sail's Docker containers. You MUST execute all commands through Sail. +- Start services using `vendor/bin/sail up -d` and stop them with `vendor/bin/sail stop`. +- Open the application in the browser by running `vendor/bin/sail open`. +- Always prefix PHP, Artisan, Composer, and Node commands** with `vendor/bin/sail`. Examples: +- Run Artisan Commands: `vendor/bin/sail artisan migrate` +- Install Composer packages: `vendor/bin/sail composer install` +- Execute node commands: `vendor/bin/sail npm run dev` +- Execute PHP scripts: `vendor/bin/sail php [script]` +- View all available Sail commands by running `vendor/bin/sail` without arguments. + + === tests rules === ## Test Enforcement - Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass. -- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test` with a specific filename or filter. +- Run the minimum number of tests needed to ensure code quality and speed. Use `vendor/bin/sail artisan test` with a specific filename or filter. === 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 the `list-artisan-commands` tool. -- If you're creating a generic PHP class, use `php artisan make:class`. +- Use `vendor/bin/sail artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool. +- If you're creating a generic PHP class, use `vendor/bin/sail 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. ### Database @@ -523,7 +797,7 @@ ### Database - Use Laravel's query builder for very complex database operations. ### Model Creation -- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`. +- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `vendor/bin/sail artisan make:model`. ### 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. @@ -547,10 +821,10 @@ ### Configuration ### 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. +- When creating tests, make use of `vendor/bin/sail artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests. ### Vite Error -- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`. +- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `vendor/bin/sail npm run build` or ask the user to run `vendor/bin/sail npm run dev` or `vendor/bin/sail composer run dev`. === laravel/v12 rules === @@ -579,7 +853,7 @@ ### Models ## Livewire Core - Use the `search-docs` tool to find exact version specific documentation for how to write Livewire & Livewire tests. -- Use the `php artisan make:livewire [Posts\CreatePost]` artisan command to create new components +- Use the `vendor/bin/sail artisan make:livewire [Posts\CreatePost]` artisan command to create new components - State should live on the server, with the UI reflecting it. - All Livewire requests hit the Laravel backend, they're like regular HTTP requests. Always validate form data, and run authorization checks in Livewire actions. @@ -622,48 +896,12 @@ ## Testing Livewire -=== livewire/v3 rules === - -## Livewire 3 - -### Key Changes From Livewire 2 -- These things changed in Livewire 2, but may not have been updated in this application. Verify this application's setup to ensure you conform with application conventions. - - Use `wire:model.live` for real-time updates, `wire:model` is now deferred by default. - - Components now use the `App\Livewire` namespace (not `App\Http\Livewire`). - - Use `$this->dispatch()` to dispatch events (not `emit` or `dispatchBrowserEvent`). - - Use the `components.layouts.app` view as the typical layout path (not `layouts.app`). - -### New Directives -- `wire:show`, `wire:transition`, `wire:cloak`, `wire:offline`, `wire:target` are available for use. Use the documentation to find usage examples. - -### Alpine -- Alpine is now included with Livewire, don't manually include Alpine.js. -- Plugins included with Alpine: persist, intersect, collapse, and focus. - -### Lifecycle Hooks -- You can listen for `livewire:init` to hook into Livewire initialization, and `fail.status === 419` for the page expiring: - - -document.addEventListener('livewire:init', function () { - Livewire.hook('request', ({ fail }) => { - if (fail && fail.status === 419) { - alert('Your session expired'); - } - }); - - Livewire.hook('message.failed', (message, component) => { - console.error(message); - }); -}); - - - === pint/core rules === ## Laravel Pint Code Formatter -- You must run `vendor/bin/pint --dirty` before finalizing changes to ensure your code matches the project's expected style. -- Do not run `vendor/bin/pint --test`, simply run `vendor/bin/pint` to fix any formatting issues. +- You must run `vendor/bin/sail bin pint --dirty` before finalizing changes to ensure your code matches the project's expected style. +- Do not run `vendor/bin/sail bin pint --test`, simply run `vendor/bin/sail bin pint` to fix any formatting issues. === pest/core rules === @@ -673,7 +911,7 @@ ### Testing - If you need to verify a feature is working, write or update a Unit / Feature test. ### Pest Tests -- All tests must be written using Pest. Use `php artisan make:test --pest {name}`. +- All tests must be written using Pest. Use `vendor/bin/sail artisan make:test --pest {name}`. - You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files - these are core to the application. - Tests should test all of the happy paths, failure paths, and weird paths. - Tests live in the `tests/Feature` and `tests/Unit` directories. @@ -686,9 +924,9 @@ ### Pest Tests ### Running Tests - Run the minimal number of tests using an appropriate filter before finalizing code edits. -- To run all tests: `php artisan test`. -- To run all tests in a file: `php artisan test tests/Feature/ExampleTest.php`. -- To filter on a particular test name: `php artisan test --filter=testName` (recommended after making a change to a related file). +- To run all tests: `vendor/bin/sail artisan test`. +- To run all tests in a file: `vendor/bin/sail artisan test tests/Feature/ExampleTest.php`. +- To filter on a particular test name: `vendor/bin/sail artisan test --filter=testName` (recommended after making a change to a related file). - When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing. ### Pest Assertions diff --git a/GEMINI.md b/GEMINI.md index 220d86e..e1aafa1 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -226,6 +226,266 @@ ## Reference Materials === +=== .ai/filament-v5-blueprint rules === + +## Source of Truth +If any Filament behavior is uncertain, lookup the exact section in: +- docs/research/filament-v5-notes.md +and prefer that over guesses. + +# SECTION B — FILAMENT V5 BLUEPRINT (EXECUTABLE RULES) + +# Filament Blueprint (v5) + +## 1) Non-negotiables +- Filament v5 requires Livewire v4.0+. +- Laravel 11+: register panel providers in `bootstrap/providers.php` (never `bootstrap/app.php`). +- Global search hard rule: If a Resource should appear in Global Search, it must have an Edit or View page; otherwise it will return no results. +- Destructive actions must execute via `Action::make(...)->action(...)` and include `->requiresConfirmation()` (no exceptions). +- Prefer render hooks + CSS hook classes over publishing Filament internal views. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/actions/modals +- https://filamentphp.com/docs/5.x/advanced/render-hooks +- https://filamentphp.com/docs/5.x/styling/css-hooks + +## 2) Directory & naming conventions +- Default to Filament discovery conventions for Resources/Pages/Widgets unless you adopt modular architecture. +- Clusters: directory layout is recommended, not mandatory; functional behavior depends on `$cluster`. + +Sources: +- https://filamentphp.com/docs/5.x/navigation/clusters +- https://filamentphp.com/docs/5.x/advanced/modular-architecture + +## 3) Panel setup defaults +- Default to a single `/admin` panel unless multiple audiences/configs demand multiple panels. +- Verify provider registration (Laravel 11+: `bootstrap/providers.php`) when adding a panel. +- Use `path()` carefully; treat `path('')` as a high-risk change requiring route conflict review. +- Assets policy: + - Panel-only assets: register via panel config. + - Shared/plugin assets: register via `FilamentAsset::register()`. + - Deployment must include `php artisan filament:assets`. + +Sources: +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/advanced/assets + +## 4) Navigation & information architecture +- Use nav groups + sort order intentionally; apply conditional visibility for clarity, but enforce authorization separately. +- Use clusters to introduce hierarchy and sub-navigation when sidebar complexity grows. +- Treat cluster code structure as a recommendation (organizational benefit), not a required rule. +- User menu: + - Configure via `userMenuItems()` with Action objects. + - Never put destructive actions there without confirmation + authorization. + +Sources: +- https://filamentphp.com/docs/5.x/navigation/overview +- https://filamentphp.com/docs/5.x/navigation/clusters +- https://filamentphp.com/docs/5.x/navigation/user-menu + +## 5) Resource patterns +- Default to Resources for CRUD; use custom pages for non-CRUD tools/workflows. +- Global search: + - If a resource is intended for global search: ensure Edit/View page exists. + - Otherwise disable global search for that resource (don’t “expect it to work”). + - If global search renders relationship-backed details: eager-load via global search query override. + - For very large datasets: consider disabling term splitting (only when needed). + +Sources: +- https://filamentphp.com/docs/5.x/resources/overview +- https://filamentphp.com/docs/5.x/resources/global-search + +## 6) Page lifecycle & query rules +- Treat relationship-backed rendering in aggregate contexts (global search details, list summaries) as requiring eager loading. +- Prefer render hooks for layout injection; avoid publishing internal views. + +Sources: +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/advanced/render-hooks + +## 7) Infolists vs RelationManagers (decision tree) +- Interactive CRUD / attach / detach under owner record → RelationManager. +- Pick existing related record(s) inside owner form → Select / CheckboxList relationship fields. +- Inline CRUD inside owner form → Repeater. +- Default performance stance: RelationManagers stay lazy-loaded unless explicit UX justification exists. + +Sources: +- https://filamentphp.com/docs/5.x/resources/managing-relationships +- https://filamentphp.com/docs/5.x/infolists/overview + +## 8) Form patterns (validation, reactivity, state) +- Default: minimize server-driven reactivity; only use it when schema/visibility/requirements must change server-side. +- Prefer “on blur” semantics for chatty inputs when using reactive behavior (per docs patterns). +- Custom field views must obey state binding modifiers. + +Sources: +- https://filamentphp.com/docs/5.x/forms/overview +- https://filamentphp.com/docs/5.x/forms/custom-fields + +## 9) Table & action patterns +- Tables: always define a meaningful empty state (and empty-state actions where appropriate). +- Actions: + - Execution actions use `->action(...)`. + - Destructive actions add `->requiresConfirmation()`. + - Navigation-only actions should use `->url(...)`. + - UNVERIFIED: do not assert modal/confirmation behavior for URL-only actions unless verified. + +Sources: +- https://filamentphp.com/docs/5.x/tables/empty-state +- https://filamentphp.com/docs/5.x/actions/modals + +## 10) Authorization & security +- Enforce panel access in non-local environments as documented. +- UI visibility is not security; enforce policies/access checks in addition to hiding UI. +- Bulk operations: explicitly decide between “Any” policy methods vs per-record authorization. + +Sources: +- https://filamentphp.com/docs/5.x/users/overview +- https://filamentphp.com/docs/5.x/resources/deleting-records + +## 11) Notifications & UX feedback +- Default to explicit success/error notifications for user-triggered mutations that aren’t instantly obvious. +- Treat polling as a cost; set intervals intentionally where polling is used. + +Sources: +- https://filamentphp.com/docs/5.x/notifications/overview +- https://filamentphp.com/docs/5.x/widgets/stats-overview + +## 12) Performance defaults +- Heavy assets: prefer on-demand loading (`loadedOnRequest()` + `x-load-css` / `x-load-js`) for heavy dependencies. +- Styling overrides use CSS hook classes; layout injection uses render hooks; avoid view publishing. + +Sources: +- https://filamentphp.com/docs/5.x/advanced/assets +- https://filamentphp.com/docs/5.x/styling/css-hooks +- https://filamentphp.com/docs/5.x/advanced/render-hooks + +## 13) Testing requirements +- Test pages/relation managers/widgets as Livewire components. +- Test actions using Filament’s action testing guidance. +- Do not mount non-Livewire classes in Livewire tests. + +Sources: +- https://filamentphp.com/docs/5.x/testing/overview +- https://filamentphp.com/docs/5.x/testing/testing-actions + +## 14) Forbidden patterns +- Mixing Filament v3/v4 APIs into v5 code. +- Any mention of Livewire v3 for Filament v5. +- Registering panel providers in `bootstrap/app.php` on Laravel 11+. +- Destructive actions without `->requiresConfirmation()`. +- Shipping heavy assets globally when on-demand loading fits. +- Publishing Filament internal views as a default customization technique. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/actions/modals +- https://filamentphp.com/docs/5.x/advanced/assets + +## 15) Agent output contract +For any implementation request, the agent must explicitly state: +1) Livewire v4.0+ compliance. +2) Provider registration location (Laravel 11+: `bootstrap/providers.php`). +3) For each globally searchable resource: whether it has Edit/View page (or global search is disabled). +4) Which actions are destructive and how confirmation + authorization is handled. +5) Asset strategy: global vs on-demand and where `filament:assets` runs in deploy. +6) Testing plan: which pages/widgets/relation managers/actions are covered. + +Sources: +- https://filamentphp.com/docs/5.x/upgrade-guide +- https://filamentphp.com/docs/5.x/panel-configuration +- https://filamentphp.com/docs/5.x/resources/global-search +- https://filamentphp.com/docs/5.x/advanced/assets +- https://filamentphp.com/docs/5.x/testing/testing-actions + + +=== .ai/filament-v5-checklist rules === + +# SECTION C — AI REVIEW CHECKLIST (STRICT CHECKBOXES) + +## Version Safety +- [ ] Filament v5 explicitly targets Livewire v4.0+ (no Livewire v3 references anywhere). + - Source: https://filamentphp.com/docs/5.x/upgrade-guide — “Upgrading Livewire” +- [ ] All references are Filament `/docs/5.x/` only (no v3/v4 docs, no legacy APIs). +- [ ] Upgrade assumptions match the v5 upgrade guide requirements and steps. + - Source: https://filamentphp.com/docs/5.x/upgrade-guide — “New requirements” + +## Panel & Navigation +- [ ] Laravel 11+: panel providers are registered in `bootstrap/providers.php` (not `bootstrap/app.php`). + - Source: https://filamentphp.com/docs/5.x/panel-configuration — “Creating a new panel” +- [ ] Panel `path()` choices are intentional and do not conflict with existing routes (especially `path('')`). + - Source: https://filamentphp.com/docs/5.x/panel-configuration — “Changing the path” +- [ ] Cluster usage is correctly configured (discovery + `$cluster` assignments). + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Creating a cluster” +- [ ] Cluster semantics (sub-navigation + grouped navigation behavior) are understood and verified against the clusters docs. + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Introduction” +- [ ] Cluster directory structure is treated as recommended, not mandatory. + - Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Code structure recommendations for panels using clusters” +- [ ] User menu items are registered via `userMenuItems()` and permission-gated where needed. + - Source: https://filamentphp.com/docs/5.x/navigation/user-menu — “Introduction” + +## Resource Structure +- [ ] `$recordTitleAttribute` is set for any resource intended for global search. + - Source: https://filamentphp.com/docs/5.x/resources/overview — “Record titles” +- [ ] Hard rule enforced: every globally searchable resource has an Edit or View page; otherwise global search is disabled for it. + - Source: https://filamentphp.com/docs/5.x/resources/global-search — “Setting global search result titles” +- [ ] Relationship-backed global search details are eager-loaded via the global search query override. + - Source: https://filamentphp.com/docs/5.x/resources/global-search — “Adding extra details to global search results” + +## Infolists & Relations +- [ ] Each relationship uses the correct tool (RelationManager vs Select/CheckboxList vs Repeater) based on required interaction. + - Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Choosing the right tool for the job” +- [ ] RelationManagers remain lazy-loaded by default unless there’s an explicit UX justification. + - Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Disabling lazy loading” + +## Forms +- [ ] Server-driven reactivity is minimal; chatty inputs do not trigger network requests unnecessarily. + - Source: https://filamentphp.com/docs/5.x/forms/overview — “Reactive fields on blur” +- [ ] Custom field views obey state binding modifiers (no hardcoded `wire:model` without modifiers). + - Source: https://filamentphp.com/docs/5.x/forms/custom-fields — “Obeying state binding modifiers” + +## Tables & Actions +- [ ] Tables define a meaningful empty state (and empty-state actions where appropriate). + - Source: https://filamentphp.com/docs/5.x/tables/empty-state — “Adding empty state actions” +- [ ] All destructive actions execute via `->action(...)` and include `->requiresConfirmation()`. + - Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” +- [ ] No checklist rule assumes confirmation/modals for `->url(...)` actions unless verified in docs (UNVERIFIED behavior must not be asserted as fact). + - Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” + +## Authorization & Security +- [ ] Panel access is enforced for non-local environments as documented. + - Source: https://filamentphp.com/docs/5.x/users/overview — “Authorizing access to the panel” +- [ ] UI visibility is not treated as authorization; policies/access checks still enforce boundaries. +- [ ] Bulk operations intentionally choose between “Any” policy methods vs per-record authorization where required. + - Source: https://filamentphp.com/docs/5.x/resources/deleting-records — “Authorization” + +## UX & Notifications +- [ ] User-triggered mutations provide explicit success/error notifications when outcomes aren’t instantly obvious. + - Source: https://filamentphp.com/docs/5.x/notifications/overview — “Introduction” +- [ ] Polling (widgets/notifications) is configured intentionally (interval set or disabled) to control load. + - Source: https://filamentphp.com/docs/5.x/widgets/stats-overview — “Live updating stats (polling)” + +## Performance +- [ ] Heavy frontend assets are loaded on-demand using `loadedOnRequest()` + `x-load-css` / `x-load-js` where appropriate. + - Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading CSS” / “Lazy loading JavaScript” +- [ ] Styling overrides use CSS hook classes discovered via DevTools (no brittle selectors by default). + - Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Discovering hook classes” + +## Testing +- [ ] Livewire tests mount Filament pages/relation managers/widgets (Livewire components), not static resource classes. + - Source: https://filamentphp.com/docs/5.x/testing/overview — “What is a Livewire component when using Filament?” +- [ ] Actions that mutate data are covered using Filament’s action testing guidance. + - Source: https://filamentphp.com/docs/5.x/testing/testing-actions — “Testing actions” + +## Deployment / Ops +- [ ] `php artisan filament:assets` is included in the deployment process when using registered assets. + - Source: https://filamentphp.com/docs/5.x/advanced/assets — “The FilamentAsset facade” + + === foundation rules === # Laravel Boost Guidelines @@ -236,10 +496,10 @@ ## Foundational Context This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. - php - 8.4.15 -- filament/filament (FILAMENT) - v4 +- filament/filament (FILAMENT) - v5 - laravel/framework (LARAVEL) - v12 - laravel/prompts (PROMPTS) - v0 -- livewire/livewire (LIVEWIRE) - v3 +- livewire/livewire (LIVEWIRE) - v4 - laravel/mcp (MCP) - v0 - laravel/pint (PINT) - v1 - laravel/sail (SAIL) - v1 @@ -260,7 +520,7 @@ ## Application Structure & Architecture - Do not change the application's dependencies without approval. ## Frontend Bundling -- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them. +- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `vendor/bin/sail npm run build`, `vendor/bin/sail npm run dev`, or `vendor/bin/sail composer run dev`. Ask them. ## Replies - Be concise in your explanations - focus on what's important rather than explaining obvious details. @@ -338,20 +598,35 @@ ## Enums - Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`. +=== sail rules === + +## Laravel Sail + +- This project runs inside Laravel Sail's Docker containers. You MUST execute all commands through Sail. +- Start services using `vendor/bin/sail up -d` and stop them with `vendor/bin/sail stop`. +- Open the application in the browser by running `vendor/bin/sail open`. +- Always prefix PHP, Artisan, Composer, and Node commands** with `vendor/bin/sail`. Examples: +- Run Artisan Commands: `vendor/bin/sail artisan migrate` +- Install Composer packages: `vendor/bin/sail composer install` +- Execute node commands: `vendor/bin/sail npm run dev` +- Execute PHP scripts: `vendor/bin/sail php [script]` +- View all available Sail commands by running `vendor/bin/sail` without arguments. + + === tests rules === ## Test Enforcement - Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass. -- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test` with a specific filename or filter. +- Run the minimum number of tests needed to ensure code quality and speed. Use `vendor/bin/sail artisan test` with a specific filename or filter. === 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 the `list-artisan-commands` tool. -- If you're creating a generic PHP class, use `php artisan make:class`. +- Use `vendor/bin/sail artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool. +- If you're creating a generic PHP class, use `vendor/bin/sail 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. ### Database @@ -362,7 +637,7 @@ ### Database - Use Laravel's query builder for very complex database operations. ### Model Creation -- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`. +- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `vendor/bin/sail artisan make:model`. ### 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. @@ -386,10 +661,10 @@ ### Configuration ### 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. +- When creating tests, make use of `vendor/bin/sail artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests. ### Vite Error -- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`. +- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `vendor/bin/sail npm run build` or ask the user to run `vendor/bin/sail npm run dev` or `vendor/bin/sail composer run dev`. === laravel/v12 rules === @@ -418,7 +693,7 @@ ### Models ## Livewire Core - Use the `search-docs` tool to find exact version specific documentation for how to write Livewire & Livewire tests. -- Use the `php artisan make:livewire [Posts\CreatePost]` artisan command to create new components +- Use the `vendor/bin/sail artisan make:livewire [Posts\CreatePost]` artisan command to create new components - State should live on the server, with the UI reflecting it. - All Livewire requests hit the Laravel backend, they're like regular HTTP requests. Always validate form data, and run authorization checks in Livewire actions. @@ -461,48 +736,12 @@ ## Testing Livewire -=== livewire/v3 rules === - -## Livewire 3 - -### Key Changes From Livewire 2 -- These things changed in Livewire 2, but may not have been updated in this application. Verify this application's setup to ensure you conform with application conventions. - - Use `wire:model.live` for real-time updates, `wire:model` is now deferred by default. - - Components now use the `App\Livewire` namespace (not `App\Http\Livewire`). - - Use `$this->dispatch()` to dispatch events (not `emit` or `dispatchBrowserEvent`). - - Use the `components.layouts.app` view as the typical layout path (not `layouts.app`). - -### New Directives -- `wire:show`, `wire:transition`, `wire:cloak`, `wire:offline`, `wire:target` are available for use. Use the documentation to find usage examples. - -### Alpine -- Alpine is now included with Livewire, don't manually include Alpine.js. -- Plugins included with Alpine: persist, intersect, collapse, and focus. - -### Lifecycle Hooks -- You can listen for `livewire:init` to hook into Livewire initialization, and `fail.status === 419` for the page expiring: - - -document.addEventListener('livewire:init', function () { - Livewire.hook('request', ({ fail }) => { - if (fail && fail.status === 419) { - alert('Your session expired'); - } - }); - - Livewire.hook('message.failed', (message, component) => { - console.error(message); - }); -}); - - - === pint/core rules === ## Laravel Pint Code Formatter -- You must run `vendor/bin/pint --dirty` before finalizing changes to ensure your code matches the project's expected style. -- Do not run `vendor/bin/pint --test`, simply run `vendor/bin/pint` to fix any formatting issues. +- You must run `vendor/bin/sail bin pint --dirty` before finalizing changes to ensure your code matches the project's expected style. +- Do not run `vendor/bin/sail bin pint --test`, simply run `vendor/bin/sail bin pint` to fix any formatting issues. === pest/core rules === @@ -512,7 +751,7 @@ ### Testing - If you need to verify a feature is working, write or update a Unit / Feature test. ### Pest Tests -- All tests must be written using Pest. Use `php artisan make:test --pest {name}`. +- All tests must be written using Pest. Use `vendor/bin/sail artisan make:test --pest {name}`. - You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files - these are core to the application. - Tests should test all of the happy paths, failure paths, and weird paths. - Tests live in the `tests/Feature` and `tests/Unit` directories. @@ -525,9 +764,9 @@ ### Pest Tests ### Running Tests - Run the minimal number of tests using an appropriate filter before finalizing code edits. -- To run all tests: `php artisan test`. -- To run all tests in a file: `php artisan test tests/Feature/ExampleTest.php`. -- To filter on a particular test name: `php artisan test --filter=testName` (recommended after making a change to a related file). +- To run all tests: `vendor/bin/sail artisan test`. +- To run all tests in a file: `vendor/bin/sail artisan test tests/Feature/ExampleTest.php`. +- To filter on a particular test name: `vendor/bin/sail artisan test --filter=testName` (recommended after making a change to a related file). - When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing. ### Pest Assertions diff --git a/boost.json b/boost.json index e337384..0a16d2e 100644 --- a/boost.json +++ b/boost.json @@ -1,7 +1,16 @@ { "agents": [ "codex", + "copilot", + "gemini", "opencode" ], - "guidelines": [] + "editors": [ + "codex", + "gemini", + "opencode", + "vscode" + ], + "guidelines": [], + "sail": true } diff --git a/docs/research/filament-v5-notes.md b/docs/research/filament-v5-notes.md new file mode 100644 index 0000000..c7dc91a --- /dev/null +++ b/docs/research/filament-v5-notes.md @@ -0,0 +1,114 @@ +# SECTION A — FILAMENT V5 NOTES (BULLETS + SOURCES) + +## Versioning & Base Requirements +- Rule: Filament v5 requires Livewire v4.0+. + When: Always when installing/upgrading Filament v5. When NOT: Never target Livewire v3 in a v5 codebase. + Source: https://filamentphp.com/docs/5.x/upgrade-guide — “Upgrading Livewire” + +## Panels — Configuration +- Rule: Panels are configured via dedicated panel providers; the default admin panel ships at `/admin`. + When: Always—centralize panel setup (resources/pages/widgets/plugins) here. When NOT: Don’t scatter core panel configuration across unrelated providers. + Source: https://filamentphp.com/docs/5.x/panel-configuration — “The default admin panel” +- Rule: New panel providers must be registered in `bootstrap/providers.php` for Laravel 11+ (or `config/app.php` for Laravel 10 and below). + When: When adding a new panel or if panel creation didn’t auto-register. When NOT: Don’t register panel providers in `bootstrap/app.php`. + Source: https://filamentphp.com/docs/5.x/panel-configuration — “Creating a new panel” +- Rule: Use `path()` to change a panel’s URL prefix; `path('')` mounts at `/` and can conflict with existing routes. + When: When `/admin` is not desired. When NOT: Don’t set `path('')` if `/` is already routed in `routes/web.php`. + Source: https://filamentphp.com/docs/5.x/panel-configuration — “Changing the path” +- Rule: Use render hooks to inject Blade content into Filament layouts without publishing internal views. + When: When inserting banners/scripts/partials into specific layout locations. When NOT: Don’t publish Filament views for small layout tweaks. + Source: https://filamentphp.com/docs/5.x/advanced/render-hooks — “Registering render hooks” + +## Navigation — Groups, Ordering, Visibility +- Rule: Navigation is built from resources/pages/clusters and can be grouped, sorted, and conditionally shown/hidden. + When: Always—use groups/sorting for predictable IA. When NOT: Don’t treat “hidden navigation” as authorization; still enforce policies/access checks. + Source: https://filamentphp.com/docs/5.x/navigation/overview — “Introduction” +- Rule: Clusters group resources/pages under a single nav item and provide sub-navigation. + When: When the sidebar becomes too large and needs hierarchy. When NOT: Don’t cluster if it reduces discoverability for your users. + Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Introduction” +- Rule: Cluster code structure (moving pages/resources into a cluster directory) is recommended, not required; behavior depends on `$cluster`. + When: When you want consistent organization. When NOT: Don’t treat the directory layout as mandatory. + Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Code structure recommendations for panels using clusters” +- Rule: The user menu is configured via `userMenuItems()` with Action objects and supports conditional visibility and sidebar placement. + When: When adding account-related links/actions. When NOT: Don’t put destructive actions there without confirmation + authorization. + Source: https://filamentphp.com/docs/5.x/navigation/user-menu — “Introduction” + +## Theming & Branding — CSS Hooks, Colors, Icons +- Rule: Filament exposes CSS hook classes (prefixed `fi-`) and recommends discovering them via browser DevTools. + When: When styling core UI safely. When NOT: Don’t rely on brittle selectors; request/PR missing hooks instead of hacking around them. + Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Discovering hook classes” +- Rule: Apply styling by targeting hook classes (including Tailwind `@apply`) and use `!important` sparingly. + When: When overriding default spacing/appearance in a maintainable way. When NOT: Don’t blanket `!important` your theme. + Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Applying styles to hook classes” +- Rule: Customize the default semantic color palettes via `FilamentColor::register()` (e.g., primary, danger, etc.). + When: When aligning Filament semantics to your brand palette. When NOT: Don’t hardcode per-component hex values when semantics suffice. + Source: https://filamentphp.com/docs/5.x/styling/colors — “Customizing the default colors” +- Rule: Replace default UI icons globally via `FilamentIcon::register()` (icon aliases map to your chosen icons). + When: When standardizing iconography or switching icon sets. When NOT: Don’t hardcode SVGs everywhere when aliases cover the UI. + Source: https://filamentphp.com/docs/5.x/styling/icons — “Replacing the default icons” + +## Asset System — Global + Lazy / On-Demand +- Rule: Register shared assets via `FilamentAsset::register()`; assets are published into `/public` when `php artisan filament:assets` runs. + When: For app/plugin assets loaded by Filament. When NOT: Don’t assume assets exist in production without the publish step. + Source: https://filamentphp.com/docs/5.x/advanced/assets — “The FilamentAsset facade” +- Rule: Lazy-load CSS with `x-load-css` and prevent auto-loading via `loadedOnRequest()` for page-specific styles. + When: For heavy CSS used only on certain pages/components. When NOT: Don’t lazy-load tiny styles used everywhere. + Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading CSS” +- Rule: Lazy-load JavaScript with `x-load-js` and prevent auto-loading via `loadedOnRequest()` for page-specific scripts. + When: For heavy JS libraries used only on a subset of pages/widgets. When NOT: Don’t lazy-load scripts required for baseline panel behavior. + Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading JavaScript” + +## Plugin System — Mechanics + Panel Scoping +- Rule: Panel plugins integrate via a Plugin object; standalone plugins integrate via a service provider (no panel binding). + When: When deciding whether a package needs panel-specific registration. When NOT: Don’t force a Plugin object for standalone-only components. + Source: https://filamentphp.com/docs/5.x/plugins/getting-started — “The Plugin object” +- Rule: Plugin assets should be registered in the plugin’s service provider so Filament can manage publishing/loading. + When: When shipping CSS/JS/Alpine components with a package. When NOT: Don’t register package assets ad-hoc only in app panel providers. + Source: https://filamentphp.com/docs/5.x/plugins/getting-started — “Registering assets” +- Rule: Panel-scoped module/plugin registration can be centralized using `Panel::configureUsing()` for modular architectures. + When: When modules should enable Filament artifacts only for specific panels. When NOT: Don’t register all modules globally if panels target different audiences. + Source: https://filamentphp.com/docs/5.x/advanced/modular-architecture — “Registering plugins conditionally for specific panels” +- Rule: Panel plugins are configurable per panel; treat “enabled in panel A” and “enabled in panel B” as independent configuration contexts. + When: When the same app has multiple panels with different modules enabled. When NOT: Don’t assume a plugin is active across all panels. + Source: https://filamentphp.com/docs/5.x/plugins/panel-plugins — “Introduction” + +## Actions — Modals, Confirmation, Execution +- Rule: Use `Action::make(...)->action(...)` to execute logic; use `requiresConfirmation()` for confirmation modals. + When: For destructive or high-impact actions. When NOT: Don’t skip confirmation for destructive operations. + Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” +- Rule: Action modals can render a schema to collect input; submitted modal data is available to the action handler. + When: When you need “form-in-action” workflows. When NOT: Don’t create bespoke modals when schema-in-modal is sufficient. + Source: https://filamentphp.com/docs/5.x/actions/modals — “Rendering a form in a modal” + +## Resources & Pages — Global Search, Queries +- Rule: Global search returns results for a resource only if it has an Edit or View page; otherwise the resource returns no results. + When: When enabling global search for a resource. When NOT: Don’t expect global search on resources without Edit/View pages. + Source: https://filamentphp.com/docs/5.x/resources/global-search — “Setting global search result titles” +- Rule: If global search result details access relationships, override the global search query to eager-load them to avoid N+1. + When: Any time you return relationship-backed details. When NOT: Don’t lazy-load relationships in global search rendering. + Source: https://filamentphp.com/docs/5.x/resources/global-search — “Adding extra details to global search results” +- Rule: You can disable search term splitting for performance on large datasets. + When: When global search becomes expensive. When NOT: Don’t disable if multi-word relevance is important. + Source: https://filamentphp.com/docs/5.x/resources/global-search — “Disabling search term splitting” + +## Tables — Empty States +- Rule: Tables support empty-state content and can add empty-state actions to guide first-time users. + When: Always—empty-state guidance improves “first run” UX. When NOT: Don’t leave empty lists without a clear next step when creation is expected. + Source: https://filamentphp.com/docs/5.x/tables/empty-state — “Adding empty state actions” + +## Testing +- Rule: Filament testing distinguishes Livewire components (pages/relation managers/widgets) from non-Livewire classes (resources/actions/schema objects). + When: When choosing what to mount with `Livewire::test()`. When NOT: Don’t try to Livewire-test non-Livewire classes directly. + Source: https://filamentphp.com/docs/5.x/testing/overview — “What is a Livewire component when using Filament?” +- Rule: Actions have dedicated testing guidance; follow it for asserting action execution and side effects. + When: When actions drive critical business workflows. When NOT: Don’t leave actions untested if they mutate data or trigger external effects. + Source: https://filamentphp.com/docs/5.x/testing/testing-actions — “Testing actions” + +## OPINIONATED — best practices not explicitly mandated by docs +- Rule: Standardize destructive actions: danger styling + requiresConfirmation() + policy authorization + success/error notification. + When: For delete/force-delete/restore/bulk destructive ops. When NOT: For trivial, reversible toggles. + +## UNVERIFIED — not explicitly stated in the cited v5 pages above +- Rule: If you need confirmation or a modal, prefer `->action(...)` + `->requiresConfirmation()`; use `->url(...)` for navigation links. + When: When deciding whether an action should execute vs navigate. When NOT: Don’t assume modal/confirmation behavior for URL-only actions without verifying in docs. + Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals” \ No newline at end of file diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..6f800ed --- /dev/null +++ b/opencode.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "laravel-boost": { + "type": "local", + "enabled": true, + "command": [ + "vendor/bin/sail", + "artisan", + "boost:mcp" + ] + } + } +} \ No newline at end of file diff --git a/specs/057-filament-v5-upgrade/checklists/requirements.md b/specs/057-filament-v5-upgrade/checklists/requirements.md index 3ceac5d..7cb996b 100644 --- a/specs/057-filament-v5-upgrade/checklists/requirements.md +++ b/specs/057-filament-v5-upgrade/checklists/requirements.md @@ -31,5 +31,5 @@ ## Feature Readiness ## Notes -- This is a technical upgrade, but the spec intentionally describes outcomes and guardrails rather than implementation steps. -- Proceed to planning: identify concrete packages, versions, and migration steps in plan/tasks. +- This is a technical upgrade, but the spec describes outcomes and guardrails rather than implementation steps. +- Planning can capture concrete versions, dependency changes, and migration steps. diff --git a/specs/057-filament-v5-upgrade/spec.md b/specs/057-filament-v5-upgrade/spec.md index 06208b0..4bc3e16 100644 --- a/specs/057-filament-v5-upgrade/spec.md +++ b/specs/057-filament-v5-upgrade/spec.md @@ -3,7 +3,7 @@ # Feature Specification: Admin UI Stack Upgrade (Panel + Suite) **Feature Branch**: `057-filament-v5-upgrade` **Created**: 2026-01-20 **Status**: Draft -**Input**: Upgrade the existing admin UI stack to the next supported major release to maintain compatibility and support, and ensure no regressions for tenant isolation and Ops-UX/Monitoring guardrails. +**Input**: Upgrade the existing admin UI stack to the next supported major release to maintain compatibility and support, and ensure no regressions for tenant isolation and Monitoring/Operations safety guardrails. ## Clarifications @@ -19,7 +19,7 @@ ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Admin UI keeps working after upgrade (Priority: P1) -Administrators can sign in and use the admin panel (navigation, resources, forms, tables, actions) without runtime errors after the upgrade. +Administrators can sign in and use the admin panel (navigation, lists, forms, actions) without runtime errors after the upgrade. **Why this priority**: This is the minimum bar for the upgrade to be safe; if the admin UI is unstable, all operational work stops. @@ -75,18 +75,18 @@ ## Requirements *(mandatory)* ### Functional Requirements -- **FR-001**: The application MUST upgrade the admin panel stack to the next supported major release and its required compatible reactive UI layer. -- **FR-002**: The application MUST continue to support the existing styling system without asset build failures. -- **FR-003**: All existing Filament panels MUST load successfully for authorized users and preserve core interactions (navigation, tables, forms, actions, notifications, widgets). -- **FR-004**: SPA-style navigation flows MUST continue to work, including global widget mounting and event delivery across navigation. -- **FR-005**: Everything rendered under the Monitoring → Operations navigation section (including widgets/partials/tabs) MUST remain DB-only: no outbound HTTP requests are permitted during page render or during background/automatic Livewire requests (polling/auto-refresh/hydration). -- **FR-010**: Any remote work initiated from Monitoring/Operations pages MUST be triggered only by explicit user actions (e.g., buttons) and MUST enqueue tracked operations (e.g., `OperationRun`-backed jobs) rather than performing outbound HTTP inline. -- **FR-006**: Tenant isolation MUST be preserved across requests and Live UI interactions: all reads/writes/events/caches MUST scope to the active tenant. -- **FR-007**: Compatibility risks MUST be managed by producing an explicit inventory of affected third-party packages and documenting upgrade/replacement decisions. -- **FR-011**: If a third-party package is incompatible with the upgraded stack, the system MUST preserve equivalent functionality by upgrading or replacing the package; mixed-version pinning is not allowed. Any unavoidable feature loss MUST be handled as an explicit scope/decision change. -- **FR-012**: Database migrations are allowed only if strictly required for compatibility; they MUST be reversible and non-destructive (no data loss). Migrations MUST include a safe `down()` path and avoid drops without an explicit plan, and MUST be mentioned in release notes. +- **FR-001**: The system MUST upgrade the admin UI stack to the next supported major release and remain fully functional for all in-scope admin workflows. +- **FR-002**: The system MUST continue to support the existing styling and asset pipeline without build failures. +- **FR-003**: All existing admin pages MUST load successfully for authorized users and preserve core interactions (navigation, lists, forms, actions, notifications, and global UI elements). +- **FR-004**: In-app navigation between admin pages MUST continue to work reliably, including any global progress indicators and event-driven UI behavior. +- **FR-005**: Everything rendered under the Monitoring → Operations navigation section (including widgets/partials/tabs) MUST remain DB-only: no outbound HTTP requests are permitted during page render or during background/automatic requests (polling/auto-refresh/hydration). +- **FR-006**: Tenant isolation MUST be preserved across requests and interactive UI behavior: all reads/writes/events/caches MUST scope to the active tenant. +- **FR-007**: Compatibility risks MUST be managed by producing an explicit inventory of affected third-party dependencies and documenting upgrade/replacement decisions. - **FR-008**: The upgrade MUST not introduce new Microsoft Graph read/write behavior; if any Graph-touching behavior changes are required, they MUST be explicitly specified with safety gates and observability updates. - **FR-009**: The upgrade MUST include a documented rollback procedure that restores the previous working state. +- **FR-010**: Any remote work initiated from Monitoring/Operations pages MUST be triggered only by explicit user actions and MUST enqueue a tracked operation (with an observable run record) rather than performing outbound HTTP inline. +- **FR-011**: If a third-party dependency is incompatible with the upgraded stack, the system MUST preserve equivalent functionality by upgrading or replacing the dependency; mixed-version pinning is not allowed. Any unavoidable feature loss MUST be handled as an explicit scope/decision change. +- **FR-012**: Database migrations are allowed only if strictly required for compatibility; they MUST be reversible and non-destructive (no data loss) and MUST be mentioned in release notes. ### Assumptions & Dependencies @@ -97,7 +97,7 @@ ### Assumptions & Dependencies ### Key Entities *(include if feature involves data)* - **Tenant**: The active tenant context that scopes all data access and UI state. -- **OperationRun**: The persisted record of long-running operations shown in Monitoring/Operations views. +- **Run Record**: The persisted record of long-running operations shown in Monitoring/Operations views. - **AuditLog**: The tenant-scoped audit trail used to retain accountability for sensitive actions. ## Success Criteria *(mandatory)* diff --git a/specs/058-tenant-ui-polish/checklists/requirements.md b/specs/058-tenant-ui-polish/checklists/requirements.md new file mode 100644 index 0000000..e4bf746 --- /dev/null +++ b/specs/058-tenant-ui-polish/checklists/requirements.md @@ -0,0 +1,35 @@ +# Specification Quality Checklist: Tenant UI Polish (v1) + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-01-20 +**Feature**: [specs/058-tenant-ui-polish/spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- Validation run: 2026-01-20 +- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan` diff --git a/specs/058-tenant-ui-polish/contracts/polling.md b/specs/058-tenant-ui-polish/contracts/polling.md new file mode 100644 index 0000000..30ee404 --- /dev/null +++ b/specs/058-tenant-ui-polish/contracts/polling.md @@ -0,0 +1,22 @@ +# Polling Contract — Calm UI Rules + +## Principle +Polling is allowed only when it materially improves UX (active operations). It must be DB-only and must stop when no longer needed. + +## Dashboard +- Polling is enabled only while active runs exist (queued/running) for the current tenant. +- Polling is disabled when: + - No active runs exist. + +## Operations index +- Polling is enabled only while active runs exist. +- Polling is disabled when: + - No active runs exist. + +## Modals +- No polling inside modals. +- When a modal is open, polling should not cause churn in the background. + +## Technical approach +- Widgets: use `$pollingInterval = null` to disable polling. +- Tables: apply `$table->poll('10s')` only when active runs exist. diff --git a/specs/058-tenant-ui-polish/contracts/ui.md b/specs/058-tenant-ui-polish/contracts/ui.md new file mode 100644 index 0000000..b6b6256 --- /dev/null +++ b/specs/058-tenant-ui-polish/contracts/ui.md @@ -0,0 +1,28 @@ +# UI Contracts — Tenant UI Polish + +This feature does not introduce HTTP APIs. These contracts describe UI routing, filters, and definitions that must remain stable. + +## Routes (tenant-scoped) +- Dashboard: tenant dashboard page (new custom page; replaces default dashboard entry). +- Inventory hub: Inventory cluster root (routes to first page/resource in cluster). +- Inventory items: Inventory items resource index, under cluster prefix. +- Inventory sync runs: Inventory sync runs resource index, under cluster prefix. +- Inventory coverage: Inventory coverage page, under cluster prefix. +- Operations index: `OperationRunResource` index (`/operations`). +- Operation run detail: `OperationRunResource` view page. + +## Operations Tabs (FR-009) +Tabs filter the Operations table by: +- All: no extra constraints. +- Active: `status IN ('queued','running')` +- Succeeded: `status = 'completed' AND outcome = 'succeeded'` +- Partial: `status = 'completed' AND outcome = 'partial'` +- Failed: `status = 'completed' AND outcome = 'failed'` + +## KPI Definitions +- Inventory coverage % = Restorable / Total (Partial is separate, does not inflate %). +- Drift stale threshold = 7 days. +- “Recent” lists default size = 10. +- “Active operations” shows two counts: + - All active runs (queued + running) + - Inventory-active runs (type = `inventory.sync`, queued + running) diff --git a/specs/058-tenant-ui-polish/data-model.md b/specs/058-tenant-ui-polish/data-model.md new file mode 100644 index 0000000..d328232 --- /dev/null +++ b/specs/058-tenant-ui-polish/data-model.md @@ -0,0 +1,76 @@ +# Data Model — Tenant UI Polish + +This feature is read-only. It introduces no schema changes. + +## Entities + +### Tenant +- **Role**: scope boundary for all queries. +- **Source**: `Tenant::current()` (Filament tenancy). + +### OperationRun +- **Role**: operations feed, KPIs, and canonical “View run” destinations. +- **Key fields used** (existing): + - `tenant_id` + - `type` + - `status` (`queued|running|completed`) + - `outcome` (`succeeded|partial|failed|...`) + - `created_at`, `started_at`, `completed_at` + - `summary_counts`, `failure_summary` (JSONB) + +**Derived values**: +- **Active**: `status IN ('queued','running')` +- **Terminal**: `status = 'completed'` +- **Avg duration (7 days)**: only terminal runs with `started_at` and `completed_at`. + +### InventoryItem +- **Role**: inventory totals and coverage chips. +- **Key fields used** (existing, inferred from resources): + - `tenant_id` + - coverage-related flags / fields used to categorize: Restorable, Partial, Risk, Dependencies + +**Derived values**: +- Total items +- Coverage % = `restorable / total` (if total > 0) +- Chip counts: Restorable, Partial, Risk, Dependencies + +### InventorySyncRun +- **Role**: “Last Inventory Sync” and “Sync Runs” list. +- **Key fields used**: + - `tenant_id` + - status + timestamps + - any “selection_hash / selection payload” metadata used for display + +### Finding (Drift Finding) +- **Role**: drift KPIs and “Needs Attention”. +- **Key fields used** (existing migration): + - `tenant_id` + - `severity` (enum-like string) + - `status` (open/closed) + - timestamps + - `scope_key` for grouping + +**Derived values**: +- Open findings by severity +- Staleness: last drift scan older than 7 days + +## KPI Queries (read-only) + +### Dashboard +- Drift KPIs: counts of open findings by severity + stale drift indicator. +- Operations health: counts of active runs + failed/partial recent. +- Recent lists: latest 10 findings + latest 10 operation runs. + +### Inventory hub +- Total items +- Coverage % (restorable/total) +- Last inventory sync (status + timestamp) +- Active operations: (all active runs) + (inventory.sync active runs) + +### Operations index +- Total runs (30d) +- Active runs (queued + running) +- Failed/partial (7d) +- Avg duration (7d, terminal runs only) + +All queries must be tenant-scoped. diff --git a/specs/058-tenant-ui-polish/plan.md b/specs/058-tenant-ui-polish/plan.md new file mode 100644 index 0000000..540c767 --- /dev/null +++ b/specs/058-tenant-ui-polish/plan.md @@ -0,0 +1,164 @@ +# Implementation Plan: Tenant UI Polish (Dashboard + Inventory Hub + Operations) + +**Branch**: `058-tenant-ui-polish` | **Date**: 2026-01-20 | **Spec**: `specs/058-tenant-ui-polish/spec.md` +**Input**: Feature specification from `specs/058-tenant-ui-polish/spec.md` + +**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts. + +## Summary + +- Build a drift-first, tenant-scoped dashboard with “Needs Attention” and recent lists. +- Make Inventory a hub using a Filament cluster to provide consistent left-side sub-navigation across Items / Sync Runs / Coverage. +- Upgrade Operations index to “orders-style” with KPIs + status tabs filtering the existing `OperationRunResource` table. +- Enforce DB-only renders (and DB-only polling) and a calm UI: polling only while active runs exist, and no polling churn in modals. + +## Technical Context + + + +**Language/Version**: PHP 8.4.15 (Laravel 12.47.0) +**Primary Dependencies**: Filament v5.0.0, Livewire v4.0.1 +**Storage**: PostgreSQL +**Testing**: Pest v4 (+ PHPUnit v12 runtime) +**Target Platform**: Web application (Filament admin panel) +**Project Type**: Web (Laravel monolith) +**Performance Goals**: Dashboard/Inventory/Operations render quickly (target <2s for typical tenants) with efficient tenant-scoped queries and no N+1. +**Constraints**: DB-only for all page renders and any polling/auto-refresh; avoid UI churn in modals. +**Scale/Scope**: Tenant-scoped surfaces; KPI math on existing `operation_runs`, `findings`, inventory tables. + +## Constitution Check + +*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* + +- Inventory-first: clarify what is “last observed” vs snapshots/backups +- Read/write separation: any writes require preview + confirmation + audit + tests +- Graph contract path: Graph calls only via `GraphClientInterface` + `config/graph_contracts.php` +- Deterministic capabilities: capability derivation is testable (snapshot/golden tests) +- Tenant isolation: all reads/writes tenant-scoped; cross-tenant views are explicit and access-checked +- Run observability: long-running/remote/queued work creates/reuses `OperationRun`; start surfaces enqueue-only; Monitoring is DB-only; DB-only <2s actions may skip runs but security-relevant ones still audit-log +- Automation: queued/scheduled ops use locks + idempotency; handle 429/503 with backoff+jitter +- Data minimization: Inventory stores metadata + whitelisted meta; logs contain no secrets/tokens + +Status: ✅ No constitution violations for this feature (read-only, DB-only, tenant-scoped; no Graph calls added). + +## Project Structure + +### Documentation (this feature) + +```text +specs/058-tenant-ui-polish/ +├── plan.md # This file (/speckit.plan command output) +├── research.md # Phase 0 output (/speckit.plan command) +├── data-model.md # Phase 1 output (/speckit.plan command) +├── quickstart.md # Phase 1 output (/speckit.plan command) +├── contracts/ # Phase 1 output (/speckit.plan command) +└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan) +``` + +### Source Code (repository root) + + +```text +app/ +├── Filament/ +│ ├── Clusters/ # New: Inventory cluster +│ ├── Pages/ # New/updated: tenant dashboard, inventory landing, coverage +│ ├── Resources/ # Updated: attach inventory resources to cluster; operations tabs/KPIs +│ └── Widgets/ # New/updated: KPI header widgets +├── Models/ # Existing: Tenant, OperationRun, Finding, InventoryItem, InventorySyncRun +└── Providers/Filament/ + └── AdminPanelProvider.php # Update: discoverClusters(), dashboard page class + +resources/ +└── views/ # Optional: partials/views for dashboard sections + +tests/ +└── Feature/ + ├── Monitoring/ # Existing: Operations DB-only + tenant scope tests + └── Filament/ # Existing + new: Inventory/Dashboard page tests +``` + +**Structure Decision**: Laravel monolith + Filament (v5) conventions. Implement UI changes via: +- Filament Pages (dashboard + inventory pages) +- Filament Clusters (inventory sub-navigation) +- Filament Widgets (KPI headers / recent lists) +- Filament Resource list tabs (operations index filtering) + +## Complexity Tracking + +> **Fill ONLY if Constitution Check has violations that must be justified** + +None. + +## Phase 0 — Outline & Research (complete) + +- Output: `specs/058-tenant-ui-polish/research.md` +- Key decisions captured: + - Use Filament clusters for the Inventory hub sub-navigation. + - Use Filament widgets for KPI headers. + - Enable polling only while active runs exist. + +## Phase 1 — Design & Contracts (complete) + +### Data model +- Output: `specs/058-tenant-ui-polish/data-model.md` +- No schema changes required. + +### UI contracts +- Output: `specs/058-tenant-ui-polish/contracts/ui.md` +- Output: `specs/058-tenant-ui-polish/contracts/polling.md` + +### Provider registration (Laravel 11+) +- Panel providers remain registered in `bootstrap/providers.php` (no changes required for this feature unless adding a new provider). + +### Livewire / Filament version safety +- Livewire v4.0+ (required by Filament v5) is in use. + +### Asset strategy +- Prefer existing Filament theme CSS and hook classes; avoid publishing Filament internal views. +- No heavy assets expected; if any new panel assets are added, ensure deployment runs `php artisan filament:assets`. + +### Destructive actions +- None introduced in this feature. + +### Constitution re-check (post-design) +- ✅ Inventory-first: dashboard uses Inventory/Findings/OperationRun as last-observed state. +- ✅ Read/write separation: this feature is read-only. +- ✅ Graph contract path: no Graph calls added. +- ✅ Tenant isolation: all queries remain tenant-scoped. +- ✅ Run observability: only consumes existing `OperationRun` records; no new long-running work is introduced. +- ✅ Data minimization: no new payload storage. + +## Phase 2 — Implementation Plan (next) + +### Story 1 (P1): Drift-first tenant dashboard +- Create a custom Filament dashboard page (tenant-scoped) and wire it in `AdminPanelProvider` instead of the default `Dashboard::class`. +- Implement drift + ops KPIs and “Needs Attention” + recent lists using DB-only Eloquent queries. +- Implement conditional polling (only while active runs exist) using widget polling controls. +- Tests: + - Add DB-only coverage tests for the dashboard (no outbound HTTP; no queued jobs on render). + - Add tenant scope tests for the dashboard. + +### Story 2 (P2): Inventory becomes a hub +- Add `discoverClusters()` to `AdminPanelProvider`. +- Create `InventoryCluster` and assign `$cluster` on inventory pages/resources. +- Add a shared inventory KPI header (widget) across the cluster surfaces. +- Tests: + - Extend existing inventory page tests to assert cluster pages load and remain tenant-scoped. + +### Story 3 (P3): Operations index “orders-style” +- Update `OperationRunResource` list page to: + - Add KPI header widgets. + - Add tabs: All / Active / Succeeded / Partial / Failed. + - Enable table polling only while active runs exist. +- Tests: + - Extend operations tests to assert page renders with tabs and remains DB-only/tenant-scoped. diff --git a/specs/058-tenant-ui-polish/quickstart.md b/specs/058-tenant-ui-polish/quickstart.md new file mode 100644 index 0000000..70a0ffb --- /dev/null +++ b/specs/058-tenant-ui-polish/quickstart.md @@ -0,0 +1,27 @@ +# Quickstart — Tenant UI Polish + +## Prereqs +- Run everything via Sail. + +## Setup +- `vendor/bin/sail up -d` +- `vendor/bin/sail composer install` + +## Run tests (targeted) +- `vendor/bin/sail artisan test tests/Feature/Monitoring/OperationsDbOnlyTest.php` +- `vendor/bin/sail artisan test tests/Feature/Monitoring/OperationsTenantScopeTest.php` +- `vendor/bin/sail artisan test tests/Feature/Filament/InventoryPagesTest.php` + +When the feature is implemented, add + run: +- Dashboard DB-only + tenant scope tests (new). + +## Manual QA (tenant-scoped) +- Sign in, select a tenant. +- Visit Dashboard: verify drift/ops KPIs, needs attention, and recent lists. +- Visit Inventory cluster: Items / Sync Runs / Coverage share left sub-navigation and KPI header. +- Visit Operations (`/operations`): KPI header + tabs filter table. + +## Frontend assets +If UI changes don’t show: +- `vendor/bin/sail npm run dev` +- or `vendor/bin/sail npm run build` diff --git a/specs/058-tenant-ui-polish/research.md b/specs/058-tenant-ui-polish/research.md new file mode 100644 index 0000000..b436fe7 --- /dev/null +++ b/specs/058-tenant-ui-polish/research.md @@ -0,0 +1,66 @@ +# Research — Tenant UI Polish (Dashboard + Inventory Hub + Operations) + +## Goal +Deliver a drift-first, tenant-scoped UI polish pass that is: +- DB-only on render and on any auto-refresh. +- Calm (polling only when needed; no modal churn). +- Consistent IA (Inventory hub sub-navigation; canonical Operations). + +## Existing Code & Patterns (to reuse) + +### Operations +- Canonical list/detail already exist via `OperationRunResource` (`/operations`). +- Tenant scoping already enforced in `OperationRunResource::getEloquentQuery()`. +- Detail view already uses conditional polling with safeguards (tab hidden / modal open) via `RunDetailPolling`. + +### Inventory +- Inventory entry page exists as `InventoryLanding`. +- Inventory “Items” and “Sync Runs” are currently resources (`InventoryItemResource`, `InventorySyncRunResource`). +- Inventory sync “start surface” already follows constitution rules: authorize → create/reuse `OperationRun` → enqueue job → “View run”. + +### Monitoring DB-only + Tenant isolation tests +- Monitoring/Operations has DB-only tests and tenant scope tests. +- Inventory landing + coverage have basic smoke tests. + +## Key Decisions + +### Decision: Use Filament clusters to implement the Inventory “hub” navigation +- **Decision**: Create an Inventory cluster and attach: + - `InventoryLanding` (page) + - Inventory items resource + - Inventory sync runs resource + - `InventoryCoverage` (page) +- **Rationale**: Filament clusters are designed for “common sub-navigation between pages”, including mixing pages and resources. +- **Notes**: + - Requires enabling cluster discovery in the panel provider. + - Sub-navigation position will be set to `Start` to achieve left-side navigation. + +### Decision: Implement KPI headers as widgets (StatsOverviewWidget / TableWidget) +- **Decision**: Use Filament widgets for KPI headers on: + - Tenant dashboard (drift + ops) + - Inventory hub (inventory KPIs) + - Operations index (ops KPIs) +- **Rationale**: Widgets are first-class, composable, and can optionally poll (with `$pollingInterval`) while remaining DB-only. + +### Decision: “Calm UI” auto-refresh strategy +- **Decision**: + - Dashboard + Operations index: enable polling only while active runs exist. + - Widgets/tables: polling is disabled when no active runs exist. + - No polling inside modals. +- **Rationale**: Matches FR-012 and avoids background churn. +- **Implementation approach**: + - Use Filament polling mechanisms: + - Widgets: `$pollingInterval = null | '10s'` depending on “active runs exist”. + - Tables: enable `$table->poll('10s')` only when “active runs exist”. + +### Decision: No Graph / remote dependencies +- **Decision**: All queries for this feature are Eloquent/PostgreSQL queries. +- **Rationale**: Matches constitution and SC-005. + +## Alternatives Considered +- **Custom Blade layouts for hub navigation**: Rejected because clusters provide consistent sub-nav across resources/pages without fragile view overrides. +- **Always-on polling**: Rejected to comply with calm UI rules and avoid waste. +- **Keep `Monitoring/Operations` as canonical**: Rejected because `OperationRunResource` is already the canonical Operations surface with correct routing and detail pages. + +## Open Questions +None — all “NEEDS CLARIFICATION” items are resolved for planning. diff --git a/specs/058-tenant-ui-polish/spec.md b/specs/058-tenant-ui-polish/spec.md new file mode 100644 index 0000000..eed7ac4 --- /dev/null +++ b/specs/058-tenant-ui-polish/spec.md @@ -0,0 +1,218 @@ +# Feature Specification: Tenant UI Polish (Dashboard + Inventory Hub + Operations) + +**Feature Branch**: `058-tenant-ui-polish` +**Created**: 2026-01-20 +**Status**: Draft +**Input**: User description: "Feature 058 — Tenant UI Polish: Dashboard + Inventory Hub + Operations \"Orders-style\" (v1)" + +## Clarifications + +### Session 2026-01-20 + +- Q: Coverage % definition for Inventory KPI header? → A: Coverage % = Restorable / Total (Partial remains a separate chip/number; main % stays conservative) +- Q: Drift stale threshold (last scan older than X days)? → A: 7 days +- Q: Inventory KPI “Active Operations” definition? → A: Show both counts: All active runs (queued + running) and Inventory-active runs (queued + running) +- Q: How many rows in “Recent” lists by default? → A: 10 + +### Session 2026-01-21 + +- Q: Operations index "Stuck" tab in v1? -> A: No "Stuck" tab in v1 + +## User Scenarios & Testing *(mandatory)* + + + +### User Story 1 - Drift-first tenant dashboard (Priority: P1) + +As a tenant admin, I can open a tenant-scoped dashboard that immediately surfaces drift risk and operations health, without triggering any remote calls. + +**Why this priority**: This is the primary entry point for day-to-day operations and should be actionable at a glance. + +**Independent Test**: Visiting the dashboard shows drift + operations KPIs, a “needs attention” list with working CTAs, and recent lists, while confirming no outbound HTTP happens during render and any background UI updates. + +**Acceptance Scenarios**: + +1. **Given** I am signed in to a tenant, **When** I open the Dashboard, **Then** I see tenant-scoped drift KPIs, operations health KPIs, and recent lists. +2. **Given** there are urgent drift issues (e.g., high severity open findings), **When** I view the Dashboard, **Then** they appear in the “Needs Attention” section with a CTA that navigates to a filtered view. +3. **Given** drift generation has a recent failed run, **When** I view the Dashboard, **Then** I can navigate from “Needs Attention” to the related operation run details. +4. **Given** there is no drift data yet, **When** I view the Dashboard, **Then** the dashboard renders calmly with empty-state messaging and no errors. +5. **Given** the last drift scan is older than 7 days, **When** I view the Dashboard, **Then** “Needs Attention” includes a “Drift stale” item with a CTA to investigate. +6. **Given** there are more than 10 drift findings and operation runs, **When** I view the Dashboard, **Then** each “Recent” list shows the 10 most recent items. + +--- + +### User Story 2 - Inventory becomes a hub module (Priority: P2) + +As a tenant admin, I can use Inventory as a “hub” with consistent sub-navigation and a shared KPI header across Inventory subpages. + +**Why this priority**: Inventory is a high-traffic area; a hub layout reduces cognitive load and makes it easier to find the right view quickly. + +**Independent Test**: Navigating Inventory Items / Sync Runs / Coverage keeps the same shared KPI header, the left sub-navigation is consistent, and all data remains tenant-scoped and DB-only. + +**Acceptance Scenarios**: + +1. **Given** I am signed in to a tenant, **When** I open Inventory, **Then** I see hub navigation (Items / Sync Runs / Coverage) and a shared KPI header. +2. **Given** I switch between Inventory subpages, **When** I navigate Items → Sync Runs → Coverage, **Then** the KPI header remains visible and consistent. +3. **Given** the tenant has an inventory sync run history, **When** I open “Sync Runs”, **Then** I see only sync runs relevant to inventory synchronization. + +--- + +### User Story 3 - Operations index “Orders-style” (Priority: P3) + +As a tenant admin, I can view Operations in an “orders-style” overview (KPIs + status tabs + table) to quickly assess activity and failures. + +**Why this priority**: Operations is the canonical place to investigate work; better scanning and filtering reduces time-to-triage. + +**Independent Test**: Visiting Operations index shows KPI cards and status tabs that correctly filter the table without introducing polling churn or any remote calls. + +**Acceptance Scenarios**: + +1. **Given** I am signed in to a tenant, **When** I open Operations, **Then** I see KPIs, status tabs, and the operations table. +2. **Given** there are active runs, **When** I click the “Active” tab, **Then** the table filters to queued + running runs only. +3. **Given** there are failed runs, **When** I click the “Failed” tab, **Then** the table filters to failed runs only. +4. **Given** I navigate away and back, **When** I return to Operations, **Then** the UI remains calm (no refresh loops) and loads quickly. + +--- + +[Add more user stories as needed, each with an assigned priority] + +### Edge Cases + +- No data yet: dashboard/inventory/operations render with empty states and helpful CTAs. +- Large tenants: KPI calculations remain fast enough to keep pages responsive. +- Mixed outcomes: partial/failed/succeeded runs are correctly categorized and discoverable via tabs/filters. +- Tenant switching: no cross-tenant leakage of KPIs, lists, or links. +- Time windows: KPI windows (e.g., last 7/30 days) handle timezones consistently. +- “Unknown” states: missing duration/end time renders gracefully (e.g., avg duration excludes non-terminal runs). + +## Requirements *(mandatory)* + +**Constitution alignment (required):** If this feature introduces any Microsoft Graph calls, any write/change behavior, +or any long-running/queued/scheduled work, the spec MUST describe contract registry updates, safety gates +(preview/confirmation/audit), tenant isolation, run observability (`OperationRun` type/identity/visibility), and tests. +If security-relevant DB-only actions intentionally skip `OperationRun`, the spec MUST describe `AuditLog` entries. + + + +### Functional Requirements + +- **FR-001 (Tenant scope)**: System MUST ensure the Dashboard, Inventory hub, and Operations views are tenant-scoped, with no cross-tenant visibility. +- **FR-002 (DB-only surfaces)**: System MUST keep Dashboard, Inventory hub header, and Operations index DB-only during render and any background UI updates. +- **FR-003 (Placement policy)**: System MUST show KPI cards only on these entry-point pages: Dashboard, Inventory hub (shared header), and Operations index. +- **FR-004 (Inventory hub layout)**: System MUST provide an Inventory hub with left sub-navigation for Items, Sync Runs, and Coverage. +- **FR-005 (Inventory KPIs)**: Inventory hub MUST show a shared KPI header across Inventory subpages with: + - Total Items + - Coverage % (restorable items / total items; partial shown separately) + - Last Inventory Sync (status + timestamp) + - Active Operations (queued + running), showing both: + - All active runs + - Inventory-active runs +- **FR-006 (Inventory sync runs view)**: System MUST provide a “Sync Runs” view that lists only inventory synchronization runs. +- **FR-007 (Coverage chips)**: System MUST standardize coverage chips to this set only: Restorable, Partial, Risk, Dependencies. +- **FR-008 (Operations index KPIs)**: Operations index MUST show tenant-scoped KPIs: + - Total Runs (30 days) + - Active Runs (queued + running) + - Failed/Partial (7 days) + - Avg Duration (7 days, terminal runs only) +- **FR-009 (Operations tabs)**: Operations index MUST provide status tabs that filter the operations table: All, Active, Succeeded, Partial, Failed. No "Stuck" tab in v1. +- **FR-010 (Canonical terminology)**: System MUST use “Operations” as the canonical label (no legacy naming on these surfaces). +- **FR-011 (Canonical links)**: “View run” links MUST always navigate to the canonical operation run detail view. +- **FR-012 (Calm UI rules)**: System MUST avoid polling/churn in modals and avoid refresh loops; background updates should be used only where clearly necessary. Auto-refresh on Dashboard and Operations index is allowed only while active runs (queued/running) exist, and MUST stop when there are no active runs. +- **FR-013 (Drift stale rule)**: System MUST flag drift as “stale” when the last drift scan is older than 7 days and surface it in “Needs Attention” with an investigation CTA. +- **FR-014 (Recent list sizing)**: System MUST show 10 rows by default for “Recent Drift Findings” and “Recent Operations”. + +### OperationRun status mapping (for tabs and KPIs) + +OperationRun uses two canonical fields that drive UI filters: + +- `status`: execution lifecycle (e.g., queued/running/completed) +- `outcome`: terminal result (e.g., succeeded/partially_succeeded/failed/cancelled) + +Tab filters MUST map exactly as: + +- **All**: no status/outcome filter +- **Active**: `status IN (queued, running)` +- **Succeeded**: `status = completed AND outcome = succeeded` +- **Partial**: `status = completed AND outcome = partially_succeeded` +- **Failed**: `status = completed AND outcome = failed` + +Notes: + +- No “Stuck” tab in v1. +- Runs with `outcome = cancelled` appear under **All** only (unless a future “Cancelled” tab is added). +- Any legacy status/outcome values must already be normalized before reaching this UI (out of scope for this feature). + +### KPI window definitions (timestamp basis) + +All KPI windows are tenant-scoped and DB-only. + +- **Total Runs (30 days)**: count OperationRuns by `created_at` within the last 30 days (includes all statuses/outcomes). +- **Active Runs**: current count where `status IN (queued, running)` (no time window). +- **Failed/Partial (7 days)**: count terminal runs where `status = completed AND outcome IN (failed, partially_succeeded)` and `completed_at` is within the last 7 days. +- **Avg Duration (7 days)**: average of `(completed_at - started_at)` for runs where `status = completed`, `started_at` and `completed_at` are present, and `completed_at` is within the last 7 days. + +### Inventory coverage classification (Restorable/Partial/Risk/Dependencies) + +Coverage chips and KPI aggregation MUST derive from the existing “policy type meta” and dependency capability signals (DB-only): + +- `inventory_items.policy_type` +- `config('tenantpilot.supported_policy_types')` meta fields: + - `restore` (e.g., enabled / preview-only) + - `risk` (e.g., medium / medium-high / high) +- Dependency support computed via the existing coverage dependency resolver (based on contracts/config). + +Definitions: + +- **Restorable**: inventory items whose policy type meta has `restore = enabled` +- **Partial**: inventory items whose policy type meta has `restore = preview-only` +- **Risk**: inventory items whose policy type meta has `risk IN (medium-high, high)` +- **Dependencies**: inventory items whose policy type supports dependencies per the existing dependency capability resolver + +Notes: + +- This feature does not redefine coverage semantics; it standardizes UI rendering and KPI aggregation based on the existing policy type meta. +- If a policy type is unknown/missing meta, it MUST be treated conservatively (non-restorable) for KPI aggregation. + +**Assumptions**: +- Drift findings, inventory items, and operation runs already exist as tenant-scoped data sources. +- “Coverage %” is Restorable/Total; Partial is shown separately (e.g., chips/secondary metric). If total is 0, coverage shows as not available. +- “Drift stale” default threshold is 7 days. +- “Recent” list default size is 10. +- Auto-refresh behavior (DB-only): Dashboard and Operations index auto-refresh only while active runs exist; otherwise it stops. +- Creating/generating drift is out of scope unless it can be performed as an explicit, enqueue-only user action that results in an operation run. + +### Key Entities *(include if feature involves data)* + +- **Tenant**: The scope boundary for all dashboards and lists in this feature. +- **Operation Run**: A tenant-scoped record of work execution, including status, timestamps, and outcomes used for Operations KPIs and recent lists. +- **Drift Finding**: A tenant-scoped record representing detected drift, including severity and state (open/closed) used for Dashboard KPIs and “Needs Attention”. +- **Inventory Item**: A tenant-scoped record representing inventory coverage and totals used in the Inventory hub. + +## Success Criteria *(mandatory)* + + + +### Measurable Outcomes + +- **SC-001 (Task speed)**: A tenant admin can reach “what needs attention” (drift/ops) from the dashboard within 30 seconds. +- **SC-002 (Discoverability)**: A tenant admin can find inventory sync runs within 2 clicks from Inventory. +- **SC-003 (Triage efficiency)**: A tenant admin can filter Operations to “Active” or “Failed” within 1 click and identify a run to investigate within 60 seconds. +- **SC-004 (Calm UI)**: No refresh loops are observed on Dashboard, Inventory hub pages, or Operations index during normal navigation. +- **SC-005 (Safety)**: Viewing Dashboard, Inventory hub pages, and Operations index does not trigger any outbound HTTP. diff --git a/specs/058-tenant-ui-polish/tasks.md b/specs/058-tenant-ui-polish/tasks.md new file mode 100644 index 0000000..c4ba39c --- /dev/null +++ b/specs/058-tenant-ui-polish/tasks.md @@ -0,0 +1,192 @@ +--- +description: "Task list for feature implementation" +--- + +# Tasks: Tenant UI Polish (Dashboard + Inventory Hub + Operations) + +**Input**: Design documents from `specs/058-tenant-ui-polish/` + +**Tests**: Required (Pest) — this feature changes runtime UI behavior. + +--- + +## Phase 1: Setup (Shared Infrastructure) + +- [ ] T001 Confirm feature inputs exist: specs/058-tenant-ui-polish/spec.md, specs/058-tenant-ui-polish/plan.md +- [ ] T002 Confirm Phase 0/1 artifacts exist: specs/058-tenant-ui-polish/research.md, specs/058-tenant-ui-polish/data-model.md, specs/058-tenant-ui-polish/contracts/ui.md, specs/058-tenant-ui-polish/contracts/polling.md, specs/058-tenant-ui-polish/quickstart.md + +--- + +## Phase 2: Foundational (Blocking Prerequisites) + +- [ ] T003 Create shared helper to detect “active runs exist” for tenant polling in app/Support/OpsUx/ActiveRuns.php +- [ ] T004 [P] Add focused tests for the helper in tests/Feature/OpsUx/ActiveRunsTest.php + +**Checkpoint**: Shared polling predicate exists and is covered. + +--- + +## Phase 3: User Story 1 — Drift-first tenant dashboard (Priority: P1) 🎯 MVP + +**Goal**: Tenant-scoped dashboard that surfaces drift + ops KPIs and “Needs Attention”, DB-only. + +**Independent Test**: Visiting the dashboard renders drift KPIs, ops KPIs, needs-attention CTAs, and recent lists (10 rows), with no outbound HTTP and no background work dispatched. + +### Tests (US1) + +- [ ] T005 [P] [US1] Add DB-only render test (no outbound HTTP, no background work) in tests/Feature/Filament/TenantDashboardDbOnlyTest.php +- [ ] T006 [P] [US1] Add tenant isolation test (no cross-tenant leakage) in tests/Feature/Filament/TenantDashboardTenantScopeTest.php + +### Implementation (US1) + +- [ ] T007 [US1] Create tenant dashboard page in app/Filament/Pages/TenantDashboard.php +- [ ] T008 [US1] Register the tenant dashboard page in app/Providers/Filament/AdminPanelProvider.php (replace default Dashboard page entry) +- [ ] T009 [P] [US1] Create dashboard KPI widget(s) in app/Filament/Widgets/Dashboard/DashboardKpis.php +- [ ] T010 [P] [US1] Create “Needs Attention” widget in app/Filament/Widgets/Dashboard/NeedsAttention.php +- [ ] T011 [P] [US1] Create “Recent Drift Findings” widget (10 rows) in app/Filament/Widgets/Dashboard/RecentDriftFindings.php +- [ ] T012 [P] [US1] Create “Recent Operations” widget (10 rows) in app/Filament/Widgets/Dashboard/RecentOperations.php +- [ ] T013 [US1] Wire widgets into the dashboard page in app/Filament/Pages/TenantDashboard.php (header/sections) and implement conditional polling per specs/058-tenant-ui-polish/contracts/polling.md +- [ ] T014 [US1] Implement drift stale rule (7 days) + CTA wiring in app/Filament/Widgets/Dashboard/NeedsAttention.php +- [ ] T015 [US1] Ensure all dashboard queries are tenant-scoped + DB-only in app/Filament/Pages/TenantDashboard.php and app/Filament/Widgets/Dashboard/*.php + +**Checkpoint**: US1 is shippable as an MVP. + +--- + +## Phase 4: User Story 2 — Inventory becomes a hub module (Priority: P2) + +**Goal**: Inventory becomes a cluster “hub” with consistent left sub-navigation and a shared KPI header across Items / Sync Runs / Coverage. + +**Independent Test**: Navigating Items → Sync Runs → Coverage keeps consistent sub-navigation and shared KPI header, tenant-scoped and DB-only. + +### Tests (US2) + +- [ ] T016 [P] [US2] Add DB-only render test for Inventory hub surfaces in tests/Feature/Filament/InventoryHubDbOnlyTest.php +- [ ] T017 [P] [US2] Extend/adjust inventory navigation smoke coverage in tests/Feature/Filament/InventoryPagesTest.php + +### Implementation (US2) + +- [ ] T018 [US2] Enable cluster discovery in app/Providers/Filament/AdminPanelProvider.php (add `discoverClusters(...)`) +- [ ] T019 [US2] Create Inventory cluster class in app/Filament/Clusters/Inventory/InventoryCluster.php +- [ ] T020 [US2] Assign Inventory cluster to inventory pages in app/Filament/Pages/InventoryLanding.php and app/Filament/Pages/InventoryCoverage.php +- [ ] T021 [US2] Assign Inventory cluster to inventory resources in app/Filament/Resources/InventoryItemResource.php and app/Filament/Resources/InventorySyncRunResource.php +- [ ] T022 [P] [US2] Create shared Inventory KPI header widget in app/Filament/Widgets/Inventory/InventoryKpiHeader.php +- [ ] T023 [US2] Add Inventory KPI header widget to InventoryLanding in app/Filament/Pages/InventoryLanding.php +- [ ] T024 [US2] Add Inventory KPI header widget to InventoryCoverage in app/Filament/Pages/InventoryCoverage.php +- [ ] T025 [US2] Add Inventory KPI header widget to Inventory items list in app/Filament/Resources/InventoryItemResource.php (or its list page) +- [ ] T026 [US2] Add Inventory KPI header widget to Inventory sync runs list in app/Filament/Resources/InventorySyncRunResource.php (or its list page) +- [ ] T027 [US2] Ensure Inventory KPI definitions match specs/058-tenant-ui-polish/contracts/ui.md (coverage % restorable/total; partial separate; two active operations counts) +- [ ] T041 [US2] Inventory coverage semantics reference (A2) + - Identify and document the exact source-of-truth fields for Inventory KPI aggregation: + - `inventory_items.policy_type` + - `config('tenantpilot.supported_policy_types')` meta fields (`restore`, `risk`) + - Dependency support via existing dependency capability resolver + - Ensure KPI and chips read from this source only (DB-only). + - DoD: + - One canonical place documented and referenced by inventory KPIs. + - No “magic” or duplicated classification logic across pages/widgets. +- [ ] T028 [US2] Ensure “Sync Runs” view is inventory-only per spec in app/Filament/Resources/InventorySyncRunResource.php (query/filter by run type/intent if needed) +- [ ] T029 [US2] Standardize coverage chips set on coverage-related surfaces in app/Filament/Pages/InventoryCoverage.php (Restorable, Partial, Risk, Dependencies only) + +**Checkpoint**: Inventory hub behaves as a module with consistent sub-navigation + header. + +--- + +## Phase 5: User Story 3 — Operations index “Orders-style” (Priority: P3) + +**Goal**: Operations index shows KPIs + status tabs + table, with calm conditional polling. + +**Independent Test**: Visiting Operations index shows KPIs and tabs that filter runs per specs/058-tenant-ui-polish/contracts/ui.md, DB-only, calm. + +### Tests (US3) + +- [ ] T030 [P] [US3] Extend Operations DB-only test assertions in tests/Feature/Monitoring/OperationsDbOnlyTest.php (assert tabs/KPI labels appear) +- [ ] T031 [P] [US3] Extend Operations tenant isolation coverage in tests/Feature/Monitoring/OperationsTenantScopeTest.php (assert tab views don’t leak) + +### Implementation (US3) + +- [ ] T032 [P] [US3] Create Operations KPI header widget in app/Filament/Widgets/Operations/OperationsKpiHeader.php +- [ ] T033 [US3] Add KPIs to the Operations list page in app/Filament/Resources/OperationRunResource/Pages/ListOperationRuns.php +- [ ] T034 [US3] Implement status tabs (All/Active/Succeeded/Partial/Failed) on Operations list page in app/Filament/Resources/OperationRunResource/Pages/ListOperationRuns.php +- [ ] T035 [US3] Ensure tab filter logic matches specs/058-tenant-ui-polish/contracts/ui.md by adjusting queries in app/Filament/Resources/OperationRunResource/Pages/ListOperationRuns.php +- [ ] T036 [US3] Implement conditional polling for Operations list (only while active runs exist) by wiring table polling in app/Filament/Resources/OperationRunResource.php and/or app/Filament/Resources/OperationRunResource/Pages/ListOperationRuns.php +- [ ] T037 [US3] Ensure canonical “View run” links still route to OperationRunResource view pages (no legacy routes) + - Verify existing canonical link helper `App\Support\OperationRunLinks` is used consistently. + - If no suitable helper exists for a given surface, add a minimal equivalent and use it everywhere. + +- [ ] T042 [US3] Operations terminology sweep (FR-010) + - Goal: The UI uses the canonical label “Operations” consistently; no legacy naming remains. + - Audit + fix in: + - Navigation label(s) + - Page titles / breadcrumbs + - Resource titles / headings + - Any links mentioning “Bulk Operation Runs” or legacy run naming + - DoD: + - No occurrences of legacy labels remain in UI surfaces for Monitoring/Operations. + - `rg -n "Bulk Operation|BulkOperationRun|Bulk Operation Runs" app resources` returns 0 matches (or matches only in tests explicitly allowed). + - If ripgrep is unavailable, use `grep -R` with the same patterns. + +**Checkpoint**: Operations index is “orders-style” with calm refresh behavior. + +--- + +## Phase 6: Polish & Cross-Cutting Concerns + +- [ ] T038 [P] Run formatting on changed files in app/** and tests/** via `vendor/bin/sail bin pint --dirty` +- [ ] T039 Run targeted tests from specs/058-tenant-ui-polish/quickstart.md and ensure green +- [ ] T040 [P] Smoke-check key pages render for a tenant in tests/Feature/Filament/AdminSmokeTest.php (add assertions only if gaps are found) + +--- + +## Dependencies & Execution Order + +### User Story Dependencies + +- US1 (P1) is standalone and can ship first. +- US2 (P2) can be implemented after foundational polling helper; it touches the panel provider and inventory pages/resources. +- US3 (P3) can be implemented after foundational polling helper; it touches the operations resource list page. + +Suggested order (MVP-first): Phase 1 → Phase 2 → US1 → US2 → US3 → Polish. + +### Parallel Opportunities (examples) + +- US1: T009–T012 can be developed in parallel (different widget files). +- US2: T022 can be developed in parallel while T018–T021 are in review. +- US3: T032 can be developed in parallel with test updates (T030–T031). + +--- + +## Parallel Execution Examples (per user story) + +### US1 +- T005 [P] [US1] tests/Feature/Filament/TenantDashboardDbOnlyTest.php +- T006 [P] [US1] tests/Feature/Filament/TenantDashboardTenantScopeTest.php +- T009 [P] [US1] app/Filament/Widgets/Dashboard/DashboardKpis.php +- T010 [P] [US1] app/Filament/Widgets/Dashboard/NeedsAttention.php +- T011 [P] [US1] app/Filament/Widgets/Dashboard/RecentDriftFindings.php +- T012 [P] [US1] app/Filament/Widgets/Dashboard/RecentOperations.php + +### US2 +- T016 [P] [US2] tests/Feature/Filament/InventoryHubDbOnlyTest.php +- T022 [P] [US2] app/Filament/Widgets/Inventory/InventoryKpiHeader.php + +### US3 +- T030 [P] [US3] tests/Feature/Monitoring/OperationsDbOnlyTest.php +- T031 [P] [US3] tests/Feature/Monitoring/OperationsTenantScopeTest.php +- T032 [P] [US3] app/Filament/Widgets/Operations/OperationsKpiHeader.php + +--- + +## Implementation Strategy + +### MVP First (US1 only) + +1. Complete Phase 1 + Phase 2 +2. Implement US1 (dashboard) +3. Run: `vendor/bin/sail artisan test tests/Feature/Filament/TenantDashboardDbOnlyTest.php` +4. Run: `vendor/bin/sail artisan test tests/Feature/Filament/TenantDashboardTenantScopeTest.php` + +### Incremental Delivery + +- Add US2 next (Inventory hub), then US3 (Operations index). +- After each story, run its targeted tests + the cross-cutting DB-only tests.