## 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
135 lines
8.2 KiB
Markdown
135 lines
8.2 KiB
Markdown
# 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<string> | 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<string> | 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<string> | 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<string> | 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.
|