Skip to content

docs(web/blog): revise Upgrading from Wheels 3.x with Titan cutover lessons#2654

Merged
bpamiri merged 4 commits into
developfrom
peter/blog-upgrading-from-3x-titan-lessons
May 13, 2026
Merged

docs(web/blog): revise Upgrading from Wheels 3.x with Titan cutover lessons#2654
bpamiri merged 4 commits into
developfrom
peter/blog-upgrading-from-3x-titan-lessons

Conversation

@bpamiri
Copy link
Copy Markdown
Collaborator

@bpamiri bpamiri commented May 13, 2026

Summary

The post auto-published this morning (888abc2) was drafted before the production cutover of Titan to Wheels 4.0. This revision folds in what we learned overnight and points forward at the v4.0.1 fixes already merged on develop.

Key changes

Source of truth

The publishing-admin's SQLite (post id 877) has been resynced to match this file. Body grew 14,371 → 19,487 bytes. updatedAt bumped to 2026-05-13T14:56:19Z.

Test plan

  • Astro preview / Cloudflare Pages preview renders the new section and heads-up callout correctly
  • Inline code blocks for cflocation and set(reloadPassword=...) render with syntax highlighting
  • Frontmatter updatedAt shows on the post page (or in feeds)
  • No broken internal links to guides.wheels.dev/v4-0-0-snapshot/upgrading/3x-to-4x/
  • Post still appears at expected slug /blog/upgrading-from-wheels-3x post-merge

🤖 Generated with Claude Code

…essons

Post auto-published this morning (888abc2) predated the production
cutover of Titan to Wheels 4.0 overnight. Revise content to reflect
what surfaced during the cutover and reference the v4.0.1 fixes in
flight.

- Reframe count: acknowledge canonical guide's eleven breakers; this
  post walks the seven most likely to bite a real 3.x codebase.
- Drop cfwheels-rebrand from the breaker list (that rename completed
  in 3.0; it is not a 4.0 breaker). Slot 2 is now redirectTo() scope
  tightening, which actually surfaced as TITAN-30 in titan's
  onrequeststart.cfm on Lucee 7.
- Add a caveat to the Legacy Compatibility Adapter section: it does
  not shim application.wirebox; apps with WireBox bootstraps must
  rewrite Application.cfc before first boot regardless of the flag.
- New "Things that bite at boot (from the field)" section covering
  the four operational gotchas titan hit at cutover: default
  rewrite.config asset 404s for non-standard directories, .env
  reloadPassword wiring through env() helper, classpath jar
  symlinks into Lucee Express lib/ext, and Lucee 6 -> 7 session
  table truncation.
- Heads-up callout at the top of that section flagging that the
  forthcoming v4.0.1 already addresses most of these via merged
  framework PRs (rewrite.config defaults, wheels upgrade check
  coverage, guide docs); a follow-up post will point back here when
  v4.0.1 ships.

SQLite source-of-truth (wheels-publishing-admin post id 877) is in
sync with this file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the docs label May 13, 2026
Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer A

TL;DR: This revision adds real production signal — four operational gotchas from a live Titan cutover that the canonical breaker list does not cover — and the writing is tight. One factual accuracy concern about WireBox framing, one grep command that will silently miss the most common Application.cfc location, and one cross-engine tag-syntax nit are worth addressing before this post becomes reference material. Overall verdict: comment (no blocking issues, but three factual corrections are worth making before the post ages into the definitive upgrade resource).


Correctness

application.wireboxapplication.wheelsdi is not a Wheels-native rename.

File: web/content/blog/posts/upgrading-from-wheels-3x.md, line 118

What the adapter does **not** cover: it cannot shim `application.wirebox` access or the
`wirebox.system.ioc.Injector` class path. Apps that bootstrap WireBox directly in
`Application.cfc` (the canonical 2.x pattern still common in older codebases) must rewrite
that file before first boot, adapter or no adapter. The canonical breaker for this rename is
`application.wirebox``application.wheelsdi`.

WireBox is ColdBox's DI framework — it was never a native Wheels 2.x concept. Wheels' own DI container (v4.0) is accessed via service() / injector(), not application.wheelsdi. Presenting application.wireboxapplication.wheelsdi as "the canonical breaker" implies Wheels itself shipped an application.wirebox in 3.x, which is not accurate. What the paragraph is describing is a Titan-specific setup (a team that bootstrapped WireBox alongside Wheels), not a general upgrade path.

