TenantAtlas/specs/167-derived-state-memoization/quickstart.md
2026-03-28 15:57:45 +01:00

5.4 KiB

Quickstart: Request-Scoped Derived State and Resolver Memoization

Implement Spec 167 by adding one explicit request-scoped derived-state store beneath the existing presenter and resolver families, then adopt it on representative list, detail, and canonical surfaces without changing operator-visible semantics.

Implementation Steps

  1. Add the narrow request-scoped derived-state support types under the existing support layer and bind the store in app/Providers/AppServiceProvider.php with request-local lifecycle semantics.
  2. Define the deterministic key contract for family, record identity, variant, and scope-sensitive context, plus an explicit invalidation path for mutation-sensitive derivations.
  3. Route ArtifactTruthPresenter::forBaselineSnapshot(), forEvidenceSnapshot(), forTenantReview(), forReviewPack(), and forOperationRun() through the shared store.
  4. Refactor repeated consumer seams on ReviewRegister, EvidenceOverview, BaselineSnapshotResource, BaselineSnapshotResource/Pages/ViewBaselineSnapshot.php, EvidenceSnapshotResource, TenantReviewResource, ReviewPackResource, and OperationRunResource so the first-slice badge, description, next-step, and helper consumers share one per-record derivation.
  5. Route covered OperationUxPresenter guidance/explanation and RelatedNavigationResolver primary/detail/header entry paths through the same store.
  6. Converge the existing finding-specific related-entry cache and other repeated navigation consumers toward the shared contract instead of leaving multiple local cache patterns in place.
  7. Add focused unit and feature tests for derivation counts, negative-result reuse, mutation freshness, and cross-scope safety, including tests/Feature/Filament/DerivedStateMutationFreshnessTest.php as the dedicated freshness suite.
  8. Add tests/Feature/Guards/DerivedStateConsumerAdoptionGuardTest.php so CI fails with actionable output if covered families reintroduce ad hoc local caches or adopt the shared store without explicit consumer declaration metadata.

Future Family Adoption

  1. Confirm the candidate family is deterministic for the proposed access path and that adopting the shared store does not introduce a second semantic layer.
  2. Declare the full consumer metadata set under the top-level x-derived-state-consumers extension in contracts/request-scoped-derived-state.logical.openapi.yaml before adding the consumer: surface, family, variant, accessPattern, scopeInputs, freshnessPolicy, and guardScope. Add requiredMarkers and maxOccurrences guard metadata so the adoption guard can point to the intended helper seam and reject bypasses or resurrected local caches. Advisory hints such as mutationSensitive or capabilitySensitive may be added when they help review, but they do not replace the required declaration fields.
  3. Choose one supported accessPattern per surface: row_safe, page_safe, or direct_once; do not introduce a new page-local static cache for a covered family.
  4. Add or update the focused Pest coverage that proves repeated reads collapse to one derivation, scope boundaries remain intact, and any mutation-sensitive path is fresh after state changes.
  5. If a family cannot satisfy deterministic keying or freshness rules, use the explicit no-reuse path instead of weakening the shared contract.
  6. Run tests/Feature/Guards/DerivedStateConsumerAdoptionGuardTest.php after adding or widening adoption so undeclared scope inputs, freshness gaps, missing guard scope, and ad hoc local caches fail before merge.

Verification

Automated

vendor/bin/sail up -d
vendor/bin/sail artisan test --compact tests/Unit/Support/Ui/DerivedState/RequestScopedDerivedStateStoreTest.php
vendor/bin/sail artisan test --compact tests/Feature/Filament/ReviewRegisterDerivedStateMemoizationTest.php
vendor/bin/sail artisan test --compact tests/Feature/Filament/EvidenceOverviewDerivedStateMemoizationTest.php
vendor/bin/sail artisan test --compact tests/Feature/Filament/OperationRunDerivedStateMemoizationTest.php
vendor/bin/sail artisan test --compact tests/Feature/Filament/DerivedStateMutationFreshnessTest.php
vendor/bin/sail artisan test --compact tests/Feature/Navigation/RelatedNavigationResolverMemoizationTest.php
vendor/bin/sail artisan test --compact tests/Feature/Guards/DerivedStateConsumerAdoptionGuardTest.php
vendor/bin/sail bin pint --dirty --format agent

Manual

  1. Open the review register and verify artifact-truth label, publication state, and next-step text remain unchanged while repeated presenter calls are eliminated.
  2. Open the evidence overview and verify one active snapshot row per tenant still renders the same truth and freshness messaging, then confirm one canonical authorization regression still behaves correctly: non-member or wrong-scope access remains deny-as-not-found and an in-scope user lacking capability remains forbidden.
  3. Open the tenantless operation-run viewer and verify related context, guidance, and artifact-truth details remain consistent.
  4. Exercise one covered mutating flow and verify any post-action truth or related navigation shown in the same request reflects the updated business state.

Expected Outcome

  • Covered presenter and resolver families resolve deterministic results once per request for the same key.
  • Covered surfaces retain the same operator-visible semantics and navigation destinations.
  • No persistent caches, no new semantic state families, and no cross-tenant or cross-workspace reuse leakage are introduced.