Source
Discussion #3023 symptom 1. Verified by code trace (identical on v4.0.3 and develop) and reproduced live on Lucee 7: ?reload=testing&password=X → 302 Found, location: / → environment still development.
Bug
The only path that restarts a stock app is public/Application.cfc's reload gate: it calls applicationStop() then redirects via $buildRedirectUrl(), which strips reload, password, and lock from the query string (anti-loop logic). But the framework's environment switch (vendor/wheels/events/onapplicationstart.cfc:171-191) requires URL.reload to be present on the request that starts the new application — which is the post-strip redirect. So the parameter is always gone, the else branch re-includes config/environment.cfm, and the environment never changes. The switch code is unreachable through the stock flow; the two halves were written against incompatible assumptions.
Affected copies (same lineage, all need the fix + a parity spec)
cli/lucli/templates/app/public/Application.cfc (what wheels new ships)
public/Application.cfc (repo demo app — this is what the docker harness exercises)
examples/starter-app/public/Application.cfc
examples/tweet/public/Application.cfc
Proposed fix (loop-safe)
In $buildRedirectUrl(), preserve reload + password when the reload value is an environment switch (!IsBoolean(url.reload)); keep stripping for reload=true. Loop-break in the gate: skip applicationStop() when !IsBoolean(url.reload) AND application.wheels.environment already equals url.reload — i.e. the switch has been applied by the restart this redirect came from; proceed normally (or strip-redirect to the clean URL). Note redirectAfterReload defaults to false, so the gate-side loop-break is required — do not rely on onapplicationstart.cfc:449.
Documented trade-off: ?reload=<current-env>&password=X becomes a no-op (use ?reload=true to force a same-environment restart).
Acceptance
- Live repro on the docker demo app (Lucee 7):
?reload=testing&password=X → after the redirect chain settles, /wheels/info (in an env where it's reachable) or a probe shows environment=testing; no redirect loop (assert the chain terminates in ≤2 hops).
?reload=true&password=X behavior unchanged.
- A parity spec pinning the gate/redirect logic across all four file copies (mold: the structural specs in
vendor/wheels/tests/specs/cli/Bot*ShaThreadingSpec.cfc).
Related: explicit-override bug is tracked separately as #3031 — switching INTO testing/production currently also one-way-trapdoors because of it.
Source
Discussion #3023 symptom 1. Verified by code trace (identical on v4.0.3 and develop) and reproduced live on Lucee 7:
?reload=testing&password=X→302 Found, location: /→ environment stilldevelopment.Bug
The only path that restarts a stock app is
public/Application.cfc's reload gate: it callsapplicationStop()then redirects via$buildRedirectUrl(), which stripsreload,password, andlockfrom the query string (anti-loop logic). But the framework's environment switch (vendor/wheels/events/onapplicationstart.cfc:171-191) requiresURL.reloadto be present on the request that starts the new application — which is the post-strip redirect. So the parameter is always gone, theelsebranch re-includesconfig/environment.cfm, and the environment never changes. The switch code is unreachable through the stock flow; the two halves were written against incompatible assumptions.Affected copies (same lineage, all need the fix + a parity spec)
cli/lucli/templates/app/public/Application.cfc(whatwheels newships)public/Application.cfc(repo demo app — this is what the docker harness exercises)examples/starter-app/public/Application.cfcexamples/tweet/public/Application.cfcProposed fix (loop-safe)
In
$buildRedirectUrl(), preservereload+passwordwhen the reload value is an environment switch (!IsBoolean(url.reload)); keep stripping forreload=true. Loop-break in the gate: skipapplicationStop()when!IsBoolean(url.reload)ANDapplication.wheels.environmentalready equalsurl.reload— i.e. the switch has been applied by the restart this redirect came from; proceed normally (or strip-redirect to the clean URL). NoteredirectAfterReloaddefaults tofalse, so the gate-side loop-break is required — do not rely ononapplicationstart.cfc:449.Documented trade-off:
?reload=<current-env>&password=Xbecomes a no-op (use?reload=trueto force a same-environment restart).Acceptance
?reload=testing&password=X→ after the redirect chain settles,/wheels/info(in an env where it's reachable) or a probe showsenvironment=testing; no redirect loop (assert the chain terminates in ≤2 hops).?reload=true&password=Xbehavior unchanged.vendor/wheels/tests/specs/cli/Bot*ShaThreadingSpec.cfc).Related: explicit-override bug is tracked separately as #3031 — switching INTO testing/production currently also one-way-trapdoors because of it.