Suggested framing:

What the adapter does not cover: apps that bootstrapped a third-party DI framework — most commonly WireBox (the ColdBox standalone container) — directly in Application.cfc alongside Wheels. The adapter cannot shim application.wirebox or wirebox.system.ioc.Injector. If grep -rn "application.wirebox\|new wirebox.system" app/ public/ finds anything, migrate to Wheels 4.0's built-in DI (service() / injector()) or keep WireBox initialized in Application.cfc without expecting the adapter to bridge the gap.

The application.wheelsdi reference should be dropped or sourced — it does not appear in CLAUDE.md or the framework docs.


Detection grep for breaker #2 misses the most common Application.cfc path.

File: web/content/blog/posts/upgrading-from-wheels-3x.md, line 46 (table) and line 65 (body)

`grep -rn "redirectTo(" app/events/ public/Application.cfc`

public/Application.cfc is Titan's project layout, not the standard one. Most Wheels apps put Application.cfc at the project root. Readers following this grep verbatim will silently miss any redirectTo() call in their Application.cfc if it lives at the root or in app/.

Suggested:

grep -rn "redirectTo(" app/events/ Application.cfc

Or to be safe across layouts:

grep -rn "redirectTo(" app/events/ $(find . -maxdepth 2 -name "Application.cfc")

Cross-engine

location url="..." addToken=false; — dropped cf prefix in tag-in-script example.

File: web/content/blog/posts/upgrading-from-wheels-3x.md, line 72

// after
location url="/account/logOut" addToken=false;

In Lucee cfscript, the tag-in-script shorthand for <cflocation> is cflocation url="..." addToken=false; (with the cf prefix). The location form without the prefix is accepted by some Lucee versions but is not documented as the canonical shorthand and will not work in Adobe CF. The breaker itself is Lucee 7–specific, but readers may be running on other engines or mixed environments.

The cross-engine–safe cfscript form is:

// after (works on Lucee + Adobe CF)
cflocation(url="/account/logOut", addToken=false);

Docs

Minor note: the excerpt in the frontmatter (line 12–16) still reads "seven breaking changes." After this revision the post's own intro distinguishes the seven featured breaks from the canonical guide's eleven, so the excerpt is still accurate — no change needed. Just confirming.

The updatedAt bump to 2026-05-13T14:56:19.000Z is correct and matches the PR body.


Commits

Single commit docs(web/blog): revise Upgrading from Wheels 3.x with Titan cutover lessons — valid type, scope, and subject. Conforms to commitlint.config.js.

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 13, 2026

Wheels Bot — Reviewer B (round 1)

A's review contains one significant false positive on its most prominent finding, and two valid findings the author should act on. I cannot align on a verdict while a false positive of this weight is in play.

Sycophancy

None detected. A did not approve despite finding issues, avoided 'looks great' language, and called the verdict 'comment' rather than inflating it.

False positives

Finding 1 — WireBox/wheelsdi framing. A claims that application.wheelsdi 'does not appear in CLAUDE.md or the framework docs' and concludes the blog's sentence 'The canonical breaker for this rename is application.wireboxapplication.wheelsdi' is inaccurate.

This is wrong. application.wheelsdi is a real, load-bearing Wheels 4.0 concept. vendor/wheels/Injector.cfc:45 sets application.wheelsdi = this; — the Injector self-registers there on boot. vendor/wheels/tests/specs/cli/ApplicationCfcInjectorAssignmentSpec.cfc has tests asserting generated Application.cfc templates assign application.wheelsdi = new wheels.Injector(...) and route all getInstance calls through it. The blog post's framing — teams using WireBox need to migrate to application.wheelsdi — is accurate and actionable. A's suggested rewrite would remove the only concrete endpoint the reader needs to migrate toward. A should retract this finding.

Missed issues

None detected. A scanned the new operational section, checked the frontmatter timestamp, and confirmed the commit message. The 'Things that bite at boot' section contains field-reported operational details; nothing in it is obviously contradicted by the framework source.

Verdict alignment

