diff --git a/.github/workflow-results/test-lucee7-mysql.md b/.github/workflow-results/test-lucee7-mysql.md
index 14115da90e..13a9ea6da9 100644
--- a/.github/workflow-results/test-lucee7-mysql.md
+++ b/.github/workflow-results/test-lucee7-mysql.md
@@ -1,15 +1,15 @@
# Workflow Results: Test Lucee 7 + MySQL
**Status:** PASSED
-**Run:** [#18](https://github.com/wheels-dev/wheels/actions/runs/22270240179)
-**Commit:** ff8fe7c99be289e1e15b11cfb867e0b1c2fe7b24
-**Branch:** claude/fix-pr-1891-workflow-pGc9Z
-**Date:** 2026-02-22 04:18:58 UTC
+**Run:** [#23](https://github.com/wheels-dev/wheels/actions/runs/26032173536)
+**Commit:** 6b6d1acc0e60fe9cb42d3d731cd80a1d1a6f4829
+**Branch:** claude/evaluate-rustcfml-feasibility-40oUw
+**Date:** 2026-05-18 12:03:32 UTC
## Test Results
```
-Bundle: wheels.tests_testbox.specs.BasicsSpec
+Bundle: wheels.tests.specs.BasicsSpec
CFML Engine: Lucee 7.0.1.100
Duration: 7ms
Labels:
@@ -20,9 +20,20 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.cachingSpec
+Bundle: wheels.tests.specs.channel.DatabaseAdapterSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 102ms
+Duration: 203ms
+Labels:
+╔═══════════════════════════════════════════════════════════╗
+║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
+╠═══════════════════════════════════════════════════════════╣
+║ 1 ║ 8 ║ 8 ║ 0 ║ 0 ║ 0 ║
+╚═══════════════════════════════════════════════════════════╝
+
+
+Bundle: wheels.tests.specs.controller.cachingSpec
+CFML Engine: Lucee 7.0.1.100
+Duration: 76ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -31,9 +42,20 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.csrf.cookieSpec
+Bundle: wheels.tests.specs.controller.channelSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 298ms
+Duration: 88ms
+Labels:
+╔═══════════════════════════════════════════════════════════╗
+║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
+╠═══════════════════════════════════════════════════════════╣
+║ 3 ║ 22 ║ 22 ║ 0 ║ 0 ║ 0 ║
+╚═══════════════════════════════════════════════════════════╝
+
+
+Bundle: wheels.tests.specs.controller.csrf.cookieSpec
+CFML Engine: Lucee 7.0.1.100
+Duration: 277ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -42,9 +64,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.csrf.sessionSpec
+Bundle: wheels.tests.specs.controller.csrf.sessionSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 219ms
+Duration: 211ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -53,9 +75,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.filtersSpec
+Bundle: wheels.tests.specs.controller.filtersSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 68ms
+Duration: 61ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -64,9 +86,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.flashSpec
+Bundle: wheels.tests.specs.controller.flashSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 132ms
+Duration: 152ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -75,9 +97,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.initializationSpec
+Bundle: wheels.tests.specs.controller.initializationSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 41ms
+Duration: 43ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -86,9 +108,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.miscellaneousSpec
+Bundle: wheels.tests.specs.controller.miscellaneousSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 188ms
+Duration: 146ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -97,9 +119,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.providesSpec
+Bundle: wheels.tests.specs.controller.providesSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 64ms
+Duration: 43ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -108,9 +130,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.redirectionSpec
+Bundle: wheels.tests.specs.controller.redirectionSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 99ms
+Duration: 68ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -119,9 +141,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.renderingSpec
+Bundle: wheels.tests.specs.controller.renderingSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 1126ms
+Duration: 955ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -130,9 +152,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.requestSpec
+Bundle: wheels.tests.specs.controller.requestSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 23ms
+Duration: 28ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -141,9 +163,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.sseSpec
+Bundle: wheels.tests.specs.controller.sseSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 46ms
+Duration: 52ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -152,9 +174,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.controller.verifiesSpec
+Bundle: wheels.tests.specs.controller.verifiesSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 30ms
+Duration: 40ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -163,9 +185,20 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.dispatch.createParamsSpec
+Bundle: wheels.tests.specs.di.InjectorSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 17ms
+Duration: 44ms
+Labels:
+╔═══════════════════════════════════════════════════════════╗
+║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
+╠═══════════════════════════════════════════════════════════╣
+║ 8 ║ 25 ║ 25 ║ 0 ║ 0 ║ 0 ║
+╚═══════════════════════════════════════════════════════════╝
+
+
+Bundle: wheels.tests.specs.dispatch.createParamsSpec
+CFML Engine: Lucee 7.0.1.100
+Duration: 19ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -174,9 +207,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.dispatch.findMatchingRouteMegaSpec
+Bundle: wheels.tests.specs.dispatch.findMatchingRouteMegaSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 741ms
+Duration: 859ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -185,9 +218,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.dispatch.findMatchingRouteSpec
+Bundle: wheels.tests.specs.dispatch.findMatchingRouteSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 35ms
+Duration: 25ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -196,7 +229,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.dispatch.getrequestmethodSpec
+Bundle: wheels.tests.specs.dispatch.getrequestmethodSpec
CFML Engine: Lucee 7.0.1.100
Duration: 5ms
Labels:
@@ -207,9 +240,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.dispatch.requestSpec
+Bundle: wheels.tests.specs.dispatch.requestSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 9ms
+Duration: 8ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -218,9 +251,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.dispatch.setCorsHeadersSpec
+Bundle: wheels.tests.specs.dispatch.setCorsHeadersSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 1ms
+Duration: 0ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -229,7 +262,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.environment.ipbasedaccessSpec
+Bundle: wheels.tests.specs.environment.ipbasedaccessSpec
CFML Engine: Lucee 7.0.1.100
Duration: 4ms
Labels:
@@ -240,9 +273,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.events.onerrorSpec
+Bundle: wheels.tests.specs.events.onerrorSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 237ms
+Duration: 246ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -251,7 +284,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.global.cachingSpec
+Bundle: wheels.tests.specs.global.cachingSpec
CFML Engine: Lucee 7.0.1.100
Duration: 5ms
Labels:
@@ -262,9 +295,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.global.dbinfoSpec
+Bundle: wheels.tests.specs.global.dbinfoSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 84ms
+Duration: 66ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -273,9 +306,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.global.internalSpec
+Bundle: wheels.tests.specs.global.internalSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 60ms
+Duration: 62ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -284,9 +317,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.global.listcleanSpec
+Bundle: wheels.tests.specs.global.listcleanSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 6ms
+Duration: 3ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -295,9 +328,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.global.publicSpec
+Bundle: wheels.tests.specs.global.publicSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 42ms
+Duration: 46ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -306,9 +339,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.global.stringsSpec
+Bundle: wheels.tests.specs.global.stringsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 28ms
+Duration: 17ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -317,9 +350,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.global.urlforSpec
+Bundle: wheels.tests.specs.global.urlforSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 45ms
+Duration: 32ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -328,7 +361,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.internal.model.validationsSpec
+Bundle: wheels.tests.specs.internal.model.validationsSpec
CFML Engine: Lucee 7.0.1.100
Duration: 31ms
Labels:
@@ -339,9 +372,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.jobs.JobQueueSpec
+Bundle: wheels.tests.specs.jobs.JobQueueSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 63ms
+Duration: 126ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -350,7 +383,18 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapper.MapperSpec
+Bundle: wheels.tests.specs.jobs.JobWorkerSpec
+CFML Engine: Lucee 7.0.1.100
+Duration: 101ms
+Labels:
+╔═══════════════════════════════════════════════════════════╗
+║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
+╠═══════════════════════════════════════════════════════════╣
+║ 8 ║ 33 ║ 33 ║ 0 ║ 0 ║ 0 ║
+╚═══════════════════════════════════════════════════════════╝
+
+
+Bundle: wheels.tests.specs.mapper.MapperSpec
CFML Engine: Lucee 7.0.1.100
Duration: 11ms
Labels:
@@ -361,9 +405,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapper.MappingSpec
+Bundle: wheels.tests.specs.mapper.MappingSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 9ms
+Duration: 11ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -372,9 +416,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapper.MatchingSpec
+Bundle: wheels.tests.specs.mapper.MatchingSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 39ms
+Duration: 47ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -383,9 +427,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapper.NestedResourcesSpec
+Bundle: wheels.tests.specs.mapper.NestedResourcesSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 101ms
+Duration: 97ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -394,7 +438,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapper.ResourcesSpec
+Bundle: wheels.tests.specs.mapper.ResourcesSpec
CFML Engine: Lucee 7.0.1.100
Duration: 31ms
Labels:
@@ -405,9 +449,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapper.RootSpec
+Bundle: wheels.tests.specs.mapper.RootSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 7ms
+Duration: 5ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -416,7 +460,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapper.UtilsSpec
+Bundle: wheels.tests.specs.mapper.UtilsSpec
CFML Engine: Lucee 7.0.1.100
Duration: 15ms
Labels:
@@ -427,7 +471,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapper.WildcardSpec
+Bundle: wheels.tests.specs.mapper.WildcardSpec
CFML Engine: Lucee 7.0.1.100
Duration: 17ms
Labels:
@@ -438,9 +482,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapperModernSpec
+Bundle: wheels.tests.specs.mapperModernSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 50ms
+Duration: 64ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -449,9 +493,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.mapperSpec
+Bundle: wheels.tests.specs.mapperSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 125ms
+Duration: 124ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -460,9 +504,31 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.migrator.migration.mysqlTextSizesSpec
+Bundle: wheels.tests.specs.middleware.MultiTenantIntegrationSpec
+CFML Engine: Lucee 7.0.1.100
+Duration: 96ms
+Labels:
+╔═══════════════════════════════════════════════════════════╗
+║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
+╠═══════════════════════════════════════════════════════════╣
+║ 1 ║ 9 ║ 9 ║ 0 ║ 0 ║ 0 ║
+╚═══════════════════════════════════════════════════════════╝
+
+
+Bundle: wheels.tests.specs.middleware.TenantResolverSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 222ms
+Duration: 9ms
+Labels:
+╔═══════════════════════════════════════════════════════════╗
+║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
+╠═══════════════════════════════════════════════════════════╣
+║ 5 ║ 10 ║ 10 ║ 0 ║ 0 ║ 0 ║
+╚═══════════════════════════════════════════════════════════╝
+
+
+Bundle: wheels.tests.specs.migrator.migration.mysqlTextSizesSpec
+CFML Engine: Lucee 7.0.1.100
+Duration: 213ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -471,9 +537,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.migrator.migrationSpec
+Bundle: wheels.tests.specs.migrator.migrationSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 1021ms
+Duration: 920ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -482,9 +548,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.migrator.migratorSpec
+Bundle: wheels.tests.specs.migrator.migratorSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 426ms
+Duration: 299ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -493,9 +559,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.associationsSpec
+Bundle: wheels.tests.specs.model.associationsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 605ms
+Duration: 600ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -504,9 +570,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.calculationsSpec
+Bundle: wheels.tests.specs.model.calculationsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 189ms
+Duration: 117ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -515,9 +581,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.callbacksSpec
+Bundle: wheels.tests.specs.model.callbacksSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 429ms
+Duration: 297ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -526,9 +592,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.crudSpec
+Bundle: wheels.tests.specs.model.crudSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 1210ms
+Duration: 1082ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -537,9 +603,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.deleteSpec
+Bundle: wheels.tests.specs.model.deleteSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 139ms
+Duration: 170ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -548,9 +614,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.errorsSpec
+Bundle: wheels.tests.specs.model.errorsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 86ms
+Duration: 111ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -559,9 +625,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.miscellaneousSpec
+Bundle: wheels.tests.specs.model.miscellaneousSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 74ms
+Duration: 107ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -570,9 +636,20 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.nestedpropertiesSpec
+Bundle: wheels.tests.specs.model.MultiTenantSpec
+CFML Engine: Lucee 7.0.1.100
+Duration: 16ms
+Labels:
+╔═══════════════════════════════════════════════════════════╗
+║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
+╠═══════════════════════════════════════════════════════════╣
+║ 7 ║ 14 ║ 14 ║ 0 ║ 0 ║ 0 ║
+╚═══════════════════════════════════════════════════════════╝
+
+
+Bundle: wheels.tests.specs.model.nestedpropertiesSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 610ms
+Duration: 686ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -581,9 +658,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.onmissingmethod.belongsToSpec
+Bundle: wheels.tests.specs.model.onmissingmethod.belongsToSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 36ms
+Duration: 65ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -592,9 +669,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.onmissingmethod.hasManySpec
+Bundle: wheels.tests.specs.model.onmissingmethod.hasManySpec
CFML Engine: Lucee 7.0.1.100
-Duration: 163ms
+Duration: 224ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -603,9 +680,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.onmissingmethod.hasOneSpec
+Bundle: wheels.tests.specs.model.onmissingmethod.hasOneSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 111ms
+Duration: 125ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -614,9 +691,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.propertiesSpec
+Bundle: wheels.tests.specs.model.propertiesSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 429ms
+Duration: 544ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -625,9 +702,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.raceconditionSpec
+Bundle: wheels.tests.specs.model.raceconditionSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 133ms
+Duration: 162ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -636,20 +713,20 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.raisedErrorsSpec
+Bundle: wheels.tests.specs.model.raisedErrorsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 26ms
+Duration: 28ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
╠═══════════════════════════════════════════════════════════╣
-║ 1 ║ 5 ║ 5 ║ 0 ║ 0 ║ 0 ║
+║ 1 ║ 6 ║ 6 ║ 0 ║ 0 ║ 0 ║
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.readSpec
+Bundle: wheels.tests.specs.model.readSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 277ms
+Duration: 271ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -658,20 +735,20 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.sqlSpec
+Bundle: wheels.tests.specs.model.sqlSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 74ms
+Duration: 49ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
╠═══════════════════════════════════════════════════════════╣
-║ 1 ║ 10 ║ 10 ║ 0 ║ 0 ║ 0 ║
+║ 1 ║ 11 ║ 11 ║ 0 ║ 0 ║ 0 ║
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.transactionsSpec
+Bundle: wheels.tests.specs.model.transactionsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 477ms
+Duration: 408ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -680,9 +757,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.useindexSpec
+Bundle: wheels.tests.specs.model.useindexSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 70ms
+Duration: 59ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -691,9 +768,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.validationsSpec
+Bundle: wheels.tests.specs.model.validationsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 1516ms
+Duration: 1328ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -702,7 +779,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.model.viewsSpec
+Bundle: wheels.tests.specs.model.viewsSpec
CFML Engine: Lucee 7.0.1.100
Duration: 74ms
Labels:
@@ -713,9 +790,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.pluginsSpec
+Bundle: wheels.tests.specs.pluginsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 399ms
+Duration: 363ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -724,9 +801,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.routingSpec
+Bundle: wheels.tests.specs.routingSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 36ms
+Duration: 21ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -735,9 +812,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.assetsSpec
+Bundle: wheels.tests.specs.view.assetsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 788ms
+Duration: 705ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -746,7 +823,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.autoLinkSpec
+Bundle: wheels.tests.specs.view.autoLinkSpec
CFML Engine: Lucee 7.0.1.100
Duration: 8ms
Labels:
@@ -757,9 +834,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.checkboxSpec
+Bundle: wheels.tests.specs.view.checkboxSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 30ms
+Duration: 27ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -768,9 +845,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.contentSpec
+Bundle: wheels.tests.specs.view.contentSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 97ms
+Duration: 96ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -779,7 +856,7 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.csrfSpec
+Bundle: wheels.tests.specs.view.csrfSpec
CFML Engine: Lucee 7.0.1.100
Duration: 9ms
Labels:
@@ -790,9 +867,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.dateselectSpec
+Bundle: wheels.tests.specs.view.dateselectSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 111ms
+Duration: 84ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -801,9 +878,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.datesSpec
+Bundle: wheels.tests.specs.view.datesSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 51ms
+Duration: 44ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -812,9 +889,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.errorsSpec
+Bundle: wheels.tests.specs.view.errorsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 56ms
+Duration: 38ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -823,9 +900,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.flashMessagesSpec
+Bundle: wheels.tests.specs.view.flashMessagesSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 10ms
+Duration: 15ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -834,9 +911,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.formsdateobjectSpec
+Bundle: wheels.tests.specs.view.formsdateobjectSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 172ms
+Duration: 144ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -845,9 +922,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.formsdateplainSpec
+Bundle: wheels.tests.specs.view.formsdateplainSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 353ms
+Duration: 271ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -856,9 +933,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.formsdateSpec
+Bundle: wheels.tests.specs.view.formsdateSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 23ms
+Duration: 5ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -867,9 +944,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.formsSpec
+Bundle: wheels.tests.specs.view.formsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 334ms
+Duration: 436ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -878,9 +955,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.html5FormHelpersSpec
+Bundle: wheels.tests.specs.view.html5FormHelpersSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 53ms
+Duration: 46ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -889,9 +966,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.linksSpec
+Bundle: wheels.tests.specs.view.linksSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 117ms
+Duration: 126ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -900,9 +977,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.miscellaneousSpec
+Bundle: wheels.tests.specs.view.miscellaneousSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 51ms
+Duration: 35ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -911,9 +988,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.sanitizeSpec
+Bundle: wheels.tests.specs.view.sanitizeSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 13ms
+Duration: 8ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -922,9 +999,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.textfieldSpec
+Bundle: wheels.tests.specs.view.textfieldSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 11ms
+Duration: 8ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -933,9 +1010,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.textSpec
+Bundle: wheels.tests.specs.view.textSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 32ms
+Duration: 20ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
@@ -944,9 +1021,9 @@ Labels:
╚═══════════════════════════════════════════════════════════╝
-Bundle: wheels.tests_testbox.specs.view.urlsSpec
+Bundle: wheels.tests.specs.view.urlsSpec
CFML Engine: Lucee 7.0.1.100
-Duration: 100ms
+Duration: 84ms
Labels:
╔═══════════════════════════════════════════════════════════╗
║ Suites ║ Specs ║ Passed ║ Failed ║ Errored ║ Skipped ║
diff --git a/.github/workflow-results/test-rustcfml.md b/.github/workflow-results/test-rustcfml.md
new file mode 100644
index 0000000000..9e84dbef0c
--- /dev/null
+++ b/.github/workflow-results/test-rustcfml.md
@@ -0,0 +1,22 @@
+# RustCFML Compatibility Report (Experimental)
+
+**Date:** 2026-05-18 12:36:58 UTC
+**Commit:** bc0c04c68ec548a7d3a5cb056869ccf1b389d68c
+**Branch:** claude/evaluate-rustcfml-feasibility-40oUw
+**Run:** [#1](https://github.com/wheels-dev/wheels/actions/runs/26033864937)
+
+## Stage Results
+
+| Stage | Result |
+|-------|--------|
+| Docker build | success |
+| Server startup | skipped |
+| Smoke test | skipped |
+| Test suite | skipped |
+
+
+## Container Logs
+
+```
+Error response from daemon: No such container: wheels-rustcfml-1
+```
diff --git a/.github/workflows/test-rustcfml.yml b/.github/workflows/test-rustcfml.yml
new file mode 100644
index 0000000000..ee747ecea4
--- /dev/null
+++ b/.github/workflows/test-rustcfml.yml
@@ -0,0 +1,239 @@
+name: Test RustCFML Compatibility (Experimental)
+
+on:
+ push:
+ branches:
+ - 'claude/**'
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+jobs:
+ test-rustcfml:
+ name: RustCFML + SQLite (experimental)
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ env:
+ PORT: 60100
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Build RustCFML image
+ id: build
+ continue-on-error: true
+ run: |
+ echo "Building RustCFML Docker image (this may take several minutes on first run)..."
+ docker compose build rustcfml 2>&1 | tail -20
+ echo "build_ok=$?" >> $GITHUB_OUTPUT
+
+ - name: Start RustCFML
+ if: steps.build.outcome == 'success'
+ run: docker compose up -d rustcfml
+
+ - name: Wait for RustCFML to be ready
+ if: steps.build.outcome == 'success'
+ id: health
+ continue-on-error: true
+ run: |
+ echo "Waiting for RustCFML on port ${PORT}..."
+ MAX_WAIT=30
+ WAIT_COUNT=0
+ while [ "$WAIT_COUNT" -lt "$MAX_WAIT" ]; do
+ WAIT_COUNT=$((WAIT_COUNT + 1))
+ HTTP_CODE=$(curl -s -o /dev/null --connect-timeout 2 --max-time 5 \
+ -w "%{http_code}" "http://localhost:${PORT}/" 2>/dev/null || echo "000")
+ echo "Attempt ${WAIT_COUNT}/${MAX_WAIT}: HTTP ${HTTP_CODE}"
+ if echo "$HTTP_CODE" | grep -q "200\|404\|302\|500"; then
+ echo "RustCFML is responding (HTTP ${HTTP_CODE})"
+ echo "ready=true" >> $GITHUB_OUTPUT
+ exit 0
+ fi
+ sleep 3
+ done
+ echo "RustCFML did not become ready within ${MAX_WAIT} attempts"
+ echo "ready=false" >> $GITHUB_OUTPUT
+
+ - name: Smoke test - basic CFML execution
+ if: steps.health.outcome == 'success' && steps.health.outputs.ready == 'true'
+ id: smoke
+ continue-on-error: true
+ run: |
+ echo "=== Smoke test: basic page load ==="
+ HTTP_CODE=$(curl -s -o /tmp/smoke-result.txt --max-time 30 \
+ -w "%{http_code}" "http://localhost:${PORT}/" || echo "000")
+ echo "Root page: HTTP ${HTTP_CODE}"
+ if [ -f /tmp/smoke-result.txt ]; then
+ head -50 /tmp/smoke-result.txt
+ fi
+
+ echo ""
+ echo "=== Smoke test: reload request ==="
+ HTTP_CODE=$(curl -s -o /tmp/reload-result.txt --max-time 60 \
+ -w "%{http_code}" "http://localhost:${PORT}/?reload=true" || echo "000")
+ echo "Reload: HTTP ${HTTP_CODE}"
+ if [ -f /tmp/reload-result.txt ]; then
+ head -50 /tmp/reload-result.txt
+ fi
+
+ - name: Run test suite
+ if: steps.health.outcome == 'success' && steps.health.outputs.ready == 'true'
+ id: run-tests
+ continue-on-error: true
+ run: |
+ TEST_URL="http://localhost:${PORT}/wheels/core/tests?db=sqlite&format=json"
+ RESULT_FILE="/tmp/rustcfml-sqlite-result.txt"
+
+ echo "Running test suite: ${TEST_URL}"
+ echo "This may take several minutes..."
+
+ HTTP_CODE=$(curl -s -o "${RESULT_FILE}" \
+ --max-time 900 \
+ --write-out "%{http_code}" \
+ "${TEST_URL}" || echo "000")
+
+ echo "HTTP Code: ${HTTP_CODE}"
+ echo "http_code=${HTTP_CODE}" >> $GITHUB_OUTPUT
+
+ if [ -f "$RESULT_FILE" ]; then
+ # Try to parse as JSON for summary stats
+ python3 -c "
+ import json, sys
+ try:
+ d = json.load(open('$RESULT_FILE'))
+ total = int(d.get('totalSpecs', 0))
+ passed = int(d.get('totalPass', 0))
+ failed = int(d.get('totalFail', 0))
+ errors = int(d.get('totalError', 0))
+ skipped = int(d.get('totalSkipped', 0))
+ print(f'Total: {total} | Passed: {passed} | Failed: {failed} | Errors: {errors} | Skipped: {skipped}')
+ pct = round(passed / total * 100, 1) if total > 0 else 0
+ print(f'Pass rate: {pct}%')
+ except Exception as e:
+ print(f'Could not parse JSON results: {e}')
+ # Show first 200 chars of response
+ with open('$RESULT_FILE') as f:
+ content = f.read()
+ print(f'Response preview ({len(content)} bytes):')
+ print(content[:500])
+ " 2>&1 || echo "Result parsing failed"
+ else
+ echo "No result file generated"
+ fi
+
+ - name: Generate compatibility report
+ if: always()
+ run: |
+ RESULTS_DIR=".github/workflow-results"
+ RESULTS_FILE="${RESULTS_DIR}/test-rustcfml.md"
+ mkdir -p "$RESULTS_DIR"
+
+ {
+ echo "# RustCFML Compatibility Report (Experimental)"
+ echo ""
+ echo "**Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
+ echo "**Commit:** ${{ github.sha }}"
+ echo "**Branch:** ${{ github.ref_name }}"
+ echo "**Run:** [#${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
+ echo ""
+ echo "## Stage Results"
+ echo ""
+ echo "| Stage | Result |"
+ echo "|-------|--------|"
+ echo "| Docker build | ${{ steps.build.outcome }} |"
+ echo "| Server startup | ${{ steps.health.outcome }} |"
+ echo "| Smoke test | ${{ steps.smoke.outcome }} |"
+ echo "| Test suite | ${{ steps.run-tests.outcome }} |"
+ echo ""
+ } > "$RESULTS_FILE"
+
+ # Add test results if available
+ if [ -f "/tmp/rustcfml-sqlite-result.txt" ]; then
+ {
+ echo "## Test Results"
+ echo ""
+ python3 -c "
+ import json, sys
+ try:
+ d = json.load(open('/tmp/rustcfml-sqlite-result.txt'))
+ total = int(d.get('totalSpecs', 0))
+ passed = int(d.get('totalPass', 0))
+ failed = int(d.get('totalFail', 0))
+ errors = int(d.get('totalError', 0))
+ skipped = int(d.get('totalSkipped', 0))
+ pct = round(passed / total * 100, 1) if total > 0 else 0
+ print(f'- **Total specs:** {total}')
+ print(f'- **Passed:** {passed} ({pct}%)')
+ print(f'- **Failed:** {failed}')
+ print(f'- **Errors:** {errors}')
+ print(f'- **Skipped:** {skipped}')
+ print()
+ # List first 20 failures
+ if failed > 0 or errors > 0:
+ print('### First failures/errors')
+ print()
+ count = 0
+ for b in d.get('bundleStats', []):
+ for s in b.get('suiteStats', []):
+ for sp in s.get('specStats', []):
+ if sp.get('status') in ('Failed', 'Error') and count < 20:
+ print(f'- **{sp.get(\"name\", \"unknown\")}**: {sp.get(\"failMessage\", \"\")}')
+ count += 1
+ if count == 0:
+ # Check nested suites
+ for b in d.get('bundleStats', []):
+ for s in b.get('suiteStats', []):
+ for child in s.get('suiteStats', []):
+ for sp in child.get('specStats', []):
+ if sp.get('status') in ('Failed', 'Error') and count < 20:
+ print(f'- **{sp.get(\"name\", \"unknown\")}**: {sp.get(\"failMessage\", \"\")}')
+ count += 1
+ except Exception as e:
+ print(f'Could not parse results: {e}')
+ " 2>&1 || echo "Parse error"
+ } >> "$RESULTS_FILE"
+ fi
+
+ # Add debug info
+ {
+ echo ""
+ echo "## Container Logs"
+ echo ""
+ echo '```'
+ docker logs wheels-rustcfml-1 2>&1 | tail -80 || echo "No container logs available"
+ echo '```'
+ } >> "$RESULTS_FILE"
+
+ - name: Debug information
+ if: always()
+ run: |
+ echo "=== Docker Container Status ==="
+ docker ps -a --filter "name=rustcfml" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
+
+ echo -e "\n=== RustCFML Container Logs (last 80 lines) ==="
+ docker logs wheels-rustcfml-1 2>&1 | tail -80 || echo "No logs available"
+
+ - name: Commit compatibility report
+ if: always()
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git add ".github/workflow-results/test-rustcfml.md"
+
+ if git diff --cached --quiet; then
+ echo "No changes to commit"
+ else
+ git commit -m "ci: add RustCFML compatibility report [skip ci]"
+ git push origin HEAD:${{ github.ref_name }}
+ fi
+
+ - name: Upload test results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: rustcfml-test-results
+ path: |
+ /tmp/rustcfml-sqlite-result.txt
+ /tmp/smoke-result.txt
+ /tmp/reload-result.txt
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 228d23d8e4..ea02d658bb 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -22,6 +22,9 @@ jobs:
cfengine:
["lucee5", "lucee6", "lucee7", "adobe2018", "adobe2021", "adobe2023", "adobe2025", "boxlang"]
experimental: [false]
+ include:
+ - cfengine: rustcfml
+ experimental: true
env:
PORT_lucee5: 60005
PORT_lucee6: 60006
@@ -31,6 +34,7 @@ jobs:
PORT_adobe2023: 62023
PORT_adobe2025: 62025
PORT_boxlang: 60001
+ PORT_rustcfml: 60100
steps:
- name: Checkout Repository
uses: actions/checkout@v5
@@ -50,6 +54,10 @@ jobs:
# adobe2018 also excludes sqlite
DATABASES="mysql,postgres,sqlserver,oracle"
;;
+ rustcfml)
+ # RustCFML uses native Rust drivers, not JDBC — start with SQLite only
+ DATABASES="sqlite"
+ ;;
esac
echo "databases=${DATABASES}" >> $GITHUB_OUTPUT
@@ -63,7 +71,13 @@ jobs:
-O ./.engine/${{ matrix.cfengine }}/WEB-INF/lib/ojdbc10.jar
- name: Start CF engine
- run: docker compose build --no-cache ${{ matrix.cfengine }} && docker compose up -d ${{ matrix.cfengine }}
+ run: |
+ # RustCFML builds Rust from source — allow Docker layer caching to avoid 15+ min rebuild
+ if [ "${{ matrix.cfengine }}" = "rustcfml" ]; then
+ docker compose build ${{ matrix.cfengine }} && docker compose up -d ${{ matrix.cfengine }}
+ else
+ docker compose build --no-cache ${{ matrix.cfengine }} && docker compose up -d ${{ matrix.cfengine }}
+ fi
- name: Start all databases
run: |
@@ -514,7 +528,7 @@ jobs:
MATRIX_MD="${MATRIX_MD}
|--------|:-----:|:----------:|:----------:|:--:|:------:|:------:|"
- for engine in lucee5 lucee6 lucee7 adobe2018 adobe2021 adobe2023 adobe2025 boxlang; do
+ for engine in lucee5 lucee6 lucee7 adobe2018 adobe2021 adobe2023 adobe2025 boxlang rustcfml; do
ROW="| **${engine}** |"
for db in mysql postgres sqlserver h2 oracle sqlite; do
FILE="results/test-results-${engine}/${engine}-${db}-result.txt"
diff --git a/compose.yml b/compose.yml
index f83279f205..c55aa61d27 100644
--- a/compose.yml
+++ b/compose.yml
@@ -259,6 +259,24 @@ services:
networks:
- wheels-network
+ rustcfml:
+ build:
+ context: ./
+ dockerfile: ./tools/docker/rustcfml/Dockerfile
+ image: wheels-test-rustcfml:v0.1.0
+ volumes:
+ - ./:/wheels-test-suite
+ - type: bind
+ source: ./tools/docker/rustcfml/settings.cfm
+ target: /wheels-test-suite/config/settings.cfm
+ - type: bind
+ source: ./tools/docker/rustcfml/app.cfm
+ target: /wheels-test-suite/config/app.cfm
+ ports:
+ - "60100:60100"
+ networks:
+ - wheels-network
+
mysql:
image: mysql:9
restart: always
diff --git a/docs/plans/rustcfml-feasibility-assessment.md b/docs/plans/rustcfml-feasibility-assessment.md
new file mode 100644
index 0000000000..6944da9c31
--- /dev/null
+++ b/docs/plans/rustcfml-feasibility-assessment.md
@@ -0,0 +1,195 @@
+# RustCFML Feasibility Assessment for Wheels Framework
+
+**Date:** 2026-05-18 (updated from initial 2026-03-15 evaluation)
+**Project:** [pixl8/RustCFML](https://github.com/pixl8/RustCFML)
+**Version Evaluated:** 0.9.1 (released May 3, 2026)
+**Previous Version Evaluated:** 0.4.0 (March 14, 2026)
+
+## Executive Summary
+
+RustCFML is a **complete CFML interpreter written in Rust** — not a library or extension. It replaces Lucee/ACF as a runtime, compiling CFML to bytecode executed on a stack-based VM with no JVM dependency.
+
+**Updated Verdict: Feasibility has improved significantly. Several previous blockers are now resolved. A proof-of-concept trial is now reasonable.**
+
+Since the initial evaluation two months ago, RustCFML has gone from v0.4.0 to v0.9.1 (14 releases, 108 commits), addressing three of the four critical blockers identified previously. The project has also grown from 3 to 20 stars and gained its first fork, indicating rising community awareness.
+
+## Progress Since Initial Evaluation (March 2026)
+
+### Previously Identified Blockers — Status Update
+
+| Blocker | March Status | May Status | Impact |
+|---------|-------------|------------|--------|
+| **`onMissingMethod`** | Unknown/unlikely | **Implemented** | Wheels' dynamic finders (`findAllByEmail()`) may now work |
+| **`cflock`** | Unknown | **Implemented** (RwLock-based) | Thread-safe rate limiting, caching, initialization possible |
+| **`getMetadata`** | Unknown | **Implemented** | Model configuration, routing, test discovery possible |
+| **`CreateObject`** | Not supported | **Implemented** (for components) | Component instantiation works |
+| **Java interop (`CreateObject("java", ...)`)** | Not supported | **Still not supported** | Remains a blocker for `ConcurrentHashMap` usage |
+| **`cfthread` (true concurrency)** | Sequential only | **Still sequential** | Background jobs would block |
+| **Query-of-Queries** | Not supported | **Still not supported** | Low impact for Wheels |
+
+### New Capabilities Since v0.4.0
+
+- **`cflock`** with real named locks using RwLock-based concurrency
+- **`onMissingMethod`** for dynamic method dispatch
+- **`getMetadata`** for component introspection
+- **`createObject`** for component instantiation
+- **`cfinvoke`** tag support
+- **Session lifecycle** (`onSessionStart`/`onSessionEnd`)
+- **`cfthread`** tag (sequential model)
+- **`cfzip`** operations
+- **`cfexecute`** for OS command execution
+- **`cfstoredproc`/`cfprocparam`** for stored procedures
+- **Custom tags** with `cf_` prefix
+- **`cfimport`** with `.tld` support
+- **Bytecode caching** with modification-time tracking (no recompilation for unchanged files)
+- **Performance tuning** across VM hot paths (v0.8.0-v0.9.0 focus)
+- **CFMX_COMPAT** encryption algorithm support
+- **Taffy framework** compatibility (claimed feature-complete)
+
+### Project Growth Metrics
+
+| Indicator | March 2026 | May 2026 | Change |
+|-----------|-----------|----------|--------|
+| Version | 0.4.0 | 0.9.1 | 14 releases in 2 months |
+| Stars | 3 | 20 | 7x increase |
+| Forks | 0 | 1 | First community fork |
+| Commits | 39 | 108 | 69 new commits |
+| Test assertions | 1,181 / 89 suites | 1,181+ / 89+ suites | Baseline maintained |
+| Release cadence | ~weekly | ~weekly | Consistent |
+
+## Revised Compatibility Analysis
+
+### Now Supported (previously blocked or unknown)
+
+| Wheels Feature | RustCFML Support | Notes |
+|---------------|-----------------|-------|
+| Dynamic finders (`findAllByEmail`) | `onMissingMethod` implemented | Needs behavioral verification |
+| Thread-safe middleware | `cflock` with RwLock | Real concurrency, not just advisory |
+| Component metadata inspection | `getMetadata` implemented | Model config, test discovery |
+| Component instantiation | `createObject` for components | CFC creation works |
+| CSRF tokens | `csrfGenerateToken`/`csrfVerifyToken` | Behavioral compat with Lucee TBD |
+| Encrypt/Decrypt | AES, DES, DESEDE, Blowfish, CFMX_COMPAT | Cookie compat needs testing |
+| Session management | Full lifecycle with CFID cookie | `onSessionStart`/`onSessionEnd` |
+| Transaction management | `cftransaction` | Rollback/savepoint needs testing |
+| Named locks | `cflock` (read/write) | RwLock-based, real concurrency |
+| Password hashing | bcrypt, scrypt, argon2, PBKDF2 | Better than stock Lucee |
+| Stored procedures | `cfstoredproc`/`cfprocparam` | Database-dependent |
+| File operations | 23+ file I/O functions | Upload, read, write, directory ops |
+| Caching | 8 cache functions | `cachePut`, `cacheGet`, etc. |
+| Error context | `cfcatch.tagContext` with stack traces | Line/column/template info |
+
+### Remaining Blockers
+
+| Wheels Requirement | Status | Severity | Workaround |
+|-------------------|--------|----------|------------|
+| **`CreateObject("java", ...)`** | Not supported | **Medium** | Replace `ConcurrentHashMap` with CFML struct + `cflock`. Replace `StringBuilder` with string concatenation or array join. Only 2-3 call sites in Wheels core. |
+| **`cfthread` true concurrency** | Sequential only | **Low** | Background jobs (`wheels.Job`) would block, but most Wheels apps don't use in-request threading. Worker processes are typically separate. |
+| **Query-of-Queries** | Not supported | **Low** | Wheels rarely uses QoQ. Any instances could be rewritten as database queries. |
+| **`GetComponentMetaData`** (as distinct from `getMetadata`) | Unconfirmed | **Low** | `getMetadata` is confirmed; `GetComponentMetaData` may work as an alias or may not be needed. |
+
+### Critical Behavioral Questions (need testing)
+
+These features are nominally supported but Wheels depends on specific behavioral contracts:
+
+1. **`onMissingMethod` argument passing**: Wheels passes `missingMethodName` and `missingMethodArguments` — does RustCFML match this signature exactly?
+2. **`getMetadata` depth**: Wheels inspects `functions`, `extends`, `name`, `fullname`, `path` from metadata. How complete is the returned struct?
+3. **`Encrypt`/`Decrypt` output format**: CSRF cookies encrypted on Lucee must be decryptable on RustCFML if migrating a running app (probably not a concern for fresh deploys).
+4. **Application scope persistence**: Wheels stores extensive config in `application.$wheels`. Does `application` scope persist correctly across requests with the same semantics?
+5. **`cflock` timeout behavior**: Wheels uses `timeout=1` on rate limiter locks. Does RustCFML respect lock timeout and fail gracefully?
+6. **Scope resolution order**: Wheels depends on specific scope precedence (`local` > `arguments` > `variables`). CFML engines vary subtly here.
+7. **`isInstanceOf` behavior**: Wheels uses this for type checking in DI and model resolution.
+8. **Interface `implements` enforcement**: Wheels middleware uses `implements="wheels.middleware.MiddlewareInterface"`.
+
+## Wheels Code Changes Needed
+
+### Minimal changes (to eliminate remaining blockers)
+
+```
+vendor/wheels/middleware/RateLimiter.cfc (line 55):
+ - CreateObject("java", "java.util.concurrent.ConcurrentHashMap").init()
+ + StructNew() // with cflock protection (already present)
+
+vendor/wheels/Channel.cfc:
+ - Same ConcurrentHashMap replacement
+
+vendor/wheels/model/read.cfc (if StringBuilder used):
+ - Replace java.lang.StringBuilder with array + ArrayToList
+```
+
+These are 2-3 small, isolated changes that would make the Wheels core Java-interop-free without affecting Lucee/ACF compatibility (struct + cflock works on all engines).
+
+### No changes needed for
+
+- CSRF protection (uses standard `Encrypt`/`Decrypt`/`GenerateSecretKey`/`CsrfGenerateToken`)
+- Hash-based cache keys (uses standard `Hash()`)
+- Model identification and transactions (uses standard `Hash()`)
+- Route matching and dispatch (pure CFML)
+- View rendering (pure CFML)
+- Migration system (uses `queryExecute` which is supported)
+- Validation framework (pure CFML)
+- Association/relationship definitions (pure CFML, depends on `onMissingMethod`)
+
+## Performance Implications for Wheels
+
+| Scenario | Expected Benefit | Confidence |
+|----------|-----------------|------------|
+| Cold start (container/serverless) | **Massive** — instant vs ~15s JVM warmup | High |
+| Memory per instance | **44x reduction** — 8 MB vs 350 MB | High (trivial workloads verified) |
+| Simple page renders | **3x throughput** — 1,949 vs 635 req/s | High |
+| ORM-heavy pages | **Unknown** — dynamic method dispatch overhead not benchmarked | Low |
+| Complex model graphs | **Unknown** — component instantiation + metadata perf not tested | Low |
+| Concurrent requests | **Uncertain** — Axum is async but cfthread is sequential | Medium |
+
+## Recommendations (Updated)
+
+### Immediate (now): Proof-of-concept trial
+
+The resolution of `onMissingMethod`, `cflock`, and `getMetadata` makes a PoC reasonable:
+
+1. **Build RustCFML from source** and attempt to boot the Wheels demo app (`app/`)
+2. **Run the core test suite** against RustCFML to get a compatibility baseline
+3. **Document failures** — categorize as (a) missing function, (b) behavioral difference, (c) Java interop dependency
+4. **Estimate gap size** — if >80% of tests pass, active pursuit is justified
+
+### Short-term (1-3 months): Eliminate Java interop in Wheels core
+
+Regardless of RustCFML adoption, removing `CreateObject("java", ...)` from the framework core is good hygiene:
+
+- Makes Wheels more portable across all CFML engines (including BoxLang)
+- Only 2-3 call sites need changes
+- The `cflock`-protected struct pattern already exists alongside the Java calls
+
+### Medium-term (3-6 months): Engage with Pixl8
+
+If the PoC shows >80% compatibility:
+
+1. **File issues** for specific behavioral gaps discovered during testing
+2. **Contribute test cases** from Wheels' test suite that exercise edge cases
+3. **Explore adding Wheels/RustCFML to the CI compat matrix** as a soft-fail target (like Oracle)
+4. **Document the "Wheels on RustCFML" story** for containerized/edge deployments
+
+### Long-term (6-12 months): Lightweight deployment target
+
+Position RustCFML as an optional deployment runtime for:
+- **Docker/Kubernetes** — 8 MB image vs 350+ MB
+- **Serverless/edge functions** — instant cold start
+- **CI/test environments** — faster test cycles
+- **API-only Wheels apps** — no view layer complexity
+
+## Risk Assessment
+
+| Risk | Likelihood | Impact | Mitigation |
+|------|-----------|--------|------------|
+| Project abandoned (single maintainer) | Medium | High | MIT license allows forking; Pixl8 is an established CFML shop |
+| Behavioral incompatibilities surface at scale | High | Medium | Incremental adoption starting with simple apps |
+| Performance gains disappear with complex apps | Medium | Low | Still wins on memory/startup even if throughput is similar |
+| Breaking changes in pre-1.0 releases | High | Low | Pin versions; don't depend on internals |
+| Community doesn't grow | Medium | Medium | Wheels adoption would itself grow the community |
+
+## Conclusion
+
+RustCFML has made remarkable progress in two months. The three most critical Wheels blockers — `onMissingMethod`, `cflock`, and `getMetadata` — are now implemented. The remaining gap (Java interop) affects only 2-3 isolated call sites in Wheels core, and the fix is straightforward and engine-agnostic.
+
+**The project has moved from "watch and wait" to "try it and see."** A proof-of-concept attempt to boot Wheels on RustCFML is now the right next step. The outcome will determine whether this becomes a real deployment option or needs another 6-12 months of runtime maturation.
+
+The 44x memory reduction and instant cold start alone would make RustCFML a compelling option for containerized Wheels deployments — if the compatibility gap can be bridged.
diff --git a/tools/docker/rustcfml/Dockerfile b/tools/docker/rustcfml/Dockerfile
new file mode 100644
index 0000000000..ad5544cd15
--- /dev/null
+++ b/tools/docker/rustcfml/Dockerfile
@@ -0,0 +1,33 @@
+# Stage 1: Build RustCFML from source
+FROM rust:1.78-bookworm AS builder
+
+ARG RUSTCFML_VERSION=v0.9.1
+
+RUN apt-get update && apt-get install -y \
+ git pkg-config libssl-dev libsqlite3-dev \
+ && rm -rf /var/lib/apt/lists/*
+
+RUN git clone --depth 1 --branch ${RUSTCFML_VERSION} \
+ https://github.com/pixl8/RustCFML.git /build
+
+WORKDIR /build
+RUN cargo build --release --features "database,xml"
+
+# Stage 2: Minimal runtime image
+FROM debian:bookworm-slim
+
+RUN apt-get update && apt-get install -y \
+ sqlite3 libsqlite3-0 libssl3 curl ca-certificates \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY --from=builder /build/target/release/rustcfml /usr/local/bin/rustcfml
+
+RUN sqlite3 /app/wheelstestdb.db "SELECT 1;" \
+ && sqlite3 /app/wheelstestdb_tenant_b.db "SELECT 1;"
+
+ENV PORT=60100
+WORKDIR /wheels-test-suite
+
+EXPOSE ${PORT}
+
+CMD ["rustcfml", "--serve", "public", "--port", "60100"]
diff --git a/tools/docker/rustcfml/app.cfm b/tools/docker/rustcfml/app.cfm
new file mode 100644
index 0000000000..c013ca66a7
--- /dev/null
+++ b/tools/docker/rustcfml/app.cfm
@@ -0,0 +1,17 @@
+
+ /*
+ RustCFML datasource configuration.
+ Overrides config/app.cfm for RustCFML which uses native Rust database
+ drivers (rusqlite) instead of JDBC.
+ */
+
+ this.datasources["wheelstestdb_sqlite"] = {
+ database: "/app/wheelstestdb.db",
+ type: "sqlite"
+ };
+
+ this.datasources["wheelstestdb_sqlite_tenant_b"] = {
+ database: "/app/wheelstestdb_tenant_b.db",
+ type: "sqlite"
+ };
+
diff --git a/tools/docker/rustcfml/settings.cfm b/tools/docker/rustcfml/settings.cfm
new file mode 100644
index 0000000000..fe61927c3c
--- /dev/null
+++ b/tools/docker/rustcfml/settings.cfm
@@ -0,0 +1,15 @@
+
+ /*
+ RustCFML-specific settings for the Wheels test suite.
+ Only SQLite is configured — RustCFML uses native Rust database drivers,
+ not JDBC, so the datasource configuration differs from Lucee/ACF.
+ */
+
+ set(coreTestDataSourceName="wheelstestdb_sqlite");
+ set(dataSourceName="wheelstestdb_sqlite");
+
+ set(URLRewriting="On");
+ set(reloadPassword="");
+
+ // CLI-Appends-Here
+