You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(cli): reload and packages add no longer claim onApplicationStart won't re-fire (#3126)
* fix(cli): reload and packages add no longer claim onApplicationStart won't re-fire
An authorized ?reload=true&password=... calls applicationStop(), so the next
request re-fires onApplicationStart in full — config/services.cfm and the
PackageLoader ($loadPackages) all re-run (verified live on Lucee 7, #3110).
The reload command's note and the packages-install activation line claimed the
opposite, telling users to run `wheels stop && wheels start`. Correct both:
the reload note now describes the real full-restart behavior and surfaces the
password-resolution caveat (a missing/wrong password silently skips the
restart, #3059 / #3062); the install output now says `wheels reload` (or
restart) activates the package.
Tutorial/guide passages making the same claim are handled separately by
bot-update-docs.yml.
Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
* docs(web/guides): correct reload contract — wheels reload re-fires onApplicationStart
Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
---------
Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Amiri <petera@pai.com>
-`wheels reload` and `wheels packages add` no longer claim that `?reload=true` skips `onApplicationStart`. An authorized reload calls `applicationStop()`, so the next request re-fires `onApplicationStart` in full (re-running `config/services.cfm` and the package loader) — the CLI now says a reload activates an installed package and notes that only a missing/wrong reload password silently skips the restart (#3110)
// Surface the hot-vs-cold reload contract — Wheels does NOT
903
-
// re-fire onApplicationStart on `?reload=true`. Users editing
904
-
// app/events/onapplicationstart.cfm or config/services.cfm need
905
-
// a full restart. See finding #8 in the 2026-04-29 fresh-VM
906
-
// triage.
907
-
out("Note: onApplicationStart does NOT re-fire. For init-code edits, run `wheels stop && wheels start`.", "cyan");
902
+
// Surface the actual reload contract (verified live on Lucee 7,
903
+
// see #3110): an authorized `?reload=true&password=...` calls
904
+
// applicationStop(), so the next request re-fires onApplicationStart
905
+
// in full — app/events/onapplicationstart.cfm, config/services.cfm,
906
+
// and the PackageLoader all re-run. Caveat: the restart only
907
+
// happens when the reload password resolves; a missing or wrong
908
+
// password silently serves the request without restarting
909
+
// (#3059 / #3062).
910
+
out("Note: an authorized reload re-fires onApplicationStart (re-runs config/services.cfm and the package loader). A missing or wrong reload password silently skips the restart.", "cyan");
Copy file name to clipboardExpand all lines: web/sites/guides/src/content/docs/v4-0-0/command-line-tools/commands/packages/install.mdx
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@ sidebar:
7
7
order: 4
8
8
---
9
9
10
-
Adds a package into `vendor/<name>/` from the registry. To activate it, do a cold restart (`wheels stop && wheels start`) — `PackageLoader` discovers the new `vendor/<name>/package.json`at startup. A `wheels reload` is not enough to pick up a newly-installed package.
10
+
Adds a package into `vendor/<name>/` from the registry. To activate it, run `wheels reload` (or `wheels stop && wheels start`) — `PackageLoader` discovers the new `vendor/<name>/package.json`when `onApplicationStart` re-fires. An authorized `wheels reload` is sufficient; a missing or wrong reload password silently skips the restart.
11
11
12
12
:::note[Why `add`, not `install`?]
13
13
LuCLI's built-in extension installer intercepts the literal subcommand `install` across every module. Typing `wheels packages install <name>` runs LuCLI's own dependency resolver — it prints `No git or extension dependencies to install` and exits without touching `vendor/`. Use `add` instead. (The same rename happened to `wheels browser setup`, which was previously `wheels browser install`.)
@@ -51,9 +51,9 @@ The same `SemVer` matcher that `PackageLoader` uses at runtime.
Copy file name to clipboardExpand all lines: web/sites/guides/src/content/docs/v4-0-0/digging-deeper/packages.mdx
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -36,7 +36,7 @@ Install a package:
36
36
37
37
```bash title="your shell"
38
38
wheels packages add wheels-hotwire
39
-
wheels stop && wheels start
39
+
wheels reload
40
40
```
41
41
42
42
Browse, search, inspect, update, or remove:
@@ -57,7 +57,7 @@ Deactivation is a single command — `remove` deletes the `vendor/<name>/` direc
57
57
The registry caches its index for 24 hours. If you want to see a just-published version immediately, `wheels packages registry refresh` busts the cache. `wheels packages registry info` shows the configured registry URL and cache state.
58
58
</Aside>
59
59
60
-
The restart re-runs `PackageLoader` at startup, which rediscovers what's in `vendor/` and reconciles the mixin, service, and middleware tables with the new state. A plain`wheels reload`is not enough — `PackageLoader` only re-scans `vendor/` on a cold start.
60
+
The reload re-runs `PackageLoader`, which rediscovers what's in `vendor/` and reconciles the mixin, service, and middleware tables with the new state. An authorized`wheels reload`calls `applicationStop()` so the next request re-fires `onApplicationStart` in full — `wheels stop && wheels start` also works. Caveat: a missing or wrong reload password silently skips the restart.
61
61
62
62
## First-party packages
63
63
@@ -156,7 +156,7 @@ component output="false" {
156
156
}
157
157
```
158
158
159
-
After installing the package into `vendor/myfeature/` and restarting the server (`wheels stop && wheels start`), every controller has `myHelper()` available. The framework collects public methods from the package instance and merges them into the application mixin tables — the same machinery that runs for plugins, but scoped to the targets you named in the manifest.
159
+
After installing the package into `vendor/myfeature/` and reloading (`wheels reload` or `wheels stop && wheels start`), every controller has `myHelper()` available. The framework collects public methods from the package instance and merges them into the application mixin tables — the same machinery that runs for plugins, but scoped to the targets you named in the manifest.
160
160
161
161
Lifecycle hook names the loader specifically skips when collecting mixins: `init`, `onPluginLoad`, `onPluginActivate`, `register`, `boot`. If you name a method any of those, it won't be mixed in — the loader treats them as package infrastructure.
Copy file name to clipboardExpand all lines: web/sites/guides/src/content/docs/v4-0-0/start-here/tutorial/06-authentication.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -557,7 +557,7 @@ if (StructKeyExists(application, "wheelsdi") && application.wheelsdi.containsIns
557
557
558
558
Don't put this in `config/app.cfm` — that file is for `Application.cfc``this`-scope settings (`this.name`, `this.datasources`, `this.sessionTimeout`, etc.), not for init code. The DI container isn't initialized when `config/app.cfm` runs, so `application.wheelsdi` doesn't exist yet and the registration silently no-ops.
559
559
560
-
Both `wheels reload` and a full `wheels stop && wheels start` re-fire `onApplicationStart` — an authorized reload stops the application and the next request boots it fresh, re-running this file and `config/services.cfm`. Either way, this registers the strategy exactly once, and the `hasStrategy` check keeps repeated restarts from stacking duplicates. (Some CLI messages still claim `wheels reload` skips `onApplicationStart`; that's outdated — tracked in [#3110](https://github.com/wheels-dev/wheels/issues/3110).)
560
+
Both `wheels reload` and a full `wheels stop && wheels start` re-fire `onApplicationStart` — an authorized reload stops the application and the next request boots it fresh, re-running this file and `config/services.cfm`. Either way, this registers the strategy exactly once, and the `hasStrategy` check keeps repeated restarts from stacking duplicates. (CLI versions predating the [#3110](https://github.com/wheels-dev/wheels/issues/3110) fix printed a note claiming `wheels reload` skips `onApplicationStart`; that note was wrong.)
561
561
562
562
### Rewrite the Sessions controller
563
563
@@ -744,7 +744,7 @@ Whether you stopped at 6a or continued to 6b, the user-facing behavior should be
744
744
745
745
**"The password always mismatches, even for a user I just created."** The `beforeValidation` callback isn't firing, or the form is storing plaintext. Open the database and look at the `users` row directly: `passwordHash` should be exactly 64 uppercase hex characters, `passwordSalt` should be populated with a ~44-char base64 string. If either is blank, check that you named the callback correctly in `config()` (`beforeValidation("hashPassword")` — case-sensitive) and that the form submits `user[password]`, not just `password`.
746
746
747
-
**"6b: `authenticator` service not found."** Three things to check, in this order. **(1) Does the file have a `<cfscript>` wrapper?** Without it, Lucee treats the body as markup — you'll see the bare `local.di = injector();` lines printed at the top of every page after a cold restart, and the registration code never runs. Compare against `config/settings.cfm` if unsure of the shape. **(2) Did the application actually restart?** Both `wheels reload` and `wheels stop && wheels start` re-fire `onApplicationStart` and re-run `config/services.cfm` — but a reload with a wrong or missing reload password silently skips the restart. If in doubt, `wheels stop && wheels start` removes that variable. (CLI messages claiming `wheels reload` never re-fires `onApplicationStart` are outdated — see [#3110](https://github.com/wheels-dev/wheels/issues/3110).) **(3) Component path typo?** The `.to(...)` argument must be the exact dotted path — `wheels.auth.Authenticator`, not `Authenticator` — and the `injector()` call has to come first.
747
+
**"6b: `authenticator` service not found."** Three things to check, in this order. **(1) Does the file have a `<cfscript>` wrapper?** Without it, Lucee treats the body as markup — you'll see the bare `local.di = injector();` lines printed at the top of every page after a cold restart, and the registration code never runs. Compare against `config/settings.cfm` if unsure of the shape. **(2) Did the application actually restart?** Both `wheels reload` and `wheels stop && wheels start` re-fire `onApplicationStart` and re-run `config/services.cfm` — but a reload with a wrong or missing reload password silently skips the restart. If in doubt, `wheels stop && wheels start` removes that variable. (CLI messages claiming `wheels reload` never re-fires `onApplicationStart` predate the [#3110](https://github.com/wheels-dev/wheels/issues/3110) fix.) **(3) Component path typo?** The `.to(...)` argument must be the exact dotted path — `wheels.auth.Authenticator`, not `Authenticator` — and the `injector()` call has to come first.
@@ -83,7 +83,7 @@ The canonical way to install a Wheels package is `wheels packages add <name>`. T
83
83
wheels reload
84
84
```
85
85
86
-
`wheels reload` works because an authorized reload stops and restarts the application, re-running `onApplicationStart` — and with it the package loader. A full `wheels stop && wheels start` (which the install message suggests) does the same thing, just more heavily. (The CLI message claiming only a stop/start activates a package is outdated — tracked in [#3110](https://github.com/wheels-dev/wheels/issues/3110).)
86
+
`wheels reload` works because an authorized reload stops and restarts the application, re-running `onApplicationStart` — and with it the package loader. A full `wheels stop && wheels start` (as the install message also hints) does the same thing, just more heavily. (CLI versions predating the [#3110](https://github.com/wheels-dev/wheels/issues/3110) fix claimed only a stop/start activates a package; that claim was wrong.)
87
87
88
88
</Steps>
89
89
@@ -296,7 +296,7 @@ Three things to verify:
296
296
297
297
**`No matching function [UIBUTTON] found`** — the package didn't activate. Both `wheels reload` and `wheels stop && wheels start` re-run `PackageLoader` (an authorized reload restarts the application, re-firing `onApplicationStart`), so the usual cause is a reload that silently no-op'd — a wrong or missing reload password — or the package landing somewhere other than `vendor/`. Run `wheels stop && wheels start` to rule out the password variable, and `ls vendor/wheels-basecoat` to confirm the files are there.
298
298
299
-
**`No matching function [$UIBUILDID] found`** (or `[$UILUCIDEICON]`) — the package activated but its internal helpers can't be found. Fixed in `wheels-basecoat 1.0.3`. Older versions declared the `$`-prefixed helpers `private`, but Wheels' `PackageLoader` only carries PUBLIC methods across the mixin boundary, so public callers like `uiField` couldn't reach them. Run `wheels packages update wheels-basecoat --yes && wheels stop && wheels start`.
299
+
**`No matching function [$UIBUILDID] found`** (or `[$UILUCIDEICON]`) — the package activated but its internal helpers can't be found. Fixed in `wheels-basecoat 1.0.3`. Older versions declared the `$`-prefixed helpers `private`, but Wheels' `PackageLoader` only carries PUBLIC methods across the mixin boundary, so public callers like `uiField` couldn't reach them. Run `wheels packages update wheels-basecoat --yes` then `wheels reload` (or restart).
300
300
301
301
**`No version of 'wheels-basecoat' satisfies runtime '0.0.0-dev'`** — the framework can't read its own version. Likely on a Wheels release older than `4.0.0-SNAPSHOT+1670` (the snapshot that includes the runtime-detection fix). Upgrade with `brew upgrade wheels` (macOS/Linux) or `scoop update wheels` (Windows) and re-run.
0 commit comments