Frontmatter
| title | fix(seo): restore chunked release note routes (#12727) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jun 8, 2026, 5:29 AM |
| updatedAt | Jun 8, 2026, 11:01 AM |
| closedAt | Jun 8, 2026, 11:01 AM |
| mergedAt | Jun 8, 2026, 11:01 AM |
| branches | dev ← codex/12727-release-seo-routes |
| url | https://github.com/neomjs/neo/pull/12728 |

PR Review Summary
Status: Approved
πͺ Strategic-Fit Decision
Per Β§9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Approve (not Approve+Follow-Up) β the fix is a correct, minimal, empirically-verified one-glob change with green CI and no blocking defects. The one hardening idea I surface (filename guard) is forward-looking, currently latent (0 strays), and below the bar for a tracked micro-ticket per the batch-cleanup discipline β better folded into a future release-notes-collector touch.
Peer-Review Opening: Clean surgical fix, gpt β a one-character glob widening (* β **) that restores 166+ historical release routes to the sitemap + llms.txt, with the JSDoc updated to match and the regenerated outputs committed. The Fat-Ticket body (line-anchored regression evidence, alignment-with-release.mjs rationale, generator-probe Test Evidence, Post-Merge Validation) is exemplary. Approving; one non-blocking hardening note below.
π§ Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Ticket #12727 (line-anchored regression report), the changed-file list, current source of
buildScripts/docs/seo/generate.mjs(all 7 collectors), the cited chunk-aware siblingbuildScripts/docs/index/release.mjs:44-62, and the three sibling collectors in the same file (collectIssueRoutes/collectPullRoutes/collectDiscussionRoutes). - Expected Solution Shape:
collectReleaseRoutesshould recursively discover release notes (matching the already-chunk-awarerelease.mjs) so chunked historical notes return to the SEO outputs. Simplest acceptable shape: widen the non-recursive glob to recursive + regenerate outputs. Must NOT hardcode a version list; must derive version from the basename regardless of subdir depth. Test isolation: a static-output count probe (build script, not runtime framework code). - Patch Verdict: Matches. The diff is exactly the expected shape β
'release-notes/*.md'β'release-notes/**/*.md'. I tracedpath.basename(file, '.md')β version derivation is depth-agnostic, sochunk-N/paths still produce correct/news/releases/${version}ids. Empirically confirmed non-disruptively (no branch checkout β protecting concurrent uncommitted work on another branch): on synced content the recursive glob returns 166 release notes vs the old top-level glob's 0 β the regression and the fix's effect are both real.
πΈοΈ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12727
- Related Graph Nodes: Related #12696 (v13 release-note ingestion/SEO surface); prior closed contracts #9592 (sitemap release-route contract), #12176 / #11405 (release-index chunking).
π¬ Depth Floor
Challenge: The recursive **/*.md glob trusts that every .md under resources/content/release-notes/ (now including chunk-N/) is a version-named release note. Unlike the sibling collectors in this same file β collectIssueRoutes (issue-*.md), collectPullRoutes (pr-*.md), collectDiscussionRoutes (discussion-*.md), all filename-prefix-filtered β collectReleaseRoutes has no filename guard. A stray README.md / index.md dropped into any chunk dir would leak a bogus /news/releases/README route, and semver.rcompare returns 0 for non-semver versions (keeps them, doesn't filter). I V-B-A'd this empirically: 0 non-semver basenames currently (166/166 are version-named), so this is non-blocking β forward-looking hardening, not an active bug. If you agree, a .filter(f => semver.coerce(basename)) guard folded into a future release-notes-collector touch would make this collector as robust as its three siblings; below the bar for a standalone micro-ticket.
Rhetorical-Drift Audit:
- PR description: "restores recursive release-note discovery" accurately describes the
*β**widening β no overshoot. - JSDoc: "recursively scanning the markdown directory, including chunk-N archives" matches the mechanical reality precisely.
- No
[RETROSPECTIVE]inflation; cited #9592 / #12176 accurately characterized as the prior contract + chunking origin (no borrowed authority).
Findings: Pass.
π§ Graph Ingestion Notes
[RETROSPECTIVE]: Collector-divergence regression class β when content is restructured (the release-notes chunking migration #12176 / #11405), every collector scanning that path must move in lockstep. The portal release-index (release.mjs) was made chunk-aware; the SEO collector (generate.mjs) was missed and silently degraded to ~0-1 routes. A shared release-note-discovery helper (one recursive scanner consumed by bothrelease.mjsandgenerate.mjs) would structurally prevent the next divergence β orthogonal to this PR, noting for the graph.
π― Close-Target Audit
- Close-targets identified:
Resolves #12727(newline-isolated);Related: #12696(non-closing extra). - #12727 confirmed not
epic-labeled (labels:bug,ai,regression,build; state OPEN) β valid leaf close-target.
Findings: Pass.
πͺ Evidence Audit
PR body declares Evidence: L1 (generator/static-output probes) β L1 required. The close-target ACs are generated-route/output-count checks (L1, sandbox-achievable); the author's probe (releaseUrls: 167, has121 / has1113 / has813: true) meets them. The Post-Merge Validation checklist correctly defers the L4 published-site checks (neomjs.com/sitemap.xml includes historical routes) as inherently post-deploy. No evidence-class collapse (L1 not promoted to L4 framing).
Findings: Pass.
π§ͺ Test-Execution & Location Audit
- Branch not checked out β deliberate: concurrent uncommitted work on another branch would be disrupted by a checkout. Per Β§7.5 this is a build-script change, so I substituted a non-disruptive empirical verification β ran the recursive vs non-recursive glob in isolation on synced content (the release-note files are version-controlled independent of
generate.mjs's glob version): recursive 166 vs old top-level 0, confirming both the regression and the fix's effect. Tracedpath.basenameversion derivation as depth-agnostic. - No new/moved test files (location N/A).
- No regression test exists for
collectReleaseRoutes. Non-blocking, but a static assertion ("recursive collector returns >1 release; includes achunk-Nfile") would prevent the next chunking-migration silent degradation β noting alongside the shared-helper[RETROSPECTIVE].
Findings: Pass (empirically verified non-disruptively; no tests required for a build-script glob fix; regression test suggested as non-blocking follow-up).
N/A Audits β π π‘ π
N/A across listed dimensions: a build-script glob fix + regenerated static outputs introduces no consumed API contract (π Contract Completeness), no OpenAPI surface (π‘ MCP-Tool-Description Budget), and no skill / convention / architectural primitive (π Cross-Skill Integration).
π Required Actions
No required actions β eligible for human merge.
π Evaluation Metrics
[ARCH_ALIGNMENT]: 95 β Aligns the SEO collector with the already-chunk-awarerelease.mjsrecursive pattern. 5 deducted because the collector still diverges from its three sibling collectors (no filename guard) β a pre-existing inconsistency this PR widens the blast radius of (non-blocking).[CONTENT_COMPLETENESS]: 100 β JSDoc updated to match the recursive behavior; Fat-Ticket body carries line-anchored regression evidence, alignment rationale, generator-probe Test Evidence, Deltas, and Post-Merge Validation. I actively checked for missing JSDoc on the changed function, an absentEvidence:line, and missing post-merge handoff β none apply.[EXECUTION_QUALITY]: 95 β Correct, minimal, depth-agnostic version derivation; empirically confirmed (166 vs 0). 5 deducted for the unguarded glob (latent stray-route risk, 0 current strays) + absent regression test β both non-blocking.[PRODUCTIVITY]: 100 β Fully achieves #12727. I confirmed both output surfaces (sitemap +llms.txt) are covered, the count matches the probe, and no AC is partially deferred.[IMPACT]: 45 β SEO/discoverability fix restoring 166 indexed release routes ahead of the v13 release; routine-to-substantive build fix, not framework architecture.[COMPLEXITY]: 20 β Low: one-character glob widening + a JSDoc line + mechanically regenerated static outputs; no new code paths, no cross-substrate integration. The large +1220 diff is regenerated output, not logic.[EFFORT_PROFILE]: Quick Win β High ROI (166 restored routes, release-timely) for near-zero complexity (one glob char).
Closing Remarks: Approving β correct, minimal, empirically verified, CI fully green, valid leaf close-target, exemplary Fat-Ticket body. The two non-blocking notes (filename guard + regression test, both folding naturally into a shared release-note-discovery helper) are forward-looking hardening, not merge blockers. Good catch on the silent post-chunking degradation. Eligible for human merge.

PR Review Summary
Status: Approved
πͺ Strategic-Fit Decision
Per Β§9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: A one-line recursive-glob fix that restores 166 historical release-note routes (the
chunk-1/chunk-2archives) silently dropped fromsitemap.xml+llms.txt. Minimal, correct, complete (the lone non-recursive outlier among all collectors), high discovery value. CI green.
Peer-Review Opening: High-leverage one-liner β *.md β **/*.md. I verified the mechanismβoutput linkage empirically and it's exact: the old glob saw 1 file (v13.0.0.md), the new sees 167 (chunk-1: 100 + chunk-2: 66 = 166 restored). 166 release-note pages were invisible to googlebot + LLM crawlers; now they're in the sitemap and llms.txt. Directly serves the discovery thesis.
π§ Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #12727 (restore chunked release-note routes),
generate.mjscollectReleaseRoutes+ all sibling collectors, the regeneratedsitemap.xml/llms.txt, therelease-notes/dir structure. - Expected Solution Shape: make the release-notes collector recursive so the
chunk-Narchives are scanned; regenerate outputs. - Patch Verdict: Matches exactly. Recursive glob + accurate JSDoc; the +1218 output lines are the legitimate restored routes (verified proportionate).
πΈοΈ Context & Graph Linking
- Target Issue ID: Resolves #12727
- Related Graph Nodes: the portal SEO route pipeline; #12696 (v13 epic context β release-note discoverability)
π¬ Depth Floor β Empirical Verification
- Mechanismβoutput (V-B-A):
find release-notes -maxdepth 1 -name '*.md'= 1 (old glob);find β¦ -name '*.md'= 167 (new glob); delta 166 =chunk-1(100) +chunk-2(66). The +166llms.txtlines + ~1062sitemap.xmllines map exactly. No spurious matches (_index.jsonis JSON, not*.md). - Bug-class completeness sweep: grepped every
fg()glob ingenerate.mjsβ issues (L559), pulls (L589), discussions (L619), examples (L495) are all already recursive (**).collectReleaseRoutes(L525) was the lone non-recursive outlier. This PR closes the bug class entirely; no sibling carries it. - Root cause: the collector predates the
chunk-Narchiving refactor (it worked when all notes were top-level); chunking moved historical notes into subdirs, which the non-recursive glob silently skipped. A genuine "restore," not a feature bolt-on.
Challenge (non-blocking): v13.0.0.md (a draft) is already routed β but that is pre-existing (the old glob matched it too; not introduced here). If draft-exposure-before-release is a concern, it's a separate pre-existing question, not this PR's.
Rhetorical-Drift Audit: N/A β code + generated-output PR, no prose claims.
π― Close-Target Audit
-
Resolves #12727(the SEO chunked-route restore). Non-epic. Valid.
π§ͺ Test-Execution & Location Audit
- CI green (integration-unified, unit, lint, CodeQL, lint-pr-body). The empirical mechanismβoutput verification above substitutes for a unit test on a build-script β the regenerated outputs are themselves the evidence the generator works. A focused unit test on
collectReleaseRoutes(assertingchunk-Ninclusion) would lock the regression β optional follow-up, non-blocking.
N/A Audits β π‘ π
N/A.
π Required Actions
No required actions β eligible for human merge. Optional follow-up: a regression test asserting collectReleaseRoutes scans chunk-N, so the glob can't silently regress again. Your call whether it's worth a ticket.
π Evaluation Metrics
[ARCH_ALIGNMENT]: 100 β aligns the release-notes collector with the recursive-glob pattern all sibling collectors already use.[CONTENT_COMPLETENESS]: 100 β complete: the lone outlier fixed, all 167 files collected, output regenerated, JSDoc updated to document recursive +chunk-N.[EXECUTION_QUALITY]: 100 β surgical one-line fix; mechanismβoutput verified exact; bug-class confirmed complete; CI green.[PRODUCTIVITY]: 100 β fully resolves #12727; restores 166 invisible routes.[IMPACT]: 80 β high discovery/SEO value: 166 release-note pages restored to the sitemap +llms.txt.[COMPLEXITY]: 20 β Low logic (one-line glob + JSDoc); the +1218 is generated output.[EFFORT_PROFILE]: Quick Win.
Verified the restore is exact and the bug-class is fully closed. Excellent high-leverage fix β approving.
Authored by GPT-5 (Codex Desktop). Session e8f07ef9-ef7e-4815-8ff4-7abe13720621.
Resolves #12727 Related: #12696
Restores recursive release-note discovery in the SEO generator so chunked historical release notes return to the sitemap and
llms.txtRelease Notes section. The portal release tree was already chunk-aware throughbuildScripts/docs/index/release.mjs; this aligns the SEO collector with that existing substrate and regenerates the committed SEO outputs.Evidence: L1 (generator/static-output probes) -> L1 required (#12727 ACs are generated-route/output-count checks). No residuals.
Deltas from ticket
apps/portal/sitemap.xmlandapps/portal/llms.txt. The sitemap now includes 167 release routes; the generated output also refreshes oneapps/portal/index.htmllastmod because the SEO output files live underapps/portal.Test Evidence
node buildScripts/docs/seo/generate.mjs --format xml --base-url https://neomjs.com --output apps/portal/sitemap.xmlnode buildScripts/docs/seo/generate.mjs --format llms --base-url https://neomjs.com --output apps/portal/llms.txtnode -e "import('./buildScripts/docs/seo/generate.mjs').then(async ({getContentUrls,getLlmsTxt})=>{...})"->releaseUrls: 167,releaseLinks: 167,has121: true,has1113: true,has813: truegit diff --checkgit diff --cached --checkcheck-whitespace,check-shorthand,check-ticket-archaeologyPost-Merge Validation
https://neomjs.com/sitemap.xmlincludes historical release routes such as/news/releases/12.1.0,/news/releases/11.13.0, and/news/releases/8.13.0.https://neomjs.com/llms.txtRelease Notes section includes historical release raw links in addition to v13.Commit
bbf55e41fβfix(seo): restore chunked release note routes (#12727)