Skip to content

feat: scaffolded home page onboarding + /wheels welcome production gate - #3260

Merged
bpamiri merged 3 commits into
developfrom
peter/welcome-page-onboarding-and-gate
Jun 26, 2026
Merged

feat: scaffolded home page onboarding + /wheels welcome production gate#3260
bpamiri merged 3 commits into
developfrom
peter/welcome-page-onboarding-and-gate

Conversation

@bpamiri

@bpamiri bpamiri commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Context

Pre-4.0, no root route was wired, so / showed the framework's congratulations page. 4.0 wires the root to main#index, which left the redesigned (#2098) onboarding page orphaned at /wheels (where nobody lands) and the scaffolded home page a bare two-liner.

Changes

1. Onboarding on the scaffolded home page — feat(cli)

wheels new now generates a home page with a runtime status line (Wheels version, engine, database, environment) and a Next-steps command guide — semantic HTML styled by the template's existing simple.css. Framework-internal chrome, the CLI install banner, and the version-pinned "What's New" grid stay on /wheels (centrally maintained, not frozen into user apps). scaffoldProject mirror + assertions updated in ProjectScaffoldTest.cfc.

2. Defense-in-depth gate on the /wheels welcome page — fix(security)

Public.index was the only Public.cfc handler without $blockInProduction() — a deliberate #2233 exception. The redesigned page surfaces version/engine/database/environment (the same class of detail every other gated handler protects), so it now gates itself too: it no longer renders outside development when enablePublicComponent is manually enabled. Reverses the #2233 exception; development still renders.

Verification

  • Generated home page renders in a live app — version/engine/db/environment + Next steps all resolve.
  • Module.cfc compiles — CLI suite 1082 pass / 0 fail.
  • index() gate: TDD red → green (security spec 286p/2f → 288p/0f).
  • Full framework suite: 4612 pass / 0 fail.

🤖 Generated with Claude Code

bpamiri added 2 commits June 25, 2026 18:49
`wheels new` generated a bare two-line placeholder at `/`, while the
redesigned welcome page (Public.index -> congratulations.cfm) sat
orphaned at `/wheels` where nobody lands. Port the first-run essentials
into the generated app/views/main/index.cfm: a runtime status line
(Wheels version, engine, database, environment) and a Next-steps
command guide, as semantic HTML styled by the template's simple.css.
Leaves framework-internal chrome, the CLI install banner, and the
version-pinned "What's New" grid on the /wheels page.

Resync the scaffoldProject mirror in ProjectScaffoldTest.cfc (it mirrors
Module.cfc exactly) and strengthen the view assertion to lock in the new
content and the ## -> # escaping.

