26 KiB
Implementation Plan: Shared Detail Micro-UI Contract
Branch: 197-shared-detail-contract | Date: 2026-04-15 | Spec: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/197-shared-detail-contract/spec.md
Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/197-shared-detail-contract/spec.md
Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.
Summary
Standardize two already real shared detail families without introducing a generic UI framework. The first slice extends the existing verification support seam so OperationRunResource, ManagedTenantOnboardingWizard, and TenantVerificationReport render the same family-owned verification surface with one tab contract, one diagnostics contract, and explicit host-owned variation slots for assist, acknowledge, and host framing. The second slice standardizes normalized settings and normalized diff around family-owned wrappers that make subtype handling, unavailable states, section structure, and view behavior explicit across PolicyResource, PolicyVersionResource, and FindingResource, while preserving the existing Livewire settings table and rich diff rendering where the domain still needs it. The implementation stays derived-only, keeps Filament v5 + Livewire v4 semantics, adds no new assets or persistence, and protects the result with cross-host parity tests plus a small guard against reintroducing ad hoc host forks.
Technical Context
Language/Version: PHP 8.4.15
Primary Dependencies: Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing VerificationReportViewer, VerificationReportChangeIndicator, PolicyNormalizer, VersionDiff, DriftFindingDiffBuilder, and SettingsCatalogSettingsTable
Storage: PostgreSQL unchanged; no new persistence, cache store, or durable UI artifact
Testing: Pest 4 unit and feature tests, including Livewire component coverage for widgets/pages and focused guard tests, run through Laravel Sail
Target Platform: Laravel monolith web application in Sail locally and containerized Linux deployment for staging and production
Project Type: web application
Performance Goals: Keep verification viewers DB-only at render time, avoid any new outbound HTTP or queued work, preserve current diff and settings render responsiveness, and prevent repeated host-local surface logic from re-normalizing or re-shaping the same data differently
Constraints: No new persisted truth, no new Graph call path, no new panel/provider registration change, no hidden shell or monitoring-state refactor, no generic component framework, and no new destructive actions in the shared family core
Scale/Scope: Two shared families across seven concrete host surfaces: verification in operation detail, onboarding wizard, and tenant widget; normalized settings or diff in policy detail, policy-version detail, and finding detail, plus the existing standard-settings sibling view that must be absorbed or demoted into an explicit subtype
Constitution Check
GATE: Passed before Phase 0 research. Re-checked after Phase 1 design and still passing.
| Principle | Pre-Research | Post-Design | Notes |
|---|---|---|---|
| Inventory-first / snapshots-second | PASS | PASS | Both families remain pure renderers of already stored OperationRun, policy snapshot, and finding evidence truth. No new snapshot or inventory semantics are added. |
| Read/write separation | PASS | PASS | The shared-family contracts are read-only. Existing host actions such as start verification, refresh, assist, or acknowledge remain host-owned and keep their current mutation semantics. |
| Graph contract path | N/A | N/A | No Graph calls or config/graph_contracts.php changes are introduced. |
| Deterministic capabilities | PASS | PASS | No new capability family is introduced. Existing host authorization and capability registries remain authoritative. |
| Workspace + tenant isolation | PASS | PASS | Every host continues to resolve data inside existing workspace and tenant scope. Tenantless operation detail remains subject to existing entitlement checks before rendering tenant-owned verification data. |
| RBAC-UX authorization semantics | PASS | PASS | No 404/403 rules change. Non-members remain not found, in-scope capability denial stays forbidden where host actions already enforce it. |
| Run observability / Ops-UX | PASS | PASS | No new OperationRun type, lifecycle, or notification path is added. Existing operation links remain navigation-only. |
| Data minimization | PASS | PASS | Contracts stay runtime-only and derive from existing stored payloads without new mirrors or caches. |
| Proportionality / no premature abstraction | PASS WITH JUSTIFIED ABSTRACTION | PASS WITH JUSTIFIED ABSTRACTION | One narrow contract seam per family is justified because both families already exist across multiple concrete hosts. The design explicitly rejects a cross-domain shared-detail framework. |
| Persisted truth / behavioral state | PASS | PASS | No new tables, persisted UI artifacts, or new state families are introduced. |
| UI semantics / few layers | PASS | PASS | The plan replaces duplicated host ownership with family-owned contracts. It does not add a new presenter stack or badge taxonomy. |
| Badge semantics (BADGE-001) | PASS | PASS | Existing badge domains remain authoritative. Shared-family work reuses them rather than introducing host-local mappings. |
| Filament-native UI / Action Surface Contract | PASS | PASS | Family surfaces remain embedded detail/evidence surfaces inside existing Filament pages and widgets. No new row or bulk action hierarchy is introduced. |
| Filament UX-001 | PASS | PASS | The plan changes detail-family ownership only. Existing view-style layouts remain in place and must stay operator-first. |
| Filament v5 / Livewire v4 compliance | PASS | PASS | The implementation remains within Filament v5 and Livewire v4 only. No legacy Filament or Livewire v3 APIs are introduced. |
| Provider registration location | PASS | PASS | No panel or provider registration change is required. Laravel 11+ provider registration remains in bootstrap/providers.php. |
| Global search hard rule | PASS | PASS | No global-search behavior changes are planned. The touched resources already render viewable detail pages, so the Filament global-search requirement remains satisfied. |
| Destructive action safety | PASS | PASS | No new destructive actions are added inside either shared family. Any host mutation remains outside the family core and keeps existing confirmation and authorization rules. |
| Asset strategy | PASS | PASS | No new JS or CSS assets are planned. No filament:assets deployment change is required. |
| Testing truth (TEST-TRUTH-001) | PASS | PASS | The test plan focuses on cross-host parity, DB-only rendering, unavailable-state consistency, and guard coverage against re-forking, instead of thin indirection-only tests. |
Phase 0 Research
Research outcomes are captured in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/197-shared-detail-contract/research.md.
Key decisions:
- Standardize the Verification Report family by extending the existing
VerificationReportViewerseam and one family-owned Blade root instead of creating a new Livewire component or a custom entry type that only solves one host class. - Treat onboarding-specific assist and acknowledge behavior as explicit host-owned variation slots of the verification family rather than a parallel report UI.
- Converge
normalized-settings.blade.phpandpolicy-settings-standard.blade.phpunder one family-owned settings wrapper with explicit subtypes, instead of continuing host-level view switching. - Move normalized diff unavailable and partial-state behavior into the family contract so
FindingResourceandPolicyVersionResourcestop expressing different availability semantics for the same concept. - Keep
SettingsCatalogSettingsTableas the existing Livewire subtype used by the settings family, since it already provides search, sort, pagination, and context-safe query-string isolation. - Protect the family contracts with parity tests and a small fork guard, not with a generic UI meta-framework or a screenshot-heavy suite.
Phase 1 Design
Design artifacts are created under /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/197-shared-detail-contract/:
data-model.md: runtime contracts, host variation model, and source-truth boundaries for both shared familiescontracts/verification-report-family.openapi.yaml: internal logical contract for the verification family and its approved hostscontracts/normalized-detail-family.openapi.yaml: internal logical contract for normalized settings and normalized diff families and their approved hostsquickstart.md: focused implementation, verification, and smoke workflow
Release-acceptance artifact maintained during implementation:
migration-note.md: closing migrated-host inventory, bounded allowed variations, manual smoke evidence, and out-of-scope follow-ups required for release acceptance
Design decisions:
- Extend the existing
VerificationReportViewersupport seam instead of replacing it. - Add narrow normalized-detail support builders under existing Filament support paths rather than creating a new base framework.
- Keep subtype richness for settings catalog tables, standard key-value settings, script content rendering, and diff block rendering, but move wrapper ownership and unavailable-state ownership into one family contract.
- Keep host no-run and in-progress framing local where the host genuinely owns that state, while making the completed or unavailable family core the same surface everywhere.
Project Structure
Documentation (this feature)
specs/197-shared-detail-contract/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── migration-note.md
├── quickstart.md
├── contracts/
│ ├── normalized-detail-family.openapi.yaml
│ └── verification-report-family.openapi.yaml
├── checklists/
│ └── requirements.md
└── tasks.md
Source Code (repository root)
apps/platform/
├── app/
│ ├── Filament/
│ │ ├── Pages/
│ │ │ └── Workspaces/
│ │ │ └── ManagedTenantOnboardingWizard.php
│ │ ├── Resources/
│ │ │ ├── FindingResource.php
│ │ │ ├── OperationRunResource.php
│ │ │ ├── PolicyResource.php
│ │ │ └── PolicyVersionResource.php
│ │ ├── Support/
│ │ │ ├── VerificationReportChangeIndicator.php
│ │ │ ├── VerificationReportViewer.php
│ │ │ ├── NormalizedSettingsSurface.php
│ │ │ └── NormalizedDiffSurface.php
│ │ └── Widgets/
│ │ └── Tenant/
│ │ └── TenantVerificationReport.php
│ └── Livewire/
│ └── SettingsCatalogSettingsTable.php
├── resources/
│ └── views/
│ └── filament/
│ ├── components/
│ │ ├── verification-report-viewer.blade.php
│ │ └── verification-report/
│ ├── forms/
│ │ └── components/
│ │ └── managed-tenant-onboarding-verification-report.blade.php
│ ├── infolists/
│ │ └── entries/
│ │ ├── normalized-diff.blade.php
│ │ ├── normalized-settings.blade.php
│ │ └── policy-settings-standard.blade.php
│ └── widgets/
│ └── tenant/
│ └── tenant-verification-report.blade.php
└── tests/
├── Feature/
│ ├── Drift/
│ ├── Filament/
│ ├── Onboarding/
│ ├── Verification/
│ └── Guards/
└── Unit/
Structure Decision: Keep the existing Laravel monolith layout and current Filament resource/widget/view directories. Add only narrow support builders under app/Filament/Support and family-owned partials under existing resources/views/filament/... paths. Do not add a new shared UI framework directory or new base package.
Implementation Strategy
Phase A — Cut the Verification Report Family Contract
Goal: Make one family-owned verification surface the semantic owner of completed and unavailable report rendering across the covered hosts.
| Step | File | Change |
|---|---|---|
| A.1 | apps/platform/app/Filament/Support/VerificationReportViewer.php |
Extend the existing seam so it can build the shared verification surface contract, not only extract sanitized report payloads. |
| A.2 | apps/platform/resources/views/filament/components/verification-report-viewer.blade.php and new family-owned partials under apps/platform/resources/views/filament/components/verification-report/ |
Move summary, issues, passed, diagnostics, and optional action-zone ownership into one shared Blade root with one tab or view contract. |
| A.3 | apps/platform/resources/views/filament/forms/components/managed-tenant-onboarding-verification-report.blade.php |
Remove local verification-family tab ownership and local structural duplication; render the same family core with explicit onboarding-only variation slots for assist, acknowledge, and technical details. |
| A.4 | apps/platform/app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php, apps/platform/app/Filament/Resources/OperationRunResource.php, apps/platform/app/Filament/Widgets/Tenant/TenantVerificationReport.php, and apps/platform/resources/views/filament/widgets/tenant/tenant-verification-report.blade.php |
Pass only explicit host context and host-owned actions into the verification family contract while keeping no-run or in-progress framing local where appropriate. |
Phase B — Standardize the Normalized Settings Family
Goal: Make one family-owned settings wrapper the contract owner for warnings, empty state, subtype selection, and section behavior.
| Step | File | Change |
|---|---|---|
| B.1 | apps/platform/app/Filament/Support/NormalizedSettingsSurface.php |
Add a narrow runtime builder that shapes normalized settings state into one explicit family contract with subtype markers and render expectations. |
| B.2 | apps/platform/resources/views/filament/infolists/entries/normalized-settings.blade.php and subtype partials under apps/platform/resources/views/filament/infolists/entries/normalized-settings/ |
Own warnings, empty-state behavior, wrapper headings, and subtype delegation inside one family surface instead of splitting ownership between sibling host-selected blades. |
| B.3 | apps/platform/resources/views/filament/infolists/entries/policy-settings-standard.blade.php |
Absorb this blade into the normalized-settings family as an internal subtype partial or retire it as a direct host-facing sibling view. |
| B.4 | apps/platform/app/Filament/Resources/PolicyResource.php and apps/platform/app/Filament/Resources/PolicyVersionResource.php |
Stop choosing between sibling settings views at the host level and always pass family contract state to the normalized settings surface. |
| B.5 | apps/platform/app/Livewire/SettingsCatalogSettingsTable.php |
Keep the existing table component as the settings-catalog subtype renderer while ensuring the shared family contract owns when and how it is shown. |
Phase C — Standardize the Normalized Diff Family
Goal: Make one family-owned diff wrapper the contract owner for summary, unavailable state, zero-diff messaging, and grouped detail rendering.
| Step | File | Change |
|---|---|---|
| C.1 | apps/platform/app/Filament/Support/NormalizedDiffSurface.php |
Add a narrow runtime builder that shapes diff state, availability state, and zero-change semantics into one explicit family contract. |
| C.2 | apps/platform/resources/views/filament/infolists/entries/normalized-diff.blade.php and family-owned partials under apps/platform/resources/views/filament/infolists/entries/normalized-diff/ |
Move unavailable, partial, summary, and grouped-render ownership into the shared diff family. Preserve script highlighting and grouped row rendering as explicit subzones of that family. |
| C.3 | apps/platform/app/Filament/Resources/FindingResource.php |
Replace host-owned diff-unavailable TextEntry behavior with family-owned unavailable state input so drift detail uses the same contract as other normalized diff hosts. |
| C.4 | apps/platform/app/Filament/Resources/PolicyVersionResource.php |
Route normalized diff state through the shared diff builder so policy-version diff and finding diff expose the same family semantics for available and unavailable paths. |
Phase D — Protect the Family Boundaries
Goal: Make future host changes extend the family contracts instead of silently re-forking them.
| Step | File | Change |
|---|---|---|
| D.1 | apps/platform/tests/Feature/Guards/SharedDetailFamilyContractGuardTest.php |
Add a focused guard that blocks new ad hoc host forks, such as host-selected policy-settings-standard references or duplicated verification-family tab ownership outside the shared core. |
| D.2 | apps/platform/tests/Feature/Filament/SharedVerificationReportFamilyContractTest.php and apps/platform/tests/Feature/Filament/NormalizedDetailFamilyContractTest.php |
Add cross-host parity tests that assert the same family zones, unavailable semantics, and allowed host-variation boundaries. |
| D.3 | apps/platform/tests/Feature/Verification/VerificationReportViewerDbOnlyTest.php, apps/platform/tests/Feature/Filament/TenantVerificationReportWidgetTest.php, apps/platform/tests/Feature/Onboarding/OnboardingVerificationTest.php, apps/platform/tests/Feature/Onboarding/OnboardingVerificationV1_5UxTest.php, apps/platform/tests/Feature/MonitoringOperationsTest.php, apps/platform/tests/Feature/Spec085/DenyAsNotFoundSemanticsTest.php, apps/platform/tests/Feature/Onboarding/OnboardingDraftAccessTest.php, apps/platform/tests/Feature/Filament/PolicyVersionSettingsTest.php, apps/platform/tests/Feature/Filament/SettingsCatalogPolicyNormalizedDiffTest.php, apps/platform/tests/Feature/Filament/GroupPolicyConfigurationNormalizedDiffTest.php, apps/platform/tests/Feature/Drift/DriftFindingDiffUnavailableTest.php, apps/platform/tests/Feature/Filament/PolicyResourceAdminTenantParityTest.php, apps/platform/tests/Feature/Filament/PolicyVersionAdminTenantParityTest.php, and apps/platform/tests/Feature/Findings/FindingRbacTest.php |
Extend existing feature coverage so it asserts the new family contracts without losing current DB-only, RBAC-safe, deny-as-not-found, capability-safe, and domain-rich behavior. |
Phase E — Close the Migration and Verify Operator Sameness
Goal: Finish the feature with an explicit migrated-host inventory and a manual smoke path that matches the spec’s acceptance model.
| Step | File | Change |
|---|---|---|
| E.1 | specs/197-shared-detail-contract/tasks.md |
Break implementation into dependency-ordered tasks across verification family, normalized settings family, normalized diff family, tests, and migration notes. |
| E.2 | specs/197-shared-detail-contract/migration-note.md |
Record migrated hosts, consciously allowed remaining variations, smoke-review evidence, and out-of-scope follow-ups required by release acceptance. |
| E.3 | specs/197-shared-detail-contract/quickstart.md |
Use the verification order and smoke path below to prove operator sameness across hosts and feed the result into the migration note. |
Key Design Decisions
D-001 — Verification Report remains one Blade-rooted family because its hosts are heterogeneous
The verification family already spans an infolist-style detail section, a form ViewField, and a widget include. A new custom infolist entry or a new Livewire component would only solve one host class and would risk over-abstracting the rest. The narrowest path is to extend the existing VerificationReportViewer seam and let one family-owned Blade root own structure.
D-002 — Onboarding keeps host-owned actions but loses structural ownership
The onboarding wizard legitimately owns verify-step actions, assist routing, and acknowledge mutations. It does not need to own the report’s tab system or its core diagnostics structure. The plan therefore keeps these actions as explicit host slots, not as a second verification UI.
D-003 — policy-settings-standard becomes a subtype, not a peer family
The repo already exposes two sibling settings surfaces for the same conceptual family. The plan explicitly keeps settings-catalog and standard-settings richness, but moves them under one family wrapper so hosts stop selecting unrelated siblings at the top level.
D-004 — Unavailable-state ownership belongs inside normalized diff
FindingResource currently owns unavailable messaging outside the diff family, while other diff hosts do not. The family contract should own availability, partial-state, and zero-diff semantics so the same content concept cannot drift at the host boundary.
D-005 — The test strategy protects family sameness, not only helper output
The plan prefers cross-host parity tests, DB-only safety tests, and one small fork guard. It explicitly avoids a new UI meta-test framework or broad screenshot infrastructure because the product need is bounded and already well represented by existing feature tests.
Risk Assessment
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
| Verification onboarding loses assist or acknowledge power while removing its fork | High | Medium | Keep assist and acknowledge as host-owned variation slots and extend onboarding tests before removing duplicated structural markup. |
| Standardizing settings removes useful subtype richness | High | Medium | Keep explicit subtypes for settings-catalog tables, standard blocks, and script-heavy content; standardize the wrapper, not the domain richness away. |
| Diff unavailable semantics collapse distinct host cases | Medium | Medium | Make unavailable and partial states explicit contract inputs and cover missing-version and empty-side finding scenarios in feature tests. |
| The feature grows into a generic shared-detail framework | High | Low | Limit support additions to one existing verification seam plus one settings builder and one diff builder under current Filament support paths. No cross-domain base class or registry is introduced. |
| New hosts later reintroduce ad hoc forks | Medium | Medium | Add one focused fork guard and internal contract YAMLs that document approved consumers and forbidden host-level patterns. |
Test Strategy
- Extend verification-family feature coverage so
OperationRunResource, onboarding verification, and the tenant verification widget all assert the same family-owned summary, issues, passed, diagnostics, and unavailable semantics for equivalent report data. - Keep
VerificationReportViewerDbOnlyTestandTenantVerificationReportWidgetTestas truth guards that the standardized family remains DB-only and does not introduce outbound work. - Extend onboarding verification tests to prove host-specific assist and acknowledge actions survive as bounded variations of the same family.
- Extend normalized settings and diff coverage in
PolicyVersionSettingsTest,SettingsCatalogPolicyNormalizedDiffTest,GroupPolicyConfigurationNormalizedDiffTest, andDriftFindingDiffUnavailableTestso they assert family-level structure and unavailable behavior instead of only content presence. - Add one new parity test per family and one focused guard test that blocks direct host fork patterns.
- Use Livewire component tests for widgets and pages, and do not try to mount non-Livewire resource classes directly. This follows Filament v5 testing guidance.
- Run the smallest Sail verification pack plus
pint --dirty --format agentbefore implementation completion.
Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| One new normalized-settings builder and one new normalized-diff builder | The repo already has multiple concrete hosts for each family, but no existing support seam analogous to VerificationReportViewer for normalized detail families |
Leaving host files to shape state ad hoc would preserve the exact drift this spec is meant to remove |
| One focused fork guard | The feature’s long-term value depends on preventing new host forks after the first cleanup | Relying only on reviewer memory or prose documentation would not reliably stop drift from returning |
Proportionality Review
- Current operator problem: Operators encounter the same verification or normalized detail concept in multiple hosts, but the structure, next-step zone, diagnostics placement, and unavailable behavior vary enough to slow recognition and trust.
- Existing structure is insufficient because: Shared Blade reuse exists, but shared contract ownership does not. Hosts still decide core structure and availability semantics themselves, which is why drift already exists.
- Narrowest correct implementation: Extend the existing verification support seam, add two narrow normalized-detail support builders, and move family wrapper ownership into current Blade paths. Do not add persistence, a new framework, or a cross-domain taxonomy.
- Ownership cost created: Three narrow support seams in current Filament support paths, a handful of family-owned partials, two internal contract YAMLs, and focused parity or guard tests.
- Alternative intentionally rejected: Continue local host cleanups or invent a full shared-detail framework. Local cleanup would not stop drift, and a framework would import much more permanent complexity than this feature needs.
- Release truth: Current-release truth. The affected families and host drift already exist in shipped operator surfaces.