docs(web/guides): correct routing guides to verified router behavior (audit batch 2) - #3092
Conversation
Guide-behavioral-audit batch 2 (p1-10-routing) fixes for basics/routing.mdx and
core-concepts/how-routing-works.mdx, verified live on Lucee 7 and Adobe 2023:
- Namespaced sections: replace the .namespace(name="admin", callback=...) example
with the working .namespace("admin")...end() form. scope()/namespace() silently
ignore callback= and the unclosed scope swallows subsequent routes (#3072); add a
caution Aside and the extends="app.controllers.Controller" note for subfolder
controllers.
- Match algorithm + order rules: document the static-route O(1) index — literal
paths resolve before the ordered placeholder scan regardless of declaration
position; declaration order governs placeholder-vs-placeholder and ties between
identical static patterns (#3073). Invert the /posts/featured order-rules bullet
accordingly.
- Resource routes: update registers both PATCH and PUT; seven REST actions (not
seven routes) and every route gets a .[format] twin.
- Placeholder default match is [^\./]+ (non-slash AND non-dot), not "any
non-slash string".
verify:docs: 7 tagged blocks pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer
TL;DR: This PR corrects the two routing guides to match verified router behavior (namespace callback trap, PATCH+PUT for update, .[format] twins, [^\./]+ placeholder default, static-route-index precedence). I verified every behavioral claim against the framework source and all of them are accurate; the cited issues (#3072, #3073) exist and match. Verdict: comment — one minor arithmetic/phrasing nit in the new prose, nothing blocking.
Verification of the diff claims (all confirmed)
- PATCH/PUT both map to
update—$addMemberRoutesregisters both verbs (vendor/wheels/mapper/mapping.cfc:102-105). .[format]twins —(.[format])is an optional segment that$matchexpands into a separate route row (vendor/wheels/mapper/matching.cfc:449-470), andmapFormatdefaults totrue(vendor/wheels/Mapper.cfc:19).namespace()has no callback support — its signature is(name, package, path)and it forwards toscope(), which never invokes a callback (vendor/wheels/mapper/scoping.cfc:106-114); an unclosed scope stays on the stack, so the "subsequent routes get swallowed" warning is correct. The rewritten example uses the same manual.namespace("admin")…end()form the framework specs use (vendor/wheels/tests/specs/mapperSpec.cfc:165).- Placeholder default is
[^\./]+—$patternToRegex(vendor/wheels/Mapper.cfc:117). - Static-first matching —
$addRouteindexes placeholder-free patterns per-method with first-registration-wins (vendor/wheels/Mapper.cfc:238-278), and$findMatchingRouteconsults that index before the ordered scan (vendor/wheels/Dispatch.cfc:197-212). So the inverted/posts/featuredorder-rules bullet and the two-step match-algorithm description are both accurate. extends="app.controllers.Controller"for subfolder controllers — matches existing prior art: everyexamples/starter-app/app/controllers/admin/*.cfc, the CLI admin templates (cli/lucli/templates/admin/controller.txt), and the v3 nesting-controllers guide.- Issues #3072 and #3073 are open and their titles describe exactly the behaviors cited.
Docs
web/sites/guides/src/content/docs/v4-0-0/core-concepts/how-routing-works.mdx:32— minor nit: "Each row is also registered with a.[format]twin … so the actual table holds twice as many rows." The table shows 7 rows but the PATCH/PUT row is two route registrations, so the actual count is 8 base × 2 = 16, not 7 × 2 = 14. Same family of imprecision at line 28 ("registers an entry per REST action" —updateregisters two). The sibling phrasing inbasics/routing.mdx:54("more rows than seven") is safely vague; consider mirroring it here, e.g. "…so the actual table holds twice as many entries as the routes generated (andupdateitself is two routes, PATCH and PUT)." Non-blocking — the doubled-per-route claim is itself true.
Commits
Single commit docs(web/guides): correct routing guides to verified router behavior — valid type, header ≤ 100 chars, DCO sign-off present and matching the author. No changelog fragment needed for a docs guide correction.
No correctness, cross-engine, security, or test findings — docs-only change, and the {test:compile} blocks were re-verified by the PR's own verify:docs run (harness confirmed at web/sites/guides/scripts/verify-docs/).
…sides now that scope() honors it #3092 and #3095 landed caution asides on develop (routing.mdx, cors.mdx, rate-limiting.mdx) stating scope()/namespace() silently ignore callback=, tracked in #3072. This branch fixes exactly that, so merge develop in and rewrite the three statements: document the now-supported callback= form (auto-closing the scope), keep the unclosed-scope warning for the explicit .end() form, and note that releases <= 4.0.3 still ignore callback=. Adds a compile-tested namespace(callback=) example to routing.mdx, matching the already-correct callback examples in route-model-binding.mdx and multi-tenancy.mdx. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Peter Amiri <petera@pai.com>
…cope (#3100) * fix(router): scope()/namespace() honor callback= and auto-close the scope scope() declared no callback argument, so the documented `.scope(path="/x", callback=function(map){...})` form (and namespace()/package()/controller(), which forward to scope()) silently dropped it: the callback never ran (its routes 404'd) and nothing closed the scope, so every route declared after inherited the scope's path prefix and middleware. scope() now consumes callback the same way group() does — runs callback(this) then auto-end()s — using the cross-engine-proven IsCustomFunction guard. Adds MapperRobustnessSpec coverage for the scope/namespace/package callback forms plus the no-swallow and no-middleware-leak guarantees for routes declared after the block. Refs #3072 Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> * docs: update scope()/namespace() callback= examples in CLAUDE.md (#3072) Three focused updates: - Anti-pattern #3: note that scope()/namespace()/package()/controller() also accept callback= and auto-close the scope (not just resources()) - Middleware Quick Reference: use callback form for route-scoped .scope() - Route Model Binding: use callback form for the .scope(binding=true) example Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> * docs(web/guides): flip the three #3072 'callback= silently ignored' asides now that scope() honors it #3092 and #3095 landed caution asides on develop (routing.mdx, cors.mdx, rate-limiting.mdx) stating scope()/namespace() silently ignore callback=, tracked in #3072. This branch fixes exactly that, so merge develop in and rewrite the three statements: document the now-supported callback= form (auto-closing the scope), keep the unclosed-scope warning for the explicit .end() form, and note that releases <= 4.0.3 still ignore callback=. Adds a compile-tested namespace(callback=) example to routing.mdx, matching the already-correct callback examples in route-model-binding.mdx and multi-tenancy.mdx. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> 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: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Peter Amiri <petera@pai.com>
Guide-behavioral-audit batch 2, work item p1-10-routing. Fixes the four docs-affected findings (R4, R14b, H1, H13) plus the docs side of the two code-broken findings (R11/R12), all verified live on Lucee 7 (docker, H2/SQLite) and Adobe 2023 (SQLite).
Corrections
basics/routing.mdx.namespace(name="admin", callback=function(map){...})form never executes its callback —namespace()forwards toscope(), which has no callback handling — and the scope stays open, so the following outer.resources("posts")was silently absorbed into/admin(public/posts→ 404Wheels.RouteNotFound). Rewrote the example to the verified manual.namespace("admin") … .end()form (the only form framework specs use,mapperSpec.cfc:165), added a caution Aside citing #3072, and noted that controllers underapp/controllers/admin/needextends="app.controllers.Controller"(bareextends="Controller"fails to resolve from the subfolder on Lucee). Corrected{test:compile}block passes the harness.$addMemberRoutesregisters BOTHPATCHandPUTforupdate, and every route gets a.[format]twin — changed "PATCH /posts/:key" to "PATCH/PUT /posts/:key", softened "seven REST routes" to "the seven REST actions", and mentioned the.[format]twins.$patternToRegexcompiles placeholders to([^\./]+)— verified/posts/20.26/04does NOT match an unconstrained placeholder. Replaced "any non-slash string matches" with "any string without a slash or dot ([^\./]+— dots are reserved for the optional.[format]suffix)".core-concepts/how-routing-works.mdxMapper.cfc:262-278,Dispatch.cfc:196-211, introduced byee13e31e0) — verified:/posts/featuredhitsfeaturedin BOTH declaration orders, and first-declared wins between identical static patterns. Rewrote "The match algorithm" as the two-step static-first/then-declaration-order description and cited #3073 for the docs-vs-behavior reconciliation..[format]twin of every row; existing PATCH/PUT row kept (already correct).Evidence
Raw verifier output: guide-behavioral-audit task
wp6mteswq, claims R4/R14b/H1/H13 (plus R11/R12) — manifest/tmp/p1b2-docs-manifest.md, item p1-10-routing.Verification
pnpm verify:docs src/content/docs/v4-0-0/basics/routing.mdx src/content/docs/v4-0-0/core-concepts/how-routing-works.mdx→ exit 0, 7 passed, 0 failed (all tagged blocks, including the rewritten namespace{test:compile}block).Issue refs: #3072, #3073
🤖 Generated with Claude Code