Skip to content

Add shared serializable class policy and harden model caches - #458

Merged
binaryfire merged 36 commits into
0.4from
fix/cache-serializable-class-policy
Jul 28, 2026
Merged

Add shared serializable class policy and harden model caches#458
binaryfire merged 36 commits into
0.4from
fix/cache-serializable-class-policy

Conversation

@binaryfire

@binaryfire binaryfire commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Hypervel defaults cache.serializable_classes to false, but Auth and Sanctum cache Eloquent models without a way for their providers to contribute the framework-known classes those models require. Applications must repeat root model classes manually, nested model graphs can still deserialize incompletely, and a cache store created early can freeze the policy before later providers finish booting.

This PR makes the serializable-class policy a worker-lifetime capability owned by CacheManager. Packages contribute lazy class resolvers during boot, then the policy is finalized after configuration is ready for the process. Auth and Sanctum contribute the model classes they can derive from configuration. Application-owned relation models, morph targets, and custom Eloquent containers remain explicit declarations through Cache::allowSerializableClassesUsing().

Design

  • Add SerializableClassPolicy with the existing config semantics: false denies classes, arrays allow explicit classes, and null or true remain unrestricted. Provider contributions are additive, normalized, and deduplicated.
  • Inject one live policy into every manager-built direct PHP-serializing cache store. Existing Laravel-compatible scalar constructor parameters remain available for direct construction.
  • Finalize the policy once after application boot in console processes or after worker configuration reload in each Swoole worker and taskworker. Earlier store construction remains safe because stores hold the shared policy object rather than a copied array.
  • Replay package configuration merge operations against freshly loaded worker configuration instead of replaying master-process results that may contain stale environment values.
  • Add one model-cache store validator shared by Auth and Sanctum. It recursively validates stack layers, accepts persistent or explicitly node-local model stores, and rejects stores that cannot provide a coherent cross-request identity cache.
  • Accept Redis serializers that preserve PHP object types. Native PHP, igbinary, and PHP-only msgpack remain available; JSON, non-PHP msgpack, and unknown modes are rejected for first-party model caches. Ordinary Redis cache usage is unchanged.
  • Keep Session cache values under the Session serialization boundary. No cache class policy or extra framing is added there.

Auth and Sanctum

Auth automatically contributes enabled Eloquent provider models plus the stock Eloquent collection and pivot containers. Sanctum contributes its configured personal access token model and Eloquent models selected by Sanctum guards.

Sanctum token caching now uses one resolved tokenable instance throughout authentication. PAT cache entries never embed the tokenable relation, cache hits and misses expose the same live relation shape, and the internal last_used_at audit write invalidates only the PAT entry. Application-visible token updates and deletions still clear both token and tokenable entries.

Configured Auth and Sanctum stores are validated at process startup. Directly constructed cached Auth providers also validate before enabling their cache.

Cache correctness

FailoverStore::forget() and flush() now attempt every configured leaf. First-success invalidation could leave a stale lower-priority value that later became visible during failover. Reads and writes retain their existing first-success behavior.

Stack layers are normalized to a stable list for recursive validation and tag error reporting. Storage cache paths use xxh128 for their internal non-cryptographic key digest.

Laravel compatibility

Laravel-compatible cache-store constructors, named arguments, protected scalar policy properties, and Sanctum extension points are preserved.

CacheManager::getSerializableClasses(array $config) is intentionally removed. Its per-store copied-array contract cannot represent the live global worker policy without keeping two competing policy systems. The supported extension point is Cache::allowSerializableClassesUsing().

Performance

The policy and model-store checks are resolved at process startup. Cache reads do not perform graph traversal, extra serialization, provider resolution, container lookup, or additional backend requests. Native Redis serializer paths continue to bypass PHP unserialization as before.

Validation

  • Focused Foundation configuration, Support provider, Redis, Cache, Auth, Sanctum, and Queue compatibility tests
  • composer fix, including formatting, static analysis, the full parallel suite, Testbench package coverage, and dogfood coverage

Summary by CodeRabbit

  • New Features
    • Introduced centralized, boot-time controlled cache deserialization rules for safer object restoration.
    • Added validation for identity/model cache backends and Redis serializer compatibility, including support for nested cache stacks.
    • Enhanced authentication and token caching with safer payload shapes, stricter cache-store rules, and fewer repeated lookups.
    • Improved worker config replay behavior after environment/config reloads.
  • Bug Fixes
    • Updated failover cache invalidation so forget/flush attempt every backing store and report partial failures correctly.
  • Documentation
    • Expanded guidance on cache serialization security, supported auth/token cache stores, and cross-node invalidation behavior.

