Skip to content

fix(public): isolate helpers include from $init promote scan - #3425

Merged
bpamiri merged 2 commits into
developfrom
cursor/issue-3379-emptystack-62b1
Aug 26, 2026
Merged

fix(public): isolate helpers include from $init promote scan#3425
bpamiri merged 2 commits into
developfrom
cursor/issue-3379-emptystack-62b1

Conversation

@bpamiri

@bpamiri bpamiri commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adobe CF 2023 + CommandBox first cold start throws EmptyStackException from NeoPageContext.popSuperScope while onapplicationstart.cfc instantiates the Public GUI ($createObjectFromRootPublic.$init). This is a 4.0.6 regression: discarding the 4.0.6 files and returning to 4.0.5 clears it; a later page load can succeed.

This PR is the first-boot EmptyStack path only. It is not the torn-down application.wo / onError String[] report on issue 3379. No onError, EventHandlerInterface, Application.cfc template, or onSessionEnd edits.

Refs #3379. HEAD: d96d6024ad0b6c839d5d20f2aae8620c5e7b1f6b.

Root cause (verified)

The desk hypothesis holds. Investigation, not just the write-up:

  1. Field stack from the 4.0.6 CommandBox report lands on vendor/wheels/events/onapplicationstart.cfc:409$createObjectFromRoot(path="wheels", fileName="Public", method="$init"). Adobe then pops a super-scope inside UDFMethod.invoke (NeoPageContext.popSuperScope) and the stack is empty.
  2. That call site already existed in 4.0.5 (line 419). It is not new work.
  3. The 4.0.6 delta is inside Public.$init (commit 6bff05441f / PR 3302): after include "/wheels/public/helpers.cfm" it now calls $scanAndPromoteIncludedGlobals(). That helper is a parent-class method on wheels.Global. 4.0.5 $init is include + return only.
  4. Same-method include + parent-method promote nests Adobe's include page-context with a super-scope push. On the first compile of helpers.cfm after a CommandBox cold start, popSuperScope runs against an empty stack. A later request succeeds because the include is already compiled — matching the report.
  5. The promote itself is still required (Lucee 6 / Adobe 2023 / Adobe 2025 do not lift include UDFs onto this). The memoized $promoteIncludedGlobalsToThis() wrapper cannot be used here: Global's pseudo-constructor already cached a pre-include key list for wheels.Public.

4.0.5 vs 4.0.6 vs develop

Tree SHA Public.$init
4.0.5 tag 51eb6e47 include "/wheels/public/helpers.cfm" then return this
4.0.6 tag 5928131d same include, then $scanAndPromoteIncludedGlobals(), then return
develop (this base) c64f6a9b identical to 4.0.6

$createObjectFromRoot(..., Public, $init): 4.0.5 line 419, 4.0.6 and develop line 409. Same call.

What changed

  • Extracted the helpers include into $includePublicHelpers() so its UDF / page-context frame returns before $init calls the parent-class promote scan.
  • $init still calls the raw $scanAndPromoteIncludedGlobals() (not the memoized wrapper) so the 4.0.6 this-visibility contract stays.
  • Smallest production edit: Public.$init / the new include helper only.

PROVEN

Claim Evidence
Hypothesis confirmed 4.0.5 $init has no promote call; 4.0.6 / develop add $scanAndPromoteIncludedGlobals() in the same method as the include; field stack is $createObjectFromRoot$initpopSuperScope
Include and promote no longer share a $init frame PublicInitPromoteSpec: trace is include-start, include-return, promote
4.0.6 promote behavior kept Same spec: $$findMatchingRoutes and pageHeader are custom functions on this after $init; $createObjectFromRoot path still returns a promoted instance
Nest cannot silently return Structural scan: $init body has no raw include statement
$init does not throw on LuCLI Same spec (Lucee). Adobe 2023 first-boot EmptyStack cannot be fully simulated in this VM
Not the torn-down-scope onError report Diff is Public.cfc + WheelsTest + changelog fragment only
Full core suite green wheels test --core --ci5332 passed (LuCLI / Lucee 7 + SQLite)
GitHub CI All 12 checks on d96d6024 completed without failures

Tests

  • New: vendor/wheels/tests/specs/global/PublicInitPromoteSpec.cfc (extends wheels.WheelsTest) — 5/5 passed
  • Fixture: vendor/wheels/tests/_assets/global/PublicInitTraceFixture.cfc
  • Driver used: wheels test --core --ci --timeout=1800 (full framework suite)
  • --filter=global (directory=wheels.tests.specs.global) also hits this spec; confirmed 219 pass / 0 fail including all 5 new examples

Adobe CF 2023 + CommandBox first-boot is the field reproduction and cannot be fully simulated here. CI (LuCLI) is the real green for this VM. GitHub CI on this HEAD is also green (12/12).

Out of scope / leftover

Left on issue 3379, not this PR:

  • Torn-down application.wo during onError (Element wo is undefined in a Java object of type class [Ljava.lang.String;)
  • EventHandlerInterface missing-template during $invoke from onError
  • Copying the v4.0.6 Application.cfc (that drop would remove the develop onSessionEnd guard)

Type of Change

  • Bug fix
  • DCO sign-off
  • Tests
  • Changelog fragment (changelog.d/3379-public-init-emptystack.fixed.md)
  • Framework docs / AI reference / CLAUDE.md (no public API change beyond the internal include helper)

Test Plan

  1. wheels test --core --ci — done here, 5332 passed
  2. GitHub CI on this HEAD — 12/12 green
  3. Field check (Adobe CF 2023 + CommandBox): stop the server, start cold, first GET must not EmptyStack; second GET still serves the Public GUI helpers.
Open in Web Open in Cursor 

cursoragent and others added 2 commits August 26, 2026 02:35
Pin the include-then-promote order and the 4.0.6 helper visibility
contract so the first-boot EmptyStack nest cannot return unnoticed.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Peter Amiri <peter@alurium.com>
Move the helpers.cfm include into its own method so Adobe's page
context pops before the parent-class promote scan runs.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Peter Amiri <peter@alurium.com>
@bpamiri
bpamiri marked this pull request as ready for review August 26, 2026 02:47
@bpamiri
bpamiri merged commit 51edf9d into develop Aug 26, 2026
15 checks passed
@bpamiri
bpamiri deleted the cursor/issue-3379-emptystack-62b1 branch August 26, 2026 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants