diff --git a/app/Filament/Pages/InventoryCoverage.php b/app/Filament/Pages/InventoryCoverage.php index b7214c5..39c8b34 100644 --- a/app/Filament/Pages/InventoryCoverage.php +++ b/app/Filament/Pages/InventoryCoverage.php @@ -2,6 +2,7 @@ namespace App\Filament\Pages; +use App\Services\Inventory\CoverageCapabilitiesResolver; use BackedEnum; use Filament\Pages\Page; use UnitEnum; @@ -31,7 +32,24 @@ public function mount(): void $policyTypes = config('tenantpilot.supported_policy_types', []); $foundationTypes = config('tenantpilot.foundation_types', []); - $this->supportedPolicyTypes = is_array($policyTypes) ? $policyTypes : []; - $this->foundationTypes = is_array($foundationTypes) ? $foundationTypes : []; + $resolver = app(CoverageCapabilitiesResolver::class); + + $this->supportedPolicyTypes = collect(is_array($policyTypes) ? $policyTypes : []) + ->map(function (array $row) use ($resolver): array { + $type = (string) ($row['type'] ?? ''); + + return array_merge($row, [ + 'dependencies' => $type !== '' && $resolver->supportsDependencies($type), + ]); + }) + ->all(); + + $this->foundationTypes = collect(is_array($foundationTypes) ? $foundationTypes : []) + ->map(function (array $row): array { + return array_merge($row, [ + 'dependencies' => false, + ]); + }) + ->all(); } } diff --git a/app/Services/Inventory/CoverageCapabilitiesResolver.php b/app/Services/Inventory/CoverageCapabilitiesResolver.php new file mode 100644 index 0000000..217549e --- /dev/null +++ b/app/Services/Inventory/CoverageCapabilitiesResolver.php @@ -0,0 +1,25 @@ +Type Label Category + Dependencies Restore Risk @@ -18,6 +19,7 @@ {{ $row['type'] ?? '' }} {{ $row['label'] ?? '' }} {{ $row['category'] ?? '' }} + {{ ($row['dependencies'] ?? false) ? '✅' : '—' }} {{ $row['restore'] ?? 'enabled' }} {{ $row['risk'] ?? 'normal' }} @@ -36,6 +38,7 @@ Type Label Category + Dependencies Restore Risk @@ -46,6 +49,7 @@ {{ $row['type'] ?? '' }} {{ $row['label'] ?? '' }} {{ $row['category'] ?? '' }} + {{ ($row['dependencies'] ?? false) ? '✅' : '—' }} {{ $row['restore'] ?? 'enabled' }} {{ $row['risk'] ?? 'normal' }} diff --git a/specs/047-inventory-foundations-nodes/checklists/requirements.md b/specs/047-inventory-foundations-nodes/checklists/requirements.md index 41259c9..408fb41 100644 --- a/specs/047-inventory-foundations-nodes/checklists/requirements.md +++ b/specs/047-inventory-foundations-nodes/checklists/requirements.md @@ -17,6 +17,7 @@ ## Feature 047 Functional Coverage - [x] FR-002 include_foundations=false produces no foundation node sync side effects. - [x] FR-003 Foundation nodes stored as InventoryItems with stable identity (tenant_id + policy_type + external_id). - [x] FR-004 Inventory Coverage UI shows Policies + Foundations. +- [x] FR-COV-DEP: Coverage shows deterministic Dependencies support column (✅/—) derived from existing capabilities (no Graph calls). - [x] FR-005 Inventory Items UI can filter/browse foundations. ## Test Gates diff --git a/specs/047-inventory-foundations-nodes/spec.md b/specs/047-inventory-foundations-nodes/spec.md index 8313383..79f2356 100644 --- a/specs/047-inventory-foundations-nodes/spec.md +++ b/specs/047-inventory-foundations-nodes/spec.md @@ -98,6 +98,27 @@ ### FR-004 Inventory Coverage UI - “Policies” table (existing behavior) - “Foundations” table (new; derived from `tenantpilot.foundation_types`) +#### FR-COV-DEP-001 Dependencies column +Coverage MUST display an additional column: +- Header: `Dependencies` +- Value: `✅` or `—` + +#### FR-COV-DEP-002 Deterministic derivation +The `Dependencies` value MUST be derived deterministically from existing capabilities (config/contracts) only: + +`✅` if at least one holds: +- the type supports Assignments extraction, or +- the type supports Scope Tags, or +- the type can reference Assignment Filters, or +- the type has dependency extraction rules in Spec 042 (relationship taxonomy / extractor mapping) + +Otherwise: `—`. + +This is **feature support**, not “Graph supports $expand”. + +MVP decision: +- For foundation types, default to `—`. + ### FR-005 Inventory Items UI Inventory Items list MUST allow: - filtering to Foundations (e.g., Category = Foundations) diff --git a/specs/047-inventory-foundations-nodes/tasks.md b/specs/047-inventory-foundations-nodes/tasks.md index 1be837c..88eb6ba 100644 --- a/specs/047-inventory-foundations-nodes/tasks.md +++ b/specs/047-inventory-foundations-nodes/tasks.md @@ -70,6 +70,12 @@ ## Phase 5: User Story 3 — Coverage communication (Priority: P2) - [ ] T015 [US3] Update Coverage Blade view to render two tables in resources/views/filament/pages/inventory-coverage.blade.php - [ ] T016 [P] [US3] Add/adjust Pest test assertions for both headings in tests/Feature/Filament/InventoryPagesTest.php +### Coverage Dependencies Support (UI-only) + +- [x] T026 [US3] Add Coverage table column `Dependencies` (✅/—) in resources/views/filament/pages/inventory-coverage.blade.php +- [x] T027 [US3] Add deterministic resolver CoverageCapabilitiesResolver::supportsDependencies($type) (contracts/config derived) + unit test in tests/Unit/CoverageCapabilitiesResolverTest.php +- [x] T028 [P] [US3] Update Pest UI/feature test to assert Coverage renders `Dependencies` column and at least one ✅ in tests/Feature/Filament/InventoryPagesTest.php + --- ## Phase 6: User Story 4 — Resolve dependency names (Priority: P3) diff --git a/tests/Feature/Filament/InventoryPagesTest.php b/tests/Feature/Filament/InventoryPagesTest.php index 21f0ab8..2ec5039 100644 --- a/tests/Feature/Filament/InventoryPagesTest.php +++ b/tests/Feature/Filament/InventoryPagesTest.php @@ -25,5 +25,7 @@ ->assertOk() ->assertSee('Coverage') ->assertSee('Policies') - ->assertSee('Foundations'); + ->assertSee('Foundations') + ->assertSee('Dependencies') + ->assertSee('✅'); }); diff --git a/tests/Unit/CoverageCapabilitiesResolverTest.php b/tests/Unit/CoverageCapabilitiesResolverTest.php new file mode 100644 index 0000000..de5e818 --- /dev/null +++ b/tests/Unit/CoverageCapabilitiesResolverTest.php @@ -0,0 +1,14 @@ +supportsDependencies($type))->toBe($expected); +})->with([ + 'settingsCatalogPolicy' => ['settingsCatalogPolicy', true], + 'deviceConfiguration' => ['deviceConfiguration', true], + 'conditionalAccessPolicy' => ['conditionalAccessPolicy', false], + 'roleScopeTag (foundation, MVP)' => ['roleScopeTag', false], +]);