Skip to content

Make session persistence lifecycle-safe and Laravel-aligned - #456

Merged
binaryfire merged 16 commits into
0.4from
audit/session-lifecycles-persistence-parity
Jul 27, 2026
Merged

Make session persistence lifecycle-safe and Laravel-aligned#456
binaryfire merged 16 commits into
0.4from
audit/session-lifecycles-persistence-parity

Conversation

@binaryfire

@binaryfire binaryfire commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

This change makes Session state and persistence safe for long-lived workers while restoring the current supported Laravel surface.

Session stores now keep their mutable state per Store object and coroutine. Saves build storage snapshots without mutating live state, persist them first, and publish flash and error-bag changes only after the handler confirms the write. Middleware uses the request session as the startup commit flag, delegates lock cleanup to Cache, and prevents a failed after-response retry from replacing the request failure that was already reported and rendered.

The handler layer is tightened at the same time: cookie sessions use a strict binary-safe envelope, file sessions verify complete writes and truthful garbage collection, database sessions reset object-specific existence state across construction and cloning, and Redis sessions receive an isolated application-scoped prefix on the cloned cache store.

For more details, see: docs/plans/2026-07-27-0530-session-lifecycle-persistence-and-current-laravel-parity.md

Why

Hypervel caches Session drivers for the worker lifetime. The previous fixed coroutine keys therefore allowed two Store objects in one coroutine to share IDs, attributes, and started state. PHP object-ID reuse could also expose stale handler state after a driver was forgotten and rebuilt.

Persistence had a separate publication problem. Flash data and JSON error bags were modified before the handler write committed. A throwing or false write left the live Store in a partially saved state, so an after-response retry could age flash data twice, lose validation errors, or accept persistence loss as success.

These are lifecycle ownership issues rather than reasons to clone the manager, add a registry, or introduce a session transaction abstraction. The changes keep the existing manager and handler design, then fix each state transition at the lowest owner.

Store and request lifecycle

  • Derive Store ID, attributes, and started context keys once per Store object.
  • Initialize every per-object slot so reused PHP object IDs cannot inherit state.
  • Keep the active request Store on the established fixed context key.
  • Synchronize only the active Store and its dynamic slots through HTTP testing.
  • Restore the non-null Laravel getId() contract with lazy per-coroutine generation.
  • Marshal decoded JSON error bags before merging them into live attributes.
  • Age flash data through a pure transformation.
  • Publish aged attributes and stopped state only after a successful handler write.
  • Treat a false SessionHandlerInterface::write() result as persistence failure.

Middleware ownership

  • Register after-response persistence only after session startup attaches the Store to the request.
  • Preserve route and render failure persistence after successful startup.
  • Contain only a failing after-response retry so it cannot replace the primary rendered failure.
  • Use Lock::block(..., $callback) for blocked routes, preserving request-failure precedence and avoiding release after acquisition timeout.
  • Remove call-site defaults now owned by the merged Session configuration.

Handler correctness

  • Replace the cookie handler's JSON envelope with strict PHP serialization that safely carries arbitrary serialized bytes.
  • Disable class construction while decoding cookie data and validate the exact payload and expiry types.
  • Require file writes to return the complete byte count.
  • Use Finder pathnames directly and count only successful garbage-collection deletes.
  • Refresh database existence after cold reads and reset the object-specific slot during construction and cloning.
  • Remove the unused Hypervel-only database connection mutator while retaining fresh pooled connection access.
  • Complete the native Array and Null handler signatures without widening valid covariance.

Configuration and Laravel parity

  • Default application Session serialization to JSON while retaining PHP for direct Store construction.
  • Declare the complete blocking configuration surface in config/session.php.
  • Add SESSION_PREFIX, defaulting to app_id() . '_session:'.
  • Apply Redis connection and prefix changes only to the cloned cache repository.
  • Preserve explicit null and empty prefix fallback and the valid prefix "0".
  • Reject a non-Redis cache store selected for the Redis session driver with a descriptive error.
  • Port current Store collection predicates and early-exit behavior.
  • Keep Laravel-facing contracts and facade metadata aligned with the truthful non-null ID.

