TenantAtlas/specs/132-guid-context-resolver/research.md
ahmido 8ee1174c8d feat: add resolved reference presentation layer (#161)
## Summary
- add the shared resolved-reference foundation with registry, resolvers, presenters, and badge semantics
- refactor related context, assignment evidence, and policy-version assignment rendering toward label-first reference presentation
- add Spec 132 artifacts and focused Pest coverage for reference resolution, degraded states, canonical linking, and tenant-context carryover

## Verification
- `vendor/bin/sail bin pint --dirty --format agent`
- focused Pest verification was marked complete in the task artifact

## Notes
- this PR is opened from the current session branch
- `specs/132-guid-context-resolver/tasks.md` reflects in-progress completion state for the implemented tasks

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #161
2026-03-10 18:52:52 +00:00

6.0 KiB
Raw Blame History

Phase 0 Research: GUID Context Resolver & Human-Readable Reference Presentation

Decision 1: Extend the existing shared navigation layer instead of replacing it

  • Decision: Build the new reference semantics on top of the current shared related-context/navigation foundation rather than starting from a second unrelated abstraction.
  • Rationale: The codebase already has RelatedNavigationResolver, CrossResourceNavigationMatrix, RelatedContextEntry, shared related-context Blade rendering, and canonical route helpers. Reusing those extension points reduces migration risk and keeps Spec 131 and Spec 132 aligned.
  • Alternatives considered:
    • Replace the navigation layer entirely with a new reference-only stack: rejected because it would duplicate route, availability, and action-label logic that already exists and is already wired into multiple resources.
    • Keep page-level arrays and only restyle the Blade partial: rejected because it would not satisfy the specs requirement for explicit reference classes, shared semantics, and distinct degraded states.

Decision 2: Model references through explicit input and output contracts

  • Decision: Introduce a structured ReferenceDescriptor input contract and a normalized ResolvedReference output contract with explicit state, type, label, technical detail, and optional canonical target.
  • Rationale: The current RelatedContextEntry payload is useful for immediate rendering but too shallow for the broader semantics required here. A descriptor plus resolved-reference pair makes support for partial resolution, unsupported classes, and future providers predictable and testable.
  • Alternatives considered:
    • Continue passing free-form arrays between resources and partials: rejected because it invites page-specific drift and makes regression testing weak.
    • Resolve everything directly inside Blade partials: rejected because presentation should not own resolution logic or authorization decisions.

Decision 3: Keep render-time resolution DB-only and best-effort

  • Decision: Resolve references at render time using existing relations, local model lookups, current workspace or tenant context, stored fallback labels, and existing helper services only; do not make live Microsoft Graph or provider calls.
  • Rationale: The constitution and current architecture keep monitoring and governance views DB-only at render time. Best-effort local resolution is sufficient for the target surfaces and avoids latency, availability, and authorization leakage problems.
  • Alternatives considered:
    • Make on-demand provider calls to improve labels: rejected because it violates the DB-only rendering expectation, adds latency, and complicates authorization.
    • Cache external labels aggressively in new tables: rejected because the spec explicitly avoids introducing new persistent models solely for resolution.

Decision 4: Use two shared presentation variants with one semantic payload

  • Decision: Provide a compact variant for dense tables and a detailed variant for related-context or infolist sections, both powered by the same resolved-reference payload.
  • Rationale: Some target surfaces are list-dense and cannot absorb a full detail block without becoming noisy, while detail pages need more explanatory context and degraded-state messaging. One semantic contract with two renderers keeps the hierarchy stable while fitting each surface.
  • Alternatives considered:
    • Force one heavy component everywhere: rejected because it would make list screens noisy and reduce scanability.
    • Allow each page to design its own variant independently: rejected because it would recreate the inconsistency the spec is meant to solve.

Decision 5: Authorization controls linkability first, label disclosure second

  • Decision: Canonical targets become actionable only when current policy allows access; label and state disclosure must degrade to inaccessible or unresolved only to the extent policy allows.
  • Rationale: The products 404-vs-403 semantics and deny-as-not-found rules are already explicit. The reference layer must not accidentally leak object existence or friendly labels just because it has enough data to resolve them internally.
  • Alternatives considered:
    • Hide every unauthorized reference completely: rejected because some surfaces legitimately need to preserve the existence of a reference without allowing navigation.
    • Always show the resolved label even when navigation is forbidden: rejected because that can leak protected detail.

Decision 6: Upgrade narrow label helpers into structured resolver collaborators

  • Decision: Existing helpers such as EntraGroupLabelResolver should become collaborators that feed structured resolution outputs rather than emitting final UI labels.
  • Rationale: Current helpers return a single string like Name (…token), which is not enough to separate primary label, type/context, state, and technical detail. They remain useful, but inside a richer resolver contract.
  • Alternatives considered:
    • Leave helpers untouched and parse their formatted strings back into UI parts: rejected because it is brittle and loses state intent.
    • Rewrite all provider-backed label logic from scratch: rejected because the current local lookup behavior is already useful and should be preserved.

Decision 7: Leverage the repos existing registry-and-DTO pattern

  • Decision: Mirror the BaselineSnapshotPresenter and SnapshotTypeRendererRegistry pattern by using immutable value objects, registry dispatch, and fallback behavior for references.
  • Rationale: The repo already favors registry-driven support layers for normalization and rendering. Following that pattern keeps the reference system familiar, testable, and consistent with current architecture.
  • Alternatives considered:
    • Put the entire resolution switch in one large service class: rejected because explicit per-class resolvers are easier to extend and test.
    • Encode resolution rules in configuration only: rejected because authorization-aware destination logic and best-effort lookup behavior require application code.