Repository navigation
feat(engine): recognize RustCFML + degrade caching when cfcache is absent - #2837
Conversation
…sent
Adds RustCFML to the engine-adapter abstraction so Wheels boots on it instead
of rejecting it as an unsupported "Adobe ColdFusion 0.x", and makes the
cfcache-backed template/static cache degrade gracefully (no-op) on engines that
don't implement the cfcache built-in.
- onapplicationstart.cfc: detect RustCFML via server.coldfusion.productName
("RustCFML"; it exposes no server.lucee/server.boxlang) and instantiate a
RustCFMLAdapter, ordered before the Adobe fallback.
- engineAdapters/RustCFML/RustCFMLAdapter.cfc (new): extends Base (whose
defaults are Lucee-shaped, matching RustCFML's semantics); isRustCFML()=true,
supportsCfcache()=false.
- engineAdapters/Base.cfc: add isRustCFML() (false) + a supportsCfcache()
capability (true) — the seam for tracking per-engine divergences.
- Global.cfc $checkMinimumVersion: accept RustCFML (pre-1.0, evolving) rather
than failing the version guard.
- Global.cfc $cache(): when the adapter reports supportsCfcache()=false, no-op
and leave reCache=true so requests still render cacheless. On every JVM engine
supportsCfcache() is true, so behavior is unchanged.
Additive and engine-gated: the new branches only match RustCFML and the $cache
guard is inert on Lucee/Adobe/BoxLang. Verified on Lucee 7 (clean boot + core
model suite 843/843 — every changed path runs at boot). Full matrix runs in CI.
Signed-off-by: Peter Amiri <peter@alurium.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR This PR correctly adds RustCFML recognition via a new RustCFMLAdapter, inserts it into the detection chain in onapplicationstart.cfc before the Adobe ColdFusion fallback, and gates the $cache() cfcache call behind a supportsCfcache() capability — all clean, additive, and following the established adapter pattern. However, two existing tests in engineAdapterSpec.cfc would fail when executed on a RustCFML instance (the engine-name allowlist and the identity-count spec), and neither is updated by the PR; those are correctness issues that should be fixed before merge. Requesting changes.
Correctness
engineAdapterSpec.cfc:12 — getName() assertion rejects RustCFML
// vendor/wheels/tests/specs/engineAdapterSpec.cfc line 12
var name = application.wheels.engineAdapter.getName();
expect(ListFind("Lucee,Adobe ColdFusion,BoxLang", name)).toBeGT(0);ListFind("Lucee,Adobe ColdFusion,BoxLang", "RustCFML") returns 0. On RustCFML this spec will throw a failed expectation. The PR introduces first-class RustCFML support but doesn't update the allowlist. Suggested fix:
expect(ListFind("Lucee,Adobe ColdFusion,BoxLang,RustCFML", name)).toBeGT(0);engineAdapterSpec.cfc:76-83 — identity count doesn't include isRustCFML()
// vendor/wheels/tests/specs/engineAdapterSpec.cfc lines 77-83
var count = 0;
if (adapter.isLucee()) count++;
if (adapter.isAdobe()) count++;
if (adapter.isBoxLang()) count++;
expect(count).toBe(1);On a RustCFML instance all three predicates return false → count == 0 → the assertion fails. Suggested fix: add if (adapter.isRustCFML()) count++; to the list, and add a parallel identity guard below (matching the "identity matches engine name for …" pattern already there for Lucee / Adobe / BoxLang).
Neither failure shows up in CI today because the matrix doesn't include a RustCFML runner, but they would greet anyone who first points CI at RustCFML — which is the whole motivation for this PR.
Tests
Beyond the two broken specs above:
-
internalSpec.cfc— no$checkMinimumVersioncase for RustCFML. The function already has unit tests for Railo, Lucee, Adobe (lines 139-182). A singleitassertingLen($checkMinimumVersion(engine="RustCFML", version="0.41.0")) == 0(any version is accepted) would pin the "accept-all" behaviour and prevent it from being accidentally broken. -
No capability tests.
Base.supportsCfcache()returningtrueandRustCFMLAdapter.supportsCfcache()returningfalseare new API surface. A describe block inengineAdapterSpec.cfccovering both would be small and valuable:
describe("Engine Adapter - Capabilities", function() {
it("supportsCfcache returns true on non-RustCFML engines", function() {
var base = new wheels.engineAdapters.Base("7.0.0");
expect(base.supportsCfcache()).toBeTrue();
});
it("RustCFMLAdapter.supportsCfcache returns false", function() {
var rustAdapter = new wheels.engineAdapters.RustCFML.RustCFMLAdapter("0.41.0");
expect(rustAdapter.supportsCfcache()).toBeFalse();
});
});Docs
-
CHANGELOG.md [Unreleased]has no entry for this change. The commit isfeat(engine):; per project conventions a### Addedbullet under[Unreleased]is expected — e.g. "RustCFML is now recognized as a first-class engine; Wheels boots and runs cacheless-but-working on RustCFML by degrading its cfcache-backed template cache to a no-op whencfcacheis not implemented." -
.ai/wheels/cross-engine-compatibility.mddoesn't mention RustCFML. The doc currently lists the four engines (Lucee 5/6/7, Adobe CF 2018-2025, BoxLang) in its header. A brief note that RustCFML is supported on a best-effort basis — and that the confirmed divergence is the missingcfcachebuilt-in — would keep the reference accurate for future contributors.
Informational (pre-existing, not introduced by this PR)
$checkMinimumVersion at vendor/wheels/Global.cfc:3016 and :3021 contains two dead else if (arguments.engine == "Adobe ColdFusion") branches. Because the condition is identical to the branch at :3011, they can never execute. This is a pre-existing issue unrelated to RustCFML; flagging for awareness only.
Everything else
The core changes are well-executed:
- Detection order in
onapplicationstart.cfcis correct: BoxLang → Lucee → RustCFML → Adobe ColdFusion. The comment explaining why RustCFML must precede the Adobe fallback is clear. $cache()guard ($hasEngineAdapter() && !$engineAdapter().supportsCfcache()) is safe: the$hasEngineAdapter()short-circuit means the check is inert before the adapter is wired up, and the adapter IS wired up (lines 59-67) well before the only$cache()call inonapplicationstart.cfc(line 341).Base.supportsCfcache()returningtruemeans the guard is a no-op on Lucee / Adobe / BoxLang — the original cfcache path runs unchanged.RustCFMLAdapteris minimal, documented, and follows the establishedextends="wheels.engineAdapters.Base"pattern. Thevariables.engineName = "RustCFML"class variable + noinit()override is consistent withLuceeAdapterandAdobeAdapter.- Commit message conforms to
commitlint.config.js.
Wheels Bot — Reviewer B (round 1)A's review is solid — the two identified test failures are real, the verdict (CHANGES_REQUESTED) is appropriate, and the informational note on pre-existing dead branches is correctly scoped as non-blocking. However, A scanned SycophancyNone detected. False positivesNone detected. Both claims A made about A's path for the Missed issues
it("returns a valid major version", function() {
expect(application.wheels.engineAdapter.getMajorVersion()).toBeGT(0);
});
The fix should either guard on Verdict alignmentA's ConvergenceNot aligned yet. A identified 2 of 3 failing
|
There was a problem hiding this comment.
Wheels Bot — Reviewer A response (round 1)
B's only challenge was a missed issue: engineAdapterSpec.cfc:20-22's getMajorVersion() > 0 assertion also fails on RustCFML. After reading Base.cfc lines 12 and 16, the computation is variables.engineMajorVersion = Val(ListFirst(arguments.version, ".,")). For RustCFML at 0.41.0, that resolves to Val("0") = 0, making 0 > 0 false — test fails. B is correct and I concede the point. B detected no false positives and agrees on the CHANGES_REQUESTED verdict. Consensus is now complete: 7-item change list, verdict unchanged.
Conceded points
engineAdapterSpec.cfc:20-22—getMajorVersion() > 0fails on RustCFML (B's addition).Base.cfc:16setsvariables.engineMajorVersion = Val(ListFirst(arguments.version, ".,")). For0.41.0that isVal("0") = 0;0 > 0is false. I applied the same "would greet anyone who first points CI at RustCFML" standard to the two failures I found and should have caught this one by the same logic. The fix should either guard onadapter.isRustCFML()(matching the identity-guard pattern in the"identity matches engine name for …"tests below) or relax the assertion to>= 0with a comment noting that pre-1.0 engines legitimately return major version 0.
Defended points
- None required. B raised no false-positive claims against my findings. B's note that I cited
internalSpec.cfcrather than the full pathvendor/wheels/tests/specs/global/internalSpec.cfcis accurate as a cosmetic slip; the underlying finding is correct and I stand by it.
Updated verdict
Still CHANGES_REQUESTED — the complete consensus change list is now B's seven items, with item 3 (engineAdapterSpec.cfc:20-22) confirmed by this round. The verdict does not change; we are aligned.
…ability Addresses the wheels-bot review on #2837. The PR adds first-class RustCFML recognition but several existing specs and docs hadn't caught up: - engineAdapterSpec: add RustCFML to the getName() allowlist; include isRustCFML() in the exactly-one-identity count + a parallel identity guard; guard the major-version assertion (pre-1.0 engines like RustCFML 0.x legitimately report major 0, so >= 0 for RustCFML, > 0 otherwise). - engineAdapterSpec: new Capabilities describe block covering the new API surface — Base.supportsCfcache()=true and RustCFMLAdapter.supportsCfcache() =false / isRustCFML()=true. - internalSpec: pin $checkMinimumVersion's accept-any-version behavior for RustCFML (returns empty across 0.41.0 / 0.50.0 / 1.0.0). - CHANGELOG [Unreleased] ### Added entry for the RustCFML engine support. - cross-engine-compatibility.md: note RustCFML best-effort support + the cfcache divergence. Full core suite green on Lucee 7 + SQLite (3794 passed, 0 fail, 0 error); the 5 new/changed specs all execute and pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Peter Amiri <peter@alurium.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR This follow-up commit addresses every item from my previous CHANGES_REQUESTED review cleanly. All seven change-list items are resolved: the engine-name allowlist, the identity-count spec, the getMajorVersion() > 0 false-failure for pre-1.0 engines, the $checkMinimumVersion RustCFML test, the capability specs, the CHANGELOG entry, and the .ai/wheels/cross-engine-compatibility.md note. The implementation itself was already sound; only the test coverage and docs were lagging. With those now in place, approving.
Resolved from prior round
All seven items from <!-- wheels-bot:review-a:2837:2e04decf9b898cbfcc7d5bda692f24b5d3cfa541 --> are resolved:
engineAdapterSpec.cfc:13—"RustCFML"added to thegetName()allowlist. ✓engineAdapterSpec.cfc:87—if (adapter.isRustCFML()) count++added to the exactly-one-identity spec. ✓engineAdapterSpec.cfc:20-26—getMajorVersion()assertion now conditionally usestoBeGTE(0)for pre-1.0 engines (B's addition, conceded last round). ✓internalSpec.cfc:185-190— Newitpinning the accept-any-version behavior for 0.41.0 / 0.50.0 / 1.0.0. ✓engineAdapterSpec.cfc:130-143— New"Engine Adapter - Capabilities"describe block coveringBase.supportsCfcache()=trueandRustCFMLAdapter.supportsCfcache()=false+isRustCFML()=true. ✓CHANGELOG.md:25—### Addedbullet under[Unreleased]is present and accurate. ✓.ai/wheels/cross-engine-compatibility.md:5— RustCFML best-effort note (with confirmedcfcachedivergence and known remaining blockers) added at the top of the file. ✓
Fresh pass — nothing new found
Verified on the current diff:
$cache()early-return guard ($hasEngineAdapter() && !$engineAdapter().supportsCfcache()) is correctly placed afterreCache = false(line 111) and before thecfcachecall; net effect isreCache = trueon every RustCFML request — cacheless-but-rendering. The$hasEngineAdapter()short-circuit means the guard is inert during startup before the adapter is wired up (lines 59-67 ofonapplicationstart.cfcset the adapter well before any$cache()call can be reached at line 341).onapplicationstart.cfc:50-51readsserver.coldfusion.productVersionwithout aStructKeyExistsguard, but this is identical to the existing Adobe fallback at line 54, and the PR description confirms RustCFML exposes that field (the pre-fix classification was "Adobe ColdFusion 0.x", meaning the version was read successfully). No new risk.$checkMinimumVersionRustCFML branch atGlobal.cfc:3026-3031setslocal.rv = ""and leaveslocal.minimumMajorunset; the downstreamStructKeyExists(local, "minimumMajor")guard at line 3035 is therefore skipped and""is returned —Len("") == 0in the caller, version guard bypassed. Correct.- No
privatemixin functions, noobj.map()collisions, noLeft(str, 0), no Adobe 2023attributeCollection-with-argumentsissues introduced. - Commit message conforms to
commitlint.config.js; DCO sign-off (Signed-off-by: Peter Amiri <peter@alurium.com>) present on the new commit.
Informational nit (no action required)
engineAdapterSpec.cfc:132 — the it-description reads "non-RustCFML engines (Base default)" but the test instantiates Base directly rather than a live engine adapter. The test is correct and the intent is clear; renaming to something like "Base default is true (all non-RustCFML adapters inherit this)" would be marginally more precise, but this is purely cosmetic.
Wheels Bot — Reviewer B (round 1)A's review is clean. Every one of the seven consensus items from round 1 is present in the diff, A's fresh-pass verification claims are accurate, and the approval is warranted. SycophancyNone detected. A itemized each resolved finding with file and line references and performed an independent fresh-pass scan before approving. False positivesNone detected. Verified A's four fresh-pass claims against the actual code:
Missed issuesNone detected. Rescanned the diff against the CLAUDE.md anti-pattern list and cross-engine invariants:
Verdict alignmentA's ConvergenceAligned. A and B agree: the implementation is correct and all review items are addressed. The PR is review-clean on this SHA. |
…n-browser-login-as-set Resolve CHANGELOG.md [Unreleased] Added conflict by keeping both the RustCFML engine-recognition entry (#2837) and the browserLoginAsHandler fixture-override entry (#2830). Brings in #2831/#2853, which repair the CLI test suite masking (the prior 605/5/-8 result). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Peter Amiri <peter@alurium.com>
What
First step toward running Wheels on RustCFML (the JVM-free, Rust-based CFML runtime). Two additive, engine-gated changes:
server.coldfusion.productName = "RustCFML"and exposes noserver.lucee/server.boxlang, so today Wheels misclassifies it as "Adobe ColdFusion 0.x" and the$checkMinimumVersionguard rejects it atonApplicationStart. This adds detection + aRustCFMLAdapterto the existing engine-adapter layer, and accepts it in the version guard.cfcache-backed cache gracefully. RustCFML doesn't implement thecfcachebuilt-in. A newsupportsCfcache()capability on the engine adapter lets$cache()no-op (leavingreCache=trueso requests still render) instead of erroring at boot. The framework runs cacheless-but-working.How
events/onapplicationstart.cfc— detect RustCFML (ordered before the Adobe fallback) + instantiateRustCFMLAdapter.engineAdapters/RustCFML/RustCFMLAdapter.cfc(new) — extendsBase(whose defaults are Lucee-shaped, matching RustCFML's semantics);isRustCFML()=true,supportsCfcache()=false.engineAdapters/Base.cfc—isRustCFML()(false) +supportsCfcache()(true). ThesupportsCfcache()capability is the seam for tracking per-engine divergences going forward.Global.cfc—$checkMinimumVersionaccepts RustCFML;$cache()guards onsupportsCfcache().Why it's safe for existing engines
Every change is additive and engine-gated:
Base.supportsCfcache()returnstrue, so the$cache()guard is inert on Lucee/Adobe/BoxLang — the originalcfcachepath runs exactly as before.Basemethods are purely additive.Verification
tools/test-local.sh model. Because all changes are boot-level, a clean boot exercises every one of them (engine detection,$checkMinimumVersion, theonApplicationStart$cache(action="flush")through the new guard, and adapter instantiation) withsupportsCfcache()=true.cfcachewall (it previously died at both).Scope / follow-ups
This is engine recognition + cacheless degradation — not a claim that Wheels fully renders on RustCFML yet. Further runtime divergences exist past this point (e.g. RustCFML's
cfdirectoryfunction-call form, filed upstream) and are being worked through separately. Companion to the cross-engine test PRs contributed to RustCFML itself.