Cleanup and package completeness

  • Remove the redundant StartSession binding, empty provider boot hook, dead unsupported-driver wrapper, stale call-site defaults, and verified unmatched PHPStan suppressions.
  • Declare the Session split package's complete direct runtime dependencies and enforce them with metadata coverage.
  • Document JSON/PHP serialization choices, worker-local array sessions, Redis prefixes, blocking options, custom handler failures, and garbage-collection units.
  • Record intentionally omitted unsupported Laravel cache-backed drivers and the Session::extend() alternative.
  • Track the separate framework-wide unmatched PHPStan suppression audit without expanding this work.
  • Correct one stale Pool test name so it describes explicit flush teardown rather than a destructor that no longer exists.

Performance

Normal Store state access remains one coroutine-context lookup against a key allocated once per Store. The lazy ID path adds only an absent-slot branch. Save adds no lock, retry, container lookup, yield, or I/O beyond the existing handler operation; PHP copy-on-write touches only the snapshots that change.

Redis prefix setup occurs once during driver construction and performs no command or pool checkout. The database direct-write cold path adds one in-memory lookup while avoiding duplicate-insert failure and unnecessary database work. File writes compare the byte count already returned by the filesystem.

No registry, state machine, compatibility decoder, retry system, worker cache, or hot-path synchronization is introduced.

Validation

  • Ran the complete formatting, static-analysis, parallel component, Testbench package, and dogfood gates.
  • Ran focused Session, Foundation testing, Filesystem, Pool, and database-backed Session coverage.
  • Verified split-package metadata, stale references, context ownership, failure precedence, retry behavior, handler contracts, public documentation, and the final cross-package audit routing.

Summary by CodeRabbit

  • New Features
    • Added configurable session.serialization (json or php), Redis session.prefix, and optional session request blocking (lock/store timing).
  • Bug Fixes
    • Improved coroutine isolation for concurrent session usage and refined save/after-response retry boundaries.
    • Strengthened cookie session envelope validation (including expiration) and corrected file/database session persistence and cleanup behavior.
    • Updated cache session storage behavior for get/put/increment/forget with better key handling (including dotted keys).
  • Documentation
    • Expanded guidance on session driver behavior, Redis prefixing, blocking, and cache serialization guarantees.
  • Compatibility
    • Session getId() is now guaranteed to return a non-null string; filesystem delete now accepts array|string.

Record the settled Session design, upstream research, owner decisions, rejected alternatives, implementation boundaries, and complete regression matrix.

Keep the package-specific recovery and anti-overengineering rules beside the implementation plan so long-running work can restore its full context without rereading the framework-wide audit history.
Give each Store instance its own coroutine-scoped identity, attributes, and started slots while keeping the active request store on the established fixed context key. Synchronize only that active identity through the HTTP testing bridge and restore the non-null Laravel session ID contract.

Make JSON error-bag marshalling and flash aging pure, write serialized snapshots before publishing live state, and reject false handler writes without corrupting retry state. Cover sibling stores, reused object IDs, copied request contexts, serialization failures, cache write failures, encrypted stores, and consecutive JSON saves.
Use the request session as the startup commit flag so failed reads cannot schedule an empty after-response write. Preserve the primary rendered failure when a retry also fails, while retaining persistence after route and render failures.

Delegate blocked-route acquisition and cleanup to Cache lock callback ownership so timeout paths never release an unacquired lock and request failures remain primary over release failures. Add focused middleware and Testbench-backed persistence regressions for every failure boundary.
Replace the JSON transport envelope with private PHP serialization so arbitrary serialized session bytes round-trip without UTF-8 loss. Decode untrusted cookies with classes disabled and accept only the exact string payload and integer expiry shape.

Cover binary data, expiration, malformed frames, object rejection, strict field types, real CookieJar transport, coroutine isolation, and current test typing without adding a compatibility decoder or a second framing layer.
Require Filesystem writes to return the complete byte count before reporting a successful session save. Use Finder pathnames directly during garbage collection and count only files that were actually deleted.

Align the concrete Filesystem delete signature with its existing contract and sibling implementations, keep destroy idempotent, and cover successful, false, partial, empty, and garbage-collection paths in isolated temporary storage.
Refresh the local existence decision after a cold read so direct writes update existing rows instead of attempting duplicate inserts. Reset the object-specific existence slot at construction and cloning so reused PHP object IDs cannot silently update zero rows.

