Skip to content

Harden console, server, and HTTP runtime lifecycles#450

Merged
binaryfire merged 41 commits into
0.4from
audit/console-server-http-runtime-lifecycles
Jul 23, 2026
Merged

Harden console, server, and HTTP runtime lifecycles#450
binaryfire merged 41 commits into
0.4from
audit/console-server-http-runtime-lifecycles

Conversation

@binaryfire

Copy link
Copy Markdown
Collaborator

Summary

This PR completes the Console, Server, and HTTP Server runtime lifecycle work units.

The common theme is ownership. Commands, scheduled runs, native server operations, response streams, files, and test fixtures now have one clear owner from creation through cleanup. Failures are checked where they occur, independent cleanup still runs, and the earliest failure remains the one callers observe.

It also brings the affected Laravel-facing Console and testing APIs up to date while keeping Hypervel's Swoole-specific behavior explicit.

Console and scheduling

  • Evaluate interval constraints and lastDayOfMonth() at execution time instead of freezing dates on the worker-lifetime schedule.
  • Isolate callback results, exceptions, running processes, overlap state, and failure state to the exact scheduled invocation.
  • Make command and event teardown exhaustive so output, callbacks, mutexes, processes, and signal handlers cannot be stranded by an earlier failure.
  • Report background exits, thrown callbacks, and overlap skips from the current run rather than stale event state.
  • Preserve nested prompt configuration and stream iterable output without eager buffering.
  • Add current CommandInput, callback injection, environment filtering, iterable progress, completion metadata, and scheduling documentation.
  • Keep schedule timezone output truthful by displaying the original cron expression with the timezone used to evaluate it instead of rewriting expressions with an incomplete converter.
  • Publish generated commands and migrations atomically, including collision-safe migration names and final-content publication.
  • Guard optional observational events when no listener exists and retain exact unique-job lock ownership through payload creation.
  • Correct Console split dependencies, provenance, and deterministic timer/test cleanup.

Server lifecycle

  • Fail immediately when Swoole rejects settings, callback registration, listener creation, or startup.
  • Validate reload PID files as positive integers and report success only after the required reload signals succeed.
  • Resolve process titles from the current stable configuration at lifecycle time so replacement workers do not inherit stale application names.
  • Use valid Swoole defaults for server and port builders, including omitted HTTP types and address overrides.
  • Remove the dead Hyperf-era top-level server-class setting and irrelevant constructor requirement while accepting any explicit ServerInterface implementation.
  • Standardize package exceptions, correct split dependencies, and document the standalone server reload command.
  • Restore test-owned environment state exactly.

HTTP transport

  • Make ResponseBridge the sole owner of Swoole response emission.
  • Keep Filesystem responses lazy until outbound middleware has finished, then stream through the transport boundary with checked writes and deterministic producer cleanup.
  • Remove the superseded response context, writable connection, direct emitter, stream output, and transport-fake stack.
  • Preserve split query strings, root request URIs, raw cookies, and partitioned cookies during native request/response conversion.
  • Honor prepared binary response state: ranges, lengths, statuses, HEAD requests, temporary files, trusted X-Sendfile delegation, and delete-after-send ownership.
  • Keep native zero-copy sendfile for ordinary HTTP/1.1 files. Use bounded chunks for HTTP/2 and deletion-owned files so memory does not scale with the full file size per concurrent request.
  • Preserve the earliest adapter, send, or termination failure while still running independent lifecycle cleanup. Cancellation avoids fallback writes but remains observable at termination.
  • Restore current Laravel binary TestResponse behavior without invoking stream producers for HEAD requests.

Integration coverage

The HTTP Server now has a real Testbench-backed integration fixture in the existing engine test group. It exercises the complete native path over HTTP/1.1 and HTTP/2:

  • request conversion for query, form, JSON, and multipart input;
  • request-context isolation;
  • raw and partitioned cookies;
  • callback, iterable, and Filesystem streaming;
  • binary ranges, HEAD, temporary files, and deletion ownership;
  • client disconnect cleanup and lifecycle liveness;
  • exact HTTP/2 response bodies with bounded file streaming.

The fixture uses runner-owned worker identity and consume-on-read state, so parallel workers do not share lifecycle observations.

Compatibility and performance

Laravel's supported Console, response, streaming, Filesystem, and testing APIs remain intact or gain current parity. The only removed public surface is Hypervel's ResponseContext placeholder, which had no Laravel equivalent and never represented the response returned by the application. The Server cleanup affects Hyperf-derived infrastructure rather than a Laravel-facing API.

Ordinary request and response paths gain no lock, registry, retry, polling loop, or extra container lookup. Optional event guards remove work when unused, iterable output no longer buffers the complete input, and HTTP/2 file memory is bounded per request. HTTP/1.1 preserves zero-copy delivery wherever native deletion ownership permits it.

Validation

  • composer fix
  • focused Console, Server, Foundation, Filesystem, HTTP, gRPC, Reverb, WebSocket, and Testbench coverage
  • real HTTP/1.1 and HTTP/2 integration coverage in serial and parallel execution
  • split-package Composer validation and metadata checks
  • shell syntax, stale-reference, dependency, and diff checks

Resolve each configured cache repository and underlying store once per operation, then use the exact instance for lock or repository semantics. Make command mutex probes non-destructive, normalize force-release success to the declared boolean contract, and replace PHPStan workarounds with truthful lock-provider narrowing.

The focused mutex regression proves existence checks do not acquire ownership and preserves repository-backed behavior for stores without native locks.
Retain the exact isolation mutex acquired by a command and release it through an exhaustive terminal path. Preserve the earliest command, lifecycle-event, or cleanup failure while still attempting independent teardown, and include trait setup inside the signal cleanup boundary.

Guard optional observational events when no listeners exist. Extend command regressions across setup, handler, exit, event, and mutex failures so ownership and throwable precedence remain deterministic.
Reapply the parent command's complete prompt configuration after every nested command invocation, including exceptional exits. Delete the partial output-only restoration helper so process-global prompt callbacks and fallbacks cannot remain bound to the child command.

Regression coverage verifies output, validation, fallback, and cancellation configuration are restored without masking the nested command result or failure.
Port the current CommandInput value API and expose merged command data through InteractsWithIO. Restore truthful Symfony argument and option types, normalize integer argument lookups at the string boundary, and accept iterable completion sources and indeterminate progress input.

Update console view metadata and regressions for argument precedence, nested data access, generator completion choices, and generator-backed progress without eager counting.
Write iterable output one item at a time instead of materializing it into an intermediate array. Preserve caller-supplied newline semantics, interpret Symfony verbosity bitmasks correctly, and update visible-output state only after a successful write.

Regression coverage exercises generators, suppressed verbosity, explicit and embedded newlines, empty values, and write failures. This removes duplicate memory growth while keeping output state truthful.
Move callback results, callback failures, process handles, exit codes, and overlap status to exact-event coroutine slots for each invocation. Initialize state at run start, retain live processes through terminal callbacks, forget owned resources on every exit, and preserve the earliest failure while attempting output, after callbacks, and mutex cleanup.

Require exact output writes and checked filesystem reads, port current callback dependency injection, and accept all valid callable filters. Deterministic regressions cover sequential and concurrent runs, terminal-phase failures, process lifetime, output boundaries, callback injection, and cleanup precedence.
Evaluate between and unless-between constraints when an event is checked rather than freezing Carbon values when the worker-singleton schedule is built. Represent last-day-of-month with cron's dynamic L syntax and reject zero or negative sub-minute divisors before modulo evaluation.

Regression coverage advances the framework clock across interval boundaries, exercises overnight ranges and timezones, proves dynamic month-end behavior, and validates the truthful positive repeat range.
Track scheduler failure and overlap decisions in invocation-local variables instead of rendering from mutable Event snapshots. Treat observed non-zero foreground and background exits as failures, keep same-Event background registration reference-counted, and preserve established lifecycle-event then report ordering.

The scheduler regressions cover thrown work, non-zero exits, overlap skips, concurrent runs of one Event object, cleanup, context propagation, event ordering, and earliest-failure behavior without relying on scheduling luck.
Stop schedule:test from mutating the stored Event's background flag. Hypervel owns background submission in ScheduleRunCommand, so direct Event execution is already synchronous and must retain the original object to preserve framework-created callback bindings.

The integration regression proves success callbacks execute against the configured Event, its background setting remains unchanged, and the command still describes the normal background behavior accurately.
Add current environment-based schedule filtering while preserving events without an environment restriction. For scheduled unique jobs, apply connection and queue metadata before locking, register the exact transient job in the existing Bus payload sidecar, and release its lock only when failure occurs before payload creation consumes ownership.

Unit and integration coverage verifies environment matching plus both unique-job failure boundaries: safe pre-payload release and conservative post-payload retention when transport ownership is ambiguous.
Port schedule:list environment filtering and expose the timezone in which each raw cron expression is evaluated. Keep the expression unchanged because a fixed-offset rewrite cannot preserve ranges, month boundaries, special syntax, or daylight-saving transitions, while continuing to render next-due timestamps in the requested display timezone.

Use the same resolved expression timezone for due-date calculation and display, including a directly constructed Schedule with no timezone. Integration coverage verifies JSON and CLI output, filtering, sorting, explicit timezones, and application-timezone fallback.
Make MigrationCreator own custom-stub reads, placeholder population, collision-free path selection, atomic publication, and post-create hook ordering. Keep transient path selection coroutine-local on the worker-shared creator, use the framework clock consistently, and convert native glob failure into a descriptive exception.

Regression coverage proves hooks see the final file, read and publication failures expose no partial migration, prefixes remain collision-free across concurrent paths, and protected prefix generation respects the framework clock.
Route Console, Cache, and Queue migration generators through MigrationCreator's single final-publication boundary instead of creating a generic file and overwriting it afterward. Check migration glob results through the shared owner and remove call-site configuration defaults already guaranteed by merged package configuration.

Focused generator coverage proves custom stubs are published once, modes and placeholders are correct, collisions advance safely, native scan failures surface, and success is not reported after incomplete generation.
Replace live deferred-webhook timers and timing sleeps with the manager's existing injectable Timer boundary. Assert each owned timer ID is cleared exactly once across cancel-all and per-key cancellation paths.

This removes a race from the full parallel gate while testing the real ownership contract directly, without adding production hooks or changing Reverb runtime behavior.
Declare every runtime symbol owner required by the standalone Console subtree, including native extensions, Symfony components, Guzzle, Carbon, Termwind, PSR HTTP client, and Hypervel Log. Keep constraints aligned with the monorepo's existing package versions.

Add a split-metadata regression that enumerates the complete non-PHP runtime dependency boundary and verifies every declared constraint is present and non-empty.
Document the CommandInput API for reading merged arguments and options, including argument precedence for duplicate keys and nested data access. Explain command trait setup hooks and their lifecycle so application commands can prepare resources without bypassing framework cleanup.

Keep the guidance task-focused and aligned with the current Console implementation and Laravel-shaped command APIs.
Document environment-filtered schedule listings, callback dependency injection, and expression timezone output. Clarify that cron expressions remain in their real evaluation timezone while next-due timestamps use the requested display timezone.

The guidance reflects Hypervel's Swoole scheduling ownership without exposing internal coroutine machinery or implying that display conversion changes task execution.
Add the Console package's Laravel provenance and record the intentional schedule-list presentation difference. Explain that Hypervel preserves raw cron expressions because fixed-offset conversion is not truthful across ranges, month boundaries, special syntax, and daylight-saving transitions.

Keep the package README concise and reserve user-facing task guidance for the Boost documentation.
Record the final Console findings, accepted fixes, rejected speculative mechanisms, cross-package ownership, regression strategy, validation, performance assessment, and signed-off review result. Preserve the Database migration and Reverb timer findings for their later full package audits.

Mark Console complete, close its carried revalidation entries, route the active audit to Server with core-06, and retain exact checklist parity with all 72 source packages.
Treat false returns from Swoole server configuration, listener registration, event registration, and startup as package-level server failures instead of allowing boot to continue in a partially initialized state.

Normalize invalid server-type failures onto the package exception hierarchy and add focused regressions for every native boundary, including failures while registering listeners and callbacks.
Validate the configured master PID and report reload success only when the selected Swoole signal is actually delivered. Preserve task-worker reload support while removing the signal-zero preflight race and exposing a narrow signal seam for deterministic tests.

Default task workers to zero so a standard installation does not imply a process pool it has not configured, document standalone server reloads, and cover invalid PIDs, failed signals, worker selection, and configuration defaults.
Inject the configuration repository into the process-title listener and read the application name when each worker lifecycle event fires. Replacement workers created after a reload now use current configuration rather than a value captured during initial provider registration.

Retain the existing platform and configuration guards, and exercise server, worker, task-worker, and user-process titles alongside disabled and unsupported environments.
Initialize server and listener builders with Swoole-valid process, socket, and HTTP defaults so omitted optional configuration produces a usable HTTP server instead of invalid zero-valued native constants.

Remove the unused top-level server type from ServerConfig and add regressions for default construction as well as explicit overrides.
Remove the concrete constructor signature from ServerInterface because construction dependencies are an implementation detail, not behavior every custom server must share. Accept any ServerInterface in ServerFactory so applications can replace the built-in server without extending its concrete class.

Add focused coverage proving an arbitrary interface implementation can be installed and returned by the factory.
Depend on the Foundation application contract directly instead of a concrete application singleton plus a generic container. Resolve collaborators consistently through that application and return Symfony's named success status.

Keep host and port overrides valid for servers that rely on the HTTP default type, preserve rejection for non-HTTP configurations, and update the command regressions around the tightened configuration contract.
Declare the native extensions and Hypervel packages used directly by the Server component, remove the reverse http-server dependency, and record the package's Hyperf provenance.

Add metadata coverage that validates the direct dependency boundary and prevents the server/http-server cycle from returning.
Restore APP_RUNNING_IN_CONSOLE after each test instead of leaking process-global state into later tests and construct the command through the application contract it now requires.

Strengthen the serve-command coverage for current environment ownership and the default HTTP server shape without changing the production Testbench machinery.
Record the Server audit's native startup boundaries, reload semantics, process-title freshness, default configuration, public API cleanup, package metadata, and test ownership decisions.

Capture the validated regressions and residual PID-reuse tradeoff, mark the package complete, and route the next audit work to HTTP Server response and streaming lifecycle revalidation.
Reconstruct Swoole request URIs from the split request_uri and query_string fields before applying trailing-slash normalization. Preserve already-combined inputs without duplicating their query and retain the root path for slash-only request targets.

Add focused coverage for split and combined queries, empty query strings, ordinary trailing slashes, the root URI, and all-slash paths so native request conversion remains faithful across Swoole input shapes.
Make iterable response cleanup exhaustive without allowing generator finalization failures to replace the original producer or transport failure. Apply the same earliest-failure rule to direct sendContent consumption and clear retained producers on every terminal path.

Expand regression coverage for callback failures, writer failures, throwing generator cleanup, direct content sending, idempotent completion, and reference release so lazy stream ownership is deterministic.
Return lazy iterable streamed responses from local, scoped, and pooled filesystem adapters instead of emitting through a response-owned socket. Keep resources leased until generator completion, stop reads when the transport rejects a chunk, and preserve the first read, write, close, or lease-release failure.

Reflect the truthful Symfony response types in filesystem contracts and facade metadata, preserve range and download behavior, and expand coverage for middleware-safe lazy execution, disconnect cleanup, native read failures, pooled lease ownership, and failure precedence.
Make ResponseBridge the sole owner of native status, headers, cookies, streamed chunks, binary files, and terminal response completion. Preserve raw and partitioned cookies, prepared range and HEAD semantics, trusted X-Sendfile delegation, temporary and delete-after-send ownership, and earliest-failure cleanup.

Keep ordinary HTTP/1 files on zero-copy sendfile while streaming HTTP/2 and deletion-owned files through bounded checked chunks. Propagate protocol and request evidence through HTTP Server, WebSocket, Reverb, and gRPC consumers, and make request handling, response emission, kernel termination, and RequestTerminated observation exhaustive under failures and cancellation.

Delete the obsolete ResponseContext, writable connection contract and adapter, direct Response streaming surface, and StreamOutput path. Bind mutable responses transiently and update focused lifecycle, transport, context, binary, and binding regressions to the single-owner design.
Remove the response-context and fake writable setup from in-process HTTP tests now that transport emission belongs exclusively to HTTP Server. Delete the obsolete Swoole socket and writable connection fakes rather than retaining a second testing-only transport model.

Restore current Laravel binary-file streamedContent behavior and suppress streamed producers for HEAD requests while preserving cached content semantics. Add regressions for binary content, repeated reads, callback and iterable HEAD responses, streamed JSON, and ordinary non-stream responses.
Add one Testbench-backed Swoole server to the existing engine harness and exercise the complete RequestBridge to kernel and middleware to ResponseBridge path over HTTP/1.1 and HTTP/2. Cover native query, form, JSON, and multipart conversion; request-context isolation; cookies; callback, iterable, and filesystem streaming; ranges; HEAD; temporary and delete-after-send files; disconnect cleanup; and lifecycle liveness.

Use per-test random tokens and a token-keyed consume-on-read lifecycle fixture so serial and parallel workers can share the server without cross-test state. Wire the integration group into the engine workflow, server launcher, and contributor test guidance.
Declare the native Swoole extension and coroutine package used directly by HTTP Server, and remove the former Engine and Server dependency edges after deleting the writable transport stack. Keep the split manifest aligned with the package actual imports.

Add concise Hyperf provenance and a metadata regression that pins every required dependency and verifies the two removed edges stay absent.
Scan the installed, version-matched Swoole HTTP Response and Server declarations so native cookie, listener, and response APIs are analyzed against the runtime package rather than stale broad assumptions.

Remove the imprecise local bool-or-port annotation from listener creation and let the checked native false branch narrow the authoritative return type without runtime code or suppression machinery.
Remove guidance for the deleted ResponseContext placeholder and explain that responses remain in the request control flow until the HTTP server emits them after middleware unwinds.

Clarify the testing boundary: requests are coroutine-local through RequestContext, while mutable response objects resolve transiently instead of being published as worker-global container instances or coroutine-context placeholders.
Record the final implementation, regression coverage, cross-package revalidation, validation, Laravel-facing result, performance impact, and overengineering assessment for the unified HTTP response-emission work.

Check off HTTP Server, close the carried Filesystem, Foundation, Testing, and gRPC revalidations, retain pending consumer audits, and route the next package cycle to WebSocket Server with its exact context-copy and prepared-response dependencies.
Integrate the latest 0.4 changes, including the framework-wide immutable-date default and its package, documentation, and regression updates.

Resolve Console scheduling conflicts by combining CarbonImmutable with the audited execution-time interval evaluation, native last-day cron expression, timezone-correct schedule display, and truthful failure and skip reporting. Update the remaining Console input assertion to the new immutable default.

Revalidate the affected Console and HTTP Server surfaces and pass the complete composer fix gate after the merge.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 128 files, which is 28 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cbd38f58-e3d9-46d3-a357-08b38a50d50e

📥 Commits

Reviewing files that changed from the base of the PR and between 1d48f8b and 8115df7.

📒 Files selected for processing (134)
  • .github/workflows/engine.yml
  • AGENTS.md
  • bin/test-servers.sh
  • docs/ai/differences-vs-laravel.md
  • docs/plans/2026-07-12-framework-coroutine-state-lifecycle-audit-ledger.md
  • docs/plans/2026-07-12-framework-coroutine-state-lifecycle-audit.md
  • phpstan.neon.dist
  • src/boost/docs/artisan.md
  • src/boost/docs/coroutine-context.md
  • src/boost/docs/deployment.md
  • src/boost/docs/lifecycle.md
  • src/boost/docs/scheduling.md
  • src/cache/src/Console/CacheTableCommand.php
  • src/console/README.md
  • src/console/composer.json
  • src/console/src/CacheCommandMutex.php
  • src/console/src/Command.php
  • src/console/src/CommandInput.php
  • src/console/src/Commands/ScheduleListCommand.php
  • src/console/src/Commands/ScheduleRunCommand.php
  • src/console/src/Commands/ScheduleTestCommand.php
  • src/console/src/Concerns/CallsCommands.php
  • src/console/src/Concerns/ConfiguresPrompts.php
  • src/console/src/Concerns/InteractsWithIO.php
  • src/console/src/MigrationGeneratorCommand.php
  • src/console/src/OutputStyle.php
  • src/console/src/Scheduling/CacheEventMutex.php
  • src/console/src/Scheduling/CacheSchedulingMutex.php
  • src/console/src/Scheduling/CallbackEvent.php
  • src/console/src/Scheduling/Event.php
  • src/console/src/Scheduling/ManagesAttributes.php
  • src/console/src/Scheduling/ManagesFrequencies.php
  • src/console/src/Scheduling/Schedule.php
  • src/console/src/View/Components/AskWithCompletion.php
  • src/console/src/View/Components/Factory.php
  • src/context/composer.json
  • src/context/src/ResponseContext.php
  • src/contracts/src/Engine/Http/Writable.php
  • src/database/src/Migrations/MigrationCreator.php
  • src/engine/src/Http/WritableConnection.php
  • src/filesystem/src/Concerns/InteractsWithPooledFilesystem.php
  • src/filesystem/src/FileResponseBuilder.php
  • src/filesystem/src/FilesystemAdapter.php
  • src/filesystem/src/ScopedFilesystemProxy.php
  • src/filesystem/src/ServeFile.php
  • src/foundation/config/server.php
  • src/foundation/src/Application.php
  • src/foundation/src/Http/Kernel.php
  • src/foundation/src/Testing/Concerns/MakesHttpRequests.php
  • src/grpc/src/Server/Server.php
  • src/http-server/README.md
  • src/http-server/composer.json
  • src/http-server/src/Events/Event.php
  • src/http-server/src/RequestBridge.php
  • src/http-server/src/ResponseBridge.php
  • src/http-server/src/Server.php
  • src/http/src/HttpServiceProvider.php
  • src/http/src/IterableStreamedResponse.php
  • src/http/src/Response.php
  • src/http/src/StreamOutput.php
  • src/queue/src/Console/BatchesTableCommand.php
  • src/queue/src/Console/FailedTableCommand.php
  • src/queue/src/Console/TableCommand.php
  • src/reverb/src/Servers/Hypervel/HttpServer.php
  • src/server/README.md
  • src/server/composer.json
  • src/server/src/Commands/ServerReloadCommand.php
  • src/server/src/Commands/ServerStartCommand.php
  • src/server/src/Listeners/InitProcessTitleListener.php
  • src/server/src/Port.php
  • src/server/src/Server.php
  • src/server/src/ServerConfig.php
  • src/server/src/ServerFactory.php
  • src/server/src/ServerInterface.php
  • src/support/src/Facades/Storage.php
  • src/testing/src/FakeSwooleSocket.php
  • src/testing/src/FakeWritableConnection.php
  • src/testing/src/TestResponse.php
  • src/websocket-server/src/Server.php
  • tests/Console/CacheCommandMutexTest.php
  • tests/Console/CommandMutexTest.php
  • tests/Console/CommandTest.php
  • tests/Console/Commands/FooExceptionCommand.php
  • tests/Console/Commands/FooExitCommand.php
  • tests/Console/Concerns/InteractsWithIOTest.php
  • tests/Console/ConfiguresPromptsTest.php
  • tests/Console/MigrationGeneratorCommandTest.php
  • tests/Console/OutputStyleTest.php
  • tests/Console/PackageMetadataTest.php
  • tests/Console/Scheduling/CallbackEventTest.php
  • tests/Console/Scheduling/EventTest.php
  • tests/Console/Scheduling/FrequencyTest.php
  • tests/Console/Scheduling/ScheduleRunCommandTest.php
  • tests/Console/Scheduling/ScheduleRunContextPropagationTest.php
  • tests/Console/Scheduling/ScheduleTest.php
  • tests/Console/View/ComponentsTest.php
  • tests/Context/ContextCoroutineTest.php
  • tests/Context/ContextTest.php
  • tests/Database/DatabaseMigrationCreatorTest.php
  • tests/Filesystem/ClientPooledFilesystemTest.php
  • tests/Filesystem/FileResponseBuilderTest.php
  • tests/Filesystem/FilesystemAdapterTest.php
  • tests/Filesystem/FilesystemPoolProxyTest.php
  • tests/Filesystem/ScopedFilesystemProxyTest.php
  • tests/Foundation/FoundationConfigTest.php
  • tests/Foundation/Http/KernelTest.php
  • tests/Http/IterableStreamedResponseTest.php
  • tests/HttpServer/EventTest.php
  • tests/HttpServer/PackageMetadataTest.php
  • tests/HttpServer/RequestBridgeTest.php
  • tests/HttpServer/ResponseBridgeTest.php
  • tests/HttpServer/ServerTest.php
  • tests/Integration/Console/Scheduling/ScheduleListCommandTest.php
  • tests/Integration/Console/Scheduling/ScheduleTestCommandTest.php
  • tests/Integration/Console/UniqueJobSchedulingTest.php
  • tests/Integration/Generators/CacheTableCommandTest.php
  • tests/Integration/Http/ResponseBindingTest.php
  • tests/Integration/HttpServer/HttpServerIntegrationTestCase.php
  • tests/Integration/HttpServer/LifecycleTest.php
  • tests/Integration/HttpServer/RequestConversionTest.php
  • tests/Integration/HttpServer/ResponseStreamingTest.php
  • tests/Integration/HttpServer/server.php
  • tests/Reverb/Webhooks/DeferredWebhookManagerTest.php
  • tests/Server/Listeners/InitProcessTitleListenerTest.php
  • tests/Server/PackageMetadataTest.php
  • tests/Server/PortTest.php
  • tests/Server/ServerConfigTest.php
  • tests/Server/ServerFactoryTest.php
  • tests/Server/ServerReloadCommandTest.php
  • tests/Server/ServerStartCommandTest.php
  • tests/Server/ServerTest.php
  • tests/Testbench/Foundation/Console/ServeCommandTest.php
  • tests/Testing/Fixtures/file.json
  • tests/Testing/TestResponseTest.php

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch audit/console-server-http-runtime-lifecycles

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

Copy link
Copy Markdown

Greptile Summary

This PR hardens the lifecycle of the Console scheduler, HTTP server, and Swoole response transport across 134 files. The central theme is single ownership: every resource from command output files and overlap mutexes to file streams and binary response chunks now has one explicit owner from creation through cleanup, with independent cleanup paths and earliest-failure preservation throughout.

  • Console/Scheduler: Moves per-run overlap, process, and callback state into CoroutineContext for concurrent safety; defers time-interval evaluation so dates resolve at execution time; replaces frozen day-of-month in lastDayOfMonth with the 'L' cron field; adds hasListeners guards on all observational event dispatches.
  • HTTP server: Removes WritableConnection/ResponseContext; makes ResponseBridge the sole owner of Swoole response emission including BinaryFileResponse with range, HEAD, tempfile, and delete-after-send handling; fixes RequestBridge query-string and root-path normalization.
  • File streaming: FileResponseBuilder now returns a lazy generator-backed IterableStreamedResponse instead of a StreamOutput-based callback, with deterministic resource cleanup via generator finally.

Confidence Score: 4/5

Safe to merge after resolving the double event dispatch in ScheduleRunCommand; everything else is a net improvement in lifecycle ownership and cleanup.

In ScheduleRunCommand::runEvent, dispatching ScheduledTaskFinished unconditionally before the exit-code check and then throwing a RuntimeException causes both ScheduledTaskFinished and ScheduledTaskFailed to fire for the same task run when the command exits with a non-zero code. Listener code that treats these events as mutually exclusive for a given run will behave incorrectly.

src/console/src/Commands/ScheduleRunCommand.php — the event dispatch ordering around the exit-code check.

Important Files Changed

Filename Overview
src/console/src/Commands/ScheduleRunCommand.php Adds exit-code checks, hasListeners guards, and refines runningEvents tracking. Dispatches both ScheduledTaskFinished and ScheduledTaskFailed for non-zero exit codes due to event order.
src/console/src/Scheduling/Event.php Moves overlap and process state into CoroutineContext for concurrent safety; hardens teardown so cleanup still runs after callbacks or write failures; adds wasSkippedDueToOverlapping() and callEventCallback() for current-run state.
src/http-server/src/ResponseBridge.php Adds BinaryFileResponse handling via reflection, chunked HTTP/2 streaming, trailer logic, and exhaustive cleanup with independent error preservation. Reflection on Symfony private fields is a noted coupling risk (flagged in previous threads).
src/http-server/src/RequestBridge.php Fixes query-string preservation in REQUEST_URI (Swoole splits URI and query), and corrects trailing-slash normalization for root paths with query strings.
src/http-server/src/Server.php Removes WritableConnection/ResponseContext setup, adds CanceledException handling, moves RequestHandled before response send, and passes protocol/request to ResponseBridge. Lifecycle cleanup order is now deterministic.
src/filesystem/src/FileResponseBuilder.php Converts streaming from StreamOutput to generator-backed IterableStreamedResponse; stream is now lazy and resource-safe with deterministic cleanup via generator finally block.
src/http/src/IterableStreamedResponse.php Refines clearChunks to preserve the earliest exception while still releasing the producer; reports secondary cleanup failures through PoolErrorReporter rather than swallowing or masking them.
src/console/src/Scheduling/CallbackEvent.php Moves callback result and exception storage from instance properties to CoroutineContext, isolating concurrent invocations of the same event object.
src/server/src/Server.php Adds checked return values for Swoole server operations (set, on, addlistener, start), throwing typed ServerException on each failure instead of silently continuing.
src/server/src/Commands/ServerReloadCommand.php Validates PID file is a positive integer before sending signals; returns FAILURE on each identifiable error condition with an actionable warning or error message.
src/database/src/Migrations/MigrationCreator.php Adds collision-free migration naming via CoroutineContext and a per-second advancement loop; uses Filesystem::replace for atomic publish and fixes glob() false handling.
src/console/src/Scheduling/ManagesFrequencies.php Defers time-interval evaluation into a closure so dates are resolved at execution time rather than schedule-build time; replaces frozen day-of-month in lastDayOfMonth with the standard 'L' cron field.
src/testing/src/TestResponse.php Removes FakeWritableConnection/ResponseContext streaming path; extends streamedContent to accept BinaryFileResponse and skips body production for HEAD requests.

Fix All in Claude Code Fix All in Codex

Reviews (2): Last reviewed commit: "fix(runtime): harden schedule config and..." | Re-trigger Greptile

Comment thread src/console/src/Scheduling/Event.php
Comment thread src/console/src/Commands/ScheduleListCommand.php
Comment thread src/http-server/src/ResponseBridge.php
Read the required application timezone through the typed configuration repository at both schedule-list boundaries so invalid merged configuration fails with the framework's descriptive contract.

Move binary delete-after-send unlinking behind the existing style of protected native boundary. This preserves the checked deletion postcondition and earliest failure while allowing the failure path to be exercised deterministically under root-run CI containers.

Replace permission-based failure injection with a test-only bridge override, retain proof that response emission completes first, and assert both the deletion exception and surviving file without catching PHPUnit's own assertion failure.
@binaryfire

Copy link
Copy Markdown
Collaborator Author

@greptile-apps Followed up on the complete review and the root-container CI failure.

Changes landed:

  • Both Schedule List fallbacks now read app.timezone through config()->string(). There is intentionally no nullable fallback: merged framework configuration owns the required string default, and invalid configuration should fail descriptively.
  • Binary delete-after-send cleanup now exposes only the native unlink() call through a protected boundary. The existing checked postcondition and earliest-failure behavior remain at the transport owner.
  • The deletion-failure regression no longer relies on directory permissions, which root can bypass. A test-only bridge override now injects the native failure deterministically while proving response emission completed first, the exact deletion exception propagated, and the file remained.

Two findings were intentionally left unchanged:

  • Scheduled output uses the concrete Hypervel\Filesystem\Filesystem, whose put() and append() methods return int|false byte counts. The exact-length comparison detects partial writes; a false-only check would weaken it.
  • The prepared BinaryFileResponse reflection is the smallest available way to preserve Symfony range, temporary-file, chunk-size, X-Sendfile, and deletion semantics. Symfony exposes no public getters, the reason is already documented, and focused dependency-update coverage exercises every reflected field.

Validation is green through composer fix, the focused Console and HTTP Server suites, the exact root CI container, and git diff --check.

@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown

All three original findings are resolved or confirmed intentional — the PR is in good shape:

Acknowledged as fixed:

  • config()->string('app.timezone') — typed boundary applied to both fallbacks, no nullable hedge, which is correct given the framework-owned default. Resolved.
  • Delete-after-send unlink() boundary — the protected extraction removes the test's dependency on directory permissions (which root can bypass), and the deterministic override pattern proves emission-before-deletion ordering cleanly.

My original P1 was incorrect:

  • Hypervel\Filesystem\Filesystem::put() and append() return int|false from file_put_contents(), not bool. The strlen() comparison correctly catches partial writes that a === false-only guard would let through. I was conflating it with Laravel's Flysystem-backed Illuminate\Filesystem\Filesystem. Apologies for the noise.

Intentional and documented:

  • The BinaryFileResponse reflection coupling — the test coverage that exercises every reflected field serves as the effective guard against a silent Symfony rename. That's a reasonable trade given no public API alternative exists.

Shall I remember the following rule to avoid the same mistake in future reviews: "Hypervel's Filesystem\Filesystem wraps file_put_contents() directly and returns int|false, not the bool|string of Laravel's Flysystem-backed Illuminate\Filesystem\Filesystem — do not conflate them when reviewing write-success checks"?

Comment thread src/console/src/Commands/ScheduleRunCommand.php
@binaryfire
binaryfire merged commit a3a3952 into 0.4 Jul 23, 2026
37 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