| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
* chore: bump develop snapshot target to 4.0.6 * chore: trigger CI on auto-bump PR Empty commit to fire pull_request:synchronize so the develop required checks run (the bot-created auto-bump PR gets no CI under GITHUB_TOKEN). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Peter Amiri <petera@pai.com> --------- Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: bpamiri <180555+bpamiri@users.noreply.github.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…es them (#3230) Both legs were continue-on-error because 4.0.4 couldn't install on those vectors (no arm64 apt index; RHEL Java-detection bug). 4.0.5 ships arch-independent packages + the Java-detection fix, and both are verified live (arm64 `apt install wheels` → 4.0.5; Rocky 9 rpm starts). Remove continue-on-error so a future regression on either vector blocks instead of passing silently. Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Covers the 4.0.4 hardening pass (security, performance, deploy, cross-engine) and the 4.0.5 install-anywhere packaging (arm64 Linux + daily install-smoke CI). 4.0.4 was superseded by 4.0.5 the same day, so this single post covers both. Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…skip ci] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Bumps [form-data](https://github.com/form-data/form-data) from 4.0.5 to 4.0.6. - [Release notes](https://github.com/form-data/form-data/releases) - [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md) - [Commits](form-data/form-data@v4.0.5...v4.0.6) --- updated-dependencies: - dependency-name: form-data dependency-version: 4.0.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.1 to 4.2.0. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/commits) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.2.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…gression guard) (#3234) * ci: add index-integrity probe to distribution install smoke (#3218 regression guard) The daily install-smoke installs the CLI per channel and asserts the version, but that only samples once at 14:00 UTC. The #3218 cross-channel clobber empties the stable apt index for most of the day (a bleeding-edge snapshot wipes it minutes after each stable publish; it's only briefly populated), so a clobber can land outside the install legs' sample window and slip past them — and when they do fail, "apt install failed" doesn't name the cause. Add a fast, container-free `index-integrity` job that probes the PUBLISHED apt and yum dist indexes directly: stable must be non-empty and name the current GA (per arch for apt), and bleeding-edge must stay non-empty (the clobber was bidirectional — apt-wheels#5 scopes regen per-channel to prevent both directions). An empty/missing/stale index fails with a message that names the regression. Reliability: fetches use `curl --retry ... --retry-all-errors` so a transient blip can't false-red the guardian, and all grep checks feed from a here-string (`grep -q PAT <<<"$body"`) rather than `printf | grep -q` — under `set -o pipefail` the latter false-fails when grep -q early-exits and SIGPIPEs the upstream printf of a 100KB index. Verified: 3/3 green against the live indexes, and a negative test (bogus version) correctly fails. Refs: #3218, wheels-dev/apt-wheels#5 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Peter Amiri <petera@pai.com> * ci: probe plain (edge) URLs + hash-consistency, mirroring apt/dnf exactly Two corrections to the index-integrity job after finding the real #3218 failure mode is CDN edge-cache staleness, not just the origin clobber: 1. Drop the `?cb=` cache-buster. apt/dnf fetch the plain URLs that hit Cloudflare's edge; a cache-buster hit R2 origin instead, so the probe went green while real `apt install` failed on a stale edge-cached Packages.gz. Test the SAME plain URLs clients use. 2. Verify hash-consistency, exactly what the package managers verify: parse the SHA256 the (plain) Release records for each binary-<arch>/Packages.gz, fetch the (plain) Packages.gz, and compare — this is precisely the "File has unexpected size" check that failed in #3218. yum: verify the served primary.xml.gz matches the hash repomd references. Then gunzip and confirm the content lists the GA (catches an empty-but-internally-consistent index). Binary correctness: gzip blobs are fetched to a temp file and hashed/gunzipped from the file — never captured into a shell variable, since `$(...)` is text-only and silently corrupts binary (strips trailing newlines, drops NULs), which would compute a bogus hash. Paired with apt-wheels#6 / yum-wheels#6 (set `no-store` on metadata so the edge stops caching it). Verified against the live indexes: correctly RED on the currently-stale apt stable amd64 edge, GREEN on arm64 / bleeding-edge / yum. Refs: #3218, wheels-dev/apt-wheels#6, wheels-dev/yum-wheels#6 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Peter Amiri <petera@pai.com> --------- Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…heelsdi services.cfm example (#3235) The `config/services.cfm` snippet used a top-level `var di = injector();`. A bare top-level `var` statement compile-errors on Adobe CF (#3063) — `config/services.cfm` is an included template, not a function body, so `var` isn't valid there on Adobe. Lucee/BoxLang tolerate it, but the example as shipped breaks for Adobe readers who copy it. Switch to the cross-engine-safe `local.di` form (matching the DI guide and CLAUDE.md), and update the chained references to `local.di.*`. Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#3236) Cache the mixin-integration plan (directory scan + per-file createObject + getMetaData, plus pre-resolved public-method references and a precomputed mixin-override set) once per application instead of re-paying it on every model/controller/mapper materialization. Fixes the per-instance overhead behind #3213; semantics unchanged. Includes an engine-portable guard spec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167uSbSN4vZqQqL5QZfdiQm
…itions (#3239) A custom validation `condition`/`unless` expression of the form `this.propertyIsPresent('productid')` threw `The parameter [property] to function [propertyIsPresent] is required but was not passed in`. `$parseConditionArgs()` only understood named arguments (`key='val'`), guarding on `Find("=", param)`, so a bare positional argument was silently dropped and the target method was invoked with an empty struct. Teach the parser to also collect positional arguments and map them onto the target function's declared parameter names (via GetMetaData), producing a single named-argument struct — named-arg invoke() resolves uniformly across Lucee/Adobe/BoxLang where a numeric-keyed positional argumentCollection does not. Verified on Lucee 7 + SQLite/MySQL and Adobe CF 2023 + MySQL. Fixes #3238 Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…skip ci] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…vice (#3242) The dev-UI / CLI dispatcher vendor/wheels/public/views/cli.cfm was a 935-line god template with a 44-case cfswitch whose command handlers could not be unit-tested (the template only runs under a full HTTP request context). This was the remaining open item of issue #2959 (review finding P2) — the recursive-dispatch + envelope defects shipped in #2966 and the ~1MB inline-asset defect in #3012. Extract every command into wheels.public.CliBridge — a plain, stateless component with one method per command and an explicit command->method allowlist. cli.cfm (now ~112 lines) builds the response envelope preamble (security gate, lazy migration discovery), constructs a context, checks handles(), and calls dispatch(); the result is merged into the envelope. Behavior-preserving: handlers are the exact former case bodies with request.wheels.params -> arguments.params, preamble reads -> context.*, and framework primitives (model(), get(), $cliFormatMigrationStatus(), $cliResolveDumpPath()) reached through context.host. An unknown command stays a silent no-op, matching the former default-less switch. Security: dispatch() only ever invoke()s a method named in the allowlist, and params is passed as a single named argument (never spread), so a query-string key cannot become an arbitrary function argument. The bridge is stateless, so one instance is cached on application.wheels (via $cliBridge()) and shared across requests; ?reload=true re-creates it. Verified: cli/security/migrator suites green on Lucee 7; live dispatch of read-only + POST write-path commands + the SEC-4 mutation gate + unknown no-op + reload all confirmed against a running server; cross-engine matrix (Lucee 7 + Adobe 2023, SQLite) shows the cli bundle 126/126 pass with only pre-existing unrelated failures. Refs #2959 Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…lder stability (#3244) The application template set `this.wheels.rootPath` from `GetDirectoryFromPath(GetBaseTemplatePath())` — the directory of whatever file was originally requested. When a request bootstraps under a subfolder (e.g. the web test runner), the base template is not the public front controller, so rootPath mis-anchors. Because rootPath seeds `this.name` via `Hash(rootPath)` (the issue-359 shared-app-name pattern), an unstable value silently splits one app across two application scopes — a contributor to the "reload=true fixes it" symptom in #3025/#2887. Anchor to `GetCurrentTemplatePath()` instead — always this Application.cfc's own path (the public dir), stable regardless of the requested base template. The value is identical for a normal front-controller request, so existing apps that uncommented `Hash(rootPath)` keep the same app name. (Deliberately not the reporter's `dir & "../"` form, which would shift the value to the app root and change that hash for every existing user — stability, not level, is the fix.) Same idiom already used two lines down in rocketunit_tests/Application.cfc for `this.webrootDir`. Applied to the `wheels new` scaffold, the demo app, both examples, and the legacy test app. Validated: demo app reload/boot clean; onboarding harness (wheels new -> start -> migrate -> generate -> test) green (45/0). Refs #3025, #2887 Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…skip ci] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…a cache (#3250) Two v4 guide gaps surfaced while reviewing GitHub discussions: - buttonTo's input-prefixed attribute convention (inputClass/inputId/inputRel) was documented in v3 guides but dropped in the v4-0-0 restructure (#1352). Added a buttonTo entry to the view-helpers reference explaining that button attributes are input-prefixed because the button renders inside a <form>, and that siblings like linkTo take class/id directly. - The per-model schema/column-metadata cache in application.wheels.models — the cache that goes stale on a live ALTER TABLE and which $clearCache() cannot rebuild — was undocumented (#1481). Added a "Schema and model-metadata cache" section to the caching guide with reload/restart recovery options. Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ect to wheels-base-template (#3253) The Installing with CommandBox page existed to give 2.x/3.x CommandBox teams a clear Wheels 4 answer, but it never mentioned the slug a migrating 2.x user actually has in muscle memory: `box install cfwheels-base-template`. That pre-rebrand slug still resolves on ForgeBox (76,754 installs, top 'cfwheels' search hit) but errors on its missing `cfwheels` core dependency and leaves an unrunnable skeleton. Worse, the 2.x/3.x migration table claimed `box install wheels-base-template` + `wheels-core` was 'Unchanged' — inaccurate for a genuine 2.x user, whose slug DID change (cfwheels-base-template -> wheels-base-template). The row now reads 'Slug changed' and a caution Aside maps the dead cfwheels- prefixed slugs to the supported 4.x slugs (cfwheels-base-template -> wheels-base-template, cfwheels -> wheels-core). Refs #3198 (post-merge review finding) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Permanent named workflow at .claude/workflows/triage-discussions.js,
invokable via Workflow({name:"triage-discussions"}) and visible in
/workflows. It fetches recent GitHub Discussions, drops Announcements
and bot reports, triages each thread, then adversarially verifies
whether each proposed action is genuinely needed against current code
and open issues. Read-only — produces a verified findings report and
posts nothing.
Signed-off-by: Peter Amiri <petera@pai.com>
Co-authored-by: Peter Amiri <petera@pai.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…skip ci] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#3157) * feat(storage): add storage-disk abstraction with local and S3 drivers (#3157) Introduce a pluggable storage layer under vendor/wheels/storage: a small uniform disk interface (put/get/exists/delete/url/signedUrl), LocalDisk and S3Disk drivers, and a StorageManager that resolves named disks from config (mirroring service()/model() resolution). S3 access uses a from-scratch SigV4 signer (no AWS SDK, no JARs): presigned expiring GET URLs plus Authorization-header signing for cfhttp put/get/delete. The HMAC key-derivation and SHA-256 canonical hashing reuse the same cross-engine-green primitives as wheels.auth.JwtService. Phase 1 of #2962. The hasOneAttached model macro (Phase 2), the built-in local-file-serving route, and the storage() controller/model mixin helper are deferred to follow-ups. Tests: storage spec green on Lucee 7 and Adobe CF 2023 (20/20 each); the SigV4 presign test pins the AWS-documented test vector. Fixed a reserved-scope shadowing bug in the spec (Anti-Pattern #11): `var url` reads the URL scope on both engines, so `expect(url)` saw the request struct, not the return value; renamed to `presigned`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Peter Amiri <petera@pai.com> * fix(storage): harden S3 request failures, path encoding, and local signed-url binding Addresses the review on #3248. S3Disk treated a cfhttp connection failure as success. cfhttp does not set throwOnError, so a DNS/connection error returns a non-numeric status (e.g. "Connection Failure") whose Val() is 0, which the `< 300` guards read as OK: put() reported a stored object that was never written, get() returned the error body as the object, and exists()/delete() returned true. Centralize status interpretation in $assertSuccess and throw Wheels.Storage.RequestFailed on any non-2xx or unparseable status (put/get/delete). exists() now returns true on 2xx, false on 404, and throws on a connection failure / 5xx rather than reporting a definitive absence it cannot prove. $request preserves the raw status line for diagnostics instead of collapsing it via ListFirst. The wire path was built from the raw key while the signer signs the RFC3986-encoded canonical path, so keys containing spaces / reserved characters would fail with SignatureDoesNotMatch. Expose S3Signer.encodeKey() and route $objectPath through it so the request and public URLs are byte-identical to what was signed (mirrors the signer's canonicalUri for path-style vs virtual-hosted). LocalDisk.signedUrl() appended contentDisposition outside the HMAC, so a URL holder could alter the served disposition; it is now bound into the signed payload via $signaturePayload (an empty disposition reproduces the legacy "key|expires" payload, so already-issued URLs still verify). Replaced the mislabeled "constant-time-ish" CompareNoCase, which short-circuits on the first differing character, with a length-then-XOR-accumulate $secureEquals that does not leak the match length through timing. Adds 7 specs to StorageSpec.cfc: four asserting put/get/exists/delete throw on a refused connection (pointed at 127.0.0.1:1), one for public-url encoding, one for disposition binding, and one wrong-length-token guard. Storage bundle is 27/27 green on Lucee 7 and Adobe 2023. Signed-off-by: Peter Amiri <petera@pai.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(storage): bump interface-count guard and make S3 request timeout configurable The storage PR adds vendor/wheels/interfaces/StorageDiskInterface.cfc — the 24th interface file — which tripped the structural guard in InterfaceCompilationSpec ("finds exactly 23 interface files", now 24). Bump the expected count and note that it tracks the wheels.interfaces.* contract set. The full CI suite caught this; the storage-only bundle run and the diff-based bot review did not, since the guard lives in tests/specs/interfaces. Also make the S3 request timeout configurable (config.timeout, default 60s) and use a short timeout in the connection-failure specs. In CI the get() failure spec waited the full 60s cfhttp timeout (only GET hung; PUT/HEAD/DELETE were refused instantly), adding a minute to every core run; capping it keeps the suite fast without changing the production default. Storage + interfaces bundles: 30/30 green on Lucee 7 and Adobe 2023. Signed-off-by: Peter Amiri <petera@pai.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (#3249) The $fromClause join builder decided whether to wrap inner joins in a parenthesized group bound to the LEFT OUTER JOIN purely from a regex on the include string (any `Intermediate(Target)` shape). That issue #449 HABTM/through grouping over-fired on plain belongsTo-chain nested includes such as `include="SecondaryContact(User)"`, nesting an inner join whose ON clause references the root FROM table — which scopes the root out and makes MySQL reject it with "Unknown column ... in 'on clause'". This was a regression from Wheels 2's flat joins. Consult the association metadata instead: only set needsNesting when the parenthesized intermediate association is a genuine hasMany/hasOne bridge (the OUTER-joined case the grouping was designed for). A belongsTo intermediate now falls through to the flat-join branch, restoring Wheels 2 behavior, while real HABTM/through includes still nest unchanged. Verified on SQLite and MySQL 9.7: crudSpec 163/0/0 (incl. new #3245 + #449 regression specs) and hasManyShortcutSpec 13/0/0; full model suite 923/0/0 on SQLite. Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…SQL properties (#3254) * feat(model): add includeCalculated to additively opt in select=false SQL properties Adds an `includeCalculated` argument to findAll(), findOne(), and findByKey() that opts already-declared calculated SQL properties (property(name=..., sql=..., select=false)) back into a single finder. Unlike `select`, it is additive — the named calculated properties are merged on top of the default column list inside $createSQLFieldList rather than replacing it, so the rest of the record is still returned. This closes the inverse of the existing select=false declaration: a property kept off the hot path can now be pulled back in per-call without hand-listing every other column. Unknown names throw Wheels.CalculatedPropertyNotFound in development/testing and are ignored in production, mirroring existing dev-only validation patterns. Pure list manipulation — no closures, struct member functions, or other cross-engine traps. Fixes #3252 Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> * docs: document includeCalculated finder argument for select=false SQL properties Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> * docs: fix premature code fence in calculated-properties snippet The includeCalculated usage example introduced a closing code fence mid-section, ejecting the method-based calculated-property example out of the cfm block and orphaning the trailing fence (12 -> 13 fences). Remove the stray fence so the whole section renders as one code block; fence parity restored to 12, matching develop. Signed-off-by: Peter Amiri <petera@pai.com> --------- Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Peter Amiri <petera@pai.com>
…#3255) Adds $resolveSubpathInclude() on Global.cfc to prefix the framework-relative app test-runner include with the app's resolved webPath, so /wheels/app/tests and `wheels test` work under URL-subpath / CommandBox multi-subfolder installs (item 2 of #3251). Pure helper with full unit coverage in resolveSubpathIncludeSpec.cfc, including the no-arg production call shape. Refs #3251 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…skip ci] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…hat it did not throw (#3362) $decryptCsrfCookieValue() read a cookie by trying the configured algorithm and falling back to the legacy bare "AES" (ECB) only from its catch block. That treats "did not throw" as "decrypted correctly", which is not true of a wrong-mode decrypt. Decrypting an ECB ciphertext under AES/CBC/PKCS5Padding throws only when the trailing plaintext bytes fail padding validation. They pass by chance roughly 1 time in 256 — Decrypt() then returns garbage, the legacy fallback never runs, and a perfectly good legacy cookie reads as corrupted. AES/GCM/NoPadding is authenticated and does reliably throw, so this only ever affected the engines that fall back to CBC. Impact is a reliability wart in the legacy-cookie migration window, NOT a security hole: the caller checks IsJSON() and returns "", so it fails closed. Worth saying plainly rather than inflating. The fix checks the RESULT. This cookie's plaintext is always the JSON that $generateCookieAuthenticityToken() writes, so JSON-ness is an invariant of the cookie rather than an assumption about it, and a non-JSON result means the wrong algorithm was used — which is exactly when the legacy attempt should still run. The legacy result is only preferred if it too looks like the payload, so a genuinely corrupt cookie is reported exactly as before. Found by diffing compat-matrix legs on #3351: CsrfCookieCipherSpec's legacy-AES case failed on lucee6/sqlite. It exercises this path and its payload carries a CreateUUID(), so the coin flip differs per run — across 326 non-empty legs it failed once. 1/326 = 0.31% observed against ~0.39% theoretical for random PKCS5 padding validity. Testing that deterministically needed a way to reach "returned garbage without throwing" every time rather than 1 run in 256. Probed the three behaviours on this engine: AES/CBC/PKCS5Padding -> THREW ("Given final block not properly padded") AES/CBC/NoPadding -> RETURNED, not JSON <- same state, every time AES/ECB/NoPadding -> RETURNED, valid JSON (same mode, decrypts fine) so the spec forces AES/CBC/NoPadding. Red-first on a COLD server (a warm one silently reuses the old compiled CFC): 288 pass / 1 fail / 1 error, the failure being `Expected [false] to be true` on exactly this path. lucee7 + sqlite, full core suite: develop 2c98644 4755 pass / 0 fail / 0 error this branch 4757 pass / 0 fail / 0 error Exactly +2, the new specs. Closes #3361 Signed-off-by: Peter Amiri <peter@alurium.com>
…tree (#3363) Removes seven generated TestBox stub files that should never have been committed, and stops the whole class of mistake at the source. `public/testbox/system/stubs/*.cfm`, `rewrite.config` and a root `box.json` are all written into the working tree by a local test run or dev server. None are source. None were ignored either — so any `git add -A` after running the suite swept them into the diff. That happened twice. I caught it in self-review across the #3334 / #3350 / #3349 / #3325 / #3351 batch and cleaned all five branches before they merged. I then did exactly the same thing on #3362 and did not catch it, so seven stubs are on develop. Verified they really are regenerated rather than needed: deleting every file in public/testbox/system/stubs/ and running the full core suite gives 4757 pass / 0 fail / 0 error, with TestBox recreating seven of them during the run. With the ignore in place a full run now leaves `git status` completely clean, which is the property that was missing. One file (F952D54F…) was already tracked before any of this and is deliberately left alone — ignoring a directory does not untrack what is already in the index, and it is not mine to remove in a cleanup commit. Signed-off-by: Peter Amiri <peter@alurium.com>
…e SQL text (#3360) * fix(model): read a join's type from association metadata, not from the SQL text Follow-up to #3334, raised by wheels-bot on PR #3354 and correct. The grouping decision that PR introduced still asked "is this an INNER join?" by searching the generated SQL: FindNoCase("INNER", local.joins[local.parentPosition]) That misclassifies every table whose name contains the substring — `winners`, `spinners`, `beginners`. A parent LEFT OUTER JOIN on `c_o_r_e_winners` reads as INNER, so its nested child is emitted flat instead of grouped, which silently drops the parent rows #3334 exists to preserve. Not a regression: the pre-fix code scanned the same way, and no fixture table hits it. Worth fixing anyway, because it is the exact anti-pattern that PR argues against — its whole thesis is that structure should come from the association tree rather than from re-parsing the SQL string, and this was ten lines of new code doing the opposite. `$associationJoinsInner()` reads the association's declared `joinType`, which is the value `$expandedAssociations` turns into the leading `INNER JOIN` / `LEFT OUTER JOIN` text in the first place — so it is the authoritative source by construction. It falls back to the text scan only when an entry carries no `joinType`, keeping it total for a hand-built struct. Red-first: with the helper reverted to the bare text scan, the new spec fails `Expected [true] to be false`. A note on that red-check, because it nearly fooled me: run against a WARM server the reverted build still reported green — `tools/test-local.sh` reuses a running server and the reload did not recompile the changed CFC. The failure only appears after killing the server first. Any red-check on this suite needs a cold start to mean anything. lucee7 + sqlite, full core suite: develop 2c98644 4755 pass / 0 fail / 0 error this branch 4756 pass / 0 fail / 0 error Exactly +1, the new spec. Refs #3334 Signed-off-by: Peter Amiri <peter@alurium.com> * test(job): scope JobClassRoundTripSpec locals with local. Second wheels-bot review follow-up from the same batch, on PR #3358. Every sibling in vendor/wheels/tests/specs/jobs/ declares spec variables with `local.` (e.g. `local.bootstrapJob = new wheels.Job();` in JobRobustnessSpec). JobClassRoundTripSpec assigned them unscoped, which runs green but leaks the writes into the spec's `variables` scope. Purely a convention fix — no behaviour change. Folded in here rather than opened as a third PR, since it is the same review round on the same merged batch. lucee7 + sqlite, full core suite: 4756 pass / 0 fail / 0 error, unchanged. Signed-off-by: Peter Amiri <peter@alurium.com> * fix(test): keep the catch-block struct unscoped — local. breaks it on BoxLang The compat matrix for this branch came back +24 tests on all 28 legs, which is correct, and two NEW failures on boxlang for every database: Failed | throws Wheels.JobClassNotFound naming the row and the class | Expected [Wheels.JobClassNotFound] but received [] Failed | throws Wheels.InvalidJobClass when the path resolves to something that is not a job | Expected [Wheels.InvalidJobClass] but received [] Both are mine, and both were caused by the previous commit — the `local.`-scoping convention nit from the bot review. `thrown` is written from inside a catch block, and on BoxLang the catch body runs under a nested `local` that is discarded on exit. Prefixing the struct made `local.thrown.type = e.type` land on that discarded copy instead of mutating the outer struct, so the assertion read an empty type. Cross-engine invariant 11 already covers the scalar case. What it did not say is that the struct workaround it recommends only works when the struct is accessed WITHOUT the prefix — `local.state.flag = true` fails exactly like `local.X = ...`. The prefix is what breaks it, not the assignment shape. Widened the invariant with that, plus a worked example, because `local.`-scoping spec variables IS the house style everywhere else, which makes tidying a catch-using spec to match an easy and completely invisible way to break it. So the original unscoped form was correct and the "nit" was wrong. Reverted for `thrown` only — every other variable in the file stays `local.`-scoped, since those are written from try bodies and are unaffected — with a comment at both sites explaining why, so it does not get tidied back. Worth noting the failure mode: green on Lucee, green on Adobe, wrong only on BoxLang, and silent rather than an error. Nothing local would have caught it. lucee7 + sqlite, full core suite: 4756 pass / 0 fail / 0 error, unchanged. Signed-off-by: Peter Amiri <peter@alurium.com> --------- Signed-off-by: Peter Amiri <peter@alurium.com>
…ed them (#3364) Third occurrence of the same slip, with a wrinkle worth recording. PR #3363 untracked seven generated TestBox stubs and added the .gitignore entries that stop them coming back. PR #3360 then merged on top and re-added all seven — its branch was cut from 2c98644, before the ignore existed, and it was committed with `git add -A` while the paths were still untrackable-but-not-ignored. Merging the cleanup first and the older branch second undid the cleanup. The .gitignore is on develop now, so a branch cut from this point cannot repeat it. This commit just finishes what #3363 started: seven removals, no other change. Verified after committing that exactly one stub remains tracked — F952D54F…, which predates all of this and is deliberately left alone. The lesson is about ORDER, not just about `git add -A`: after merging a cleanup PR, every already-open branch that predates it still carries the thing that was cleaned, and merging it silently reverts the fix. Check the remaining branches for it before merging them, not after. Signed-off-by: Peter Amiri <peter@alurium.com>
…t-fail check, honest summary (#3366) * ci: compat-matrix safe-slice hardening for #3302 Three workflow-plumbing fixes in compat-matrix.yml, shippable before the continue-on-error flip (which stays gated on PR #3365 merging plus one green dispatch on develop): - run-tests: per-leg zero-test guard — an HTTP 200/417 leg reporting totalSpecs below 4000 (suite runs ~4,700) now emits ::error:: and sets the leg fail flag, so a compile-wiped leg fails loudly instead of rendering as a pass. SOFT_FAIL_DBS is respected. - publish-results: fail_on: nothing on publish-unit-test-result-action, so oracle soft-fail debt stops pinning a red aggregate 'Wheels Test Results' check to innocent dispatch SHAs. Annotations, PR comments and artifacts are unchanged; leg gating stays in the tests job. - test-matrix-summary: zero-test legs render as ':warning: N tests' instead of a checkmark, soft-fail DB failures render as :warning:, and the Oracle column is annotated as soft-fail with a footnote. The rustcfml job is untouched (intentionally informational). Refs #3302 Signed-off-by: Peter Amiri <petera@pai.com> * ci: align per-engine summary with the zero-test guard Review follow-up for the #3302 safe slice: the per-engine step summary still rendered a compile-wiped leg (0 failures, 0 testcases) as a pass while the run-tests guard in the same job failed it with ::error::. Read totalSpecs alongside the failure count and render sub-floor legs as ':warning: N tests (zero-test guard)', mirroring the matrix grid. Refs #3302 Signed-off-by: Peter Amiri <petera@pai.com> --------- Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com>
…ForgeBox guidance (#3367) channels.mdx and server-sent-events.mdx still denied that WebSocket support exists, pointing readers at roadmap issue #2962. The first-party wheels-websockets package shipped at v0.2.0 and is installable via 'wheels packages add wheels-websockets' (RustCFML native since v0.1.0, Lucee 6.2+ verified live, Lucee 7 pending 7.0.2.7+ plus a jakarta-compatible extension release, Adobe/BoxLang demand-gated with graceful SSE fallback). Both pages now document the package instead of denying it, matching why-wheels.mdx. installing-with-commandbox.mdx claimed cfwheels-base-template 'still resolves on ForgeBox but errors on its missing cfwheels core dependency' — the slug is now fully unlisted and box install fails with 'entry slug invalid or does not exist'. Also adds the legacy cfwheels-cli slug to the old-to-new mapping, pointing readers at the standalone wheels CLI. Refs #3292, Refs #3182 Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
#3346) (#3368) * feat(model): allow select() and friends to start a query-builder chain (#3346) Extends the chain-entry dispatch list in model onMissingMethod with select, include, group, distinct, and forUpdate so every QueryBuilder method can start a chain directly on the model class, matching where() and orderBy(). Adds forUpdate to the ScopeChain builder-transition list for parity. User-defined scopes keep precedence in both dispatchers. Specs pin the issue's exact example (select().where().get()), the returned columnList, include/group/distinct smoke coverage, and the scope-to-forUpdate transition. Guide and CLAUDE.md quick reference now document the entry-position builder methods. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Peter Amiri <petera@pai.com> * fix(model): correct precedence note on the builder chain-entry dispatch list The comment added in the #3346 change claimed dynamic finders and association setters take precedence over the chain-entry list, but both branches dispatch AFTER it in onMissingMethod — only user-defined scopes and enum checkers run first. Comment-only change; no behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Peter Amiri <petera@pai.com> --------- Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Adds an upgrading-section page documenting the measured 4.0.x per-instance mixin-integration regression reported in #3213, the #3236 fix and its measured deltas (Lucee 7 + SQLite), the June 2026 profiling baseline, and how upgraders get the fix (bleeding-edge until the v4.0.6 cut). Registered in the v4-0-0 sidebar to avoid the orphan-page trap. Refs #3213 Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com>
Ports the v3-0-0 page into the v4-0-0 tree, corrected for 4.0.x: the super<name> convention now applies to models AND controllers/view helpers (parity landed with the #3325 fix, PR #3357). Documents placement conventions (single controller, app/controllers/Controller.cfc, app/views/helpers.cfm), the superLinkTo(argumentCollection=arguments) delegation pattern pinned by SuperOverrideSpec.cfc, the dataConfirm data-* pass-through alternative for the jsconfirm use case, and a version callout with the CreateObject workaround for 4.0.x builds predating the fix. Explicitly notes mapper internals are not an override surface. Registers the page in the v4-0-0 sidebar and section index, and adds a version-scope note to the v3-0-0 page (model-only in 3.x). Fixes #3343 Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…3025) (#3373) The web test runner (vendor/wheels/tests/runner.cfm) swaps the LIVE application.wheels struct for test configuration (backup in application.$$$wheels) and restores it at the end of the run. Overlapping test requests clobbered each other's backup, which could restore TEST config as the live config until the next reload=true, or error the parent run's restore outright (key [$$$WHEELS] doesn't exist). Stage-1 slice of the issue-3025 analysis (the titled request-scoped overlay was ruled infeasible in the 2026-06-22 adversarial analysis): - Wrap the swap->run->restore window in an exclusive named cflock ('wheelsTestRunner_<applicationName>', timeout 1800s, throwOnTimeout), following the swap-under-lock precedent in migrator/TenantMigrator.cfc. - Re-entrancy guard: ParallelRunner partition sub-requests re-enter runner.cfm via fresh top-level HTTP GETs while the parent holds the swap. They detect the already-applied swap (application.$$$wheels exists) and skip BOTH the swap and the shared lock (a unique per-request lock-name suffix), so parallel mode cannot deadlock. - The restore now runs in a finally block, and only the request that created the backup restores it — an erroring suite no longer leaves test config live. No loops in the finally block (Lucee 7 miscompiles local-scoped loops in finally). - Delete the orphaned legacy RocketUnit runner twin vendor/wheels/rocketunit_tests/Test.cfc (nothing loads it; the Global.cfc scan-exclusion entry stays). The active CLI/legacy chain via wheels.Test and $restoreTestRunnerApplicationScope is untouched. New spec vendor/wheels/tests/specs/internal/TestRunnerSwapLockSpec.cfc: structural guard for the lock acquisition + re-entrancy detection, and a behavioral nested-run test that reproduced the backup clobber before the fix. Residual gap (intentional, refs #3025): this serializes test-vs-test only. A normal request concurrent with a test run still reads swapped config; true isolation is deferred to a separate-application-context design. Refs #3025 Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com>
… un-hides (#3345) (#3370) * fix(events): render debug bar restore button outside the container it un-hides (#3345) The minimized 'Debug' restore button (##wdb-minimized) was nested inside the ##wheels-debugbar container, which wdbMinimize() hides with display:none — a descendant of a display:none element never renders, so the button could not appear and the bar stayed hidden for the whole browser session (sessionStorage re-minimizes on every load). Move the ##wdb-minimized block after the container's closing div, still inside the cfsavecontent so the whitespace-collapse ReReplace applies. It is independently position:fixed, so as a sibling it stays visible when the container is hidden. The debugbar.js script include moves below both elements because its load-time wdbMinimize() re-invocation does getElementById('wdb-minimized') and must find it in the DOM. New structural spec asserts id="wdb-minimized" appears only after the div balance for ##wheels-debugbar returns to zero (sibling, not descendant). Signed-off-by: Peter Amiri <petera@pai.com> * fix(events): isolate the relocated restore button from host-page CSS As a sibling of ##wheels-debugbar the ##wdb-minimized button no longer sits under the container's all:initial isolation or its font stack, so the host app's global CSS bled in (font-family:inherit resolved to the app's body font — verified live: the demo app's Lato replaced the system stack). Mirror the container's guard on the sibling: all:initial first, then its own declarations plus the same font-family. The JS display toggling still wins because style.display updates in place after the all shorthand. Live-verified on Lucee 7: minimize/restore cycle, sessionStorage persistence across reload, button at bottom-right 8px/8px in the system font. Full core suite 4759 passed / 0 failed / 0 errors. Signed-off-by: Peter Amiri <petera@pai.com> --------- Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com>
The debug bar's reload link was built from raw cgi.script_name, so under a subfolder (subpath) deployment it emitted /myapp/public/index.cfm/... links that the user's rewrite rules do not route. Compose the base from the resolved webPath plus the front-controller filename instead — the same idiom urlFor() uses — extracted into the unit-tested $buildDebugReloadUrl() helper in Global.cfc (public $-prefixed per cross-engine invariant 7). The helper preserves the previous behavior exactly for root installs (rewriting on and off, pinned byte-for-byte in the spec), keeps the request.cgi.path_info vs cgi.path_info branch, the rewriteFile strip, and the reload-param scrub, and falls back to the raw script name when webPath is not resolved yet (early boot/error paths). The CFML error page's displayed URL had the same class of defect and now uses the same webPath composition (plus HTML-encoding of the base). Fixes #3344 Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
) The two forUpdate() chain-entry specs added by PR #3368 used .count() as the terminal. Postgres and CockroachDB reject aggregate functions combined with FOR UPDATE, so every postgres/cockroachdb leg in the compat matrix failed with 'FOR UPDATE is not allowed with aggregate functions'. The specs pin chain-entry dispatch, not locking semantics, so a non-aggregate .get() terminal asserts the same behavior on every engine. Verified locally on lucee7 + cockroachdb (previously failing leg): 4775 pass / 0 fail / 0 error; lucee7 + sqlite: 4763 pass / 0 fail / 0 error. Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Removes continue-on-error from the tests job now that the debt gate is cleared: PR #3365 merged and dispatch run 30976993285 on develop came back fully green (all 5 engine jobs, aggregate 6 oracle soft-fails only, which stay non-blocking via SOFT_FAIL_DBS under #2663). The rustcfml job keeps its continue-on-error — it is an informational lane by design. From now on a red engine leg turns the weekly run banner red instead of being silently swallowed. Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.0 to 4.3.1. - [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.1/CHANGELOG.md) - [Commits](nodeca/js-yaml@4.3.0...4.3.1) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.3.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
…rvive Adobe teardown On Adobe ColdFusion 2023 the live application scope is unreliable during applicationStop() teardown, so bare application.wo could resolve to a stale Java String[] and throw "Element wo is undefined in a Java object of type class [Ljava.lang.String;", erroring the whole site until a CF service restart. onApplicationEnd() now invokes the Wheels global through the passed-in arguments.applicationScope.wo, guarded with StructKeyExists, so a partially reclaimed scope degrades to a no-op. Applied to the CLI app template, the demo app, and the bundled example apps. Refs #3379 Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
…Adobe CF Add cross-engine invariant #19 to CLAUDE.md and a matching section to .ai/wheels/cross-engine-compatibility.md: during applicationStop() teardown on Adobe CF 2023, bare application.wo can resolve to a stale Java String[] and throw 'Element wo is undefined...'. Route onApplicationEnd() through arguments.applicationScope.wo instead and guard with StructKeyExists (#3379, fixed in #3380). Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
…scope guard The #3380 structural spec only scanned the CLI template and demo Application.cfc. The same teardown guard was applied to the bundled example apps, so a revert there would have gone uncaught. Extend the targets list and add a discovery check over the shipped trees so a newly added onApplicationEnd copy cannot slip the list. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com>
test(cli): cover every shipped onApplicationEnd handler for #3379
build(deps-dev): Bump js-yaml from 4.3.0 to 4.3.1
…ui/ip-address-10.4.0
…er/testui build(deps-dev): Bump ip-address from 10.2.0 to 10.4.0 in /tools/docker/testui
* fix: advertise wheels packages add on docs, help, and packages site LuCLI intercepts `packages install` before Module.cfc, so user-facing copy, --help, and the packages website now lead with `add`. The Basecoat chapter also copies the showcase from vendor/ after add, not GitHub. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * chore(web): refresh packages-site visual baselines for add verb The packages index and wheels-sentry canaries now screenshot `wheels packages add` instead of the intercepted `install` verb. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
) * fix(test): isolate the web runner in a separate application scope (#3374) Bind test-runner, TestClient, and browser requests to <this.name>_wheelsTest so the live application.wheels is never swapped. Keep the #3373 named lock as a fallback for apps without the Application.cfc include. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * fix(test): avoid reserved client scope in $testClient (#3374) Rename the $testClient local so Lucee no longer throws "client scope is not enabled" on every TestClient call. Resolve Application.cfc paths via the /config mapping instead of walking GetDirectoryFromPath on a directory. Map `internal` in tools/test-local.sh. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * fix(test): clear controller caches after the isolated-app path swap Isolated-app boot mounts browser-fixture controllers on controllerPath. A later lookup of controller("wheels") then falls through to that last-path Controller.cfc stub, which has no mixins. Disable the fixture remount during the swap, clear controller/file caches, and point interface specs at Test.cfc. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* refactor(global): decompose Global.cfc into focused includes Split the 4.8k-line Global.cfc monolith into vendor/wheels/global/*.cfm collaborators included at component-body scope so inheritance stays byte-identical (no per-instance mixin copy). Adobe getMetaData() does not enumerate includes, so protected-method discovery and Mapper integration now also scan the include files. Closes #3241 Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * fix(global): wrap Global includes in cfscript so they compile An include is tag-context, so bare script leaked as response text and the app failed to boot. Match /app/global/functions.cfm. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * fix(global): restore closing brace on last Global include function The splitter treated the last function's closing brace as the component closer and dropped it, so lifecycle.cfm failed to compile. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * docs(changelog): add fragment for Global.cfc decomposition Repo DoD requires changelog.d even for internal refactors. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * fix(global): keep $include helpers on Global.cfc Lucee compiles functions from component-body includes as UDFs of the include template. include inside those UDFs resolves against vendor/wheels/global/ and skips /app mappings, so onAbort's $include of onabort.cfm looked under the webroot and every abort became HTTP 500 (LuCLI + smokes). Co-authored-by: Peter Amiri <peter@alurium.com> Signed-off-by: Cursor Agent <cursoragent@cursor.com> * fix(global): pin abort-guard depth to Global.cfc path $abortInvalidRequest compared GetBaseTemplatePath() to GetCurrentTemplatePath(). After the include split, Lucee reports the mapping-absolute include (/wheels/global/request.cfm) as the current template — fewer segments than public/index.cfm — so every front- controller request looked invalid, hit onmissingtemplate, and 500'd onAbort. Measure depth against ExpandPath("/wheels/Global.cfc"), the same filesystem path the pre-split method used. Co-authored-by: Peter Amiri <peter@alurium.com> Signed-off-by: Cursor Agent <cursoragent@cursor.com> * fix(global): resolve event includes via /app mapping Application.cfc concatenated "../../" onto eventPath (/app/events), so $include received ../../../app/events/onabort.cfm. After the include split that missed (public/app/events/...) and 500'd onAbort — LuCLI 1 fail / 4 error, plus onapplicationend in Lucee smokes. $resolveGlobalIncludeTemplate() collapses a leading ../ chain as if the include lived on Global.cfc, and shipped Application.cfc copies now pass eventPath through unprefixed. Co-authored-by: Peter Amiri <peter@alurium.com> Signed-off-by: Cursor Agent <cursoragent@cursor.com> * fix(global): hoist include resolve for Adobe reload teardown A function call inside include "#...#" made Adobe CF 2023 re-evaluate Global.cfc's component-body includes during applicationStop(). /wheels mappings are already gone, so authorized ?reload=true returned 500 instead of 302. Resolve to a local first, then include that local. Also drop an unescaped hash from GlobalSurfaceIdentitySpec's it() title so the LuCLI core suite can compile the bundle. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * fix(test): escape eventPath hashes in GlobalSurfaceIdentitySpec The it() title interpolates #eventPath# at compile time, so the LuCLI core suite errors with "variable [EVENTPATH] doesn't exist" and the bundle never runs. Escape as ##eventPath##. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * docs(test): note why GlobalSurfaceIdentitySpec escapes hashes A raw #eventPath# in the it() title interpolates at compile time and the LuCLI bundle never loads. Keep the title escaped. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> * fix(global): fall back when Adobe teardown drops /wheels mappings Authorized reload still 500ed after the resolve hoist. Adobe logs: Could not find the included template /wheels/global/locking.cfm sequence: public/index.cfm, line: 191 applicationStop() clears THIS.mappings, then onApplicationEnd calls $include and Adobe re-evaluates Global.cfc's component-body includes. The hoist never touched those mapping-absolute includes. Keep the includes in the component body (method-body includes do not become component methods) and fall back to a CFC-relative path, then a public/index.cfm-relative path. $include uses the same fallbacks so onapplicationend.cfm can load after /app is gone too. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Peter Amiri <peter@alurium.com> --------- Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Pin-only smoke of develop against cybersonic/LuCLI 0.6.1. Does not address #2963. Do not merge until Peter says so. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Promote the 48 pending changelog.d fragments into the [4.0.6] release section via tools/changelog-promote.sh, and clear the fragment folder. Done on develop first (per the release playbook) so develop's [Unreleased] does not go stale and force a back-port after the cut. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
| Back | FazBrowse Home | New Git URL |
Cut 4.0.6. See CHANGELOG.