## 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
8.2 KiB
8.2 KiB
Data Model: Request-Scoped Derived State and Resolver Memoization
This feature does not introduce persistent storage. It defines runtime-only entities that govern how deterministic presenter and resolver results are reused inside one HTTP or Livewire request.
Entities
RequestScopedDerivedStateStore
- Purpose: Holds resolved derived-state values for the lifetime of one request so repeated consumers can reuse deterministic results.
- Lifecycle: Created at request start through the Laravel container and discarded at request end.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
request_scope_id |
string | yes | Internal identifier for the active request-local store instance |
entries |
map<string, DerivedStateResolutionRecord> | yes | Resolved entries indexed by deterministic derived-state key |
invalidations |
list | no | Keys or family scopes explicitly invalidated during the request |
Validation Rules
- The store must never be serialized or persisted.
- The store must never survive beyond the current request lifecycle.
- Each key in
entriesmust be unique within the request.
DerivedStateKey
- Purpose: Defines what counts as “the same derivation” for request-local reuse.
- Contract naming note: The runtime model uses internal snake_case field names. The logical OpenAPI contract uses camelCase transport names for request and response payloads and must normalize back to this runtime key shape.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
family |
enum | yes | Covered family such as artifact_truth, operation_ux_guidance, operation_ux_explanation, related_navigation_primary, related_navigation_detail, or related_navigation_header |
record_class |
string | yes | Concrete model class or logical source type used by the family |
record_key |
string | yes | Stable string form of the source record identity |
variant |
string | yes | Surface mode or derivation variant such as list_row, detail_page, expanded, or header_action |
workspace_id |
int nullable | no | Workspace scope when relevant to the derivation |
tenant_id |
int nullable | no | Tenant scope when relevant to the derivation |
context_hash |
string nullable | no | Stable hash of any additional scope-sensitive inputs such as capability-sensitive visibility or consumer options |
Validation Rules
familymust be one of the explicitly supported family identifiers.record_classandrecord_keymust be non-empty.variantmust be non-empty and stable for the consumer path.workspace_id,tenant_id, andcontext_hashmust be included whenever omitting them could change the result.
DerivedStateResolutionRecord
- Purpose: Represents one resolved request-local entry stored under a
DerivedStateKey.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
key |
DerivedStateKey | yes | Deterministic key for the stored result |
value |
mixed | yes | The resolved presenter or resolver result |
negative_result |
bool | yes | Whether the stored value represents a stable negative result such as null, no entry, or no next action |
freshness_policy |
enum(request_stable,invalidate_after_mutation,no_reuse) |
yes | Freshness behavior for the stored result |
resolved_at |
string | yes | Internal timestamp or sequence marker for testable store behavior |
Validation Rules
negative_result = trueis allowed only when the result is deterministic for the current scope.freshness_policy = no_reusemeans the record must not be stored or reused.freshness_policy = invalidate_after_mutationrequires an explicit invalidation path on covered mutation flows.
DerivedStateFamilyContract
- Purpose: Documents the supported family-level rules for key composition, negative-result reuse, and freshness.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
family |
enum | yes | Covered family identifier |
source_method |
string | yes | Existing presenter or resolver entry point used to resolve the family |
allows_negative_result_cache |
bool | yes | Whether deterministic negative results may be reused within the request |
default_freshness_policy |
enum | yes | Default freshness behavior for the family |
required_scope_inputs |
list | yes | Key fields that must be present when the family depends on scope or capability context |
DerivedStateConsumerDeclaration
- Purpose: Declares how one UI consumer is allowed to adopt the shared request-scoped contract and provides the metadata used by the automated guardrail.
- Contract naming note: This declaration uses the same camelCase field names as the logical consumer-validation contract because the automated guard and onboarding path treat that contract as the published declaration surface.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
surface |
string | yes | Stable surface identifier such as reviews.register.table or operations.run.detail |
family |
enum | yes | Covered family identifier used by the consumer |
variant |
string | yes | Stable variant identifier such as list_row, detail_page, or header_action used by the consumer |
accessPattern |
enum(row_safe,page_safe,direct_once) |
yes | Supported consumer access pattern |
scopeInputs |
list | yes | Scope or capability inputs that must be declared for the consumer |
freshnessPolicy |
enum(request_stable,invalidate_after_mutation,no_reuse) |
yes | Freshness behavior required for this consumer |
guardScope |
list | yes | Source paths or helper seams the automated guard uses when validating adoption |
mutationSensitive |
bool | no | Advisory flag used when the consumer's visible result changes after in-request mutation and requires explicit freshness handling |
capabilitySensitive |
bool | no | Advisory flag used when capability context changes the result and the guard should require explicit scope inputs |
Validation Rules
familymust exist in a supportedDerivedStateFamilyContract.variantmust be explicit and stable for the guarded consumer path.accessPatternmust be one of the supported consumer patterns.scopeInputsmust be explicit when capability, workspace, tenant, or route context can affect the result.guardScopemust be narrow enough to produce actionable failures with file and snippet output.mutationSensitiveandcapabilitySensitiveare advisory guard hints and must never replace explicitfreshnessPolicyorscopeInputsdeclaration.
Relationships
- One
RequestScopedDerivedStateStorecontains manyDerivedStateResolutionRecordobjects. - Each
DerivedStateResolutionRecordis uniquely identified by oneDerivedStateKey. - Each
DerivedStateKeybelongs to oneDerivedStateFamilyContract. - Each
DerivedStateConsumerDeclarationreferences oneDerivedStateFamilyContractand is validated by the automated adoption guard before new consumers rely on the shared store.
State Transitions
Derived State Lifecycle
- Miss: No
DerivedStateResolutionRecordexists for the requestedDerivedStateKey. - Resolved: The existing presenter or resolver computes the result and stores one
DerivedStateResolutionRecordwhen reuse is allowed. - Reused: Additional consumers in the same request retrieve the same stored record without a new full derivation.
- Invalidated: A covered mutation explicitly invalidates affected keys or family scopes when business truth changes.
- Recomputed: The next access after invalidation resolves a fresh record under the same or updated key.
Notes
- No database migrations, model tables, or persisted read models are introduced.
- The feature must not add a generic cross-request cache abstraction.
- Existing presenter envelopes and navigation entry objects remain the business-visible payloads; the new model only governs reuse of those outputs within one request.
- The automated guardrail uses
DerivedStateConsumerDeclarationmetadata to block undeclared or unsupported adoption patterns in CI.