Part 5 of "From 2.x to 4.x: the field guide". CFWheels 2.x ran validation condition= and unless= strings through Evaluate(); Wheels 4.1 parses a small grammar and throws Wheels.InvalidValidationCondition for what it can't parse. This post lists the grammar 4.1.2 accepts, runs ten 2.x-style strings on 4.1.2 (three throw, seven give the wrong answer without an error), rewrites each one, covers the this.method() escape hatch and the two production conditions from the 4.1.1 CHANGELOG, and explains what Wheels 4.2 changes.
Part 4 of "From 2.x to 4.x: the field guide" covers public/Application.cfc, the app-owned file that a vendor/wheels/ swap never updates. It replays every template change from 4.0.0 to 4.1.2 one commit at a time: the DI container, the onError fallbacks, the fail-closed reload gate, the X-Wheels-Reload-Password header, IP-based debug access, the session cookie, the Adobe teardown handlers, the jBCrypt load path and the .env parser. A table says what each edit fixes, which release added it and whether skipping it fails safe, and the post shows what changed on Lucee 7 and Adobe ColdFusion 2025 after each edit.
Part 3 of "From 2.x to 4.1: the field guide" takes the example app from Wheels 3.0.0+33 to 4.1.2. It reads the real `wheels upgrade check` output before and after, explains why to run the check before swapping vendor/wheels, and walks the 14 commits: the framework swap, the 4.1 public/Application.cfc, plugin replacements, paginationNav(), the CSRF settings, a RocketUnit-to-WheelsTest conversion and validation conditions. It also covers the breakers only a real boot found: default='' in migrations, structs assigned to scalar columns, dates interpolated into where strings on 4.1.2, and a reload password the CLI and the app disagreed on.
Part 2 of "From 2.x to 4.1: the field guide" walks the nine commits that take the example app from CFWheels 2.2 to Wheels 3.0.0+33: moving code into app/ and assets into public/, the new public/Application.cfc, vendor/wheels pinned to 3.0.0+33, path and test fixes, and the problems the 3.0 guide doesn't mention: events/ has to move, plugins/ stays at the root, a caret range can install a 4.0 development build, joinType="left" silently drops a nested join, and a 2.3 validatesConfirmationOf change breaks login.
The first post in "From 2.x to 4.1: the field guide". Going from Wheels 2.x to 4.1 takes two passes: 2.x to 3.0.0+33 (the layout move), then 3.0 to 4.1 (the framework swap and the 4.x breaking changes). This post shows what each pass touches, which files no upgrade command ever touches, and the real CFWheels 2.2 app we upgrade on camera, wheels-dev/cfwheels-example-app, with one branch and one commit per step.
Wheels 4.1.2 is a patch release on 4.1.x: `wheels deploy` now builds and pushes the image it deploys, failing CLI commands exit non-zero, migrations can opt out of the per-step transaction, a new `baseUrl` setting, migrator and model fixes, tighter gates on the development tools, and four security fixes. Upgrading is recommended; a few generated-app files need small edits.
Wheels 4.1.1 fixes a CLI security issue (GHSA-x3cm-2j3q-jgg4), makes the first request after a restart work on Adobe ColdFusion, stops multi-database migrations from stalling, and clears out the rough edges people hit in 4.1.0. Every compatibility-matrix leg passed, test by test, before it shipped.
Wheels 4.1.0 is out: bcrypt, one-line session auth, pretty routes, DI factories, CLI diff/dry-run/offline, a security hardening pass, and model instantiation ~2.5x faster. Every one of those threads traces back to a single issue filed by @chapmandu — and the release is better because it didn't start with a features list.
There was a controller with nine hundred lines and one bug that only fired in September. Three developers touched it that week. When we pointed the complexity panel at it, the score said everything — and the coverage run said the test suite had never touched it. 4.1 ships the two tools that make that obvious before the September bug goes out.
A security review of Wheels 4.0 found a pile of "surely that's safe" that weren't: a policy that treated the string "yes" as true, a form method override that worked on a GET, a zip that could escape its directory. 4.1 closes them by failing closed. Here's the story area by area, and what you have to check on the way up.