Remove the unsafe unused connection mutator, retain fresh pooled connection resolution, document the shared container mutator as boot/test-only, and cover direct update plus deterministic constructor and clone identity reuse against the database harness.
Declare the complete Session configuration surface at its framework owner, including the JSON application default, route-block settings, and a dedicated application-scoped Redis session prefix.

Apply Redis connection and prefix changes only to the cloned cache store, preserve explicit null, empty, and zero prefix behavior, reject incompatible stores clearly, remove drift-prone call-site defaults, and delete the dead unsupported cache-driver wrapper. Cover configuration conversion, direct PHP construction, clone isolation, and every prefix boundary.
Bring the Array and Null session handlers into line with SessionHandlerInterface and the already typed Cookie, File, Database, and Cache handlers.

Add only the missing parameter and return types, preserving valid covariance and existing runtime behavior without widening the public surface or performing a broad unrelated typing sweep.
Rely on Hypervel auto-singletoning for StartSession, remove the empty provider boot hook, and keep only the canonical manager and store bindings.

Delete verified unmatched PHPStan suppressions from Session authentication and Filesystem adapter creation while preserving the real route and magic-proxy guards. Update the touched authentication coverage to the repository typing convention.
Declare the extensions, Container, and Symfony components used directly by the Session split package instead of relying on transitive installation.

Add package metadata coverage for the complete direct runtime dependency set so future source changes cannot silently drift the standalone manifest.
Document JSON serialization, deliberate PHP object-session opt-in, worker-local array sessions, Redis session prefixes, blocking configuration, and truthful custom handler write and garbage-collection contracts in the existing task-oriented guide style.

Record the intentionally omitted unsupported Laravel cache-backed drivers and point custom implementations to Session::extend() so future upstream synchronization does not reintroduce the deleted dead wrapper.
Rename the heartbeat connection regression for the explicit Pool::flush() close protocol it exercises.

The Pool lifecycle deliberately removed unreachable destructors in favor of deterministic close ownership, so retaining a destructor-named test made the supported behavior harder to verify and search.
Record the owner-approved framework-wide review of unmatched inline ignores and global patterns with reportUnmatchedIgnoredErrors enabled.

Require each suppression to be traced before removal, prohibit runtime contortions or wider types merely to satisfy PHPStan, and leave the permanent strict-setting decision with that dedicated audit rather than expanding Session work.
Record session-01 through session-22 and filesystem-12 with their final owners, regressions, rejected alternatives, performance profile, Laravel-facing result, and completed validation.

Mark Session complete, route the next standalone Queue audit through its exact prerequisite work units, close verified Cache, Log, Pool, Redis, Support, and Filesystem revalidation markers, and keep the dependency index grouped in completion order.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e477d943-3c88-446a-8cf5-560c2a0cec27

📥 Commits

Reviewing files that changed from the base of the PR and between e909ff8 and b25b012.

📒 Files selected for processing (6)
  • docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
  • docs/plans/2026-07-27-0530-session-lifecycle-persistence-and-current-laravel-parity.md
  • src/boost/docs/cache.md
  • src/boost/docs/session.md
  • src/cache/src/SessionStore.php
  • tests/Cache/CacheSessionStoreTest.php
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/boost/docs/session.md
  • docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
  • docs/plans/2026-07-27-0530-session-lifecycle-persistence-and-current-laravel-parity.md

📝 Walkthrough

Walkthrough

This change completes the session lifecycle audit, isolates coroutine-backed session state, makes persistence failure-aware, corrects middleware and handler boundaries, adds Redis/blocking configuration, updates session cache storage and package metadata, and expands documentation and regression coverage.

Changes

Session lifecycle and persistence

