Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
199 commits
Select commit Hold shift + click to select a range
c46c392
docs: record merge-method requirement and divergence recovery in rele…
bpamiri Jun 10, 2026
0688d91
chore: bump develop snapshot target to 4.0.4 (#2894)
github-actions[bot] Jun 10, 2026
0ec62e4
docs(web/blog): publish Wheels 4.0.3 release post (#2896)
bpamiri Jun 10, 2026
8375037
fix(controller): reject backslash and schemeless open-redirect bypass…
bpamiri Jun 10, 2026
aa9bab5
fix(dispatch): stop trusting X-Forwarded-For for debug-access IP allo…
bpamiri Jun 10, 2026
ab29125
fix(migrator): wire TenantMigrator to real Migrator API and hold tena…
bpamiri Jun 10, 2026
53a7f3f
fix(model): fix >=/<= parsing and fail-open swallow in validation con…
bpamiri Jun 10, 2026
a2c4a9f
fix(config): isolate ServiceProvider register/boot failures per packa…
bpamiri Jun 10, 2026
a391bf4
fix(cli): exit non-zero when wheels validate finds errors (#2907)
bpamiri Jun 10, 2026
6b5e1c2
fix(job): guard processQueue job claim with status=pending and affect…
bpamiri Jun 10, 2026
d030d5c
fix(controller): make $acceptableFormats honor onlyProvides per-actio…
bpamiri Jun 10, 2026
fa7233a
fix(view): redact secret-shaped settings on the /wheels/info HTML pag…
bpamiri Jun 10, 2026
cb6241c
fix(dispatch): block /wheels/* dev UI unless environment is developme…
bpamiri Jun 10, 2026
cc7cf79
fix(model): prefer driver generated keys for Oracle and SQL Server id…
bpamiri Jun 10, 2026
e492e5c
fix(model): key association JOIN memo by soft-delete and alias contex…
bpamiri Jun 10, 2026
68eb339
fix(events): validate url.format to prevent error-template path trave…
bpamiri Jun 10, 2026
370a807
fix(middleware): enforce database-backed rate limits with portable DD…
bpamiri Jun 10, 2026
1e82c12
fix(dispatch): onapplicationstart else-if chain and debug-bar output …
bpamiri Jun 10, 2026
473c79d
fix(view): imageTag id rename and vite manifest caching (#2918)
bpamiri Jun 10, 2026
132ac85
fix(view): autoLink state, var leaks, addclass attr, flashMessages an…
bpamiri Jun 10, 2026
b3fd8ad
fix(seed): parameterize seedOnce lookup and validate environment incl…
bpamiri Jun 10, 2026
70f5e02
perf(model): memoize adapter version probe and fix doubled IN parens …
bpamiri Jun 10, 2026
b160805
fix(dispatch): narrow binding catch, cache binding misses, unify rout…
bpamiri Jun 10, 2026
09a638d
docs(migrator): fix viewer flag, reference examples, rollback-safety …
bpamiri Jun 10, 2026
2f551d4
fix(middleware): Cors trim, HSTS default, auth delegation, duplicate-…
bpamiri Jun 10, 2026
a29e0bf
fix(migrator): addIndex alias order, dbtype caching, helper dedup, di…
bpamiri Jun 10, 2026
ff2b10b
fix(view): option comma matching, startFormTag method guard, form hel…
bpamiri Jun 10, 2026
053185e
fix(router): except= normalization, scope option passthrough, end() u…
bpamiri Jun 10, 2026
0990473
fix(controller): usesLayout crash, verifies bounds, only/except dedup…
bpamiri Jun 10, 2026
7fb8f50
fix(model): updatedAt gating, UUID probe bounds, whereParams placehol…
bpamiri Jun 10, 2026
8b28d57
fix(job): tenant context restore, retry counts, single-row claim poll…
bpamiri Jun 10, 2026
f3acd2a
fix(sse): blank-line framing, DB cursor advance, channel pruning, bou…
bpamiri Jun 10, 2026
ee848b6
fix(test): assertJson array handling, in-order overlap, base URL over…
bpamiri Jun 10, 2026
cf00d63
fix(cli): upgrade-check exit code, breaker-scan coverage, ArgSpec pos…
bpamiri Jun 10, 2026
8e4f507
fix(di): singleton locking and keying, SemVer space parsing, lazy pro…
bpamiri Jun 10, 2026
2407327
fix(migrator): transactional version rows, narrowed catches, memoized…
bpamiri Jun 10, 2026
b95cd97
fix(router): constraint validation, capture groups, whereIn escaping,…
bpamiri Jun 10, 2026
56a3136
fix(test): browser skip path, dialog listener detach, launcher lifecy…
bpamiri Jun 10, 2026
f8472c0
fix(model): SQLite recreate rollback, DEFAULT quote escaping, pg advi…
bpamiri Jun 10, 2026
2a0c815
fix(config): constant-time reload compare, hardened cookie flags, CSR…
bpamiri Jun 10, 2026
50e9526
perf(model): findEach cache retention, quadratic serialize dedupe, do…
bpamiri Jun 10, 2026
24b4b43
fix(dispatch): require POST+password for /wheels/cli destructive ops,…
bpamiri Jun 10, 2026
2a37282
fix(dispatch): gate migrator and MCP endpoints, fix payload guard, pu…
bpamiri Jun 10, 2026
84158c6
fix(cli): belongsTo scaffold views, API test signatures, cross-engine…
bpamiri Jun 10, 2026
1fdbce9
fix(dispatch): Global cache cull, date parsing, CF version gate, exis…
bpamiri Jun 10, 2026
b5e1abf
fix(migrator): quote char defaults like string/text, restore adapter …
bpamiri Jun 10, 2026
f9c3c5d
fix(controller): sendEmail multipart handling and sendFile path conta…
bpamiri Jun 10, 2026
063dd3a
fix(auth): JWT secret validation and token strategy hardening (#2926)
bpamiri Jun 10, 2026
da66839
fix(plugin): deprecation links and text, log routing, write-back refe…
bpamiri Jun 10, 2026
6ad88d5
docs(changelog): consolidate deferred remediation-campaign entries (#…
bpamiri Jun 10, 2026
8971094
test(router): add regression spec pinning root() without to= to GET d…
bpamiri Jun 10, 2026
74a4224
fix(cli): SecretResolver Left(str,0) crash and secret parsing corrupt…
bpamiri Jun 10, 2026
ed116f2
docs: note Homebrew 5.1 tap-trust step wherever brew install is docum…
bpamiri Jun 10, 2026
e77ec94
fix(middleware): cache route-scoped string middleware and preflight s…
wheels-bot[bot] Jun 10, 2026
c51b289
perf(router): cache URLFor controller/action lookups in app scope wit…
wheels-bot[bot] Jun 10, 2026
24aaa29
perf(global): lock-free warm fast path for model() and controller() (…
wheels-bot[bot] Jun 10, 2026
ee2b2f2
feat(cli): upgrade check --strict + ArgSpec toInputSchema for MCP too…
wheels-bot[bot] Jun 10, 2026
5103172
fix(public): decompose cli.cfm dbSetup recursive dispatch, normalize …
wheels-bot[bot] Jun 10, 2026
b3b0a20
fix(tests): clear cached model classes when app-runner swaps the test…
bpamiri Jun 10, 2026
ebc2870
fix(bot): use Refs not Fixes on staged propose-fix PRs so partial mer…
bpamiri Jun 10, 2026
59076c8
fix(controller): normalize whitespace and control chars in $isSafeRed…
wheels-bot[bot] Jun 10, 2026
f3ffc9d
fix(plugin): isolate legacy lifecycle hook failures per plugin (#2986)
bpamiri Jun 10, 2026
065c3d5
ci(web): land blog-baseline auto-refresh via bot PR instead of direct…
bpamiri Jun 10, 2026
5b5955c
feat(cli): mcpToolSpecs registry + non-zero exit on crashed test runs…
bpamiri Jun 10, 2026
d654586
fix(seed): fail and roll back when seedOnce entries fail validation (…
bpamiri Jun 10, 2026
1c9915e
fix(router): normalize Java named capture groups in route constraints…
bpamiri Jun 10, 2026
d258a19
fix: sweep accumulated reviewer nits from the 2026-06 campaign (#2992)
bpamiri Jun 10, 2026
4b80a9a
fix(middleware): lock, sample, and bound RateLimiter memory-store mai…
bpamiri Jun 10, 2026
5633f86
feat(docs): changelog.d fragment system for conflict-free unreleased …
bpamiri Jun 10, 2026
c449c8b
fix(public): whitelist /wheels/info JSON metadata and validate core.c…
bpamiri Jun 10, 2026
945e0c7
fix(public): redact /wheels/info JSON metadata and validate docs form…
wheels-bot[bot] Jun 10, 2026
a8f3602
fix(public): encode path in route-tester verb-mismatch message, scan …
bpamiri Jun 10, 2026
39e19bb
test(router): cover nested named capture groups in constraint normali…
bpamiri Jun 10, 2026
6db0b37
feat(global): subpath setting for URL-subfolder Wheels deployments (#…
wheels-bot[bot] Jun 11, 2026
5572d34
docs(web/blog): correct double-converted updatedAt in 4.0.3 post fron…
bpamiri Jun 11, 2026
03d08be
test(wheelstest): cover empty server_name localhost guard in base-URL…
bpamiri Jun 11, 2026
345b30d
fix(cli): scaffolded populate.cfm fails loudly on partial test-db mig…
bpamiri Jun 11, 2026
21e703d
docs(contributing): DCO sign-off is review-enforced, not a required s…
bpamiri Jun 11, 2026
b7c310b
fix(docs): handle SIGPIPE in changelog-promote.sh so --preview | head…
bpamiri Jun 11, 2026
5e07c95
perf(events): fix $wheelsHeaders memo guard and reuse stored httpRequ…
bpamiri Jun 11, 2026
4a881c2
docs(web/guides): add Channels pub/sub guide with honest WebSocket st…
bpamiri Jun 11, 2026
0f5bb6e
fix(dispatch): trustProxyHeaders setting gates X-Forwarded-* trust (i…
bpamiri Jun 11, 2026
addde26
fix(middleware): atomic upsert, v2 schema, and RMW locking for RateLi…
bpamiri Jun 11, 2026
3a47988
fix(cli): harden deploy remote-exec — stdin registry login, shell esc…
bpamiri Jun 11, 2026
4f2d14a
feat(controller): dev-mode warning when config() override skips super…
bpamiri Jun 11, 2026
65fe5d8
fix(model): MSSQL same-batch and Oracle CURRVAL identity fallbacks pl…
bpamiri Jun 11, 2026
3ab921e
perf(public): serve dev-UI assets from cacheable route instead of inl…
bpamiri Jun 11, 2026
efffbc3
fix(model): scope-arg denylist removal, select= dev warning, per-WHER…
bpamiri Jun 11, 2026
0d87445
fix(controller): coerce renderWith JSON types pre-serialization, stop…
bpamiri Jun 11, 2026
383146b
docs(web/guides): add wheels_deploy and wheels_packages to MCP tool t…
wheels-bot[bot] Jun 11, 2026
ac0249d
fix(mcp): replace phantom mcp-configuration-guide.md path with live i…
wheels-bot[bot] Jun 11, 2026
bb9089b
fix(model): struct-form enum() validates the stored value, matching s…
wheels-bot[bot] Jun 11, 2026
1d29756
fix(model): sanitize scope handler args on the QueryBuilder invocatio…
wheels-bot[bot] Jun 11, 2026
43b3a7f
ci: collapse A/B review loop into single Reviewer, opt-in address-rev…
bpamiri Jun 11, 2026
55d1999
docs(web/blog): Anatomy of a Wheels Package (#3024)
bpamiri Jun 11, 2026
65188f4
chore(docs): auto-refresh blog visual baseline after content change […
wheels-bot[bot] Jun 11, 2026
31b9f0e
fix(cli): honour subpath setting in wheels test via --base-path flag …
wheels-bot[bot] Jun 11, 2026
beca3f8
fix(dispatch): replace Lucee-only bare cfabort with abort in public-c…
bpamiri Jun 11, 2026
71a2e9f
fix(events): honor explicit set(allowEnvironmentSwitchViaUrl=true) in…
bpamiri Jun 11, 2026
a6df6be
fix: make URL environment switching work through the app reload resta…
bpamiri Jun 11, 2026
ba57f85
ci: non-dev environment smoke probes (production/testing matrix) (#3044)
bpamiri Jun 12, 2026
f668c50
ci: point docs-verify at the live v4-0-0 tree, untag illustrative blo…
bpamiri Jun 12, 2026
ff65858
fix: rename local.url in $buildRedirectUrl, fixing Adobe reload 500s …
bpamiri Jun 12, 2026
10687fb
fix(events): defer redirect-after-reload past onApplicationStart so U…
bpamiri Jun 12, 2026
db59dc5
fix(bot): reviewer approves to supersede stale request-changes when n…
bpamiri Jun 12, 2026
6c5836b
fix(web/guides): parse-compile {test:compile} blocks by wrap kind ins…
bpamiri Jun 12, 2026
bacb9d9
docs(web/guides): correct reload-password behavior claims in security…
bpamiri Jun 12, 2026
e2862eb
docs(web/guides): correct request-lifecycle stage order, config() lif…
bpamiri Jun 12, 2026
0ed0f20
docs(web/guides): correct caching guide — cache categories, finder-ca…
bpamiri Jun 12, 2026
3c53a66
docs(web/guides): correct environments guide to audited config load o…
bpamiri Jun 12, 2026
73aa81c
docs(web/guides): correct production-config claims and stale source c…
bpamiri Jun 12, 2026
905baab
docs(web/guides): correct debug-panel guide against live framework be…
bpamiri Jun 12, 2026
f73a9cd
ci(docs): attest which wheels binary verify:docs exercises (path + ve…
bpamiri Jun 12, 2026
f84bf8c
fix: case-exact Application componentReference so Adobe reloads survi…
bpamiri Jun 12, 2026
840274b
ci: add Adobe 2023 smoke legs so the #3029 failure class is PR-gated …
bpamiri Jun 12, 2026
f180fb5
docs(web/guides): correct routing guides to verified router behavior …
bpamiri Jun 12, 2026
33ef66e
docs(web/guides): fix sending-email guide mailer pattern, attachment …
bpamiri Jun 12, 2026
70eb85c
docs(web/guides): correct migrations and seeding guides to verified 4…
bpamiri Jun 12, 2026
9dc7919
docs(web/guides): fix audited rate-limiting, CORS, and datasource gui…
bpamiri Jun 12, 2026
79cd0d3
docs(web/guides): fix audited errors in file-uploads and authorizatio…
bpamiri Jun 12, 2026
f864ee8
docs(web/guides): align deploy config-reference + architecture with v…
bpamiri Jun 12, 2026
5e6adca
docs(web/guides): correct 20 audited testing-guide claims (batch 2) (…
bpamiri Jun 12, 2026
600548e
docs(web/guides): align deployment guides with verified wheels deploy…
bpamiri Jun 12, 2026
04da98d
docs: correct five stale CLAUDE.md claims from the guide behavioral a…
bpamiri Jun 12, 2026
52be83e
feat(cli): wheels upgrade applies the framework swap from the CLI bun…
bpamiri Jun 12, 2026
9bacde3
docs(web/guides): document wheels upgrade apply verb across upgrade g…
wheels-bot[bot] Jun 12, 2026
2fb562d
fix(config): catch failing config/*.cfm includes at boot instead of m…
wheels-bot[bot] Jun 12, 2026
5e37877
fix(model): tableName() with an argument fails loud in development in…
bpamiri Jun 12, 2026
957bab9
fix(router): scope()/namespace() honor callback= and auto-close the s…
wheels-bot[bot] Jun 12, 2026
1a8b99e
fix(controller): sendFile honours absolute directory outside the web …
wheels-bot[bot] Jun 12, 2026
32380d9
fix(controller): action-dispatch gate returns 404 for helper-named ac…
wheels-bot[bot] Jun 12, 2026
d0d2781
fix(cli): migration failures reach the CLI exit code (#3105)
wheels-bot[bot] Jun 12, 2026
6ff7dbd
fix(seed): make wheels seed --generate create rows and report honest …
wheels-bot[bot] Jun 12, 2026
5af7248
fix(config): onError no longer masks app-start exceptions in dev (#3108)
wheels-bot[bot] Jun 12, 2026
9b447b7
fix(config): rethrow failing config/*.cfm includes as named Wheels.Co…
bpamiri Jun 12, 2026
b7e49f7
fix(test): surface rejected directory= and 0-bundle runs in test-runn…
wheels-bot[bot] Jun 12, 2026
3a0f90a
docs: align CLI core guides with 4.0.3 behavior — MCP tool names, ver…
bpamiri Jun 12, 2026
26bae0e
docs(web/guides): correct eight behavioral-audit findings in the tuto…
bpamiri Jun 12, 2026
096201f
docs(web/guides): align deploy CLI reference with the shipped positio…
bpamiri Jun 12, 2026
5bddcd1
docs(web/guides): correct upgrading guides against live 4.0.3 behavio…
bpamiri Jun 12, 2026
7fae2a3
docs(web/guides): fix basics + digging-deeper guide audit findings (g…
bpamiri Jun 12, 2026
bb98ffe
docs(web/guides): correct start-here guide drift from the 2026-06 beh…
bpamiri Jun 12, 2026
b08dc2d
fix(dispatch): carry cgi on the middleware request context so req.cgi…
bpamiri Jun 12, 2026
024c4dd
test(router): pin static-beats-placeholder route precedence semantics…
bpamiri Jun 12, 2026
2ea1064
fix(cli): stop numeric --release values from defeating deploy --dry-r…
bpamiri Jun 12, 2026
55af07a
fix(cli): forward --destination to the loader in wheels deploy config…
bpamiri Jun 12, 2026
5bd8c6b
fix(cli): reject multi-colon hosts and dead deploy.yml keys in the de…
bpamiri Jun 12, 2026
3a1eb75
fix(cli): make post-deploy-failure hook best-effort and honor proxy.a…
bpamiri Jun 12, 2026
10b9e3e
fix(cli): resolve deploy ${VAR} secrets from the project root for con…
bpamiri Jun 12, 2026
33c60b6
fix(public): empty reloadPassword disables URL reload; log and rate-l…
bpamiri Jun 12, 2026
25dc794
fix(cli): wheels reload exits non-zero and reports the real verdict i…
bpamiri Jun 12, 2026
e868f86
fix(di): guard onError DI-container rebuild so error pages don't wipe…
bpamiri Jun 12, 2026
8c9a232
fix(middleware): global CORS path defers to wheels.middleware.Cors to…
wheels-bot[bot] Jun 12, 2026
2257dd6
fix(cli): upgrade check recommends csrfCookieEncryptionSecretKey over…
wheels-bot[bot] Jun 12, 2026
cca91dc
fix(cli): split comma-joined action tokens in generate controller (#3…
wheels-bot[bot] Jun 12, 2026
55a4625
fix(cli): wheels test --ci emits GitHub Actions error annotations for…
wheels-bot[bot] Jun 12, 2026
d8c8933
fix(model): hasMany shortcut no longer breaks the association via inc…
wheels-bot[bot] Jun 12, 2026
5ee1a40
feat(cli): restore the jobs worker CLI — wheels jobs work and wheels …
bpamiri Jun 12, 2026
50bc4aa
fix(controller): rendering residuals C13/C16/C17 and $expandedAssocia…
bpamiri Jun 12, 2026
531f126
docs(web/guides): purge retain_containers and the single-divergence c…
bpamiri Jun 12, 2026
66a9878
fix(cli): restore read-side common-port fallback for migrate info/doc…
bpamiri Jun 12, 2026
271931f
fix(controller): guard $generateIncludeTemplatePath against missing p…
wheels-bot[bot] Jun 12, 2026
274952d
perf(dispatch): construct request-start Plugins only inside the mixin…
bpamiri Jun 12, 2026
242e9ae
fix(cli): deploy fresh-host bootstrap — proxy start_or_run per host, …
bpamiri Jun 12, 2026
dd32e19
perf: memoize the promoteIncludedGlobalsToThis key scan per class sur…
bpamiri Jun 12, 2026
c363194
fix(cli): surface deploy remote output, write the audit log, seed fla…
bpamiri Jun 12, 2026
5d89911
fix(cli): wheels test --verbose renders the per-spec tree via the run…
bpamiri Jun 12, 2026
4dbbe22
ci(docs): exercise the checkout's CLI module in docs-verify and attes…
bpamiri Jun 12, 2026
816dd8c
build(deps): Bump esbuild, @vitejs/plugin-vue and vite (#3171)
dependabot[bot] Jun 12, 2026
c94be05
build(deps): Bump esbuild and tsx (#3172)
dependabot[bot] Jun 12, 2026
d0981ed
build(core): land wheels-core flat, protect version sentinel, drop in…
bpamiri Jun 13, 2026
dafbe89
fix(view): give welcome page an accurate title instead of "Wheels - E…
bpamiri Jun 13, 2026
856c49e
ci: freeze the deprecated CommandBox wheels-cli ForgeBox publish path…
bpamiri Jun 13, 2026
66c2ce2
fix(build): make wheels-starter-app installable and bootable from For…
bpamiri Jun 13, 2026
825ef70
docs(web/guides): add Installing with CommandBox support-tier page (#…
bpamiri Jun 13, 2026
9cc4644
build(forgebox): assert wheels-* slug allowlist before publishing (#3…
bpamiri Jun 13, 2026
457aa44
ci(build): add CommandBox install->serve smoke leg for the ForgeBox p…
bpamiri Jun 13, 2026
297fb9b
build(forgebox): rebuild base template from LuCLI template so box ins…
bpamiri Jun 13, 2026
b05d13a
perf(plugin): share the cached PluginObj across request-lifecycle mix…
bpamiri Jun 13, 2026
df62f46
feat(cli): deliver env.secret to containers via remote env file (#295…
bpamiri Jun 13, 2026
986e68a
fix(cli): reload and packages add no longer claim onApplicationStart …
wheels-bot[bot] Jun 13, 2026
5fb4450
fix(cli): make the deploy lock all-or-nothing across hosts and expand…
bpamiri Jun 13, 2026
96bc2ca
fix(cli): five deploy roll-up fixes from the 2957 wave-4 checklist (#…
bpamiri Jun 13, 2026
5e1e828
fix(view): render debug bar env quick-switch links only when switchin…
bpamiri Jun 13, 2026
d02136e
ci: move the single Reviewer off the deactivated Fable 5 to Opus 4.8 …
bpamiri Jun 13, 2026
45e0fc5
test(cli): pin deploy init scaffold env.secret round-trip (#3158) (#3…
bpamiri Jun 13, 2026
68fadcf
fix(deploy): redact interpolated secret values in RemoteExecutionFail…
bpamiri Jun 13, 2026
59d6207
fix(cli): redact interpolated secret values on the deploy accessory v…
bpamiri Jun 13, 2026
9ed06ea
docs: name the MCP packages tool by its bare name (#3207)
bpamiri Jun 14, 2026
35f5ebe
docs(web/blog): Migrations That Survive a Team: the Wheels 4.0 Migrator
Jun 15, 2026
ef02be9
perf: cold-start warm-up, O(1) protected-method gate, schema cache, d…
bpamiri Jun 15, 2026
c81230f
docs(web/blog): Wheels + Claude: Building a Feature via the stdio MCP
Jun 16, 2026
efe88b7
chore(docs): auto-refresh blog visual baseline after content change […
wheels-bot[bot] Jun 16, 2026
e3f64e4
docs(web/blog): close the MCP-drift paragraph in the stdio-MCP post (…
bpamiri Jun 16, 2026
b1a7d84
fix(plugin): make the deprecated plugins/ directory optional (#3211)
bpamiri Jun 16, 2026
c281b41
fix(mapper): cross-engine null-safety for BoxLang strict null handlin…
bpamiri Jun 16, 2026
66e8c2c
fix(model): resolve hasMany shortcut name in include expansion (#3208…
wheels-bot[bot] Jun 18, 2026
8c4b2f3
chore(release): promote changelog into [4.0.4] (+ fix promote tooling…
bpamiri Jun 19, 2026
99d5e2e
Merge main into release branch (ours): develop is authoritative
Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 7 additions & 3 deletions .ai/wheels/channels/channels.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,12 @@ adapter.publish(channel = "orders", event = "created", data = SerializeJSON(orde
var events = adapter.poll(channel = "orders", since = DateAdd("n", -5, Now()));
var events = adapter.poll(channel = "orders", lastEventId = "evt-123");

// Manual cleanup (automatic cleanup runs every 5 minutes)
// Manual cleanup. A throttled sweep also runs on the publish path: at most
// once every 5 minutes (every 15s while a backlog drains), bounded to 1000
// oldest expired rows per pass. Busy multi-server deployments should run a
// scheduled full sweep instead of relying on it.
adapter.cleanup(olderThanMinutes = 30);
adapter.cleanup(olderThanMinutes = 60, maxRows = 10000); // bounded pass; returns rows deleted
```

#### Database Table: `wheels_events`
Expand All @@ -158,10 +162,10 @@ Indexes: `(channel, createdAt)`, `(createdAt)`.

## JavaScript Client

Include `wheels-sse.js` (located at `/wheels/assets/js/wheels-sse.js`) for a zero-dependency EventSource client with auto-reconnect.
`wheels-sse.js` is a zero-dependency EventSource client with auto-reconnect. It is NOT served at any URL by the framework — copy it from `vendor/wheels/public/assets/js/wheels-sse.js` into your app (e.g. `public/javascripts/wheels-sse.js`) and include it with `javaScriptIncludeTag("wheels-sse")` or a plain script tag.

```html
<script src="/wheels/assets/js/wheels-sse.js"></script>
<script src="/javascripts/wheels-sse.js"></script>
<script>
// Basic usage
const sse = new WheelsSSE('/notifications/stream', {
Expand Down
26 changes: 26 additions & 0 deletions .ai/wheels/cross-engine-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,32 @@ createDynamicProxy(consumer, ["java.util.function.Consumer"]);

**Reference example**: [`vendor/wheels/wheelstest/DialogConsumer.cfc`](../../vendor/wheels/wheelstest/DialogConsumer.cfc) shows the CFC-based pattern used by `BrowserClient` to proxy Playwright's `Consumer<Dialog>`. The probe in `$requireDialogSupport` mirrors the real call shape so engine compatibility is verified on the same code path.

### `for` Loops Inside `finally` Blocks Miscompile on Lucee 7

Lucee 7.0.1+100 throws `variable [local] doesn't exist` at runtime when a `for` loop declares or iterates `local`-/`var`-scoped variables inside a `finally` block. Both loop forms are affected — `for (init; cond; step)` and `for (item in collection)`. Isolated with minimal probes: bare assignments and function calls inside `finally` compile and run fine; loops do not. One probe shape even produced a JVM `Expecting a stackmap frame` bytecode-verifier error, pointing at a codegen bug in Lucee's `finally`-block compilation.

```cfm
// WRONG — crashes at runtime on Lucee 7
try {
doWork();
} finally {
for (local.key in local.savedState) {
variables[local.key] = local.savedState[local.key];
}
}

// RIGHT — hoist the loop into a helper; the finally body is just a call
try {
doWork();
} finally {
$restoreState(local.savedState);
}
```

**Why**: Lucee 7's bytecode generation for `finally` blocks mishandles the `local` scope frame for loop constructs. Adobe CF and BoxLang are unaffected.

**Reference example**: `$restoreEmailViewVariables()` in [`vendor/wheels/controller/miscellaneous.cfc`](../../vendor/wheels/controller/miscellaneous.cfc) — the `sendEmail` variables-scope restore runs from `finally` via a `public` `$`-prefixed helper (mixin invariant: helpers must be public). Found while addressing review on [#2922](https://github.com/wheels-dev/wheels/pull/2922).

### `DirectoryCreate()` Second Argument Is Lucee-Only

Lucee accepts `DirectoryCreate(path, createPath, mode)` and recurses parent directories when `createPath=true`. Adobe CF's signature varies by version and at least some Adobe builds reject any second argument with `"The function takes 1 parameter"` (issue #2567).
Expand Down
4 changes: 2 additions & 2 deletions .ai/wheels/security/https-detection.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ component extends="Controller" {
## Important Notes
- Returns `false` for HTTP connections (port 80)
- Returns `true` for HTTPS connections (port 443)
- Works behind load balancers and reverse proxies
- Consider proxy headers (X-Forwarded-Proto) in deployment
- Works behind load balancers and reverse proxies when `set(trustProxyHeaders=true)` is configured
- `X-Forwarded-Proto` is **not** honored by default; enable with `set(trustProxyHeaders=true)` behind a trusted proxy that overwrites forwarded headers
- Test both HTTP and HTTPS scenarios during development

## Common Patterns
Expand Down
14 changes: 14 additions & 0 deletions .ai/wheels/troubleshooting/common-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ Wheels failed to initialize. Check the server log for details.

**Note:** Before the #2774 fix, this failure cascaded into a second `[WO] does not exist` exception that hid the real cause. If you see the old cascade on a version that predates this fix (i.e. 4.0.1 or earlier), the underlying cause is always a failed `onApplicationStart` — see above.

### "key [ENGINEADAPTER] doesn't exist" / "Element WHEELS.ENGINEADAPTER is undefined" in dev error page
**Error (on-page or in server log):**
```
key [ENGINEADAPTER] doesn't exist (Lucee)
Element WHEELS.ENGINEADAPTER is undefined (Adobe CF)
```

**Cause:** An exception during `onApplicationStart` (e.g. `Wheels.Cors.InvalidConfiguration` from an invalid `config/settings.cfm` value) triggered `onError`, which itself crashed because three request-lifecycle helpers — `$getRequestTimeout()`, `$statusCode()`, and `$contentType()` — read `application.wheels.engineAdapter` directly after gating on `$hasEngineAdapter()`. That gate checks both `application.wheels` and the startup-staging struct `application.$wheels`, but the subsequent read assumed the adapter had been promoted to `application.wheels`. When only the `$wheels` branch matched (the failed-startup state), the read threw and replaced the original exception (fixed in [#3108](https://github.com/wheels-dev/wheels/pull/3108)).

**Resolution:**
The `[ENGINEADAPTER]` crash is a symptom — the real error happened during startup. Check the server log for the original `onApplicationStart` exception; common causes include invalid middleware configuration, a missing CFML mapping, or a syntax error in `config/settings.cfm` or `config/routes.cfm`.

After upgrading past the #3108 fix, `onError` surfaces the original startup exception directly.

## Common Association Errors

### "Missing argument name" in hasMany()
Expand Down
18 changes: 11 additions & 7 deletions .ai/wheels/wheels-bot.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
# Wheels Bot

`wheels-bot[bot]` is a custom GitHub App that runs Claude-powered automation on issues and PRs in `wheels-dev/wheels`. Five stages, all opt-out via the `[skip-claude]` label or repo variable `WHEELS_BOT_ENABLED=false`. Slash-command prompts live in `.claude/commands/`; workflows in `.github/workflows/bot-*.yml`. Full user-facing docs: [`docs/contributing/wheels-bot.md`](../../docs/contributing/wheels-bot.md).
`wheels-bot[bot]` is a custom GitHub App that runs Claude-powered automation on issues and PRs in `wheels-dev/wheels`. All stages are opt-out via the `[skip-claude]` label or repo variable `WHEELS_BOT_ENABLED=false`. Slash-command prompts live in `.claude/commands/`; workflows in `.github/workflows/bot-*.yml`. Full user-facing docs: [`docs/contributing/wheels-bot.md`](../../docs/contributing/wheels-bot.md).

## Stages

| Stage | Trigger | Model | Output |
|---|---|---|---|
| Triage | issue opened/reopened | Opus | Comment classifying as `bug` / `framework-design` / `other` (+ confidence on `bug` path). Reads code with the allowlisted tools to resolve uncertainty before rating. |
| Research | bot triage emits `framework-design` marker | Opus | Comment comparing Rails / Laravel / Django / Phoenix / Spring Boot / +1 and recommending a Wheels-idiomatic path (+ confidence). |
| Propose Fix | bot triage emits `triage-confidence:high\|medium` OR research emits `research-confidence:high\|medium` (or `workflow_dispatch`) | Opus | TDD-mandatory draft PR on branch `fix/bot-<issue>-<slug>`. Spec-then-implementation, both required by `bot-tdd-gate.yml`. |
| Reviewer A | PR opened / synchronized / ready_for_review | Sonnet | Single PR review with line comments, verdict, and `wheels-bot:review-a:<pr>:<sha>` marker. |
| Reviewer B | Reviewer A submits a review | Sonnet | PR comment critiquing A for sycophancy, false positives, and missed issues. Loop cap = 3 rounds. |
| Propose Fix | bot triage emits `triage-confidence:high\|medium` OR research emits `research-confidence:high\|medium` (or `workflow_dispatch`). A pre-gate skips the run when a `peter/issue-<N>-*` branch or an open non-bot PR already targets the issue (campaign guard). | Opus | TDD-mandatory draft PR on branch `fix/bot-<issue>-<slug>`. Spec-then-implementation, both required by `bot-tdd-gate.yml`. |
| Reviewer | PR opened / synchronized / ready_for_review (`bot-review.yml`; fork PRs via maintainer-labeled `bot-review-fork.yml`) | Opus | Single PR review with line comments, verdict, and `wheels-bot:review-a:<pr>:<sha>` marker (legacy marker name retained). The prompt includes a self-adversarial pass — refute each finding against the actual code before posting — replacing the retired Reviewer B critique loop. Supersession (#3048): when a still-active wheels-bot `CHANGES_REQUESTED` exists and the re-review has zero blocking findings, the verdict is submitted as APPROVE (nits in the body) so the stale block clears without manual dismissal; fork PRs stay comment-only — the bot never approves a fork PR. |
| Address Review | **opt-in**: maintainer applies the `bot-address-review` label or dispatches `bot-address-review.yml` | Opus | Applies the most recent wheels-bot review's findings on the current head SHA, pushes to the PR branch, comments with `wheels-bot:address-review:<pr>:<sha>:<round>`. |

**Model policy:** judging gate = opus, coding stages = opus, janitorial = sonnet (Fable 5 was the judging gate until its 2026-06 deactivation; the reviewer moved to Opus 4.8) (auto-close, write-docs, update-docs stay on Sonnet).

**Retired (2026-06-11):** the Reviewer A / Reviewer B convergence loop (`bot-review-b.yml`, `/review-the-review`, `/respond-to-critique`) and the `converged-*` auto-fire chain into address-review. The loop was expensive and flaky, B's marginal catch rate no longer justified a second model pass, and the auto-fire push chain landed a broken spec on a PR (#3005). `bot-advisor.yml` (the loop's deadlock-breaker) is retained but inert — its trigger marker is no longer produced.

## Marker conventions (HTML comments, used for idempotency)

- `<!-- wheels-bot:triage:<issue> -->` + `<!-- wheels-bot:triage-class:<bug|framework-design|other> -->` (+ optional `<!-- wheels-bot:triage-confidence:high|medium -->` — either fires propose-fix; low omitted)
- `<!-- wheels-bot:research:<issue> -->` (+ optional `<!-- wheels-bot:research-confidence:high|medium -->` — either fires propose-fix; low omitted)
- `<!-- wheels-bot:fix:<issue> -->` / `<!-- wheels-bot:fix-held:<issue> -->`
- `<!-- wheels-bot:review-a:<pr>:<sha> -->`
- `<!-- wheels-bot:review-b:<pr>:<sha>:<round> -->`
- `<!-- wheels-bot:review-a:<pr>:<sha> -->` — the Reviewer's review marker (legacy name kept for continuity across the single-reviewer consolidation)
- `<!-- wheels-bot:address-review:<pr>:<sha>:<round> -->` / `<!-- wheels-bot:address-held:<pr>:<sha> -->`
- `<!-- wheels-bot:auto-close:<issue> -->`

## Allow-listed scopes per stage
Expand All @@ -31,7 +35,7 @@ Flip the repo variable `WHEELS_BOT_ENABLED` to `false` to halt every bot workflo

## Auto-fire safety net

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`.
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. The Reviewer then reviews whatever propose-fix produces. Address-review never auto-fires — a human opts the PR in via the `bot-address-review` label. All bot PRs land as `--draft` and require a human approving review on `develop`.

## PR-prep automation (release unblocking)

Expand Down
12 changes: 7 additions & 5 deletions .claude/commands/_shared-rails.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,12 @@ they are honored. Violating them is a bug — fix the prompt, not the rails.
matching the configured git author identity. Use `git commit -s` (the
caller workflow's `git config` for `user.name` / `user.email` makes this
the right value automatically) or append the trailer manually before the
`Co-authored-by:` lines. The [DCO GitHub App](https://github.com/apps/dco)
is a required status check on every PR and will block merges if any
commit is missing the trailer. See
`Co-authored-by:` lines. Note: sign-off is project policy verified during
code review — there is NO DCO status check on this repo, and no check will
fail for a missing trailer. When reviewing someone else's PR, treat a
missing sign-off as a fix-before-merge review request (suggest
`git rebase --signoff develop`), never as a failing or required CI check.
See
[`CONTRIBUTING.md` § DCO](../../CONTRIBUTING.md#developer-certificate-of-origin-dco)
for the contributor-facing explanation.
- **Branch naming** for bot-authored work: `fix/bot-<issue>-<slug>` or
Expand All @@ -75,8 +78,7 @@ existing comments for the marker that matches your stage and key. If found,
## Output format

- Start every comment / review with a clear H2 header naming the stage:
`## Wheels Bot — Triage`, `## Wheels Bot — Reviewer A`,
`## Wheels Bot — Reviewer B (round N)`,
`## Wheels Bot — Triage`, `## Wheels Bot — Reviewer`,
`## Wheels Bot — Cross-Framework Research`.
- End with the appropriate marker on its own line.
- No emoji unless the team's existing style uses them (CFML repo norms — minimal).
Expand Down
Loading