Repository navigation
docs(web/blog): Anatomy of a Wheels Package - #3024
Conversation
There was a problem hiding this comment.
Wheels Bot — Reviewer
TL;DR: This PR publishes the "Anatomy of a Wheels Package" blog post (web/content/blog/posts/anatomy-of-a-wheels-package.md, 297 lines, docs-only). The technical content is unusually accurate — I verified every load-bearing claim against vendor/wheels/PackageLoader.cfc, ModuleGraph.cfc, SemVer.cfc, ServiceProviderInterface.cfc, cli/lucli/services/packages/Installer.cfc, and the v4 packages guide, and found no factual errors. However, the front matter contains a malformed categories value that will render a literal [] label on the published page, so the verdict is request changes. One fix, plus two minor notes.
Correctness
1. categories front matter is a one-element array containing the literal string "[]" — renders as a visible [] label on the published page. (web/content/blog/posts/anatomy-of-a-wheels-package.md, lines 12–13)
categories:
- '[]'Every sibling post uses an empty YAML array — e.g. skip-the-plugin-rate-limited-api.md and background-jobs-without-redis.md both have categories: []. As written here, YAML parses this to ["[]"], which passes the blog's Zod schema (z.array(z.string()) in web/sites/blog/src/content.config.ts:16) and then hits the eyebrow rendering in web/sites/blog/src/layouts/PostLayout.astro:13:
const eyebrow = data.categories && data.categories.length > 0 ? data.categories[0] : null;So the live post will display the literal text [] as its category eyebrow. Fix:
categories: []This looks like a serialization bug in the wheels-publishing-admin pipeline (stringifying an empty array before emitting YAML) — worth checking the publisher so the next post doesn't ship the same defect.
Conventions
2. Code-fence title="..." annotations are silently dropped by the blog renderer. (lines 62, 76, 91, 122, 159, 202, 217, 270)
The post uses fence meta like json title="vendor/wheels-greeter/package.json". The blog site is plain Astro with default Shiki highlighting — web/sites/blog/package.json has no astro-expressive-code (or any markdown plugin) dependency, and web/sites/blog/astro.config.mjs has no markdown config. Default Shiki ignores fence meta, so all eight title annotations will be dropped at render time. Nothing breaks, but the prose leans on these as file-path labels (e.g. distinguishing the three different WheelsGreeter.cfc variants at lines 76, 122, and 217). No existing blog post uses fence titles — that convention exists only in the guides site (.mdx on a different stack). Suggest either removing the title= meta or moving the path into the snippet as a comment / preceding prose so readers keep the context.
Commits
- The single commit
3167155da docs(web/blog): Anatomy of a Wheels Packageis a valid conventional-commit header (typedocs, 44 chars, not ALL-CAPS), and the PR title matches — the squash-merge gate passes. - Minor: the commit has no
Signed-off-bytrailer. DCO sign-off is project policy perCONTRIBUTING.md(not a CI check, and prior publishing-admin blog posts such as #2825 merged without one, so flagging as a note rather than a blocker). If the publishing pipeline can pass-s, that would align these automated commits with policy.
Fact-check summary (no action needed)
For the record, the claims I verified as accurate against the code at this SHA:
- Discovery skips
vendor/wheels/and dot-prefixed dirs; per-package error isolation withfailedPackages(PackageLoader.cfc$discoverManifests,$discover). - Entry-CFC convention: dir-name match, fallback to first
.cfc($instantiatePackage, lines 601–614). - Mixin allowlist string matches exactly (line 66); default is
"none"whenprovides.mixinsis absent (line 365); unknown targets throwWheels.PackageInvalidMixinTargetnaming the valid set (lines 1058–1064). - Per-method
mixinmetadata override, including the validate-before-mutate pre-pass so a typo on method N never leaves methods 1..N−1 registered ($collectMixins, lines 734–745). requires/replaces/suggestssemantics, "Required package not found" wording, cycle exclusion (ModuleGraph.cfclines 101–156, 203, 249–257); semver^/~/ranges viawheels.SemVer.satisfiesAll()(SemVer.cfclines 90–103).- Alias derivation incl. the single-segment
LCase()caveat ($deriveMapping, line 1115), the[A-Za-z_][A-Za-z0-9_]*override regex with empty-string-fails behavior (lines 1096–1104), and "Duplicate package mapping alias" rollback of the second claimant (lines 1160–1168,$rollbackPackage). register/boottwo-phase lifecycle and the lifecycle-hook exclusion listinit, onPluginLoad, onPluginActivate, register, boot(line 727);getPackageMappings()/getFailedPackages()/getMixinCollisions()getters and theprovides.overridesacknowledged-collision behavior;application.wheels.PackageLoaderObjpath (Global.cfc:3400).- CLI:
add(notinstall, which LuCLI's extension installer intercepts —PackagesMainCli.cfc:14), sha256 tarball verification with hard abort (Installer.cfc),wheels browser install→setuprename in #2345 (Module.cfc:2523). - The "docs drift fixed" claims check out: the v4 packages guide (
web/sites/guides/src/content/docs/v4-0-0/digging-deeper/packages.mdx:101,105) now documentsrequiresand the hard-skipwheelsVersionbehavior in the words the post describes. - The internal link target
/posts/skip-the-plugin-rate-limited-api/exists.
No changelog fragment is required (docs-type blog post, not a user-facing fix/feat). No tests apply. No security concerns.
Once the categories front matter is fixed, this is ready to merge.
Review follow-ups: categories was a one-element array holding the literal string '[]' (publishing-admin import artifact, now fixed at the source); the title="..." fence annotations are dropped by the blog's Shiki renderer, so the file-path labels moved into the preceding prose per the existing live-post convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Peter Amiri <peter@alurium.com>
|
Both findings addressed in d310db1:
Also took the DCO note: this follow-up commit is signed off; adding 🤖 Generated with Claude Code |
There was a problem hiding this comment.
Wheels Bot — Reviewer
TL;DR: This PR publishes the blog post "Anatomy of a Wheels Package" (one new file, web/content/blog/posts/anatomy-of-a-wheels-package.md). Because the post makes many specific claims about framework internals, this review is primarily a fact-check against the code — and the post passes it: every load-bearing claim I checked is accurate against vendor/wheels/PackageLoader.cfc, ModuleGraph.cfc, and the CLI installer. Verdict: comment — content is clean; one commit-hygiene item (missing DCO sign-off on the initial commit) should be fixed before merge.
Correctness
No findings. Claims verified against the code, including:
- Mixin allowlist (
application,dispatch,controller,mapper,model,base,sqlserver,mysql,postgresql,h2,test) matchesvendor/wheels/PackageLoader.cfc:66verbatim;global/nonespecial values match$validateMixinTargets(PackageLoader.cfc:1050). - Default of
nonewhenprovides.mixinsis absent —PackageLoader.cfc:364-372. Wheels.PackageInvalidMixinTargetthrown for typos/unsupported targets, naming the bad value and listing the valid set —PackageLoader.cfc:1058-1064.- Per-method
mixinmetadata overrides read viaGetMetadata()(PackageLoader.cfc:756-759), with the validation pre-pass running before any mixin-table mutation exactly as the post describes (PackageLoader.cfc:730-745). - Lifecycle hooks excluded from mixin collection:
init,onPluginLoad,onPluginActivate,register,boot—PackageLoader.cfc:727. - Entry-CFC convention (dir-name match, fallback to first
.cfc) —PackageLoader.cfc:601-614. - Alias derivation (first segment
LCase'd,wheels-i18n→wheelsI18n, identifier regex, explicit-but-invalidmappingfails rather than falling back) —$deriveMapping,PackageLoader.cfc:1091-1130. Duplicate-alias failure names both claimants and rolls the second package back —PackageLoader.cfc:1160-1168,$rollbackPackage. requires/replaces/suggestssemantics, including "suggested package loads first" (soft edgesugDir → dirNameinModuleGraph.cfc),"Required package not found"(ModuleGraph.cfc:203), cycle exclusion with members named (ModuleGraph.cfc:82-91), andSemVer.satisfiesAll()used for both dependency constraints andwheelsVersion(ModuleGraph.cfc:150,235;PackageLoader.cfc:1400).getFailedPackages()/getMixinCollisions()getters and theprovides.overridesacknowledged-but-still-recorded behavior —PackageLoader.cfc:129-142,810-822.- CLI sha256 verification ("Mismatch = hard abort") —
cli/lucli/services/packages/Installer.cfc:8,62-93. - The "docs drift fixed" claims check out: the guide (
web/sites/guides/src/content/docs/v4-0-0/digging-deeper/packages.mdx:91,101,277) andCLAUDE.mdnow documentrequiresand the hardwheelsVersionskip in the terms the post describes.
Conventions
No findings. Frontmatter (quoted ISO publishedAt/updatedAt, categories: [], coverImage: null, >- excerpt) matches sibling posts exactly (skip-the-plugin-rate-limited-api.md, background-jobs-without-redis.md). The internal link target /posts/skip-the-plugin-rate-limited-api/ exists. The follow-up commit already addressed the categories import artifact and moved fence titles into prose per the live-post convention.
Commits
- Missing DCO sign-off on
3167155da(docs(web/blog): Anatomy of a Wheels Package). The commit has noSigned-off-by:trailer; the follow-upd310db130is properly signed. PerCONTRIBUTING.md§ DCO this is project policy verified at review time (there is no CI check for it). Fix before merge withgit rebase --signoff develop(or an amend + force-push of the first commit). Both commit headers and the PR title are valid conventional-commit headers (docs(web/blog): …, ≤ 100 chars), so commitlint is clean.
Tests / Docs / Security
Docs-only blog content — no tests required, no changelog fragment required (not a user-facing fix/feat), no security surface. Nothing flagged.
Publishes "Anatomy of a Wheels Package".
anatomy-of-a-wheels-packageOpened automatically by wheels-publishing-admin on initial publish.