Layer / File(s) Summary
Coroutine state and contracts
src/session/src/Store.php, src/contracts/src/Session/Session.php, src/foundation/src/Testing/Concerns/*, src/support/src/Facades/Session.php
Session state now uses per-store coroutine keys, session IDs are non-null and lazily created, serialization is validated, and HTTP test synchronization follows the active store.
Persistence, middleware, and handlers
src/session/src/{Store.php,Middleware/*,*SessionHandler.php,SessionManager.php,SessionServiceProvider.php}
Save operations use transformed snapshots and verify writes; middleware preserves primary exceptions and lock behavior; cookie, file, database, Redis, and null handlers receive boundary and typing fixes.
Cache, configuration, metadata, and documentation
src/cache/src/SessionStore.php, src/foundation/config/session.php, src/session/composer.json, src/boost/docs/*, src/session/README.md, src/filesystem/src/*
Session cache entries use a shared associative payload; serialization, Redis prefixes, blocking, package requirements, driver semantics, Laravel differences, and filesystem signatures are updated.
Regression coverage
tests/Session/*, tests/Integration/Session/*, tests/Cache/*, tests/Foundation/Testing/Concerns/*
Tests cover coroutine isolation, JSON and PHP serialization, persistence retries, cookie validation, database existence state, file writes and cleanup, cache key behavior, middleware failures and locks, configuration, and package metadata.
Audit and implementation records
docs/plans/*, docs/todo.md
Audit routing, cross-package revalidation, session findings, implementation decisions, validation plans, and PHPStan cleanup work are recorded.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.31% 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 captures the main theme of the PR: safer session lifecycle handling and Laravel parity.
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 audit/session-lifecycles-persistence-parity

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 27, 2026

Copy link
Copy Markdown

Greptile Summary

The PR makes session persistence lifecycle-safe for long-lived workers and aligns the supported API and configuration surface with Laravel.

  • Isolates Store state by object and coroutine, publishing persistence-related mutations only after successful writes.
  • Hardens middleware failure precedence, locking, cookie/file/database handlers, and Redis session-store isolation.
  • Completes session configuration, package metadata, tests, and documentation.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/session/src/DatabaseSessionHandler.php Resets object-specific existence state during construction and cloning and refreshes it after cold reads; framework-owned ID changes still reset existence through Store migration.
src/session/src/Store.php Introduces per-object coroutine state, non-null lazy IDs, and commit-after-write publication while preserving existence resets during session migration.
src/session/src/Middleware/StartSession.php Uses request session attachment as the startup commit flag and preserves primary request failures across locking and after-response persistence.
src/session/src/CookieSessionHandler.php Replaces the JSON envelope with strict binary-safe serialization and disables class construction while decoding.
src/session/src/FileSessionHandler.php Requires complete writes and reports garbage collection according to successful deletions.
src/session/src/SessionManager.php Applies Redis connection and prefix configuration to an isolated cloned cache repository and rejects incompatible stores.

Reviews (3): Last reviewed commit: "fix(cache): preserve literal session cac..." | Re-trigger Greptile

Comment thread src/session/src/DatabaseSessionHandler.php

@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: 2

🧹 Nitpick comments (2)
tests/Integration/Session/DatabaseSessionHandlerTest.php (1)

202-220: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Isolate the generic tracking fixture in a test-specific namespace.

TrackingDatabaseSessionHandler is collision-prone in the shared test namespace. Place it under a namespace ending in DatabaseSessionHandlerTest.

As per coding guidelines, “Use test-specific namespaces for collision-prone generic helper classes, with the test class name as the final namespace segment.”

🤖 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/Session/DatabaseSessionHandlerTest.php` around lines 202 -
220, Move the generic TrackingDatabaseSessionHandler fixture into a
test-specific namespace whose final segment is DatabaseSessionHandlerTest, and
update any references in the test accordingly while preserving its insert and
update tracking behavior.

Source: Coding guidelines

src/session/src/SessionManager.php (1)

147-203: 🩺 Stability & Availability | 🔵 Trivial

Verify migration path for users with previously-published config/session.php.

serialization, block, block_lock_seconds, and block_wait_seconds are now read with typed getters and no inline default (string()/boolean()/integer() without a fallback), which throw InvalidArgumentException if the key is missing. This is correct per the guideline to keep defaults in the config file, but any app that already ran vendor:publish on the pre-PR config/session.php (missing these new keys) will start throwing on every session access after upgrading, instead of silently falling back as before.

Please confirm the shipped src/foundation/config/session.php declares these keys with sensible defaults, and that the upgrade docs call out this as a breaking change for apps with an already-published session config.

Based on learnings, this repo's guideline states: "Use typed configuration getters such as string(), integer(), float(), boolean(), and array() for non-nullable values, with defaults declared in configuration files and merged through mergeConfigFrom()."

🤖 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/session/src/SessionManager.php` around lines 147 - 203, Verify the
shipped session configuration defines defaults for serialization, block,
block_lock_seconds, and block_wait_seconds so the typed getters in
SessionManager methods buildSession, shouldBlock, defaultRouteBlockLockSeconds,
and defaultRouteBlockWaitSeconds remain safe. Update the upgrade documentation
to explicitly call this a breaking change for applications using an older
published config/session.php, instructing them to republish or add the missing
keys.

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
`@docs/plans/2026-07-27-0530-session-lifecycle-persistence-and-current-laravel-parity.md`:
- Line 143: Escape or reword the literal union pipes in the finding tables:
update `string|false` at
docs/plans/2026-07-27-0530-session-lifecycle-persistence-and-current-laravel-parity.md:143,
`array|string` at the same file:160, and the corresponding union text at
docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md:1261,
using wording such as “string-or-false” or escaped pipes so each row remains
valid Markdown.

In `@src/session/src/CookieSessionHandler.php`:
- Around line 49-59: Update the read() method around the unserialize fallback so
legacy non-array cookie-session envelope values are decoded before the current
data/expires validation. Preserve support for the existing serialized array
format, and only return an empty string after both modern and legacy decoding
paths fail validation.

---

Nitpick comments:
In `@src/session/src/SessionManager.php`:
- Around line 147-203: Verify the shipped session configuration defines defaults
for serialization, block, block_lock_seconds, and block_wait_seconds so the
typed getters in SessionManager methods buildSession, shouldBlock,
defaultRouteBlockLockSeconds, and defaultRouteBlockWaitSeconds remain safe.
Update the upgrade documentation to explicitly call this a breaking change for
applications using an older published config/session.php, instructing them to
republish or add the missing keys.

In `@tests/Integration/Session/DatabaseSessionHandlerTest.php`:
- Around line 202-220: Move the generic TrackingDatabaseSessionHandler fixture
into a test-specific namespace whose final segment is
DatabaseSessionHandlerTest, and update any references in the test accordingly
while preserving its insert and update tracking behavior.
🪄 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: 6264f7ef-1548-412d-9825-1b71a54b0b47

📥 Commits

Reviewing files that changed from the base of the PR and between af5d57f and e909ff8.

📒 Files selected for processing (40)
  • docs/plans/2026-07-12-0900-framework-coroutine-state-lifecycle-audit.md
  • docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
  • docs/plans/2026-07-27-0530-session-lifecycle-persistence-and-current-laravel-parity.md
  • docs/todo.md
  • src/boost/docs/session.md
  • src/contracts/src/Session/Session.php
  • src/filesystem/src/Filesystem.php
  • src/filesystem/src/FilesystemManager.php
  • src/foundation/config/session.php
  • src/foundation/src/Testing/Concerns/InteractsWithSession.php
  • src/foundation/src/Testing/Concerns/MakesHttpRequests.php
  • src/session/README.md
  • src/session/composer.json
  • src/session/src/ArraySessionHandler.php
  • src/session/src/CookieSessionHandler.php
  • src/session/src/DatabaseSessionHandler.php
  • src/session/src/FileSessionHandler.php
  • src/session/src/Middleware/AuthenticateSession.php
  • src/session/src/Middleware/StartSession.php
  • src/session/src/NullSessionHandler.php
  • src/session/src/SessionManager.php
  • src/session/src/SessionServiceProvider.php
  • src/session/src/Store.php
  • src/support/src/Facades/Session.php
  • tests/Foundation/Testing/Concerns/MakesHttpRequestsTest.php
  • tests/Integration/Session/CookieSessionHandlerTest.php
  • tests/Integration/Session/DatabaseSessionHandlerTest.php
  • tests/Integration/Session/SessionPersistenceTest.php
  • tests/Pool/HeartbeatConnectionTest.php
  • tests/Session/CacheBasedSessionHandlerTest.php
  • tests/Session/CookieSessionHandlerCoroutineSafetyTest.php
  • tests/Session/CookieSessionHandlerTest.php
  • tests/Session/EncryptedSessionStoreTest.php
  • tests/Session/FileSessionHandlerTest.php
  • tests/Session/Middleware/AuthenticateSessionTest.php
  • tests/Session/Middleware/StartSessionTest.php
  • tests/Session/PackageMetadataTest.php
  • tests/Session/SessionConfigTest.php
  • tests/Session/SessionManagerTest.php
  • tests/Session/SessionStoreTest.php
💤 Files with no reviewable changes (3)
  • src/filesystem/src/FilesystemManager.php
  • src/foundation/src/Testing/Concerns/InteractsWithSession.php
  • src/session/src/SessionServiceProvider.php

Comment thread src/session/src/CookieSessionHandler.php
Escape union separators only where inline code appears inside Markdown table cells. Raw pipes were being parsed as additional columns, truncating the Session, Filesystem, Bus, and Console audit records in rendered documentation.

Leave identical notation in normal prose untouched, where escaping would render a visible backslash. The five repaired rows now retain their expected column counts and code spans through GitHub's GFM renderer.
@binaryfire

Copy link
Copy Markdown
Collaborator Author

Thanks @greptile-apps and @coderabbitai. I traced every finding against the current branch, the framework lifecycle, and current Laravel.

  • Fixed the malformed union types in Markdown tables in cb2847797. This covers the three flagged cells and two identical older ledger rows. All five now render with the expected column count through the GitHub GFM renderer.
  • Kept DatabaseSessionHandler unchanged. One handler intentionally owns one session ID in both Hypervel and Laravel; every framework ID migration resets existence through Store::migrate(). ID-aware handler state would solve unsupported direct reuse and add a divergent mechanism.
  • Kept the serialized cookie envelope without a JSON fallback. The 0.4 line is unreleased, the new envelope is binary-safe, class construction is disabled during decoding, and strict shape and expiry checks follow immediately.
  • Kept TrackingDatabaseSessionHandler in the shared test namespace. Its name is already feature-specific and unique, so another namespace would add ceremony without preventing a collision.
  • Added no published-config fallback or upgrade warning. Framework configuration merging supplies all four new base keys to sparse or older application session configs by default.
  • Added no coverage-driven docstrings. The repository uses targeted Laravel-style documentation rather than a generic percentage threshold.

No runtime or test changes were needed from this review round.

@greptile-apps

greptile-apps Bot commented Jul 27, 2026

Copy link
Copy Markdown

All six points check out against the PR and the review threads:

  • Markdown tables: The fix in cb2847797 is confirmed — five cells corrected, prose left unescaped.
  • DatabaseSessionHandler existence state: The one-handler-one-ID contract is accurate. Store::migrate() resets existence before assigning a new ID, and the scenario I flagged (two IDs through one handler) is unsupported direct reuse rather than a framework path. No change warranted.
  • Cookie envelope: The reasoning is sound. Pre-authenticated decryption, allowed_classes => false, and strict shape/expiry validation on an unreleased line make a legacy decoder unnecessary overhead.
  • TrackingDatabaseSessionHandler namespace: Feature-specific name, no collision risk — leaving it in the shared test namespace is fine.
  • Config fallback: Framework-level merging covers sparse configs; an explicit fallback would be redundant.
  • Docstrings: Consistent with the repo's targeted documentation approach.

No outstanding findings from this review round.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Documentation table rows containing union types ---'
rg -n -F -C 1 '|' docs/plans -g '*.md' | rg -n -C 1 'array\|string|string\|array|\?string\|' || true

printf '%s\n' '--- Relevant file locations ---'
fd -a -t f \
  'DatabaseSessionHandler.php' \
  'CookieSessionHandler.php' \
  'Store.php' \
  'session.php' \
  'DatabaseSessionHandlerTest.php' \
  'SessionConfigTest.php' \
  'CookieSessionHandlerTest.php' \
  '2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md' \
  '2026-07-12-0900-framework-coroutine-state-lifecycle-audit.md' \
  .

printf '%s\n' '--- Session implementation outlines ---'
for file in \
  src/session/src/DatabaseSessionHandler.php \
  src/session/src/CookieSessionHandler.php \
  src/session/src/Store.php \
  src/foundation/config/session.php \
  tests/Integration/Session/DatabaseSessionHandlerTest.php \
  tests/Session/CookieSessionHandlerTest.php \
  tests/Session/SessionConfigTest.php
do
  if [ -f "$file" ]; then
    echo "### $file"
    ast-grep outline "$file" --items all || true
  fi
done

printf '%s\n' '--- Targeted implementation and test references ---'
rg -n -C 3 \
  -e 'function (read|write|__clone|migrate)\b' \
  -e 'TrackingDatabaseSessionHandler\b' \
  -e 'allowed_classes' \
  -e "SESSION_(BLOCK|PREFIX|SERIALIZATION)" \
  -e "'(block|prefix|serialization)'" \
  src/session/src/DatabaseSessionHandler.php \
  src/session/src/CookieSessionHandler.php \
  src/session/src/Store.php \
  src/foundation/config/session.php \
  tests/Integration/Session/DatabaseSessionHandlerTest.php \
  tests/Session/CookieSessionHandlerTest.php \
  tests/Session/SessionConfigTest.php || true

Length of output: 25271


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Commit cb2847797: changed files and Markdown hunks ---'
git show --no-ext-diff --format=fuller --stat cb2847797
git show --no-ext-diff --format= --unified=4 cb2847797 -- 'docs/**/*.md'

