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

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 entries must 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

  • family must be one of the explicitly supported family identifiers.
  • record_class and record_key must be non-empty.
  • variant must be non-empty and stable for the consumer path.
  • workspace_id, tenant_id, and context_hash must 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 = true is allowed only when the result is deterministic for the current scope.
  • freshness_policy = no_reuse means the record must not be stored or reused.
  • freshness_policy = invalidate_after_mutation requires 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

  • family must exist in a supported DerivedStateFamilyContract.
  • variant must be explicit and stable for the guarded consumer path.
  • accessPattern must be one of the supported consumer patterns.
  • scopeInputs must be explicit when capability, workspace, tenant, or route context can affect the result.
  • guardScope must be narrow enough to produce actionable failures with file and snippet output.
  • mutationSensitive and capabilitySensitive are advisory guard hints and must never replace explicit freshnessPolicy or scopeInputs declaration.

Relationships

  • One RequestScopedDerivedStateStore contains many DerivedStateResolutionRecord objects.
  • Each DerivedStateResolutionRecord is uniquely identified by one DerivedStateKey.
  • Each DerivedStateKey belongs to one DerivedStateFamilyContract.
  • Each DerivedStateConsumerDeclaration references one DerivedStateFamilyContract and is validated by the automated adoption guard before new consumers rely on the shared store.

State Transitions

Derived State Lifecycle

  1. Miss: No DerivedStateResolutionRecord exists for the requested DerivedStateKey.
  2. Resolved: The existing presenter or resolver computes the result and stores one DerivedStateResolutionRecord when reuse is allowed.
  3. Reused: Additional consumers in the same request retrieve the same stored record without a new full derivation.
  4. Invalidated: A covered mutation explicitly invalidates affected keys or family scopes when business truth changes.
  5. 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 DerivedStateConsumerDeclaration metadata to block undeclared or unsupported adoption patterns in CI.