A's verdict ('comment') is consistent with the two valid findings (grep path, cflocation syntax). The problem is that A's most prominent finding is the false positive above, and the suggested rewrite would introduce an inaccuracy. The verdict label is fine; the justification for finding 1 is not.

Convergence

Not aligned. The false positive on finding 1 must be resolved before we proceed. If A retracts that finding and the remaining two (grep path, cflocation portability) stand, both are valid and actionable; a converged-changes outcome would follow. Until A acknowledges application.wheelsdi is a real Wheels 4.0 concept and withdraws the suggested rewrite, we remain unaligned.

The /v4-0-0-snapshot/ path on guides.wheels.dev currently 404s;
the canonical guide is served at /v4-0-0/. Replace nine references
across the body (upgrade guide, middleware pipeline, route model
binding, query builder, background jobs, packages).

SQLite source (post id 877) is in sync.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer A response (round 1)

B is correct on the false positive. I checked vendor/wheels/Injector.cfc:45 — the Injector self-registers at application.wheelsdi = this;, and vendor/wheels/tests/specs/cli/ApplicationCfcInjectorAssignmentSpec.cfc explicitly asserts that generated Application.cfc templates assign application.wheelsdi = new wheels.Injector(...) and route all getInstance calls through it. application.wheelsdi is a real, load-bearing Wheels 4.0 concept and the blog post's migration framing is accurate. I retract Finding 1 in its entirety, including the suggested rewrite. The two remaining findings (grep path silently missing the root-level Application.cfc, and the non-portable location tag-in-script shorthand) are still actionable and were not challenged by B.

Conceded points

  • Finding 1 (WireBox/wheelsdi framing): Retracted. application.wheelsdi is confirmed at vendor/wheels/Injector.cfc:45 and tested in vendor/wheels/tests/specs/cli/ApplicationCfcInjectorAssignmentSpec.cfc. The blog's sentence naming it as the canonical migration target for application.wirebox is accurate. My suggested rewrite would have removed the only concrete endpoint readers need to migrate toward. B was right to call this a false positive.

Defended points

  • Finding 2 (grep path): grep -rn "redirectTo(" app/events/ public/Application.cfc hardcodes a Titan-specific layout. Most Wheels apps place Application.cfc at the project root; readers following this verbatim will silently miss calls there. The fix (grep -rn "redirectTo(" app/events/ Application.cfc or a find-based variant) is still valid and was not disputed.

  • Finding 3 (cflocation portability): location url="..." addToken=false; without the cf prefix is not portable to Adobe CF and is not documented as the canonical shorthand even on Lucee. The cross-engine safe form is cflocation(url="...", addToken=false);. The post's own context is Lucee 7-specific, but since the post is framed as a general upgrade guide this nit is still worth noting. Not disputed by B.

Updated verdict

Comment — same label as the initial review, now resting on two actionable findings rather than three. Both (grep path and cflocation syntax) are worth a one-line author fix before the post becomes the definitive upgrade reference, but neither blocks merge.

Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer A

TL;DR: The new commit on this SHA (3fe402558) is a correct, focused link fix — swapping five stale /v4-0-0-snapshot/ paths to /v4-0-0/ across the deprecations and "where to go next" sections. No new issues introduced. Two findings from the previous review cycle remain unaddressed in the current file and are worth a one-line fix before this post becomes the definitive upgrade reference. Verdict: comment.


Correctness

Detection grep hardcodes a Titan-specific path and silently misses the standard layout.

File: web/content/blog/posts/upgrading-from-wheels-3x.md, line 46 (table) and line 65 (body)

grep -rn "redirectTo(" app/events/ public/Application.cfc

public/Application.cfc is the path Titan used; most Wheels apps place Application.cfc at the project root. A reader following this command verbatim will silently skip any redirectTo() call in their root-level file, miss the breaker entirely, and hit the runtime error in production.

Suggested fix (works for both layouts):

grep -rn "redirectTo(" app/events/ Application.cfc

Or defensively, to catch any depth:

grep -rn "redirectTo(" app/events/ $(find . -maxdepth 2 -name 'Application.cfc')

Cross-engine

location tag-in-script shorthand without cf prefix is not portable to Adobe CF.

File: web/content/blog/posts/upgrading-from-wheels-3x.md, line 72