Clarify approval for Laravel API differences and limit package README comparisons to user-facing behavior. Record the event wildcard and request-context rules that affect coroutine-safe framework work.
Prevent new entries while the guide awaits deletion and remove rare route-object guidance that does not belong in global application context.
Record semantic package config operations instead of their first evaluated result so fresh workers merge against freshly loaded configuration. Preserve mutation order, seal replay, and cover both package helpers and dotenv reload.
Combine configured and provider-contributed classes into one worker-lifetime allowlist while preserving unrestricted and deny-all modes. Normalize and deduplicate sources without autoloading, finalize once after boot, and reject late registration.
Preserve Laravel's scalar serializableClasses constructor API while allowing manager-built array and worker-array stores to use the shared live policy. Cover scalar compatibility, policy precedence, and serialized round trips.
Preserve Laravel's scalar serializableClasses constructor API while allowing manager-built database stores to use the shared live policy. Cover direct scalar construction and restricted object round trips.
Preserve Laravel's scalar serializableClasses constructor API while allowing manager-built file stores to use the shared live policy. Propagate both serialization inputs to lock stores and cover restricted round trips and subclass paths.
Route PHP-owned Redis deserialization through the shared live policy while preserving scalar direct construction, numeric fast paths, and native serializer bypass. Cover allowed and denied values at both the store and serialization-helper boundaries.
Preserve direct scalar allowlists while allowing manager-built storage stores to use the shared live policy. Derive physical paths with stable unseeded xxh128 over the prefixed key and pin the exact mapping in tests.
Give every manager-built PHP-serializing store the same worker-lifetime policy and expose boot-time class contributions through the cache facade. Keep Swoole's internal payloads outside the user-value policy and cover manager, direct-store, and interval paths.
Freeze contributed serializable classes after providers boot in console processes and after worker configuration reload on servers. Preserve the parameterless provider boot API and verify every worker and taskworker lifecycle.
Expose normalized stack repositories for recursive validation and report stable zero-based layer indexes even when configuration uses string keys. Keep tag composition and ordinary stack behavior unchanged.
Reject stores and Redis serializers that cannot safely preserve cached model objects, including unsupported leaves inside nested stacks. Inspect configured Redis options without opening a connection and return actionable feature and layer details.
Declare the cache and core packages used directly by Auth's model-cache validation and worker-start lifecycle integration. This keeps the subtree package installable without relying on monorepo-only dependency availability.
Replace Auth's shallow store whitelist with the shared recursive model-cache validator before provider state or listeners mutate. Preserve tag validation order and cover supported, rejected, and direct-construction paths.
Contribute enabled Eloquent provider models and stock Eloquent graph containers to the global class policy. Validate every configured cached provider after application boot or worker config reload without resolving guards or adding request-path work.
Exercise real serialized cache hits with automatic root-model contributions, explicit application classes, and denied nested relations. Pin zero-query hot reads and PHP's incomplete-class behavior for undeclared graph members.
Remove the manual user-model allowlist from tagged Auth caching so the integration suite proves provider contributions work through taggable stores. Retain cache-hit and invalidation coverage.
Round-trip cached Eloquent users through Redis with policy-owned serialization and each available type-preserving native serializer. Verify every accepted mode returns a concrete model without a second database query.
Declare the core package used directly by Sanctum's worker-start cache validation listener. This keeps the subtree package dependency graph complete outside the monorepo.
Use one typed constant for initial and flushed personal-access-token model state. This keeps worker and test resets aligned with the framework default.
Contribute the selected access-token model, Sanctum guard provider models, and stock Eloquent graph containers to the global policy. Validate the configured store at process startup while preserving the parameterless provider boot API.
Keep tokenable relations out of cached access-token snapshots, preserve other eager relations, and refresh audit timestamps from a clean clone. Let internal last-used writes evict only the token entry while application-visible updates and deletion still clear both caches.
Resolve and attach one tokenable instance for provider validation, callbacks, events, and withAccessToken instead of fetching a second object. Preserve the protected Laravel validation hook and zero-query hot authentication.
Run forget and flush against every configured leaf so stale lower-priority values cannot reappear. Preserve first-success behavior elsewhere, existing failure events and dedupe, and last-exception semantics when every store fails.
Describe the global false, array, and unrestricted policy modes at the configuration source. Clarify provider contributions, forged-payload safety, and native PhpRedis serializer bypass.
Document boot-time class contributions, nested application objects, denied-class behavior, native Redis serializer rules, and model-store validation. Explain failover invalidation behavior and the Session serializer boundary without exposing internal implementation details.
State which provider models and Eloquent containers Auth contributes automatically and which application-owned graph classes remain explicit. Document supported stores, Redis serializer rules, startup-only configuration, and cache staleness behavior.
Update application guidance for automatic user-model contributions, explicit nested graph classes, recursive store validation, and type-preserving Redis serializers. Clarify invalidation and maximum-staleness behavior without requiring manual root-model configuration.
Keep framework and Testbench defaults aligned while documenting storage support, recursive stack validation, shared-cache topology, and boot-only cache configuration. Replace the stale outer-stack caveat with the validated behavior.
Document automatic token and provider model contributions, explicit application graph classes, supported serializers and stores, and startup-only configuration. Clarify tokenable separation, invalidation, and staleness guarantees.
Explain automatic identity-model contributions, application-owned nested classes, recursive store validation, and Redis serializer requirements. Describe the cache shapes and invalidation rules that preserve zero-query hot authentication.
State that the configured token cache TTL is also the maximum time a cached tokenable identity may remain stale. Keep the existing last-used update interval guidance intact.
Bring the Queue lifecycle and parity audit, PostgreSQL schema correction, and migration publishing fixes into the cache policy branch before final verification.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds shared cache serialization policies, recursive model-cache store validation, auth and Sanctum cache integration, failover invalidation handling, replayable configuration mutations, and related documentation and test coverage.