Signed-off-by: Peter Amiri <peter@alurium.com>
Public.index (the congratulations/welcome page at the /wheels namespace
root) was the only Public.cfc handler without a $blockInProduction()
guard, a deliberate #2233 exception that relied on
enablePublicComponent=false to hide /wheels in production by default.
The redesigned page (#2098/#2272) surfaces version/engine/database/
environment, the same class of detail every other gated handler
protects. Add $blockInProduction() as the first statement so it no
longer renders outside development when enablePublicComponent is
manually enabled. Development still renders (the gate is an allowlist).

Flip PublicComponentProductionSpec.cfc: add "index" to the gated-handler
list and drop the now-contradictory "NOT gated" exception test.

Signed-off-by: Peter Amiri <peter@alurium.com>

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer

TL;DR — This PR ports the onboarding essentials (runtime status line + Next-steps guide) into the wheels new scaffolded home page at /, and closes the last Public.cfc gating gap by adding $blockInProduction() to index() (the /wheels welcome page). The diff is clean, well-tested (TDD red→green on the security spec, mirror + assertions resynced in ProjectScaffoldTest.cfc), carries two correctly-typed changelog fragments, and both commits are valid conventional commits with DCO sign-off. Verdict: comment — no blocking findings; one design observation worth weighing and one stale out-of-diff comment.

Security

One thing worth a deliberate decision rather than a defect: the same PR that gates the framework welcome page for disclosing version/engine/database/environment now ships that exact disclosure in the user's default home page at /:

'<p>Your <strong>Wheels ##get("version")##</strong> application is running on ##application.wheels.serverName## with ##application.wheels.dataSourceName## (##get("environment")##).</p>'

(cli/lucli/Module.cfc ~line 6195)

The fix(security) half reasons that this is "the same class of detail every other gated handler protects." A wheels new app deployed to production without editing app/views/main/index.cfm will surface engine/db/version/environment to any anonymous visitor at the root route. The mitigations are real — it's the user's own editable view (the page footer points to app/views/main/index.cfm), and starter pages are expected to be replaced — so this isn't a framework vulnerability and I'm not blocking on it. But given the security framing of the companion commit, consider whether the runtime status line should degrade outside development (e.g. wrap the status <p> so it only renders the env/engine/db detail in dev), or whether a comment in the generated file should flag it as dev-facing. Your call as maintainer.

Tests

Coverage is solid and the TDD trail is clear:

  • PublicComponentProductionSpec.cfc adds "index" to gatedHandlers (line 154); the regex at line 162 requires $blockInProduction() as the first statement, which index() satisfies. The contradictory "NOT gated" test is correctly removed.
  • ProjectScaffoldTest.cfc asserts the generated content and the ### escaping. Worth noting for future readers: expect(content).toInclude('##get("version")##') works because the test's own string literal collapses ### at compile time, the same collapse the fileWrite source underwent — so it's single-hash vs single-hash. The inline comment explains this; good.

One nit (out of diff, non-blocking): vendor/wheels/tests/specs/dispatch/InvokeMethodSpec.cfc:50-52 still describes index() as "the ungated index() handler." That's now inaccurate — index() calls $blockInProduction(). The spec still passes (the suite runs in development per config/environment.cfm:9, where the gate is a no-op), so nothing breaks, but the comment is stale.

Cross-engine

No concerns. The generated view body is a single-quoted CFML string literal with ##-escaped hashes — no closures, no struct member functions, no obj["key"](), no application-scope function members. &mdash; carries no #, so it sidesteps the entity-escaping gotcha. The generated view runs in the user's app, but get("version"), get("environment") (public Global.cfc helpers) and application.wheels.serverName / application.wheels.dataSourceName (set at app start on every engine — congratulations.cfm:19 already uses serverName) are all engine-portable and won't null-deref on a fresh app.

Conventions

  • <cfoutput> wraps the runtime expressions; no cfparam needed since the values are framework globals, not controller-passed view variables.
  • nl/tab are defined in both the Module.cfc site and the scaffoldProject mirror — the two stay byte-identical, as the mirror's docblock requires.
  • Two changelog fragments (scaffold-homepage-onboarding.changed.md, wheels-welcome-production-gate.security.md) use valid types and avoid a direct CHANGELOG.md edit. Good.

Commits

Both conform to commitlint.config.jsfeat(cli): … and fix(security): …, headers well under 100 chars, not ALL-CAPS, each with Signed-off-by:. Bodies explain the "why" (orphaned /wheels page, the #2233 exception being reversed). No issues.

@bpamiri bpamiri changed the title Surface onboarding on the scaffolded home page; gate the /wheels welcome page feat: scaffolded home page onboarding + /wheels welcome production gate Jun 26, 2026
Address wheels-bot review feedback on PR #3260:

- The generated starter home page now carries a comment warning that it
  is a development/first-run landing page which surfaces environment
  details (Wheels version, engine, database, environment) and should be
  replaced with a real homepage before production. Mirrored in the
  scaffoldProject test helper.
- InvokeMethodSpec.cfc described index() as "ungated"; fix the stale
  comment now that index() calls $blockInProduction().

Signed-off-by: Peter Amiri <peter@alurium.com>

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer

TL;DR — Re-review at head 2822e3510. This PR ports the onboarding status line + Next-steps guide into the wheels new scaffolded home page, and gates the /wheels welcome page (Public.index) with $blockInProduction(). The new commit (docs: flag starter home page as dev-facing, fix stale index() comment) directly resolves both non-blocking observations from the prior review. The diff is clean, well-tested, and both observations are addressed. Verdict: comment — no blocking findings; one accepted security tradeoff documented below.

Prior feedback — resolved

The previous review (marker SHA 4e5d4aaaf, state COMMENTED) raised two items; the head commit clears both:

  1. Status-line disclosure at / should be flagged or degraded. Resolved via the documentation route. The generated home page now leads with a tag comment warning it is a development/first-run page that surfaces version/engine/db/environment and must be replaced before production (cli/lucli/Module.cfc:6194-6200, mirrored byte-identically in cli/tests/specs/e2e/ProjectScaffoldTest.cfc:338-344).
  2. Stale "ungated index()" comment. Resolved — vendor/wheels/tests/specs/dispatch/InvokeMethodSpec.cfc:50-53 now reads "invokes index(), which now calls $blockInProduction() too," accurate for the gated handler.

Security

The residual tradeoff the prior review flagged stands and is now a deliberate, documented decision: a wheels new app deployed to production without editing app/views/main/index.cfm still surfaces engine/db/version/environment to anonymous visitors at the root route (cli/lucli/Module.cfc:6203):

'<p>Your <strong>Wheels ##get("version")##</strong> application is running on ##application.wheels.serverName## with ##application.wheels.dataSourceName## (##get("environment")##).</p>'

Not blocking — it is the user's own editable view (not framework chrome), starter pages are expected to be replaced, and the new tag comment flags it for the developer editing the file. One note: <!--- ... ---> is stripped server-side, so the warning is visible only to a developer opening the file, never in rendered output or page source. That is consistent with the "dev-facing" intent, but it means a copy-paste-and-ship user never sees it at runtime. Maintainer's call as chosen.

Tests

Coverage is intact and the gate is correctly pinned:

  • PublicComponentProductionSpec.cfc:154 adds "index" to gatedHandlers; the loop regex at line 162 requires $blockInProduction() as the first statement, which Public.index() now satisfies (vendor/wheels/Public.cfc:359). The contradictory "NOT gated" test is replaced by an explanatory comment.
  • ProjectScaffoldTest.cfc:217-226 asserts the generated content and the ### single-hash escaping.

Cross-engine

No concerns. The added block is a single-quoted CFML string literal — tag comment + <cfoutput> body with ##-escaped hashes. No closures, struct member functions, obj["key"](), or application-scope function members. application.wheels.serverName / dataSourceName are set at app start on every engine (Model.cfc:43, Job.cfc:44; congratulations.cfm:19 already reads serverName), and get() is a public Global.cfc helper (vendor/wheels/Global.cfc:620) — no null-deref on a fresh app.

Conventions

  • nl/tab are in scope at both the Module.cfc site and the scaffoldProject mirror, and the two blocks stay byte-identical as the mirror requires.
  • Two changelog fragments (scaffold-homepage-onboarding.changed.md, wheels-welcome-production-gate.security.md) use valid types and avoid a direct CHANGELOG.md edit.

Commits

All three conform to commitlint.config.jsfeat(cli):, fix(security):, docs: — headers under 100 chars, not ALL-CAPS, each DCO-signed. Bodies explain the why (orphaned /wheels page, reversed #2233 exception, addressing review feedback). No issues.

@bpamiri
bpamiri merged commit 21719ec into develop Jun 26, 2026
12 checks passed
@bpamiri
bpamiri deleted the peter/welcome-page-onboarding-and-gate branch June 26, 2026 06:17
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.

1 participant