# Implementation Plan: Admin Inventory Navigation Cutover **Branch**: `301-admin-inventory-navigation-cutover` | **Date**: 2026-05-14 | **Spec**: [spec.md](./spec.md) **Input**: Feature specification from `/specs/301-admin-inventory-navigation-cutover/spec.md` ## Summary Restore Inventory as a discoverable environment-bound admin navigation surface. The implementation should remove or narrow Inventory-only blanket admin navigation suppression, reuse the current `NavigationScope` environment-surface rule, preserve workspace-home sidebar cleanliness, and update tests so they protect the correct split: workspace home hides tenant-owned navigation, environment routes show Inventory. No application implementation is performed in this preparation step. ## Technical Context **Language/Version**: PHP 8.4.15, Laravel 12.52.0 **Primary Dependencies**: Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1 **Storage**: PostgreSQL, no schema changes **Testing**: Pest feature tests; optional Browser smoke for visible sidebar behavior **Validation Lanes**: confidence, browser if smoke is added **Target Platform**: Laravel Sail local development; Dokploy container deployment for staging/production **Project Type**: Laravel application in `apps/platform` **Performance Goals**: Navigation/page-load remains DB-only; no Graph calls or queued work are introduced **Constraints**: Workspace and managed-environment isolation must remain enforced; workspace home must not show tenant-owned navigation; no Entra Groups decision **Scale/Scope**: Two Inventory surfaces plus tests ## UI / Surface Guardrail Plan - **Guardrail scope**: changed operator-facing navigation surfaces - **Native vs custom classification summary**: native Filament navigation/cluster/page/resource - **Shared-family relevance**: navigation entry points - **State layers in scope**: shell, page, route context, Livewire update referer handling through existing `NavigationScope` - **Audience modes in scope**: operator-MSP - **Decision/diagnostic/raw hierarchy plan**: navigation-only entry points; diagnostics stay inside existing Inventory pages - **Raw/support gating plan**: N/A - **One-primary-action / duplicate-truth control**: Sidebar links open existing Inventory pages and do not add alternate status summaries or action cards - **Handling modes by drift class or surface**: review-mandatory for Inventory navigation; report-only for unrelated tenant-owned surfaces - **Repository-signal treatment**: `InventoryCluster` and `InventoryCoverage` blanket admin-hidden checks are active repair targets; Entra Groups remains follow-up - **Special surface test profiles**: standard-native-filament - **Required tests or manual smoke**: feature tests required; browser smoke recommended if sidebar rendering is touched visibly - **Exception path and spread control**: none - **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes - **Systems touched**: Filament navigation, Inventory cluster/page/resource, `NavigationScope`, panel/context tests - **Shared abstractions reused**: `NavigationScope::shouldRegisterEnvironmentNavigation()`, `WorkspaceScopedTenantRoutes`, `ResolvesPanelTenantContext`, `OperateHubShell` - **New abstraction introduced? why?**: none - **Why the existing abstraction was sufficient or insufficient**: Existing environment-navigation detection is sufficient. Current Inventory cluster/page classes are insufficient because they bypass it with a blanket admin-panel hidden rule. - **Bounded deviation / spread control**: none. Do not create a local Inventory-only replacement for `NavigationScope`. ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: no - **Central contract reused**: N/A - **Delegated UX behaviors**: Existing Inventory Sync start behavior remains unchanged - **Surface-owned behavior kept local**: Navigation visibility only - **Queued DB-notification policy**: N/A - **Terminal notification path**: N/A - **Exception path**: none ## Provider Boundary & Portability Fit - **Shared provider/platform boundary touched?**: no - **Provider-owned seams**: N/A - **Platform-core seams**: N/A - **Neutral platform terms / contracts preserved**: workspace, environment, Inventory, Coverage - **Retained provider-specific semantics and why**: Existing Inventory item provider metadata remains unchanged - **Bounded extraction or follow-up path**: none ## Constitution Check *GATE: Must pass before implementation starts. Re-check after implementation.* - Inventory-first: pass. Inventory remains last-observed managed-environment truth. - Read/write separation: pass. Navigation repair is read-only; existing Inventory Sync action remains unchanged. - Graph contract path: pass. No Graph calls or contract changes. - Deterministic capabilities: pass. Existing capability resolver remains the authorization path. - RBAC-UX: pass. UI visibility remains non-security; existing access checks stay authoritative. - Workspace isolation: pass. Workspace home negative-control tests remain mandatory. - Tenant/managed-environment isolation: pass. Wrong workspace/environment pairs remain not found. - Run observability: N/A. No new OperationRun lifecycle. - Test governance: pass if tasks keep feature/browser proof focused and avoid a route-audit sweep. - Proportionality: pass. No new structures, persistence, states, or frameworks. - Shared pattern first: pass. Reuse `NavigationScope`. - Provider boundary: pass. No provider seam change. - Filament-native UI: pass. Native Filament cluster/resource/page navigation only; no custom UI. - Filament v5 / Livewire v4: pass. The repo uses Filament 5.2.1 and Livewire 4.1.4. - Panel provider registration: unchanged. Laravel provider registration remains in `apps/platform/bootstrap/providers.php`; no provider is added to `bootstrap/app.php`. - Global search: `InventoryItemResource` already has a View page. `InventoryCoverage` is a page, not a globally searchable resource. No new globally searchable resource is introduced. - Destructive actions: none introduced or changed. Existing Inventory Sync is an operation start, not changed by this slice; any touched action must retain authorization and current confirmation/notification behavior. - Asset strategy: no assets are registered; deploy `filament:assets` posture is unchanged. ## Test Governance Check - **Test purpose / classification by changed surface**: Feature for navigation registration and context; Browser only for rendered sidebar proof - **Affected validation lanes**: confidence, browser - **Why this lane mix is the narrowest sufficient proof**: The behavior is mostly PHP navigation gating and route/context resolution. One browser smoke is enough to prove the visible sidebar if it changes. - **Narrowest proving command(s)**: - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/PanelNavigationSegregationTest.php tests/Feature/Filament/InventoryCoverageAdminTenantParityTest.php tests/Feature/Filament/InventoryHubDbOnlyTest.php tests/Feature/Filament/InventoryPagesTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec301InventoryNavigationCutoverSmokeTest.php` if added - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - **Fixture / helper / factory / seed / context cost risks**: Low; reuse existing tenant/workspace helpers and Inventory factories - **Expensive defaults or shared helper growth introduced?**: no - **Heavy-family additions, promotions, or visibility changes**: none - **Surface-class relief / special coverage rule**: standard-native-filament - **Closing validation and reviewer handoff**: Verify the feature does not change Entra Groups, does not widen into route-audit, and does not show Inventory on workspace home. - **Budget / baseline / trend follow-up**: none - **Review-stop questions**: Has workspace-home cleanliness remained a test? Are Inventory cluster/page visible in environment context? Did any unrelated surface become visible? - **Escalation path**: document-in-feature - **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage - **Why no dedicated follow-up spec is needed**: The next follow-up specs are already named in `docs/product/spec-candidates.md`; this slice only repairs Inventory. ## Project Structure ### Documentation (this feature) ```text specs/301-admin-inventory-navigation-cutover/ ├── spec.md ├── plan.md ├── tasks.md └── checklists/ └── requirements.md ``` ### Source Code (repository root) Likely affected runtime surfaces during later implementation: ```text apps/platform/app/Filament/Clusters/Inventory/InventoryCluster.php apps/platform/app/Filament/Pages/InventoryCoverage.php apps/platform/app/Filament/Resources/InventoryItemResource.php apps/platform/app/Support/Navigation/NavigationScope.php apps/platform/tests/Feature/Filament/PanelNavigationSegregationTest.php apps/platform/tests/Feature/Filament/InventoryCoverageAdminTenantParityTest.php apps/platform/tests/Feature/Filament/InventoryHubDbOnlyTest.php apps/platform/tests/Feature/Filament/InventoryPagesTest.php apps/platform/tests/Browser/Spec301InventoryNavigationCutoverSmokeTest.php ``` **Structure Decision**: Use existing Laravel/Filament app structure. Do not create new base folders or new shared navigation framework. ## Complexity Tracking No constitution violation or BLOAT-001 trigger is expected. If implementation introduces any new abstraction, route family, persistent state, status family, or UI framework, stop and update this plan before continuing. | Violation | Why Needed | Simpler Alternative Rejected Because | |---|---|---| | N/A | N/A | N/A | ## Data Model No schema, model, migration, factory, seeder, or persisted entity changes are in scope. ## Technical Approach 1. Update proof first: - Split current admin-hidden navigation expectations so workspace-home hidden behavior is preserved while environment-bound Inventory visibility is expected. - Keep Entra Groups in the admin-hidden dataset or otherwise explicitly assert it remains out of scope. - Add Coverage URL proof so `InventoryCoverage::getUrl(panel: 'admin', tenant: $environment)` resolves to the canonical workspace/environment route. 2. Converge Inventory navigation: - Replace blanket `Filament::getCurrentPanel()?->getId() === 'admin'` false returns in Inventory cluster/page classes with the shared environment-navigation rule. - Ensure `InventoryItemResource` remains aligned and no route-name workaround is regressed. 3. Preserve context behavior: - Align `InventoryCoverage` route/URL behavior with the canonical environment route if the page still uses a flat admin path. - Keep `InventoryCoverage` resolving context through `ResolvesPanelTenantContext` and `OperateHubShell`. - Do not add query-only navigation or a new remembered-environment state. 4. Validate: - Run focused Filament feature tests. - Add/run a browser smoke only if the rendered sidebar proof is needed. - Run Pint for touched platform files. ## Risk Controls - Do not touch Entra Groups. - Do not modify completed Specs `279-300`. - Do not replace `NavigationScope` with a page-local helper. - Do not expose environment-owned navigation on `/admin/workspaces/{workspace}`. - Do not add Graph calls, queue work, assets, migrations, or OperationRun lifecycle changes. ## Rollout Considerations - **Staging**: Run the focused feature tests and browser smoke before promotion. - **Production**: No migration, env var, queue, cron, storage, or asset deployment impact is expected. - **Rollback**: Revert the small navigation/test change if sidebar behavior regresses. No data rollback is required. ## Implementation Phases ### Phase 1 - Proof and Scope Lock Update tests so the current bug is expressed as a failing expectation: Inventory is visible in environment context and hidden at workspace home. ### Phase 2 - Inventory Navigation Repair Adjust Inventory cluster/page navigation registration to use the existing environment-navigation contract. ### Phase 3 - Context and DB-only Safety Re-run Inventory Coverage and Inventory hub tests to confirm context resolution, DB-only rendering, and coverage truth remain unchanged. ### Phase 4 - Validation and Close-Out Run the focused validation commands, optional browser smoke, Pint, and final scope checks. Record the close-out note in the active PR. ## Filament v5 Output Contract 1. **Livewire v4.0+ compliance**: The repo uses Livewire 4.1.4 with Filament 5.2.1. This slice must not reference Livewire v3 APIs. 2. **Provider registration location**: No provider changes are planned. Existing Laravel provider registration remains in `apps/platform/bootstrap/providers.php`. 3. **Globally searchable resources**: `InventoryItemResource` has a View page. `InventoryCoverage` is a page, not a globally searchable resource. No new global search behavior is introduced. 4. **Destructive actions**: None are introduced or changed. If any touched action becomes destructive during implementation, it must use `Action::make(...)->action(...)`, `->requiresConfirmation()`, authorization, and current audit/notification rules. 5. **Asset strategy**: No assets are registered. Deployment `filament:assets` posture remains unchanged. 6. **Testing plan**: Cover Filament navigation registration, workspace-home negative control, environment-bound visibility, Inventory Coverage context, DB-only Inventory rendering, and a bounded browser smoke if rendered sidebar behavior changes.