// after
location url="/account/logOut" addToken=false;

location url="..." addToken=false; (without the cf prefix) is accepted by some Lucee versions but is not the documented canonical form and fails to parse on Adobe CF. The post's scenario is Lucee 7-specific, but the blog is framed as a general upgrade guide and readers on other engines may copy this snippet.

The cross-engine-safe cfscript form is:

// after (works on Lucee + Adobe CF)
cflocation(url="/account/logOut", addToken=false);

Commits

Both commits conform to commitlint.config.js:

  • docs(web/blog): revise Upgrading from Wheels 3.x with Titan cutover lessons — valid type, scope, subject
  • docs(web/blog): fix 404 links — v4-0-0-snapshot → v4-0-0 in upgrade post — valid type, scope, subject

The link fix in 3fe402558 is correct — all five /v4-0-0-snapshot//v4-0-0/ replacements in the deprecations and "where to go next" sections are accurate.


Neither finding blocks merge. Both are one-line author fixes before the post hardens into reference material.

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 13, 2026

Reviewer B round 1 test

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 13, 2026

Wheels Bot - Reviewer B (round 1)

A's review on this SHA is clean. The two remaining findings from the previous cycle are real, the link-fix commit assessment is accurate, and the verdict is consistent with the findings. Aligning on converged-changes.

Sycophancy

None detected. A carried two unresolved findings forward from the previous cycle rather than approving on the strength of the link fix, and used comment rather than inflating to approve.

False positives

None detected.

Grep path finding: The diff confirms the grep command targets app/events/ and public/Application.cfc at both the table row and the body paragraph. public/Application.cfc is not a standard Wheels layout - Application.cfc lives at the project root in a conventional Wheels app; public/ is the static-asset directory. A reader following the command verbatim silently skips any redirectTo() call at the root level. A's finding is accurate.

cflocation portability finding: The diff confirms the code block uses the location url=... form without the cf prefix. Tag-in-script without the cf prefix is not portable to Adobe CF. The canonical cross-engine safe form is cflocation(url=..., addToken=false). A's finding is accurate.

Link-fix assessment: A characterizes the new commit as swapping five stale v4-0-0-snapshot paths. The diff shows 9 link instances across 8 changed lines (6 unique paths). The undercount is immaterial - A correctly validated that the fix is accurate and no regressions were introduced.

Missed issues

None detected. The PR is a documentation-only change (blog post). No CFC code, no security surface, no cross-engine CFML concerns. Both commits conform to commitlint.config.js.

Verdict alignment

A's comment verdict is consistent with two concrete, actionable, non-blocking findings. Appropriate.

Convergence

Aligned. Both remaining findings are verified and actionable: (1) change public/Application.cfc to Application.cfc in the grep command, and (2) change the location tag-in-script shorthand to the portable cflocation(...) function-call form. Neither blocks merge, but both improve the accuracy of a post that will serve as a reference for 3.x upgraders. Joint recommendation: apply both one-line fixes.

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 13, 2026

Wheels Bot — Propose Fix

No triage or research comment was found on this item, and #2654 is an open PR rather than a triaged issue. The /propose-fix flow expects to operate on an issue with a wheels-bot:triage:<number> marker (and optionally wheels-bot:research:<number>) as the authoritative spec; without that context I have no sketch, layer, or recommended path to implement against, so I'm exiting without proposing a fix.

If a human wants the bot to take action on this PR, please re-target a real issue, or open a fresh issue describing the desired change and let the triage stage classify it first.

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 13, 2026

Wheels Bot -- Reviewer B (round 1)

A correctly retracted the wheelsdi false positive and has now defended two findings I did not dispute in round 1. Both check out against the diff. I am aligned on a converged-changes outcome.

Sycophancy

None detected. A explicitly verified the source file before retracting, and maintained the two remaining findings under challenge rather than caving.

False positives

None in the response. Both defended findings are substantiated:

  • Finding 2 (grep path): The diff confirms the table and body both use grep -rn "redirectTo(" app/events/ public/Application.cfc. Standard Wheels app layout has Application.cfc at the project root, not under public/. A reader on a standard install runs this verbatim and gets zero matches even if they have redirectTo() in their root Application.cfc. The diagnosis is correct.

  • Finding 3 (cflocation portability): The diff confirms the "after" snippet uses location url="/account/logOut" addToken=false; -- the Lucee-specific tag-in-script shorthand without the cf prefix. Cross-engine safe form is cflocation(url="...", addToken=false);. The blog framing is general ("Upgrading from Wheels 3.x", not "upgrading on Lucee 7 specifically"), so showing a Lucee-only snippet as the recommended fix is a real portability concern. The finding stands.

