You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .ai/wheels/cross-engine-compatibility.md
+36Lines changed: 36 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,8 @@
2
2
3
3
Wheels runs on multiple CFML engines (Lucee 5/6/7, Adobe CF 2018-2025, BoxLang) and databases (H2, MySQL, PostgreSQL, SQL Server, CockroachDB). Each engine has runtime differences that can cause code to pass on one engine but fail on another. This guide documents the known gotchas.
4
4
5
+
**RustCFML (best-effort, experimental):**[RustCFML](https://github.com/RustCFML/RustCFML) — a young, JVM-free CFML interpreter written in Rust — is recognized as a first-class engine in the adapter layer (`server.coldfusion.productName == "RustCFML"` → `RustCFMLAdapter`), but it is NOT yet part of the CI matrix and cannot fully boot the framework today. The confirmed divergence handled in-framework is the **missing `cfcache` built-in** (the cfcache-backed cache degrades to a no-op via the adapter's `supportsCfcache()=false`). Remaining blockers are tracked upstream — chiefly an argument-scope-fidelity gap (undeclared/`argumentCollection`-forwarded named args lose their names) and no Query-of-Queries — so treat RustCFML support as in-progress.
6
+
5
7
## Engine-Specific Gotchas
6
8
7
9
### struct.map() Collision (Lucee + Adobe)
@@ -329,6 +331,40 @@ H2 is the embedded database used by default in tests. Key differences:
329
331
- Some MySQL-specific functions (e.g., `GROUP_CONCAT`) not available
When a model declares no `property()` mappings, Wheels infers its properties from `cfdbinfo` column metadata. The reported column casing varies by database, so the adapter layer carries a capability flag — `$lowerCaseColumnNames()` on `Base.cfc` — that controls whether the derived property name keeps the reported case or is forced to lowercase. Adapters override this when their database folds unquoted identifiers to a non-meaningful default that would otherwise leak into Wheels-side property names.
return true; // ISHIDDEN → ishidden (H2 folds to UPPERCASE)
359
+
}
360
+
```
361
+
362
+
**When adding a new database adapter**: check whether the database's unquoted-identifier folding rule produces case the Wheels developer actually declared. If it folds to UPPERCASE (Oracle/H2 family), override `$lowerCaseColumnNames()` to return `true`. If it preserves case (SQL Server/MySQL/SQLite) or folds to lowercase (PostgreSQL/CockroachDB), keep the Base default — the reported name is already the right property name.
363
+
364
+
**Explicit `property(name=..., column=...)` declarations bypass this entirely** — they always win, regardless of the adapter flag. The capability only affects the auto-derived path.
-**Data URLs work for most tests** — no server needed for ~95% of DSL coverage. Full HTTP integration (cookies, form submits, redirects) needs a running fixture app; that wiring is the same as Wheels Web app bootstrap (separate server + baseUrl).
66
66
-**`this.browserTestSkipped`** — when Playwright JARs aren't installed (fresh CI, clean machine), `beforeAll` sets this flag and `browserDescribe`'s hooks short-circuit. All `it`s should check `if (this.browserTestSkipped) return;` to stay green on CI.
67
67
-**CI runs browser tests** — `pr.yml` and `snapshot.yml` install Playwright JARs + Chromium (cached via `browser-manifest.json` hash). Browser specs run as part of the normal test suite. `WHEELS_BROWSER_TEST_BASE_URL=http://localhost:60007` is set automatically. The base URL is resolved at instance time through a layered lookup (`this.baseUrl` → Wheels setting → JVM property `wheels.browserTest.baseUrl` → env var → CGI auto-detect → `http://localhost:8080`); per-spec `this.baseUrl` takes priority over the env var. Set `this.baseUrl` in the component pseudo-constructor (outside any function), not inside `beforeAll()` — `super.beforeAll()` calls `$resolveBaseUrl()` and caches the result, so a `this.baseUrl =` assignment that runs after `super.beforeAll()` is silently ignored.
68
-
-**Fixture routes** — `/_browser/login-as` and `/_browser/logout` are mounted automatically in test mode. They must come before `.wildcard()` in routes.cfm. In the Routes UI (`/wheels/routes`) all `/_browser/*` routes appear under the **Internal** tab, not Application.
68
+
-**Fixture routes** — `/_browser/login-as` and `/_browser/logout` are mounted automatically in test mode. They must come before `.wildcard()` in routes.cfm. In the Routes UI (`/wheels/routes`) all `/_browser/*` routes appear under the **Internal** tab, not Application. The `/_browser/login-as` handler is configurable: `set(browserLoginAsHandler = "AuthFixture##loginAs")` in `config/settings.cfm` substitutes that `Controller##action` at route-registration time (default is `BrowserTestLogin##create`). Env-gating is handled by `wheels.middleware.BrowserTestFixtureGuard` on the whole `/_browser` scope — custom handlers do not need to re-implement the guard. Empty string or absent setting falls back to the default. (#2830)
69
69
-**Dialogs are Lucee-only** — `acceptDialog`, `dismissDialog`, `dialogMessage` use `createDynamicProxy` which is Lucee-specific. Specs skip gracefully on other engines.
Copy file name to clipboardExpand all lines: .ai/wheels/wheels-bot.md
+20Lines changed: 20 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,3 +32,23 @@ Flip the repo variable `WHEELS_BOT_ENABLED` to `false` to halt every bot workflo
32
32
## Auto-fire safety net
33
33
34
34
The bot is permitted to chain stages (triage → research → propose-fix), and handoff fires on `*-confidence:high` OR `*-confidence:medium`. Low stays manual. Sensitive areas (security, middleware, migrations, deploy, DI, cross-engine) are caught by the propose-fix prompt's own step-4 safety net, which posts a `fix-held` marker instead of opening a PR. Reviewer A and B then critique whatever propose-fix produces, escalating to the Senior Advisor on deadlock. All bot PRs land as `--draft` and require a human approving review on `develop`.
35
+
36
+
## PR-prep automation (release unblocking)
37
+
38
+
-**Commit-message gate.**`pr.yml`'s `Validate Commit Messages` lints the
39
+
**PR title** (the squash subject), not every commit — because PRs are
40
+
squash-merged, intermediate commit headers don't land in `develop`; only the
41
+
PR title does. Edit the title to fix a failure; the `edited` trigger re-runs
42
+
the check (and `fast-test` is skipped on title-only edits). Local guard:
43
+
`tools/test-commit-title.sh`.
44
+
-**Freshen (`bot-freshen.yml`).** On push to develop + a 30-min backstop:
45
+
behind-but-clean bot PRs are updated via non-destructive `update-branch`;
46
+
DIRTY ones are dispatched to the resolver. Decision logic:
Reconcile content/docs merge-conflict markers on a bot PR branch (low-risk paths only). Invoked by bot-resolve-conflicts.yml after a deterministic risk gate.
4
+
5
+
## Rails
6
+
7
+
Read `.claude/commands/_shared-rails.md` first — they apply to every step
8
+
below. Highlights for this command:
9
+
10
+
- Use `gh` for GitHub state, `git` for the PR branch only.
11
+
-**Filesystem writes are limited to the conflicted content/docs files only.**
12
+
Never touch code.
13
+
- Output is **a completed merge commit** — the workflow pushes after this
14
+
prompt completes.
15
+
16
+
## Args
17
+
18
+
-`<pr-number>` — the PR branch with content/docs conflict markers to resolve
19
+
20
+
# Resolve content conflicts — PR #<pr-number>
21
+
22
+
You are running inside `bot-resolve-conflicts.yml`. The workflow has already
23
+
merged `origin/develop` into the PR branch and a **deterministic classifier
24
+
has confirmed every conflicted file is pure documentation/content**
25
+
(markdown/MDX at any path, CHANGELOG, or under `.ai/` or `docs/`).
26
+
27
+
## Hard safety rule
28
+
29
+
Run this first:
30
+
31
+
```bash
32
+
git diff --name-only --diff-filter=U
33
+
```
34
+
35
+
Confirm EVERY listed file is in the low-risk set the upstream classifier
36
+
admits — i.e. each file is a `*.md` or `*.mdx` (any path), a `CHANGELOG`
37
+
file, or under `.ai/` or `docs/`. If ANY listed file falls OUTSIDE that set
0 commit comments