refactor(build): SRP-split build.mjs + numbered pipeline stages + sitemap and prose-check fixes#107
Merged
Conversation
Same regression class as /contribute (fixed in PR #106 commit df1b28c). The page was registered in src/build/build.mjs `subPages` and built `dist/scorecard-schema.{html,md}`, but `/scorecard-schema` was missing from src/build/sitemap.mjs's hardcoded paths array, so crawlers couldn't discover it. Documented as the "near miss" in `docs/solutions/conventions/new-content-page-requires-three-registrations-2026-05-21.md`. Add `/scorecard-schema` to the canonical paths list. dist/sitemap.xml now contains 116 entries (was 115), including the missing scorecard-schema row.
…es helper
Extract the per-rule LT denylist baseline, reachability probe, and per-file POST plumbing out of this repo's prose-check.sh and into the shared `~/dotfiles/config/shell/languagetool.sh` helper. The site script now sources the helper and contributes its own four site-local denylist rules (IN_PRINCIPAL, CONTRACT_CONTACT, TO_DO_HYPHEN, PLURAL_MODIFIER) on top of the baseline 10.
Three benefits:
- The baseline denylist (10 LT rules that misfire on RFC 2119 prose, code-fenced markdown, and CLI-domain jargon) now lives in one place across all four agentnative channel repos. Universal-rule additions edit the dotfiles helper, not four divergent scripts.
- The reachability probe and 2-second timeout policy stay consistent across consumers.
- This repo's prose-check.sh drops from 281 to 195 lines, with the LT plumbing replaced by `source "$LT_LIB"`.
Verified: `bash scripts/prose-check.sh` exits 0/1296 (0 blocking, 1296 warnings) — same totals as before the refactor.
Documents the helper contract in the script header: LT_LIB resolves to `${DOTFILES_SHELL_DIR:-$HOME/dotfiles/config/shell}/languagetool.sh`; missing helper produces exit 2 with a clear error pointing at brettdavies/dotfiles.
…lper Pairs with the prose-check.sh refactor in 86ed7fa. The inline curl-LT example in RELEASES.md's "Authoring PR bodies" section was the original 4-step prose-scrubbing recipe; the actual prose-check.sh now sources `~/dotfiles/config/shell/languagetool.sh` and invokes `lt_check` for the same coverage with less duplication. Update the snippet to match: replace the bare curl + jaq invocation with `lt_check /tmp/body.md` and point readers at `lt_rules` / `lt_info` for the rule denylist and category whitelist. Cross-references the agentnative-spec voice-enforcement nuance in CONTRIBUTING.md.
The build orchestrator had grown to 676 LOC with mixed responsibilities (homepage assembly, content sub-pages, the entire scorecard surface, llms.txt assembly, plus orchestration). Per the existing best-practices guidance in docs/solutions/best-practices/build-module-srp-dry-refactor-20260421.md, split at the natural data/render boundaries while preserving byte-identical output across all 370 dist/ artifacts. Four new emit modules carve out the largest cohesive blocks: - 06-homepage.mjs (160 LOC) — section 6: hero + live-score form + principle listing, plus the index.md twin. Owns the homepage helpers (buildHomepageBody, buildLiveScoreSection) that used to sit at module scope in build.mjs. - 07-subpages.mjs (60 LOC) — section 7: the content-driven HTML+MD twin loop. Carries the inline reminder that adding a new content/*.md file requires three coordinated registrations (this list, 10-sitemap.mjs's paths, src/build/shell.mjs's nav). - 08-scorecards-emit.mjs (292 LOC) — section 8 in full: registry loading, corpus invariants, build-time indexes, leaderboard page, per-tool scorecards + badge SVGs + binary-name redirects, stale-file reaping, coverage matrix, and skill manifest surfaces. - 09-llms-emit.mjs (102 LOC) — section 9: llms.txt index + llms-full.txt full-text bundle. Pipeline-stage modules now sort in execution order via numeric filename prefixes that match the section comments in build.mjs. Numbering is decorative (build.mjs is the actual order-enforcer via sequential awaits), but the directory listing reads in the order the orchestrator already documents: - 00-spec-version-gen.mjs (was spec-version-gen.mjs; section 0) - 01-assets.mjs (was assets.mjs; section 1) - 06-homepage.mjs (new; section 6) - 07-subpages.mjs (new; section 7) - 08-scorecards-emit.mjs (new; section 8) - 09-llms-emit.mjs (new; section 9) - 10-sitemap.mjs (was sitemap.mjs; section 10) Gaps reflect that sections 2 through 5 (the principle render loop) and section 9b (live-score-shell template) and section 11 (post-build invariant check) live inline in build.mjs. Shared helpers (badge, content, coverage, llms, registry-index, render, scorecards, scorecards-render, shell, skill, util) stay unnumbered because they do not represent a single pipeline stage. build.mjs drops from 676 to 267 LOC (60% reduction). The new build() body reads as a thirteen-step orchestrator with each emit step delegated to a module the directory listing surfaces in the matching section number. Verified: bun run build emits the same 370 dist/ artifacts with sha256 sums byte-identical to the pre-refactor state. bun test passes 736/736. bun run lint clean. The spec-version-gen banner in src/worker/spec-version.gen.ts updates to reference the renamed path; esbuild strips comments at bundle time so dist/ output is unaffected.
53e2585 to
f45b8a5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four-commit refactor branch covering one bug fix, one shared-tooling refactor, one doc update, and the headline SRP split of
src/build/build.mjs(676 LOC to 267, with pipeline-stage modules numbered in execution order so the directory listing reads as the build narrative).Changelog
Added
/scorecard-schemanow appears indist/sitemap.xml. The page rendered and was reachable by direct URL, but was missing fromsrc/build/06-sitemap.mjs's hardcoded paths so crawlers could not discover it. Flagged as the "near miss" in the convention doc landed in PR feat(nav): expose Skill and Contribute, add footer Source row #106 (docs/solutions/conventions/new-content-page-requires-three-registrations-2026-05-21.md).Changed
scripts/prose-check.shnow sources the LanguageTool wrapper from~/dotfiles/config/shell/languagetool.shrather than carrying its own baseline denylist + reachability probe inline. The site script contributes four site-local denylist rules (IN_PRINCIPAL, CONTRACT_CONTACT, TO_DO_HYPHEN, PLURAL_MODIFIER) on top of the baseline 10 in the helper. Drops the script from 281 to 195 lines and keeps universal-rule edits in one place across all four agentnative channel repos.src/build/build.mjssplit at the data/render boundary into four new per-stage emit modules. The orchestrator now reads as a 267-line sequential pipeline; each emit module owns one cohesive output surface.Fixed
RELEASES.mdreferenced the older barecurl + jaqLanguageTool invocation. Updated to calllt_check /tmp/body.mdso the documentation matches the actual script after the prose-check refactor.Documentation
02-homepage.mjs,03-subpages.mjs,04-scorecards-emit.mjs,05-llms-emit.mjseach carry module-level docstrings explaining what they emit and where downstream consumers pull from. The03-subpages.mjsheader includes the cross-reference to the three-registrations convention doc so future authors finding that module also find the convention.Type of Change
refactor: Code refactoring (no functional changes)fix: Bug fix (non-breaking change which fixes an issue)docs: Documentation updateBREAKING CHANGE: Breaking API change (requires major version bump)Related Issues/Stories
docs/solutions/best-practices/build-module-srp-dry-refactor-20260421.md(the refactor pattern this SRP split applies). Reviewed during the/ce-compound-refreshpass that paired with this work; doc remained valid without edits.Testing
Test Summary:
bun test: 736 pass, 0 fail.bun run lint: 0 errors (biome + markdownlint).bun run build: produces 370dist/artifacts.bash scripts/prose-check.sh: 0 blocking, 1296 warning (same totals as before the LT refactor, confirming the dotfiles helper produces equivalent coverage).find dist/ -type f | sort | xargs sha256sumproduces the same digest set before and after the refactor and again after the numbering pass. The spec-version-gen banner insrc/worker/spec-version.gen.tsupdates to reference the renamed path; esbuild strips comments at bundle time so the worker bundle output is unchanged.dist/sitemap.xmlrow count: 116 (was 115; the new row is/scorecard-schema).node scripts/generate-pack-readme.mjs --check: passes.Files Modified
Modified:
RELEASES.md: updated inline LanguageTool example snippetscripts/prose-check.sh: extract LT plumbing to shared~/dotfileshelper (281 to 195 lines)src/build/build.mjs: split per-stage emit logic out; now 267 lines (was 676), imports the seven numbered pipeline-stage modules in ordersrc/build/06-sitemap.mjs: add/scorecard-schemato canonical pathssrc/worker/spec-version.gen.ts: regenerated banner reflects renamed source pathtests/spec-version-gen.test.ts: updated import path + marker assertion for the renamed source moduleCreated:
src/build/02-homepage.mjs: homepage HTML + MD twin emit (section 6 of the pipeline)src/build/03-subpages.mjs: content-driven HTML + MD twin loop (section 7)src/build/04-scorecards-emit.mjs: leaderboard + per-tool scorecards + badges + coverage + skill surfaces (section 8)src/build/05-llms-emit.mjs: llms.txt + llms-full.txt assembly (section 9)Renamed:
src/build/spec-version-gen.mjstosrc/build/00-spec-version-gen.mjssrc/build/assets.mjstosrc/build/01-assets.mjssrc/build/sitemap.mjstosrc/build/06-sitemap.mjsDeleted:
Key Features
ls src/build/. Shared helpers (badge, content, coverage, llms, registry-index, render, scorecards, scorecards-render, shell, skill, util) stay unnumbered because they do not represent a single stage.build.mjs'sbuild()function reads as a thirteen-step orchestrator where each emit step is one call into a numbered module.Benefits
04-scorecards-emit.mjsonly; a future change to homepage layout touches02-homepage.mjsonly.build.mjs. A new contributor readingsrc/build/in the editor's file tree sees the same order as the orchestrator's awaits.Breaking Changes
The
dist/output is byte-identical to the pre-refactor state. The renamed source files have no public consumers outside the repo. Thetests/spec-version-gen.test.tsimport path updates in lockstep with the rename.Deployment Notes
A normal
devtostagingworker deploy ships the build.paths-ignoredoes not skip changes tosrc/build/.Screenshots/Recordings
n/a (no rendered-output changes;
dist/is byte-identical).Checklist
Additional Context
The SRP split applies the pattern documented in
docs/solutions/best-practices/build-module-srp-dry-refactor-20260421.md. The byte-identical safety net described there (find dist/ -type f | sort | xargs sha256sum) was used at every step.Out of scope for this PR: a build-time assertion that every
content/*.mdfile has a corresponding entry in thesubPageslist. The convention doc that surfaced the/scorecard-schemagap recommends this follow-up; deferred to a future PR.