Changes

Cache policy and store behavior

Layer / File(s) Summary
Shared serialization policy
src/cache/src/*Store.php, src/cache/src/SerializableClassPolicy.php, src/cache/src/CacheManager.php
Cache stores share a boot-time-finalized policy for PHP unserialization.
Model store validation
src/cache/src/ModelCacheStoreValidator.php, src/cache/src/StackStore.php
Model-compatible stores and Redis serializer modes are validated recursively through nested stacks.
Failover and storage behavior
src/cache/src/FailoverStore.php, src/cache/src/StorageStore.php
Invalidation attempts all failover stores, while storage paths use xxh128 hashing.

Auth and Sanctum

Layer / File(s) Summary
Provider integration
src/auth/src/*, src/sanctum/src/*
Auth and Sanctum providers register serializable model graphs and validate cache configuration during startup or worker startup.
Token cache behavior
src/sanctum/src/PersonalAccessToken.php, src/sanctum/src/SanctumGuard.php
Cached token entries omit tokenable; tokenable resolution and invalidation are handled separately.

Configuration replay

Layer / File(s) Summary
Replayable config mutations
src/foundation/src/Configuration/ConfigMutationTracker.php, src/support/src/ServiceProvider.php
Config merges and recursive replacements are recorded as closures and replayed against fresh worker repositories.

Documentation and tests

Layer / File(s) Summary
Documentation updates
AGENTS.md, docs/ai/*, docs/plans/*, src/*/README.md, src/boost/docs/*, src/foundation/config/*
Documentation describes the updated serialization, store, invalidation, lifecycle, configuration, and Laravel-difference rules.
Validation coverage
tests/Cache/*, tests/Auth/*, tests/Sanctum/*, tests/Integration/*, tests/Foundation/*, tests/Support/*
Tests cover policy lifecycle, Redis serializers, nested stores, auth/Sanctum caching, failover behavior, and configuration replay.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant Application
  participant AuthServiceProvider
  participant CacheManager
  participant ModelCacheStoreValidator
  participant CacheStore
  Application->>AuthServiceProvider: boot()
  AuthServiceProvider->>CacheManager: register serializable model resolver
  AuthServiceProvider->>CacheManager: resolve configured auth store
  AuthServiceProvider->>ModelCacheStoreValidator: validate repository
  CacheManager->>CacheStore: provide shared serialization policy
  Application->>CacheManager: finalize policy at startup or worker start
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: introducing a shared serializable class policy and strengthening cache validation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cache-serializable-class-policy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown

Greptile Summary

Adds a shared worker-lifetime cache serialization policy and strengthens model-cache correctness.

  • Registers Auth and Sanctum model classes with the shared serialization policy and validates configured model-cache stores.
  • Revises Sanctum token/tokenable caching and invalidation behavior.
  • Makes failover invalidation attempt every configured store.
  • Replays package configuration mutations after worker configuration reload.

Confidence Score: 5/5

The PR appears safe to merge based on the eligible follow-up review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
src/cache/src/SerializableClassPolicy.php Introduces centralized, lazily resolved class restrictions for PHP cache deserialization.
src/cache/src/CacheManager.php Owns the shared serialization policy and injects it into manager-created serializing stores.
src/cache/src/ModelCacheStoreValidator.php Recursively validates whether configured cache stores can safely support cross-request model caching.
src/cache/src/FailoverStore.php Changes destructive cache operations to attempt every failover backend rather than stopping after the first success.
src/auth/src/EloquentUserProvider.php Integrates cached Eloquent users with store validation, serialization policy registration, and model-event invalidation.
src/sanctum/src/PersonalAccessToken.php Separates token and tokenable cache payloads while refining update and deletion invalidation.
src/sanctum/src/SanctumServiceProvider.php Registers Sanctum model classes with the shared policy and validates its model cache at process startup.
src/foundation/src/Configuration/ConfigMutationTracker.php Records package configuration mutations so they can be replayed against freshly loaded worker configuration.

Reviews (2): Last reviewed commit: "docs(cache): record serialized class pol..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (9)
tests/Cache/SerializableClassPolicyTest.php (1)

110-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider covering finalize() idempotency.

SerializableClassPolicy::finalize() early-returns when already finalized; a second finalize() call after the resolvers are cleared isn't exercised, so a regression there (e.g. re-resolving to an empty set) would go unnoticed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Cache/SerializableClassPolicyTest.php` around lines 110 - 141, Extend
testUnserializeRecomputesBeforeFinalizationAndFreezesAfterward to call
SerializableClassPolicy::finalize() a second time after the first finalization,
then verify the finalized policy remains frozen and resolverRuns is unchanged,
covering idempotent finalization without re-resolving.
tests/Cache/ModelCacheStoreValidatorTest.php (2)

282-298: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Two independent scenarios in one test method.

The "last option wins" behavior is asserted twice with opposite outcomes; splitting into two methods makes a failure self-describing and avoids the mixed assertTrue(true) + expectException flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Cache/ModelCacheStoreValidatorTest.php` around lines 282 - 298, Split
testLastRecognizedRedisSerializerOptionWins into two focused test methods: one
asserting validation succeeds when Redis::OPT_SERIALIZER is the last recognized
option, and another asserting UnsupportedModelCacheStoreException when the
case-variant serializer option is last. Remove the placeholder assertTrue(true)
and keep each scenario’s setup and expectation isolated.

32-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a named "did not throw" helper instead of assertTrue(true).

The placeholder assertions communicate intent poorly. A small private helper (e.g. assertValidates(...) that calls validate() and then asserts) or a comment noting the assertion exists only to satisfy PHPUnit's risky-test detection would read better.

Also applies to: 69-83, 145-188

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Cache/ModelCacheStoreValidatorTest.php` around lines 32 - 46, Replace
the placeholder assertTrue(true) calls in
testAcceptsSupportedStoresAndSubclasses and the other referenced validation
tests with a named private helper such as assertValidates that invokes the
validator and clearly communicates the expected no-throw outcome. Update each
affected test to use the helper while preserving the existing store coverage and
validation arguments.
tests/Cache/CacheFileStoreTest.php (1)

151-172: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reflection on private state makes this assertion brittle.

Asserting the lock store's internal serializableClasses/serializableClassPolicy properties couples the test to implementation details. A behavioral assertion (e.g. round-tripping an object through the lock store and checking it comes back as __PHP_Incomplete_Class) would survive refactors of the property names.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Cache/CacheFileStoreTest.php` around lines 151 - 172, Replace the
ReflectionProperty-based assertions in
testLockStoreRetainsBothSerializationPolicies with a behavioral round-trip
through the lock store: persist and retrieve an object covered by the configured
serialization policy, then assert it is returned as __PHP_Incomplete_Class.
Remove the private-state inspection while preserving coverage that both
serializableClasses and serializableClassPolicy are propagated to the lock
store.
tests/Integration/Cache/FailoverStoreTest.php (1)

437-463: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

failoverStore() and buildFailoverRepository() largely overlap.

Both construct a mocked CacheManager + Dispatcher and a FailoverStore over ['primary', 'fallback']. buildFailoverRepository() could be expressed as new Repository($this->failoverStore([...], $events)) once the helper accepts real repositories too.

♻️ Sketch
 private function buildFailoverRepository(Repository $primary, Repository $fallback): Repository
 {
-    $cacheManager = m::mock(CacheManager::class);
-    $cacheManager->shouldReceive('store')->with('primary')->andReturn($primary);
-    $cacheManager->shouldReceive('store')->with('fallback')->andReturn($fallback);
-
     $events = m::mock(Dispatcher::class);
     $events->shouldReceive('dispatch')->withAnyArgs()->andReturnNull();
+    $events->shouldReceive('hasListeners')->withAnyArgs()->andReturn(false);
 
-    return new Repository(new FailoverStore($cacheManager, $events, ['primary', 'fallback']));
+    return new Repository($this->failoverStore([
+        'primary' => $primary,
+        'fallback' => $fallback,
+    ], $events));
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Integration/Cache/FailoverStoreTest.php` around lines 437 - 463,
Refactor the test helpers so buildFailoverRepository() delegates to
failoverStore() instead of duplicating CacheManager, Dispatcher, and
FailoverStore setup. Update failoverStore() to accept the real repository
instances needed by buildFailoverRepository(), while preserving its existing
named-repository behavior and event handling, then wrap the returned
FailoverStore in Repository.
tests/Auth/AuthEloquentUserProviderCacheTest.php (1)

52-55: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mock the concrete ModelCacheStoreValidator instead of an anonymous mock.

An untyped m::mock() bound under ModelCacheStoreValidator::class won't catch signature drift in validate() (arg count/types), unlike tests/Auth/AuthManagerTest.php which mocks the concrete class.

♻️ Proposed change
-        $this->storeValidator = m::mock();
+        $this->storeValidator = m::mock(ModelCacheStoreValidator::class);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Auth/AuthEloquentUserProviderCacheTest.php` around lines 52 - 55,
Update the test setup around storeValidator to mock the concrete
ModelCacheStoreValidator class rather than using an untyped m::mock(), while
preserving the existing default validate() expectation and container binding so
validate() signature changes are detected.
tests/Auth/AuthServiceProviderTest.php (1)

147-201: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test name promises taskworker-specific behavior that the listener doesn't distinguish.

AuthServiceProvider::boot() validates on every AfterWorkerStart, so $server->taskworker = true at Line 199 doesn't exercise any distinct branch — the assertion would pass identically with false. Either drop the flag from the name/setup, or add a second case asserting both worker types behave the same, so the test documents an actual contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Auth/AuthServiceProviderTest.php` around lines 147 - 201, Update
testServerValidationUsesWorkerDependenciesAndRunsForTaskworkers to match the
behavior it actually verifies: either remove the taskworker-specific name and
$server->taskworker setup, or add explicit coverage showing the AfterWorkerStart
listener behaves identically for taskworker and non-taskworker servers. Ensure
the assertions distinguish both cases if retaining the taskworker contract.
tests/Integration/Auth/EloquentUserProviderCacheTest.php (1)

488-495: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Byte-identical countQueriesForTable() copied into three test classes. The shared root cause is the absence of a reusable query-log assertion helper; extract it into a trait (e.g. Fixtures/testing concern) and use it in all three.

  • tests/Integration/Auth/EloquentUserProviderCacheTest.php#L488-L495: remove the local helper and pull in the shared trait.
  • tests/Integration/Auth/EloquentUserProviderRedisCacheTest.php#L185-L192: remove the local helper and pull in the shared trait.
  • tests/Sanctum/GuardTest.php#L201-L208: remove the local helper and pull in the shared trait.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Integration/Auth/EloquentUserProviderCacheTest.php` around lines 488 -
495, Extract the duplicated countQueriesForTable() helper into a reusable
testing trait, preserving its existing query-log filtering behavior. Remove the
local helper and import/use the shared trait in
tests/Integration/Auth/EloquentUserProviderCacheTest.php#L488-L495,
tests/Integration/Auth/EloquentUserProviderRedisCacheTest.php#L185-L192, and
tests/Sanctum/GuardTest.php#L201-L208.
src/sanctum/src/SanctumServiceProvider.php (1)

42-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider method-injected boot() params over manual make() calls.

Lines 42-43 resolve CacheManager and ConfigRepository via $this->app->make(...) inside boot(). Since Laravel-style containers auto-resolve type-hinted boot() parameters, injecting boot(CacheManager $cache, ConfigRepository $config) would align better with the coding guideline to inject contracts rather than construct/resolve framework services directly. This is a style improvement, not a defect, and would ripple into the mock-heavy test setup in SanctumServiceProviderTest.php.

Based on coding guidelines: "Use dependency injection for newly written classes; inject contracts instead of using helpers, facades, or constructing framework services directly."

Also applies to: 90-135

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/sanctum/src/SanctumServiceProvider.php` around lines 42 - 43, Update
SanctumServiceProvider::boot to accept CacheManager and ConfigRepository as
type-hinted parameters, removing the corresponding app->make calls. Adjust
SanctumServiceProviderTest.php mock setup and invocations as needed to supply
these dependencies while preserving existing boot behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cache/src/ModelCacheStoreValidator.php`:
- Around line 40-73: Update validateStore() to handle FailoverStore by resolving
and recursively validating each configured backing store, preserving the current
feature and layerPath context for nested locations. Place this branch before the
unsupported-store exception, and retain explicit rejection behavior for
ArrayStore and WorkerArrayStore when they are not safe for model caching.

In `@src/foundation/config/auth.php`:
- Around line 99-107: The store documentation in src/foundation/config/auth.php
lines 99-107 and src/testbench/hypervel/config/auth.php lines 25-33 conflates
standalone node-local stores with node-local stack tiers. Update both comment
blocks to state that standalone file/swoole deployments are single-instance
only, while stacks with a node-local upper tier can be eventually consistent,
explicitly including [file, redis] alongside the existing example.

In `@src/foundation/config/cache.php`:
- Around line 127-133: Update the serializable_classes=false documentation in
src/foundation/config/cache.php lines 127-133 to state that arbitrary classes
are blocked while explicit classes contributed through
allowSerializableClassesUsing() by framework, package, or application providers
remain permitted. Revise the opening false-mode explanation in
src/boost/docs/cache.md lines 85-117 to clarify that application classes can
also be allowed through resolver contributions, not only through the
configuration array.

In `@src/sanctum/src/SanctumServiceProvider.php`:
- Around line 42-88: Update the serializable-class resolver registered in
SanctumServiceProvider so it resolves the current ConfigRepository inside the
closure, rather than capturing the boot-time $config. Ensure the enabled flag,
auth.guards, providers, and model list are read from the worker-reloaded
configuration when the resolver executes, while preserving the existing
validation and returned class list.

In `@tests/Sanctum/GuardTest.php`:
- Around line 85-92: Update disableTokenCache() to write sanctum.cache.enabled
directly to false on the provided ApplicationContract, in addition to updating
tokenCacheEnabled as needed. Ensure the
#[DefineEnvironment('disableTokenCache')] callback disables caching regardless
of whether it runs before or after defineEnvironment().

---

Nitpick comments:
In `@src/sanctum/src/SanctumServiceProvider.php`:
- Around line 42-43: Update SanctumServiceProvider::boot to accept CacheManager
and ConfigRepository as type-hinted parameters, removing the corresponding
app->make calls. Adjust SanctumServiceProviderTest.php mock setup and
invocations as needed to supply these dependencies while preserving existing
boot behavior.

In `@tests/Auth/AuthEloquentUserProviderCacheTest.php`:
- Around line 52-55: Update the test setup around storeValidator to mock the
concrete ModelCacheStoreValidator class rather than using an untyped m::mock(),
while preserving the existing default validate() expectation and container
binding so validate() signature changes are detected.

In `@tests/Auth/AuthServiceProviderTest.php`:
- Around line 147-201: Update
testServerValidationUsesWorkerDependenciesAndRunsForTaskworkers to match the
behavior it actually verifies: either remove the taskworker-specific name and
$server->taskworker setup, or add explicit coverage showing the AfterWorkerStart
listener behaves identically for taskworker and non-taskworker servers. Ensure
the assertions distinguish both cases if retaining the taskworker contract.

In `@tests/Cache/CacheFileStoreTest.php`:
- Around line 151-172: Replace the ReflectionProperty-based assertions in
testLockStoreRetainsBothSerializationPolicies with a behavioral round-trip
through the lock store: persist and retrieve an object covered by the configured
serialization policy, then assert it is returned as __PHP_Incomplete_Class.
Remove the private-state inspection while preserving coverage that both
serializableClasses and serializableClassPolicy are propagated to the lock
store.

In `@tests/Cache/ModelCacheStoreValidatorTest.php`:
- Around line 282-298: Split testLastRecognizedRedisSerializerOptionWins into
two focused test methods: one asserting validation succeeds when
Redis::OPT_SERIALIZER is the last recognized option, and another asserting
UnsupportedModelCacheStoreException when the case-variant serializer option is
last. Remove the placeholder assertTrue(true) and keep each scenario’s setup and
expectation isolated.
- Around line 32-46: Replace the placeholder assertTrue(true) calls in
testAcceptsSupportedStoresAndSubclasses and the other referenced validation
tests with a named private helper such as assertValidates that invokes the
validator and clearly communicates the expected no-throw outcome. Update each
affected test to use the helper while preserving the existing store coverage and
validation arguments.

In `@tests/Cache/SerializableClassPolicyTest.php`:
- Around line 110-141: Extend
testUnserializeRecomputesBeforeFinalizationAndFreezesAfterward to call
SerializableClassPolicy::finalize() a second time after the first finalization,
then verify the finalized policy remains frozen and resolverRuns is unchanged,
covering idempotent finalization without re-resolving.

In `@tests/Integration/Auth/EloquentUserProviderCacheTest.php`:
- Around line 488-495: Extract the duplicated countQueriesForTable() helper into
a reusable testing trait, preserving its existing query-log filtering behavior.
Remove the local helper and import/use the shared trait in
tests/Integration/Auth/EloquentUserProviderCacheTest.php#L488-L495,
tests/Integration/Auth/EloquentUserProviderRedisCacheTest.php#L185-L192, and
tests/Sanctum/GuardTest.php#L201-L208.

In `@tests/Integration/Cache/FailoverStoreTest.php`:
- Around line 437-463: Refactor the test helpers so buildFailoverRepository()
delegates to failoverStore() instead of duplicating CacheManager, Dispatcher,
and FailoverStore setup. Update failoverStore() to accept the real repository
instances needed by buildFailoverRepository(), while preserving its existing
named-repository behavior and event handling, then wrap the returned
FailoverStore in Repository.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a869dc6-23b1-4bcb-9035-9f1cd76948af

📥 Commits

Reviewing files that changed from the base of the PR and between 2cc5794 and 5e219b6.

📒 Files selected for processing (64)
  • AGENTS.md
  • docs/ai/differences-vs-laravel.md
  • src/auth/README.md
  • src/auth/composer.json
  • src/auth/src/AuthServiceProvider.php
  • src/auth/src/EloquentUserProvider.php
  • src/boost/docs/authentication.md
  • src/boost/docs/cache.md
  • src/boost/docs/sanctum.md
  • src/cache/src/AbstractArrayStore.php
  • src/cache/src/ArrayStore.php
  • src/cache/src/CacheManager.php
  • src/cache/src/CacheServiceProvider.php
  • src/cache/src/DatabaseStore.php
  • src/cache/src/Exceptions/UnsupportedModelCacheStoreException.php
  • src/cache/src/FailoverStore.php
  • src/cache/src/FileStore.php
  • src/cache/src/ModelCacheStoreValidator.php
  • src/cache/src/Redis/Support/Serialization.php
  • src/cache/src/RedisStore.php
  • src/cache/src/SerializableClassPolicy.php
  • src/cache/src/StackStore.php
  • src/cache/src/StorageStore.php
  • src/cache/src/SwooleStore.php
  • src/foundation/config/auth.php
  • src/foundation/config/cache.php
  • src/foundation/src/Configuration/ConfigMutationTracker.php
  • src/sanctum/README.md
  • src/sanctum/composer.json
  • src/sanctum/config/sanctum.php
  • src/sanctum/src/PersonalAccessToken.php
  • src/sanctum/src/Sanctum.php
  • src/sanctum/src/SanctumGuard.php
  • src/sanctum/src/SanctumServiceProvider.php
  • src/support/src/Facades/Cache.php
  • src/support/src/ServiceProvider.php
  • src/testbench/hypervel/config/auth.php
  • tests/Auth/AuthEloquentUserProviderCacheTest.php
  • tests/Auth/AuthManagerTest.php
  • tests/Auth/AuthServiceProviderTest.php
  • tests/Cache/CacheArrayStoreTest.php
  • tests/Cache/CacheDatabaseStoreTest.php
  • tests/Cache/CacheFileStoreTest.php
  • tests/Cache/CacheManagerTest.php
  • tests/Cache/CacheRedisStoreTest.php
  • tests/Cache/CacheServiceProviderTest.php
  • tests/Cache/CacheStackStoreTagsTest.php
  • tests/Cache/CacheStorageStoreTest.php
  • tests/Cache/CacheSwooleStoreIntervalTest.php
  • tests/Cache/CacheSwooleStoreTest.php
  • tests/Cache/CacheWorkerArrayStoreTest.php
  • tests/Cache/ModelCacheStoreValidatorTest.php
  • tests/Cache/Redis/Support/SerializationTest.php
  • tests/Cache/SerializableClassPolicyTest.php
  • tests/Foundation/Configuration/ConfigMutationTrackerTest.php
  • tests/Foundation/Listeners/ReloadDotenvAndConfigTest.php
  • tests/Integration/Auth/EloquentUserProviderCacheTagsTest.php
  • tests/Integration/Auth/EloquentUserProviderCacheTest.php
  • tests/Integration/Auth/EloquentUserProviderRedisCacheTest.php
  • tests/Integration/Cache/FailoverStoreTest.php
  • tests/Sanctum/GuardTest.php
  • tests/Sanctum/PersonalAccessTokenCacheTest.php
  • tests/Sanctum/SanctumServiceProviderTest.php
  • tests/Support/SupportServiceProviderTest.php

Comment thread src/cache/src/ModelCacheStoreValidator.php
Comment thread src/foundation/config/auth.php
Comment thread src/foundation/config/cache.php Outdated
Comment thread src/sanctum/src/SanctumServiceProvider.php
Comment thread tests/Sanctum/GuardTest.php
Pin idempotent finalization and Redis serializer option precedence, and type the remaining validator test double.\n\nClarify provider contributions, node-local store guidance, and deliberate taskworker coverage without changing runtime behavior.
Document the final architecture, worker lifecycle, performance invariants, model cache rules, and verification coverage for serialized cache values.
@binaryfire

Copy link
Copy Markdown
Collaborator Author

@coderabbitai Thanks for the review. I checked all five inline findings and the main review nitpicks against the current source.

Changed:

  • Added a dedicated regression test for idempotent policy finalization without widening the allowlist.
  • Split Redis serializer precedence into both option-order directions and pinned the effective rejected serializer.
  • Typed the remaining validator test double.
  • Clarified application-provider contributions, direct node-local store guidance, and deliberate taskworker coverage.

Kept as designed:

  • Failover remains rejected for identity caches because supported leaves do not prevent stale-primary recovery.
  • The Sanctum resolver captures the config repository intentionally; worker reload replaces that object contents in place.
  • The Guard test property is set before defineEnvironment() writes the resulting config.
  • File lock reflection remains necessary because the public lock behavior stores scalar owners only.
  • Core provider boot() methods remain parameterless for Laravel API compatibility.
  • The remaining test-style suggestions would add shared machinery or churn without improving coverage.

The inline replies contain the source-level reasoning for each finding. composer fix is green, including formatting, static analysis, the full framework suite, Testbench, and dogfood.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@binaryfire Thanks for the thorough follow-up. The added regression coverage and serializer-precedence assertions address the outstanding findings, and the retained choices are well justified by the stated cache-safety, worker-reload, and Laravel-compatibility constraints.

I’ve noted the design context for future reviews. 🐇


✏️ Learnings added
Learnt from: binaryfire
URL: https://github.com/hypervel/components/pull/458

Timestamp: 2026-07-28T18:20:29.795Z
Learning: For Hypervel identity caches, `FailoverStore` is intentionally unsupported even when all nested leaf stores are otherwise supported: a recovered stale primary can serve outdated identity data, so supported leaves alone do not make failover safe.

Learnt from: binaryfire
URL: https://github.com/hypervel/components/pull/458

Timestamp: 2026-07-28T18:20:29.795Z
Learning: In Hypervel Sanctum worker configuration reloads, the serializable-class resolver intentionally captures the configuration repository because reload updates that repository’s contents in place.

Learnt from: binaryfire
URL: https://github.com/hypervel/components/pull/458

Timestamp: 2026-07-28T18:20:29.795Z
Learning: Hypervel core service-provider `boot()` methods remain parameterless to preserve Laravel API compatibility; dependencies should not be added as `boot()` parameters without an approved compatibility exception.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@binaryfire
binaryfire merged commit db70c7c into 0.4 Jul 28, 2026
37 of 38 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@docs/plans/2026-07-27-0615-cache-serializable-class-policy-and-model-cache-safety.md`:
- Around line 1708-1710: Correct the stale test-path references in
docs/plans/2026-07-27-0615-cache-serializable-class-policy-and-model-cache-safety.md:1708-1710
and :1868-1868 by replacing CacheStackStoreTest.php with
CacheStackStoreTagsTest.php, including the focused test command target.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ef558181-cdee-4d49-88a7-649f8ee3d011

📥 Commits

Reviewing files that changed from the base of the PR and between 5e219b6 and 216cfd6.

📒 Files selected for processing (11)
  • docs/plans/2026-07-27-0615-cache-serializable-class-policy-and-model-cache-safety.md
  • src/boost/docs/cache.md
  • src/foundation/config/auth.php
  • src/foundation/config/cache.php
  • src/testbench/hypervel/config/auth.php
  • tests/Auth/AuthEloquentUserProviderCacheTest.php
  • tests/Auth/AuthServiceProviderTest.php
  • tests/Cache/CacheServiceProviderTest.php
  • tests/Cache/ModelCacheStoreValidatorTest.php
  • tests/Cache/SerializableClassPolicyTest.php
  • tests/Sanctum/SanctumServiceProviderTest.php
🚧 Files skipped from review as they are similar to previous changes (10)
  • src/testbench/hypervel/config/auth.php
  • src/foundation/config/cache.php
  • src/foundation/config/auth.php
  • tests/Auth/AuthEloquentUserProviderCacheTest.php
  • src/boost/docs/cache.md
  • tests/Auth/AuthServiceProviderTest.php
  • tests/Cache/CacheServiceProviderTest.php
  • tests/Sanctum/SanctumServiceProviderTest.php
  • tests/Cache/SerializableClassPolicyTest.php
  • tests/Cache/ModelCacheStoreValidatorTest.php

Comment on lines +1708 to +1710
Extend `tests/Cache/CacheStackStoreTest.php` with the regression that a
string-keyed layer configuration reports the correct zero-based layer index in
tag-composition errors.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the stale cache-stack test path.

The plan consistently names tests/Cache/CacheStackStoreTest.php, but the supplied stack uses tests/Cache/CacheStackStoreTagsTest.php.

  • docs/plans/2026-07-27-0615-cache-serializable-class-policy-and-model-cache-safety.md#L1708-L1710: target tests/Cache/CacheStackStoreTagsTest.php.
  • docs/plans/2026-07-27-0615-cache-serializable-class-policy-and-model-cache-safety.md#L1868-L1868: run the focused command against tests/Cache/CacheStackStoreTagsTest.php.
📍 Affects 1 file
  • docs/plans/2026-07-27-0615-cache-serializable-class-policy-and-model-cache-safety.md#L1708-L1710 (this comment)
  • docs/plans/2026-07-27-0615-cache-serializable-class-policy-and-model-cache-safety.md#L1868-L1868
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/plans/2026-07-27-0615-cache-serializable-class-policy-and-model-cache-safety.md`
around lines 1708 - 1710, Correct the stale test-path references in
docs/plans/2026-07-27-0615-cache-serializable-class-policy-and-model-cache-safety.md:1708-1710
and :1868-1868 by replacing CacheStackStoreTest.php with
CacheStackStoreTagsTest.php, including the focused test command target.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant