Skip to content

docs: correct five stale CLAUDE.md claims from the guide behavioral audit (batch 2) - #3091

Merged
bpamiri merged 2 commits into
developfrom
peter/docs-audit2-claude-md-collateral
Jun 12, 2026
Merged

docs: correct five stale CLAUDE.md claims from the guide behavioral audit (batch 2)#3091
bpamiri merged 2 commits into
developfrom
peter/docs-audit2-claude-md-collateral

Conversation

@bpamiri

@bpamiri bpamiri commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Collateral CLAUDE.md fixes from the guide behavioral audit, batch 2 (manifest: p1b2 docs-fix wave). Each item was live-verified by the audit harness; CLAUDE.md is updated to describe current behavior, citing the tracking issue where the underlying behavior is broken-but-unfixed.

Corrections

  1. Model Quick Reference — tableName("tbl_users")table("tbl_users") (docs+model: guides and CLAUDE.md use non-existent tableName("x") setter — silent no-op, models fall back to the convention table (real setter is table()) #3079)
    tableName() is a zero-argument getter (vendor/wheels/model/miscellaneous.cfc:177); the setter is table() (:35). The extra positional argument is silently ignored, so models fall back to the convention table (audit claim ds-07-overrides-compose: live Wheels.TableNotFound with the documented form). This Quick Reference was flagged as the likely contamination source for the same mistake in 5 guide pages.

  2. RateLimiter keyFunction snippet — req.cgi.http_x_api_key ?: "anonymous" never works (dispatch: middleware request context carries no cgi key — documented req.cgi.* patterns silently fail (RateLimiter keyFunction collapses all clients into one budget; InboundRequestId never honors the inbound header) #3074)
    The dispatch middleware context is {params, route, pathInfo, method} (vendor/wheels/Dispatch.cfc:420-425) — no cgi key — so the Elvis always fired and every client shared one "anonymous" budget (audit claim rl-14, verified live with per-token 429 boundaries after the fix). Replaced with the verified form reading the real cgi scope plus a Len() guard (a missing header is empty string, not undefined), and added one prose sentence documenting the context shape.

  3. Anti-Pattern 5 — NOW() is not portable and execute() binding isn't "unreliable", it's absent
    The example failed live on SQLite (the wheels new default DB) with no such function: NOW; SQL Server has no native NOW() either, and no adapter rewrites it (audit claim mig-26). Replaced with CURRENT_TIMESTAMP (valid on MySQL/PG/MSSQL/H2/SQLite). Also reworded the rationale: execute() is execute(required string sql) (Migration.cfc) — there is no parameters argument at all (claim mig-27).

  4. Anti-Pattern 8 — Wheels.ActionNotAllowed → "404" claim is wrong today (dispatch: Wheels.ActionNotAllowed surfaces as HTTP 500, not the 404 promised by #2845 (and CLAUDE.md Anti-Pattern 8) #3075)
    Audit claim authz-helper-named-action-blocked: the block works, but the exception surfaces as HTTP 500 in development and production on both Lucee 7 and Adobe 2023 (only Wheels.*NotFound types map to 404). Updated to state the 404 intent and the current 500 behavior, citing dispatch: Wheels.ActionNotAllowed surfaces as HTTP 500, not the 404 promised by #2845 (and CLAUDE.md Anti-Pattern 8) #3075.

  5. Background Jobs — advertised wheels jobs worker CLI does not exist (docs/cli: wheels jobs ... worker CLI is advertised (observability guide + root CLAUDE.md) but does not exist #3090)
    Audit claim obs-09: wheels jobs status errors with Component [modules.wheels.Module] has no function with name [jobs]; cli/lucli/Module.cfc has no jobs command. Replaced the five-command code block with a note pointing at the working programmatic API (processQueue() / queueStats()).

Evidence

Raw audit JSON (claims ds-07-overrides-compose, rl-14-keyfunction-example-per-token-budgets, mig-26, mig-27, authz-helper-named-action-blocked, obs-09) from the batch-2 verifier run, 2026-06-12. Code anchors re-verified against this branch (miscellaneous.cfc table/tableName signatures, Dispatch.cfc:420-425 context keys, processing.cfc:140 throw site, absence of a jobs function in Module.cfc).

Refs #3074, #3075, #3079, #3090.

🤖 Generated with Claude Code

…al audit

- Model Quick Reference: tableName() is a getter, the setter is table() (#3079)
- RateLimiter keyFunction: middleware context has no cgi key; use the real
  cgi scope with a Len() guard (#3074)
- Anti-Pattern 5: execute() has no parameters argument; NOW() is not
  portable (fails on SQLite and SQL Server) — use CURRENT_TIMESTAMP
- Anti-Pattern 8: Wheels.ActionNotAllowed currently surfaces as HTTP 500,
  not the intended 404 (#3075)
- Background Jobs: the advertised wheels jobs worker CLI does not exist;
  point at the programmatic queue API (#3090)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
@github-actions github-actions Bot added the docs label Jun 12, 2026

@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 corrects five stale CLAUDE.md claims from the guide behavioral audit (table() vs tableName(), the RateLimiter keyFunction context shape, execute()/CURRENT_TIMESTAMP, Wheels.ActionNotAllowed surfacing as 500, and the nonexistent wheels jobs CLI). I re-verified all five corrections against the framework source and each one is accurate. However, the rewritten RateLimiter keyFunction snippet still passes an inline closure as a constructor named argument — the exact Adobe CF compile-crash pattern this same file documents as Cross-Engine Invariant #5, and the verbatim WRONG example in .ai/wheels/cross-engine-compatibility.md. Since this PR's whole purpose is making CLAUDE.md examples live-correct, that snippet needs the documented hoist. Verdict: request changes — one cross-engine finding, trivial one-line fix.

Cross-engine

  • CLAUDE.md lines 427–430 (Rate Limiting block, as modified by this PR): the edited snippet keeps the form

    new wheels.middleware.RateLimiter(keyFunction=function(req) {                  // rate-limit per API key
        var apiKey = cgi.http_x_api_key;
        return Len(apiKey) ? apiKey : "anonymous";
    })

    An inline function literal as a named argument to new Component(...) crashes Adobe CF's bytecode generator with java.lang.ArrayStoreException: coldfusion.compiler.ASTcffunction — Cross-Engine Invariant #5 in this same CLAUDE.md, with the full mechanism documented in .ai/wheels/cross-engine-compatibility.md:89-110, where the WRONG example is this exact RateLimiter keyFunction snippet. Every framework spec hoists the closure first (keyFunction = keyFn at 12 sites in vendor/wheels/tests/specs/middleware/RateLimiterSpec.cfc and 8 in RateLimiterDatabaseSpec.cfc). The PR body says the new form was live-verified, but that run cannot have included Adobe CF — there it fails at compile time. Fix (matches the deep-reference doc's RIGHT form):

    var apiKeyFn = function(req) {                                                 // rate-limit per API key
        var apiKey = cgi.http_x_api_key;
        return Len(apiKey) ? apiKey : "anonymous";
    };
    new wheels.middleware.RateLimiter(keyFunction=apiKeyFn)

    The closure-body fix itself (reading the real cgi scope plus the Len() guard) is verified correct against vendor/wheels/Dispatch.cfc:420-425 — only the construction form needs the hoist.

Correctness

No findings — for the audit trail, I re-verified each of the five corrections against source at this head:

  1. table() setter / tableName() getter: vendor/wheels/model/miscellaneous.cfc:35 is public void function table(required any name); :177 is public string function tableName() with no parameters. The Quick Reference fix and the (#3079) annotation are right.
  2. Middleware context shape: vendor/wheels/Dispatch.cfc:420-425 builds exactly {params, route, pathInfo, method} — no cgi key — so the old req.cgi.http_x_api_key ?: "anonymous" always collapsed every client into one bucket. The new prose paragraph is accurate, and the Len() guard is correct (a missing header reads as empty string in the cgi scope, so ?: never fires).
  3. execute() signature: vendor/wheels/migrator/Migration.cfc:460 is public void function execute(required string sql) — there is no parameters argument, confirming the reworded rationale. No NOW() rewriting exists anywhere under vendor/wheels/migrator/, and CURRENT_TIMESTAMP is the ANSI form valid on all five listed engines.
  4. Wheels.ActionNotAllowed → 500: the status mapping in vendor/wheels/events/EventMethods.cfc:84-90 sends 404 only for exception types matching ^Wheels\.[A-Za-z]*NotFound$ and 500 for everything else; ActionNotAllowed (thrown at vendor/wheels/controller/processing.cfc:140) does not match, so the "currently surfaces as HTTP 500" wording and the #3075 citation are correct.
  5. wheels jobs CLI: cli/lucli/Module.cfc has no jobs function (the only occurrence of the string is an unrelated fix-message at line 4405), confirming #3090 and the removal of the five-command block.

Commits

No findings: single commit, docs: type from the allowlist, 77-character subject, body lines under 100 characters, DCO sign-off present.

The corrected RateLimiter snippet itself used an inline closure as a
constructor named argument — the Adobe CF ArrayStoreException pattern
this same file documents. Caught by the Reviewer; hoisted to match the
RIGHT form in .ai/wheels/cross-engine-compatibility.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 of the CLAUDE.md batch-2 docs corrections at head 0d241d08c. The single blocking finding from my previous review (inline closure as a constructor named argument in the RateLimiter keyFunction snippet — Cross-Engine Invariant 5) is fixed by the new commit, and I re-verified all five underlying corrections against framework source at this head. Verdict: approve — this supersedes my earlier CHANGES_REQUESTED review on 6b72ab526 (issue #3048).

Previously blocking finding — resolved

  • RateLimiter keyFunction snippet (CLAUDE.md, Rate Limiting block). My review on 6b72ab526 blocked because the corrected snippet still passed an inline function literal as a named constructor argument — the Adobe CF ArrayStoreException: ASTcffunction compile-crash pattern (Cross-Engine Invariant 5; .ai/wheels/cross-engine-compatibility.md). Commit 0d241d08c ("docs: hoist the keyFunction closure per Cross-Engine Invariant 5") rewrites it to the documented RIGHT form, verified at this head:

    var apiKeyFn = function(req) {
        var apiKey = cgi.http_x_api_key;
        return Len(apiKey) ? apiKey : "anonymous";
    };
    new wheels.middleware.RateLimiter(keyFunction=apiKeyFn)

    This matches the hoist pattern used at every construction site in vendor/wheels/tests/specs/middleware/RateLimiterSpec.cfc and the RIGHT example in the cross-engine deep-reference doc. The closure body itself is correct: a missing header reads as empty string in the cgi scope (so the Len() guard, not ?:, is the right idiom), and the added prose correctly documents the middleware context shape.

Audit trail — all five corrections re-verified at this head

  1. table() setter / tableName() getter: vendor/wheels/model/miscellaneous.cfc declares public void function table(required any name) (the setter) and public string function tableName() with no parameters (the getter), so tableName("tbl_users") was indeed a silent no-op. The Quick Reference fix and #3079 citation are accurate.
  2. Middleware context has no cgi key: vendor/wheels/Dispatch.cfc:420-425 builds exactly {params, route, pathInfo, method}, confirming the old req.cgi.http_x_api_key ?: "anonymous" collapsed all clients into one bucket (#3074).
  3. execute() signature / CURRENT_TIMESTAMP: vendor/wheels/migrator/Migration.cfc:460 is public void function execute(required string sql) — no parameters argument exists, so "absent" (not "unreliable") is the correct rationale. CURRENT_TIMESTAMP is the ANSI form valid on all five listed engines; NOW() fails on SQLite and SQL Server.
  4. Wheels.ActionNotAllowed surfaces as 500: vendor/wheels/events/EventMethods.cfc:85-89 maps only exception types matching ^Wheels\.[A-Za-z]*NotFound$ to 404 and everything else to 500; ActionNotAllowed does not match, so the "intended 404, currently 500" wording and the #3075 citation are correct.
  5. wheels jobs worker CLI does not exist: cli/lucli/Module.cfc contains no jobs function, confirming #3090 and the replacement of the five-command block with the programmatic processQueue() / queueStats() guidance.

Commits

No findings: two commits, both docs: type from the allowlist, subjects under 100 characters, body lines under 100 characters, DCO sign-offs present and matching. The fix commit's message correctly explains the "why" (the snippet violated the invariant the same file documents).

Docs

No findings: docs-only change to CLAUDE.md; no changelog fragment required (changelog.d/ fragments are for user-facing fix/feat PRs).

@bpamiri
bpamiri merged commit 04da98d into develop Jun 12, 2026
6 checks passed
@bpamiri
bpamiri deleted the peter/docs-audit2-claude-md-collateral branch June 12, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant