release: v1.6.1 — essentials reach the worker, and APP_DEBUG means one thing - #126
Merged
Merged
Conversation
…e thing Two surfaces disagreed with themselves. WorkerLoop built its OnDemandLoader with no essentials, and the kernel had no way to hand them over, so a module the project declared app-wide in proj.json "essentials" was app-wide for requests and absent from every job. For an essential that rebinds a port per scope — tenancy rebinding DatabasePort — the failure is silent rather than loud: the binding still resolves, just to the wrong connection. The worker now registers essentials into every job container AND seeds their domains into the job's graph, the same two steps LoadStage performs for a request, so their transitive requires[] come with them. A job whose class the manifest does not know now also gets a container rather than the bare CoreContainer, since "essential" means every unit of work; an application declaring no essentials keeps its previous behaviour, that fallback included. The class-to-domain mapping both surfaces need moved to DependencyGraphCalculator::domainsFor() — a private copy in each pipeline is how they drifted apart in the first place. ErrorStage read APP_DEBUG two ways in one file: isDebug() parsed it with FILTER_VALIDATE_BOOL while publicError() compared it === 'true'. So APP_DEBUG=1 served the HTML debug page — stack trace and source excerpt — to anything sending Accept: text/html, while every JSON response still masked its message as "An internal error occurred.". One flag, two behaviours, and the more revealing of the two was the one that engaged. There is now one isDebug(), used by both, keeping FILTER_VALIDATE_BOOL because that is what every other kernel flag uses (HttpPipeline::flag()), and reading through env() rather than $_ENV/getenv() — the environment loader deliberately skips putenv(), so getenv() is not the source of truth for a .env value. Both tests were checked against the pre-fix code: the debug test fails on "APP_DEBUG=1 must reveal the message on the JSON path" while its three masking assertions still pass, so the fix does not loosen masking. 359 tests, 664 assertions. PHPStan clean on every changed file, no baseline additions.
hakeemRash
requested review from
Alshatri and
craftdevscommunity
as code owners
August 30, 2026 02:46
Alshatri
approved these changes
Aug 30, 2026
craftdevscommunity
approved these changes
Aug 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two bug fixes found while reading
src/Kernel/end to end. Merging this firesauto-release.yml: it reads## [1.6.1]off the top of the CHANGELOG, tagsv1.6.1, builds, publishes, and then thehomebrewjob bumps the formula'surl+sha256on its own.Essential modules never reached a queued job
HttpPipelinepassed its essentials intoOnDemandLoader;WorkerLoopbuilt its loader with none, andKernel::materialize()had no way to hand them over. A module the project declared app-wide inproj.json"essentials"was app-wide for requests and absent from every job.For an essential that rebinds a port per scope — tenancy rebinding
DatabasePort— the failure is silent rather than loud: the binding still resolves, just to the wrong connection.The worker now registers essentials into every job container and seeds their domains into the job's graph — the same two steps
LoadStageperforms for a request, so transitiverequires[]come with them. A job whose class the manifest does not know now also gets a container rather than the bareCoreContainer, since "essential" means every unit of work. An application declaring no essentials keeps its previous behaviour, that fallback included.The class→domain mapping both surfaces need moved to
DependencyGraphCalculator::domainsFor(); a private copy in each pipeline is how they drifted apart in the first place.APP_DEBUGmeant two different things in one fileErrorStage::isDebug()parsed the value withFILTER_VALIDATE_BOOLwhilepublicError()compared it=== 'true'. SoAPP_DEBUG=1served the HTML debug page — stack trace and source excerpt — to anything sendingAccept: text/html, while every JSON response still masked its message as "An internal error occurred.". One flag, two behaviours, and the more revealing of the two was the one that engaged.There is now one
isDebug(), used by both.FILTER_VALIDATE_BOOLis the surviving parse because it is what every other kernel flag uses (HttpPipeline::flag()), so1,on,yesandtruemean the same thing throughout. It also reads throughenv()rather than$_ENV/getenv(): the environment loader deliberately skipsputenv(), sogetenv()is not the source of truth for a.envvalue.Note the direction — with
APP_DEBUG=1the JSON path now reveals exception messages, which is what the flag was asked for. Unset or falsy masks exactly as before.Verification
ErrorStageDebugTestfails onAPP_DEBUG=1 must reveal the message on the JSON pathwhile its three masking assertions still pass — so the fix does not loosen masking.WorkerLoopEssentialsTesterrors onUnknown named parameter $essentialModules, which proves it targets the new capability rather than a changed behaviour: the old loop had no way to be told about essentials at all.origin/mainrather thandev-mac, which still carries the unsquashed 1.6.0 commits already merged as release: v1.6.0 — make the queue an authenticated channel, and make BOOT_CACHE work #124.Not touched:
HomebrewFormula/hkm.rb. Itssha256is written by the release job after publishing, and a digest cannot exist before the release does.