printf '%s\n' '--- Store migration and database handler write/existence lifecycle ---'
sed -n '88,125p' src/session/src/DatabaseSessionHandler.php
sed -n '235,280p' src/session/src/DatabaseSessionHandler.php
sed -n '595,625p' src/session/src/Store.php

printf '%s\n' '--- GFM table separator counts for added/removed Markdown rows in cb2847797 ---'
git show --no-ext-diff --format= --unified=0 cb2847797 -- 'docs/**/*.md' |
python3 -c '
import sys
for raw in sys.stdin:
    if not raw.startswith(("+", "-")) or raw.startswith(("+++", "---")):
        continue
    row = raw[1:].rstrip("\n")
    if "|" not in row:
        continue
    # Pipes inside escaped content do not split a GFM cell.
    separators = sum(ch == "|" and (i == 0 or row[i - 1] != "\\") for i, ch in enumerate(row))
    print(f"{raw[0]} separators={separators}: {row}")
'

Length of output: 20568


@binaryfire Thanks for the thorough trace. Verified:

  • cb2847797 repairs all five affected Markdown rows; their separator counts now match their respective GFM table structures.
  • Store::migrate() resets handler existence before assigning a new ID, supporting the one-session-ID-per-handler lifecycle.
  • The serialized cookie envelope is binary-safe, disables class creation, and validates shape and expiry.
  • The configuration defaults are declared and covered by SessionConfigTest.

