## Summary - add a request-scoped derived-state store with deterministic keying and freshness controls - adopt the shared contract in ArtifactTruthPresenter, OperationUxPresenter, and RelatedNavigationResolver plus the covered Filament consumers - add spec, plan, contracts, guardrails, and focused memoization and freshness test coverage for spec 167 ## Verification - vendor/bin/sail artisan test --compact tests/Feature/078/RelatedLinksOnDetailTest.php - vendor/bin/sail artisan test --compact tests/Feature/078/ tests/Feature/Operations/TenantlessOperationRunViewerTest.php tests/Feature/Monitoring/OperationsCanonicalUrlsTest.php tests/Feature/Monitoring/OperationsTenantScopeTest.php tests/Feature/Verification/VerificationAuthorizationTest.php tests/Feature/Verification/VerificationReportViewerDbOnlyTest.php tests/Feature/Verification/VerificationReportRedactionTest.php tests/Feature/Verification/VerificationReportMissingOrMalformedTest.php tests/Feature/OpsUx/FailureSanitizationTest.php tests/Feature/OpsUx/CanonicalViewRunLinksTest.php - vendor/bin/sail bin pint --dirty --format agent ## Notes - Livewire v4.0+ compliance preserved - provider registration remains in bootstrap/providers.php - no Filament assets or panel registration changes - no global-search behavior changes - no destructive action behavior changes in this PR Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #198
50 lines
5.4 KiB
Markdown
50 lines
5.4 KiB
Markdown
# Research: Request-Scoped Derived State and Resolver Memoization
|
|
|
|
## Decision 1: Use a dedicated request-scoped in-memory store bound through the Laravel container
|
|
|
|
- **Decision**: Introduce one dedicated request-scoped derived-state store with request-local lifecycle semantics instead of static arrays or persistent cache stores.
|
|
- **Rationale**: The feature needs explicit reuse within one HTTP or Livewire request and explicit isolation across requests. A request-local container binding makes that boundary visible and testable while avoiding new persistence and avoiding cross-request staleness.
|
|
- **Alternatives considered**:
|
|
- Static caches inside presenters or resources: rejected because they hide scope boundaries, duplicate behavior across families, and make invalidation inconsistent.
|
|
- `Cache::remember()` or Redis-backed caching: rejected because the spec explicitly excludes cross-request caching and because stale semantic reuse would become much harder to reason about.
|
|
|
|
## Decision 2: Key derivations by family, record identity, variant, and scope-sensitive context
|
|
|
|
- **Decision**: Define one deterministic key contract that includes the derived-state family, stable record identity, variant or surface mode, and any workspace, tenant, or visibility-sensitive context required to produce the correct result.
|
|
- **Rationale**: Existing repeated work happens because the same deterministic question is asked multiple times. Correct reuse therefore depends on a stable definition of “same question.” Model-object identity alone is insufficient because the same record can appear through different model instances or under different scopes.
|
|
- **Alternatives considered**:
|
|
- Model ID only: rejected because it cannot distinguish list vs detail variants or capability-sensitive outputs.
|
|
- `spl_object_hash()` only: rejected because it prevents convergence across separate model instances representing the same record.
|
|
|
|
## Decision 3: Integrate through the existing family entry points, not by adding a new presentation framework
|
|
|
|
- **Decision**: Route reuse through `ArtifactTruthPresenter`, `OperationUxPresenter`, and `RelatedNavigationResolver` entry points instead of creating a generic presenter base class, a universal decorator, or a new UI taxonomy layer.
|
|
- **Rationale**: The business semantics already live in these families. The feature's goal is to reduce repeated deterministic work beneath them, not to redesign how operator meaning is modeled.
|
|
- **Alternatives considered**:
|
|
- New cross-domain presentation framework: rejected because it would layer new semantics on top of already-correct families and violate the spec's narrow foundation intent.
|
|
- Surface-only fixes per page: rejected because the same repeated-cost pattern already spans multiple domains and would continue to reappear elsewhere.
|
|
|
|
## Decision 4: Converge existing hidden caches into the shared contract and keep negative results reusable
|
|
|
|
- **Decision**: Standardize existing local request-like caches, such as the finding primary related-entry cache, behind the shared contract and allow deterministic negative results like “no related entry” or “no next action” to be reused within one request.
|
|
- **Rationale**: The repo already contains evidence that request-local reuse is useful, but it is unevenly applied. Converging on one contract avoids parallel caching patterns and still prevents repeated work when the correct result is the absence of a link or action.
|
|
- **Alternatives considered**:
|
|
- Leave existing local caches untouched and optimize only the worst pages: rejected because it would preserve multiple hidden patterns and make future adoption harder.
|
|
- Cache only positive results: rejected because deterministic negative results can also drive repeated work and should be equally reusable when scope-stable.
|
|
|
|
## Decision 5: Treat mutation freshness as an explicit family rule
|
|
|
|
- **Decision**: Covered mutating flows must explicitly invalidate or bypass request-local reuse after business state changes within the same request.
|
|
- **Rationale**: The spec requires “no stale within request ambiguity.” A clear family-level freshness rule is safer than assuming the existing code path order will always avoid stale derived values.
|
|
- **Alternatives considered**:
|
|
- Cache for the full request without exceptions: rejected because post-action state could become stale and misleading.
|
|
- Disable reuse on every Livewire action: rejected because many actions still have deterministic pre- and post-action read phases where request-local reuse remains valuable.
|
|
|
|
## Decision 6: Test derivation-count behavior directly instead of proxying everything through query-count assertions
|
|
|
|
- **Decision**: Validate the feature with focused unit and feature tests that prove one full derivation per request for representative covered families, plus explicit scope-safety and mutation-path tests.
|
|
- **Rationale**: The repeated-cost problem is not just SQL chatter. It is repeated presenter and resolver work across closures and page fragments. Query-count assertions alone would miss important non-query work and would not prove freshness rules.
|
|
- **Alternatives considered**:
|
|
- Measure only query counts: rejected because the problem is broader than SQL and includes repeated in-memory translation and navigation assembly.
|
|
- Rely on manual profiling only: rejected because this feature needs regression protection against future local cache drift.
|