7.4 KiB
7.4 KiB
Research: Shared Diff Presentation Foundation
Decision 1: Keep the foundation in a dedicated presentation support layer under app/Support/Diff
- Decision: Introduce the shared diff primitives in
app/Support/Diff/as presentation-only support classes:DiffRowStatus,DiffRow,DiffSummary,DiffPresenter, andValueStringifier. - Rationale: The repo already uses support-oriented layers for reusable UI and rendering concerns, while current diff behavior is fragmented across Blade templates and a few domain services. A support-layer location makes the new foundation easy to consume from Findings, PolicyVersion, RestoreRun, and future baseline/evidence screens without implying domain ownership or database coupling.
- Alternatives considered:
- Put the foundation inside
app/Services/Intune/: rejected because the consumers are broader than Intune policy versioning and restore preview flows. - Put the foundation inside one Filament resource: rejected because the feature is intentionally consumer-agnostic.
- Build a generic diff engine package: rejected because the spec explicitly forbids a full generic diff framework.
- Put the foundation inside
Decision 2: Treat existing compare producers as inputs, not migration targets
- Decision: The foundation will adapt simple structured compare payloads from existing producers such as
VersionDiff,RestoreDiffGenerator, and diff payloads already rendered in Findings and PolicyVersion surfaces, while leaving specialized normalized diff rendering out of scope for now. - Rationale: Current compare outputs already vary:
normalized-diffrenders grouped dot-path changes with script-specific handling, RBAC diff uses side-by-side baseline/current cards, assignments and scope tags use custom section layouts, and restore preview/results wrap policy-level diff summaries. Forcing them into one canonical business payload now would add risk and block incremental adoption. - Alternatives considered:
- Rewrite all existing compare producers to emit one canonical format: rejected because it would expand the feature into a cross-domain refactor.
- Limit the feature to one local consumer such as RBAC diff only: rejected because the product needs a reusable base for multiple upcoming compare surfaces.
Decision 3: Centralize state badge semantics through the existing badge catalog
- Decision: Map shared diff states and summary badges through the existing badge catalog pattern rather than hardcoding colors in new partials.
- Rationale: The repo already centralizes badge semantics via
BadgeCatalog,BadgeDomain, and focused badge tests. Existing diff templates still inlinesuccess,danger, andwarningdecisions; this feature is the right place to stop that drift for the shared foundation. Centralizing diff-state badge semantics aligns with BADGE-001 and keeps future consumers consistent. - Alternatives considered:
- Inline state-to-color mappings in each partial: rejected because it recreates the current inconsistency problem.
- Use Tailwind-only classes without a semantic mapping layer: rejected because the repo already standardizes status-like badges centrally.
Decision 4: Use composable Blade partials under resources/views/filament/partials/diff
- Decision: Build low-magic Blade partials for summary badges, generic row rendering by state, and inline list diff rendering under
resources/views/filament/partials/diff/. - Rationale: Current consumers already use
ViewEntryand Blade partials as the main extension point. Adding composable partials fits existing Filament patterns, works inside infolists and custom page content, and avoids introducing a custom Filament field or entry framework before it is justified. - Alternatives considered:
- Introduce a custom Filament field/entry component framework now: rejected because the spec explicitly says to avoid that unless Blade composition proves insufficient later.
- Create a single master diff page component: rejected because consumers vary between summary-first views, side-by-side views, and specialized normalized diffs.
Decision 5: Keep Blade presentational and move formatting/row shaping into PHP
- Decision: Put row-state classification, list-fragment preparation, and value stringification into PHP support classes, leaving Blade responsible only for rendering.
- Rationale: Existing templates duplicate stringification rules and state grouping logic. The new foundation exists to improve consistency and testability, so the plan shifts logic into unit-testable support classes while keeping the partial APIs simple.
- Alternatives considered:
- Continue using per-template closures for formatting: rejected because it duplicates logic and weakens regression coverage.
- Put all formatting in one presenter and no standalone stringifier: rejected because the spec requires value formatting to be reusable even without the row presenter.
Decision 6: Define a small internal contract rather than a new HTTP API
- Decision: Document the foundation contract as an internal OpenAPI-style schema with no new paths and component schemas describing presenter input, row output, summary counts, and inline list fragments.
- Rationale: The feature adds no routes, but SpecKit still expects a contract artifact. A pathless schema captures the internal handoff clearly for follow-up specs and keeps the feature aligned with its “no new routes” boundary.
- Alternatives considered:
- Skip the contracts artifact entirely: rejected because the planning workflow expects one.
- Invent a fake endpoint solely for the plan: rejected because it would contradict the specification.
Decision 7: Cover rendering behavior with focused Pest unit and view-level tests
- Decision: Add unit tests for enum, row, presenter, and stringifier behavior plus view-level tests that render the shared partials with representative fixtures and assert accessible state cues, summary counts, dark-mode-safe class presence, and inline list diff output.
- Rationale: The spec requires both PHP-layer determinism and reusable rendering safety. The repo already uses Pest heavily, including badge mapping tests and feature tests around Filament rendering. This keeps the foundation verifiable without a full browser workflow.
- Alternatives considered:
- Rely on manual UI verification only: rejected because the foundation is intended for repeated reuse.
- Add browser tests first: rejected because the behavior under test is mostly server-side presentation and Blade output.
Key integration observations
app/Services/Intune/VersionDiff.phpalready produces simpleadded,removed, andchangedbuckets for normalized comparisons.app/Services/Intune/RestoreDiffGenerator.phpalready computes policy-level preview summaries and truncation metadata that a consumer can adapt into the shared presentation layer.resources/views/filament/infolists/entries/normalized-diff.blade.phpcontains substantial specialized logic including stringification, grouped rows, and script-aware diff handling; it should remain specialized in this feature.resources/views/filament/infolists/entries/rbac-role-definition-diff.blade.php,assignments-diff.blade.php, andscope-tags-diff.blade.phpshow three separate simple diff rendering patterns that are strong future adoption candidates.app/Support/Badges/BadgeCatalog.phpand existing badge tests provide the canonical place to centralize diff state badge semantics.