I agree the remaining items are intentional, supported design choices rather than changes needed in this PR.

🐇✅


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

Timestamp: 2026-07-27T06:47:45.048Z
Learning: In Hypervel Session, `DatabaseSessionHandler` intentionally owns one session ID per handler, matching Laravel. `Hypervel\Session\Store::migrate()` resets the handler’s existence state before assigning a new ID; do not recommend ID-aware handler state for unsupported direct handler reuse.

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

Timestamp: 2026-07-27T06:47:45.049Z
Learning: Hypervel Session’s cookie handler intentionally uses only a private PHP-serialized binary-safe envelope, decoded with `allowed_classes => false` and strict shape/expiry validation. The unreleased 0.4 line does not require a legacy JSON envelope fallback.

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

Timestamp: 2026-07-27T06:47:45.049Z
Learning: Hypervel framework configuration merging supplies newly declared session base keys to sparse or older application session configurations. Do not request published-config fallbacks or upgrade warnings solely for the `session.prefix`, blocking, or serialization keys.

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.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Store the session cache collection as a flat map so dotted cache keys are no longer interpreted as Session attribute paths. This prevents neighboring keys from nesting into, overwriting, or deleting one another while preserving expiration metadata during increments.

Document that the session cache inherits the configured Session serializer and record the JSON-backed store's object-fidelity limitation without adding another encoding or validation layer. Add focused regressions for dotted-key coexistence, deletion, enumeration, and expiration-preserving increments, and update the audit ledger with the final boundaries.
@binaryfire
binaryfire merged commit 0195d2e into 0.4 Jul 27, 2026
36 of 38 checks passed
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