fix(cli): generate enum() emission, view-gen warnings, route-dup message#2884
Conversation
…dup message
Final CLI audit nits (all reproduced + verified in the isolated harness):
- generate model name:enum:a,b,c now emits enum(property="name", values="a,b,c")
in the model config(). The type was parsed but never written. Adds buildModelEnums()
+ an {{enums}} placeholder to all ModelContent.txt copies (scaffold source, shared
cli/src, demo app) + an explicit substitution in Templates.cfc (M2). Migration was
already correct (enum maps to a string column). + CodeGenSpec coverage.
- generate controller / scaffold now WARN (instead of silently skipping) when a view
fails to generate — a controller reporting success with no views written is
misleading (M3).
- generate route <name> now detects an existing named-arg route .resources(name="x")
and reports 'Route already exists' instead of the misleading 'Could not find
insertion point' (M5).
- DeployArgsParserSpec: add --role/--container space-form coverage; ReleaseChannel:
tidy the dev-sentinel comment so the build's token replacer can't garble it
(bot review non-blocking nits).
Signed-off-by: Peter Amiri <peter@alurium.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: This PR correctly implements three audit fixes — enum emission in generated models, view-gen failure warnings, and named-arg route duplicate detection — plus two non-blocking nits from the prior bot review. The core logic is mechanically sound, all three ModelContent.txt copies are updated, collapseEmptyLines handles the new empty {{enums}} placeholder correctly, and DCO sign-off is present. Verdict: comment — a few minor gaps in test coverage and one low-risk defensive issue worth noting, but nothing blocking.
Correctness
routeName used unescaped inside reFindNoCase pattern — low risk, but worth hardening
cli/lucli/Module.cfc, line 2945:
var namedArgPattern = "\.resources\s*\([^)]*name\s*=\s*[""']" & routeName & "[""']";
if (reFindNoCase(namedArgPattern, content)) {routeName is concatenated directly into a regex pattern. If a user passes a name containing ., +, *, (, or \ (all valid in a shell argument, even if unusual for Wheels route names), the character is interpreted as a regex metacharacter. The simple-form check two lines above uses the safer findNoCase (literal string). Suggested fix:
// Escape dots and other special chars so the name is matched literally
var safeRouteName = reReplace(routeName, "([\.\+\*\?\(\)\[\]\{\}\^\$\|\\])", "\\1", "all");
var namedArgPattern = "\.resources\s*\([^)]*name\s*=\s*[""']" & safeRouteName & "[""']";Tests
1. Named-arg duplicate path in generateRoute has no direct unit test
cli/lucli/tests/specs/commands/GenerateCommandSpec.cfc, after line 200:
ScaffoldSpec.cfc:404 covers the service-level duplicate prevention (Scaffold's updateRoutes won't inject a second line). That's different from the new Module-level early-return and "Route already exists" message in generateRoute. There's no test that calls mod.generate() with ["route", "posts"] when routes.cfm already contains .resources(name="posts", ...). Suggested addition:
it("reports 'Route already exists' for a named-arg route duplicate", () => {
var routesPath = tempRoot & "/config/routes.cfm";
fileWrite(routesPath, 'mapper().resources(name="reviews", only="index").end();');
mod.__arguments = ["route", "reviews"];
mod.generate();
// output captured via your test harness — verify no second .resources() added
var content = fileRead(routesPath);
var matches = reMatch('\.resources\([^)]*reviews', content);
expect(arrayLen(matches)).toBe(1);
});2. View-gen warning in generateController is untested
cli/lucli/Module.cfc, lines 2797–2800 — the new else branch that emits "skip …" when a view fails has no coverage in GenerateCommandSpec. Low-friction addition: mock generateView to return {success: false, error: "templates missing"} and assert the warning is emitted.
Conventions
Minor: view-gen skip message format differs between generateController and generateScaffold paths
cli/lucli/services/Scaffold.cfc, line 134:
arrayAppend(results.skipped, "view " & action & ": " & (viewResult.error ?: "generation failed"));cli/lucli/Module.cfc, line 2800:
out(" skip app/views/#lCase(controllerName)#/#lCase(action)#.cfm: " & ..., "yellow");The direct-output path includes the full app/views/… path, which is more actionable. The Scaffold path only records "view index: reason". Users running wheels generate scaffold will see skip view index: generation failed without knowing the target path. Low-impact but worth aligning:
arrayAppend(results.skipped, "app/views/#lCase(pluralName)#/#action#.cfm: " & (viewResult.error ?: "generation failed"));(Note: pluralName is in scope at line 35 of Scaffold.cfc.)
Docs
No user-visible behavior changes to the guides docs or CHANGELOG were included, consistent with the PR description marking this as a fix-tier follow-up to #2882 and #2883. A [Unreleased] CHANGELOG entry for the three fixes would be welcome given they're user-observable (enum generation was silently broken), but not blocking given the broader audit PR likely carries the entry.
Commits
Single commit fix(cli): emit generate enum(), warn on view-gen failures, fix route-dup message — 81 chars, valid type, in-scope subject, DCO Signed-off-by trailer present. Passes commitlint.
Wheels Bot — Reviewer B (round 1)A's review is accurate and well-supported. All four findings check out against the actual diff. No sycophancy, no false positives, and no missed issues of significance. A's SycophancyNone detected. A identified specific findings with file/line citations and explicit reasoning for why each is non-blocking. False positivesNone detected.
Missed issuesNone detected. The changes are entirely in CLI code ( Verdict alignmentA's ConvergenceAligned. A's findings are accurate and the supplied fixes are correct. Per the convergence rules, a |
Wheels Bot — Address Review (round 1)Applied consensus findings from Reviewer A and Reviewer B's convergence (round 1):
Both new spec cases live inside the existing The CHANGELOG note B flagged as non-blocking was intentionally deferred — the broader audit PR carries the entry. Local test runner ( The new commit will trigger a fresh Reviewer A run on the updated SHA. Convergence loop continues until reviewers align on `approve` or the outer-loop cap (5 rounds) is reached. |
Release-cut reconciliation ahead of the 4.0.3 GA: - Rename [Unreleased] -> [4.0.3] with the 2026-06-09 release date and a summary blurb, matching the 4.0.2 promotion format (#2824). - Consolidate the duplicated ### Changed / ### Fixed sections that accumulated from incremental entry additions into one Added/Changed/Fixed trio (same order as the 4.0.2 block). - Drop the byte-identical duplicate #2844 (protectedControllerMethods) entry. - Add the missing consolidated entry for the 24-command CLI audit sweep (#2882, #2883, #2884, #2885). - Fix the separator below the promoted block to exactly '---' (3 dashes) so release.yml's release-notes awk extraction terminates correctly (the #2606/#2768 footgun its Validate Release Checklist step guards against). Cutting the release branch from develop AFTER this lands avoids the 4.0.2 back-port dance (#2824) where the rename lived only on the release branch. Signed-off-by: Peter Amiri <peter@alurium.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
What & why
The final tier of CLI-audit follow-ups (after #2882 and #2883) — the remaining in-repo nits plus the wheels-bot's non-blocking review nits from #2883. Every fix reproduced + verified against the CLI in an isolated harness.
Fixes
generate model … status:enum:a,b,cenum(property="status", values="a,b,c")inconfig()(verified)generate controller/scaffoldviewsskip … : <reason>) so the failure is visible (M3)generate route <name>.resources(name="x", …)) was misreported as "Could not find insertion point"--role/--containerflags--role value/--container valuecases@build.version@literal the build's token-replacer would garbleenumemission detailenumwas parsed (prop.values) but noenum()line was written. This addsbuildModelEnums()(mirroringbuildModelValidations()), an{{enums}}placeholder to allModelContent.txtcopies (scaffold source, sharedcli/src/templates, demoapp/snippets), and an explicit{{enums}}substitution inTemplates.cfc(the generic{{key}}loop wasn't applying it). The migration side already mappedenum→ a string column. + CodeGenSpec coverage (emits for enum props; no stray placeholder otherwise).Verification
wheels newapp for the template path; genbase copies for the rest)..claude/worktreesworktree).Remaining
The systemic LuCLI dispatch work (per-command
--help/--verbose/--version/wheels help+ brand leaks) is scoped for a dedicated upstreamcybersonic/LuCLIeffort — not in this repo'scli/lucli/.