Missed issues

None detected. Commit messages (docs(web/blog): fix 404 links, docs(web/blog): revise Upgrading from Wheels 3.x with Titan cutover lessons) are both compliant with commitlint -- valid type, scope optional, under 100 chars, not ALL-CAPS. The v4-0-0-snapshot to v4-0-0 link corrections are accurate. No security concerns in a docs-only PR.

Verdict alignment

The comment verdict is consistent with findings that are actionable but non-blocking. Both fixes are one-liners and the diff is otherwise clean.

Convergence

Aligned on changes needed. Two concrete fixes:

  1. Change public/Application.cfc to Application.cfc in both the detection table and the body paragraph so readers on standard installs get a working grep.
  2. Change location url="/account/logOut" addToken=false; to cflocation(url="/account/logOut", addToken=false); in the "after" code block so the example is portable across engines.

Neither is a blocker, but both are worth landing before this post becomes the canonical 3.x to 4.0 field guide.

- Drop the non-standard `public/` prefix from the `redirectTo()` grep
  command in the detection table and body so readers on a standard
  Wheels install actually find matches in their root `Application.cfc`.
- Replace the Lucee-only tag-in-script `location url=...` snippet with
  the cross-engine portable `cflocation(url=..., addToken=false);`
  function-call form in the "after" code block, and adjust the framing
  text accordingly.

Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 13, 2026

Wheels Bot — Address Review (round 1)

Applied consensus findings from Reviewer A and Reviewer B's convergence (round 1):

  • Finding 2 (grep path): Changed grep -rn "redirectTo(" app/events/ public/Application.cfc to grep -rn "redirectTo(" app/events/ Application.cfc in both the detection table (web/content/blog/posts/upgrading-from-wheels-3x.md:46) and the body paragraph (web/content/blog/posts/upgrading-from-wheels-3x.md:65). A reader on a standard Wheels install now finds matches in the root Application.cfc rather than silently scanning a directory that does not exist in conventional layouts.
  • Finding 3 (cflocation portability): Changed the "after" snippet from the Lucee-only tag-in-script form location url="/account/logOut" addToken=false; to the cross-engine portable function-call form cflocation(url="/account/logOut", addToken=false); (web/content/blog/posts/upgrading-from-wheels-3x.md:72). Adjusted the framing sentence at line 65 from "(tag-in-script form)" to "(function-call form, portable across engines)" so the descriptor matches the snippet.

Skipped (not in consensus):

  • Reviewer A's withdrawn application.wheelsdi finding — B verified against vendor/wheels/Injector.cfc:45 that this is a real Wheels 4.0 concept and A retracted in round 1, so no change was needed.

This is a docs-only PR (web/content/blog/posts/upgrading-from-wheels-3x.md), so no test suite run was required.

The new commit will trigger a fresh Reviewer A run on the updated SHA. Convergence loop continues until reviewers align on approve or the outer-loop cap (5 rounds) is reached.

Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer A

TL;DR: The latest commit (0f469546d) correctly addresses both findings carried forward from the previous review cycle: the grep command in the detection table and body text now uses Application.cfc (root-level, standard layout) instead of public/Application.cfc (Titan-specific path), and the non-portable location url=...; tag-in-script snippet is replaced with the cross-engine-safe cflocation(url=..., addToken=false); function-call form. One new minor accuracy nit and one metadata nit are worth a one-line author fix before the post ages into reference material. Verdict: comment.


Correctness

java.lang.ClassException is not a real Java exception class.

File: web/content/blog/posts/upgrading-from-wheels-3x.md, line 147

The text reads: throws java.lang.ClassException: cannot load class ...

java.lang.ClassException does not exist in the standard Java API or in the OSGi/Lucee runtime. The actual exceptions thrown when the JVM cannot resolve a class from a JAR are:

  • java.lang.ClassNotFoundException — thrown by Class.forName() / ClassLoader.loadClass() when the class name is not found on the classpath
  • java.lang.NoClassDefFoundError — thrown at runtime when the class was present at compile time but cannot be found when the JVM links the call site

A reader whose error tracker shows ClassNotFoundException or NoClassDefFoundError and searches for ClassException will find nothing and may doubt whether this is the same failure. The Lucee OSGi error log typically surfaces these as ClassNotFoundException: <fqcn> or a bundle-activation error wrapping NoClassDefFoundError.

Suggested fix on line 147: replace java.lang.ClassException: cannot load class with a reference to ClassNotFoundException (from Class.forName / driver registration) or NoClassDefFoundError (if the JAR loaded but the class is linked at call time).


Docs

updatedAt was not bumped in the latest commit.

File: web/content/blog/posts/upgrading-from-wheels-3x.md, line 5

The stored value is updatedAt: 2026-05-13T15:07:50.000Z but the latest commit was authored at 2026-05-13T15:17:38Z — ten minutes later. If the blog system surfaces this timestamp to readers or in the Atom/RSS feed, the post will appear not to have changed since the link-fix commit. Minor, but worth a one-line bump before merge.


Commits

All three commits conform to commitlint.config.js:

  • docs(web/blog): address Reviewer A/B consensus findings (round 1) — valid type, scope, subject; addresses both prior findings accurately.
  • docs(web/blog): fix 404 links — v4-0-0-snapshot -> v4-0-0 in upgrade post — valid.
  • docs(web/blog): revise Upgrading from Wheels 3.x with Titan cutover lessons — valid.

Neither finding blocks merge. The ClassException name is the only factual inaccuracy in an otherwise solid field-tested guide.

The previous framing referenced "an AppSerial mismatch in onrequeststart.cfm
redirecting to /account/logOut" — that was a Titan-specific example that
leaked an internal detail. Rewrite for the canonical Wheels developer
audience: frame the scenario as detecting a re-auth condition (stale
session, server-side state change, maintenance flag) and interrupting
the request to send the user to the login form. Genericize the code
example to controller="sessions"/action="new" → /login.

SQLite source (post id 877) is in sync.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 13, 2026

Wheels Bot — Reviewer B (round 1)

A's two findings are both genuine accuracy issues — no false positives. However, A missed one grep-path defect in the new content that is structurally identical to the primary finding from the previous review cycle. Not aligned this round.

Sycophancy

None detected. A did not approve without evidence, carried forward concrete findings, and used comment rather than inflating to approve.

False positives

None detected.

  • Finding 1 (java.lang.ClassException, line 147): Verified in the current HEAD. The text reads throws \java.lang.ClassException: cannot load class …`. java.lang.ClassExceptiondoes not exist in the Java standard library. The real candidates arejava.lang.ClassNotFoundException(thrown byClass.forName()/ClassLoader.loadClass()) or java.lang.NoClassDefFoundError(thrown when the class is resolved at link time but missing at runtime), or possiblylucee.runtime.exp.ClassException` (Lucee's own wrapper). A correctly identifies the problem; the suggested replacements are accurate. Finding stands.

  • Finding 2 (updatedAt not bumped, line 5): Current HEAD has updatedAt: '2026-05-13T15:07:50.000Z'; the address-review commit landed at 2026-05-13T15:17:38Z. The gap is accurate as stated. Minor but factual.

Missed issues

Line 118 — WireBox grep searches the wrong directories.

The adapter-section text reads: "Apps that bootstrap WireBox directly in Application.cfc (the canonical 2.x pattern still common in older codebases) must rewrite that file before first boot…" The detection command immediately following is:

grep -rn "application.wirebox\|new wirebox.system" app/ public/

The canonical location for WireBox initialisation is Application.cfc at the project root, not under app/ or public/. A reader who runs this command verbatim will get zero matches even if their root Application.cfc bootstraps WireBox — the same silent-miss problem that was the primary finding in the previous cycle's review of the redirectTo() grep.

The fix is the same structural pattern as the previous consensus: replace app/ public/ with directories that include the project root, e.g. grep -rn "application.wirebox\|new wirebox.system" . --include="*.cfc" --include="*.cfm" or, more narrowly, grep -rn "application.wirebox\|new wirebox.system" app/ Application.cfc.

Verdict alignment

A's comment verdict is consistent with findings that are real but non-blocking. Correct verdict choice.

Convergence

Not aligned. The WireBox grep path at line 118 is a concrete missed issue that A needs to acknowledge. If A concedes it, the three combined findings (ClassException, updatedAt, WireBox grep) are all actionable one-liners and a converged-changes outcome follows. Until A responds to the grep-path issue, we remain unaligned.

Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer A

TL;DR: The latest commit (a20e257a3) delivers exactly what its message claims — the redirectTo() example is now framed for the canonical Wheels developer audience rather than leaking Titan-internal details (AppSerial mismatch, /account/logOut). The genericized scenario (stale session, re-auth condition, maintenance flag) and the updated cflocation() code example are accurate and portable. One unresolved finding from the previous review cycle (java.lang.ClassException) was not addressed in this commit; one new minor nit (missing path prefix on onapplicationstart.cfm) surfaced in the changed section. Verdict: comment — no blocking issues, but the ClassException name is a factual inaccuracy that will mislead readers searching error logs.


Correctness

java.lang.ClassException remains unfixed from the previous review cycle.

File: web/content/blog/posts/upgrading-from-wheels-3x.md, line 147

throws `java.lang.ClassException: cannot load class …`

This was flagged in the 0f469546d review round and was not addressed in the current commit. java.lang.ClassException does not exist in the Java standard library. The two real exceptions a reader will see in this scenario are:

  • java.lang.ClassNotFoundException — thrown by ClassLoader.loadClass() / Class.forName() when the class cannot be found on the classpath
  • java.lang.NoClassDefFoundError — thrown at link time when the class was on the classpath at build time but is absent at runtime

A reader whose error tracker shows ClassNotFoundException and searches for ClassException will find nothing and may doubt whether this is the same failure. The fix is a one-word change: replace ClassException with ClassNotFoundException (the more common case for missing JARs during driver registration).


Docs

onapplicationstart.cfm missing its app/events/ path prefix on line 61.

File: web/content/blog/posts/upgrading-from-wheels-3x.md, line 61

Calls from request-lifecycle event handlers (`app/events/onrequeststart.cfm`, `onapplicationstart.cfm`) throw …

onrequeststart.cfm is correctly qualified with its directory (app/events/), but onapplicationstart.cfm is listed without any path. Both files live in app/events/ (confirmed at app/events/onapplicationstart.cfm). A reader skimming the text will have to guess where the second file lives. Suggest: app/events/onrequeststart.cfm, app/events/onapplicationstart.cfm.


Commits

All four commits in this PR conform to commitlint.config.js:

  • docs(web/blog): rewrite redirectTo() example in developer voice — valid type, scope, subject; change is well-scoped and accurate.
  • docs(web/blog): address Reviewer A/B consensus findings (round 1) — valid.
  • docs(web/blog): fix 404 links — v4-0-0-snapshot → v4-0-0 in upgrade post — valid.
  • docs(web/blog): revise Upgrading from Wheels 3.x with Titan cutover lessons — valid.

Neither finding blocks merge. The ClassException name is the only factual inaccuracy remaining in an otherwise solid, field-tested guide.

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 13, 2026

Wheels Bot — Reviewer B (round 1)

A's two findings are accurate and actionable. However, A missed one grep-path defect that is structurally identical to the primary finding from the previous cycle — the WireBox detection command silently skips the canonical location of the code it's trying to find. Not aligned this round.

Sycophancy

None detected. A used comment rather than inflating to approve, and carried the still-unfixed ClassException forward from the previous cycle rather than letting it slide.

False positives

None detected.

  • Finding 1 (java.lang.ClassException, diff line 98): Confirmed in the diff. The Things that bite at boot paragraph reads: throws \java.lang.ClassException: cannot load class …`. java.lang.ClassExceptiondoes not exist in the Java standard library. The real exceptions for a missing classpath JAR arejava.lang.ClassNotFoundException(thrown byClassLoader.loadClass()) and java.lang.NoClassDefFoundError` (thrown at link time). A's diagnosis and suggested fix are correct.

  • Finding 2 (onapplicationstart.cfm missing path prefix, diff line 51): Confirmed. The text reads app/events/onrequeststart.cfm, onapplicationstart.cfm — the second file is listed without its app/events/ directory prefix. Both live in app/events/ per the Wheels project layout in CLAUDE.md. Finding is accurate.

Missed issues

WireBox grep targets wrong directories (diff line 72).

The adapter-limits paragraph ends with:

grep -rn "application.wirebox\|new wirebox.system" app/ public/

Application.cfc — the canonical location for WireBox bootstrap code — lives at the project root, not under app/ or public/. A reader on a standard Wheels install runs this command verbatim and gets zero matches even if their root Application.cfc bootstraps WireBox. This is the same silent-miss defect as the redirectTo() grep that was the primary finding in the previous review cycle (fixed in 0f469546d) and the same defect B flagged in round 1 of SHA 0f469546d but which was not carried into the address-review consensus before a20e257a was pushed.

The fix follows the same pattern as the previous consensus: replace app/ public/ with a scope that includes the project root, e.g. grep -rn "application.wirebox\|new wirebox.system" app/ Application.cfc.

Verdict alignment

A's comment verdict is consistent with findings that are real but non-blocking. Appropriate.

Convergence

Not aligned. The WireBox grep path at diff line 72 is a concrete missed issue A needs to address. If A concedes it, the three combined findings (ClassException, onapplicationstart.cfm path, WireBox grep) are all one-liners and a converged-changes outcome follows. Until A responds to the grep-path issue, we remain unaligned.

@bpamiri bpamiri merged commit 660281f into develop May 13, 2026
13 checks passed
@bpamiri bpamiri deleted the peter/blog-upgrading-from-3x-titan-lessons branch May 13, 2026 15:31
Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer A response (round 1)

B raised one missed issue (WireBox grep path) and confirmed both of A's existing findings as genuine. I concede the missed issue: B is correct that grep -rn "application.wirebox\|new wirebox.system" app/ public/ silently misses Application.cfc at the project root — the canonical location for WireBox bootstrap in a standard Wheels app. This is structurally identical to the redirectTo() grep defect fixed in the previous cycle. Both of my existing findings (java.lang.ClassException, onapplicationstart.cfm path prefix) were confirmed by B and stand. Updated verdict: comment, with all three findings now confirmed. Converged-changes outcome follows if B aligns.

Conceded points

  • B's missed issue (WireBox grep path, diff line 118): Verified in the diff. The adapter-limits paragraph reads: If grep -rn "application.wirebox\|new wirebox.system" app/ public/ finds anything, plan the bootstrap rewrite up front. The canonical location for WireBox bootstrap code in a Wheels app is Application.cfc at the project root — not under app/ or public/. A reader on a standard install runs this command verbatim and gets zero matches even when their root Application.cfc bootstraps WireBox. The fix follows the same pattern as the prior consensus: grep -rn "application.wirebox\|new wirebox.system" app/ Application.cfc.

Defended points

  • Finding 1 (java.lang.ClassException, line 147): B confirmed this finding as accurate. java.lang.ClassException does not exist in the Java standard library or standard OSGi runtime. The actual exceptions a reader will see in error logs are java.lang.ClassNotFoundException (thrown by ClassLoader.loadClass() / Class.forName()) and java.lang.NoClassDefFoundError (thrown at link time when the class is absent at runtime). A reader whose error tracker shows ClassNotFoundException and searches for ClassException finds no match and may doubt whether this is the right section. Fix is a one-word change.

  • Finding 2 (onapplicationstart.cfm missing path prefix, line 61): B confirmed this finding as accurate. The diff line reads app/events/onrequeststart.cfm, onapplicationstart.cfm — the second file is listed without its app/events/ directory prefix. Both live in app/events/ per the CLAUDE.md directory layout. Fix: qualify both consistently as app/events/onrequeststart.cfm, app/events/onapplicationstart.cfm.

Updated verdict

Comment — same as initial review, now with three confirmed actionable findings: (1) correct java.lang.ClassException to ClassNotFoundException (or NoClassDefFoundError) at line 147, (2) add app/events/ prefix to the bare onapplicationstart.cfm reference at line 61, (3) change the WireBox grep from app/ public/ to app/ Application.cfc at line 118. All three are one-liners; none block merge. A converged-changes outcome follows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant