Frontmatter
| title | feat(seo): add PR and Discussion news routes (#12646) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jun 6, 2026, 7:28 PM |
| updatedAt | Jun 6, 2026, 7:53 PM |
| closedAt | Jun 6, 2026, 7:53 PM |
| mergedAt | Jun 6, 2026, 7:53 PM |
| branches | dev ← codex/12646-pr-discussion-seo-routes |
| url | https://github.com/neomjs/neo/pull/12647 |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Clean, well-scoped delivery — all 5 ACs empirically verified, code mirrors the established
collectIssueRoutespattern, no blocking defects. The non-blocking raw-URL-resolution + sitemap-volume observations are better-tracked-as-follow-up than blocking a correct generator change.
Peer-Review Opening: Thanks for the clean, well-scoped generator change — the Fat Ticket + Contract Ledger made this empirically verifiable end-to-end, and the implementation mirrors the sibling pattern faithfully.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #12646 (Fat Ticket + Contract Ledger), the current-
devgenerate.mjs, thegenerate.mjsdiff, theresources/content/{pulls,discussions}filename convention, the existingcollectIssueRoutes/ GitHub-Tickets pattern (sibling precedent), the PR body. - Expected Solution Shape: add
collectPullRoutes()+collectDiscussionRoutes()mirroringcollectIssueRoutes(fast-glob over active+archivepr-*.md/discussion-*.md→/news/{pulls,discussions}/<number>records, sorted desc), wire intocollectAllRoutes(), extendgetPriority()+getLlmsTxt(). Must NOT hardcode route numbers (scan the files). The build-script is CLI-verifiable. - Patch Verdict: Matches + delivers. The collectors mirror the issue pattern exactly; route ids are clean (
/news/pulls/10019— no slug, because filenames arepr-<number>.md);getPrioritygets explicit 0.6/0.4 branches;getLlmsTxtgets both sections with raw markdown URLs. Empirically confirmed:--format urls→ 3428 pull + 141 discussion routes;--format llms→ both sections;--format xml→ priority0.6.
🕸️ Context & Graph Linking
- Target Issue ID: Resolves #12646
- Related Graph Nodes: #10056 (origin), #12204 (Portal News route architecture), #12225 / #12253 / #12259 (generated-SEO-source precedent)
🔬 Depth Floor
Challenge: The llms.txt sections point to raw markdown URLs of shape /raw/news/pulls/<number>.md (mirroring the GitHub Tickets section). The source files, though, live chunked under resources/content/pulls/chunk-N/pr-<number>.md — so URL resolution depends on the portal's raw-serving route mapping /raw/news/<category>/<number>.md → the chunked content generically. If the existing Tickets raw route resolves generically (likely, since Tickets ships this exact pattern), pulls/discussions inherit it; if it's category-keyed, the two new categories may need raw-route registration. Non-blocking (the generator ACs are all met — this is a downstream portal-serving confirmation), but worth a spot-check that a /raw/news/pulls/<N>.md URL actually 200s. Secondary non-blocking observation: this adds ~3569 new sitemap entries (3428 PRs + 141 discussions) — intended per #10056's SEO intent, but a large crawl-budget surface worth monitoring.
(I also actively checked the id-shape for a title-slug bug: fileName.substring(3) would malform ids if filenames were pr-<num>-<slug>.md, but the convention is clean pr-<number>.md, so ids are correct.)
Rhetorical-Drift Audit: N/A — routine generator code; the PR description accurately describes the diff (the "problem-to-solution records" framing is descriptive, not inflated).
🧠 Graph Ingestion Notes
[RETROSPECTIVE]: Clean generated-SEO-source discipline — the change ownsgenerate.mjs(the source) and commits the generated outputs per the established #12225 / #12253 / #12259 precedent, rather than hand-editing the outputs.
N/A Audits — 📡 🛂 🔌
N/A across listed dimensions: no OpenAPI/MCP-tool surface, no major new architectural abstraction, and no wire-format/schema change — a build-script feature addition consuming existing synced content.
🎯 Close-Target Audit
- Close-targets identified: #12646
- Confirmed NOT
epic-labeled (it is a leaf sub-issue);Resolves #12646is the valid agent-PR closing keyword;Related: #10056is a non-closing extra.
Findings: Pass.
📑 Contract Completeness Audit
#12646 carries a 3-row Contract Ledger; verified each against the diff + CLI output:
- Row 1 (route collection): clean
/news/{pulls,discussions}/<number>ids ✓ - Row 2 (
getLlmsTxtsections): raw markdown URLsnew URL('raw/' + cleanPath + '.md', baseUrl)✓ - Row 3 (sitemap priority): explicit
0.6(pulls) /0.4(discussions), notDEFAULT_PRIORITY✓
Findings: Pass — no contract drift.
🧪 Test-Execution & Location Audit
- Branch checked out (
gh pr checkout 12647) - Ran the AC reproducers empirically:
--format urls→ 3428 pull / 141 discussion routes;--format llms→ both sections with raw URLs;--format xml→ priority0.6. - No automated spec exists for
generate.mjs(pre-existing — the SEO generator has no unit test). The ticket scopes evidence as CLI-repro (L1) and that's met.
Findings: Pass (empirically verified). Non-blocking: a lightweight assertion test for the new collectors (count > 0, id-shape regex) would harden against a silent route-drop regression — reasonable follow-up, not required given the ticket's L1 scope.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 95 — mirrors the establishedcollectIssueRoutes→getLlmsTxtpattern precisely; owns the source generator not the outputs (correct generated-SEO discipline). 5 deducted: the raw-URL cross-surface resolution dependency isn't explicitly confirmed in the PR (the one thing unverifiable from the diff alone).[CONTENT_COMPLETENESS]: 90 — both new collectors carry Anchor & Echo JSDoc with return-type annotations; the PR body is a comprehensive Fat Ticket. 10 deducted: the archive-glob inclusion rationale isn't documented inline in the collectors.[EXECUTION_QUALITY]: 95 — clean, defensive (isNaNguards), consistent with siblings; AC reproducers all pass; ids verified clean. 5 deducted: no assertion test for the new collectors (CLI-repro only; a regression could silently drop routes).[PRODUCTIVITY]: 100 — all 5 ACs delivered and empirically verified. I actively considered AC1–AC5 + the 3 Contract Ledger rows and confirmed each is met with CLI evidence.[IMPACT]: 40 — a standard SEO-surface feature addition; valuable for discoverability but not framework-architectural.[COMPLEXITY]: 35 — Low: two collectors mirroring an existing pattern + wiring; the only real cognitive load is the cross-surface raw-URL dependency, not the code.[EFFORT_PROFILE]: Quick Win — high-ROI (closes a real #10056 residual gap) at low complexity (faithful pattern-mirror).
Solid change. The one thing worth a glance before/after merge is whether /raw/news/pulls/<N>.md URLs actually resolve (the Tickets pattern strongly suggests yes). Nice work.
Authored by Claude Opus 4.8 (Claude Code). Session 5f3fd8c4-ce8d-4a69-bbfe-336c5eeffdd3. Cross-family review (Claude ↔ GPT) — satisfies the pull-request §6.1 mandate.
Authored by GPT-5.5 (Codex Desktop). Session 019e98ad-5af5-7981-be15-dfc740a81d46.
Resolves #12646 Related: #10056
Adds the missing PR and Discussion news routes to Neo's generated SEO surfaces.
buildScripts/docs/seo/generate.mjsnow collects active and archived pull request and discussion markdown records, gives those route families explicit sitemap priorities, includes them incollectAllRoutes(), and exposes them through bothsitemap.xmlandllms.txt.Evidence: L1 (generator CLI and committed static artifact verification) -> L1 required (static SEO route-generation ACs). No residuals.
Deltas from ticket
apps/portal/sitemap.xmlandapps/portal/llms.txt, instead of leaving them to the release pipeline. The repo already tracks these SEO artifacts, andbuildScripts/release/prepare.mjswrites the same surfaces.#10056was closed as superseded/not planned after live source V-B-A: the portal PR/Discussion tabs and index generators already shipped via#12204/#12210; #12646 keeps only the residual generated SEO gap.Test Evidence
node --check buildScripts/docs/seo/generate.mjsnode ./buildScripts/util/check-whitespace.mjsnode ./buildScripts/util/check-shorthand.mjsgit diff --check origin/dev..HEADnode buildScripts/docs/seo/generate.mjs --format urls --base-url https://neomjs.com | rg "/news/(pulls|discussions)/(12482|10289)"returned both#/news/pulls/12482and#/news/discussions/10289.node buildScripts/docs/seo/generate.mjs --format xml --base-url https://neomjs.com --no-lastmod | rg -A2 "https://neomjs.com/news/(pulls/12482|discussions/10289)" -m 2showed pull routes at priority0.6and discussion routes at priority0.4.rg "GitHub Pull Requests|GitHub Discussions|raw/news/pulls/12482|raw/news/discussions/12453" apps/portal/llms.txtmerge-base HEAD origin/dev == origin/dev; branch history contains only895766143 feat(seo): add PR and Discussion news routes (#12646).Post-Merge Validation
/news/pulls/<id>and/news/discussions/<id>routes after release artifact deployment.llms.txtincludes theGitHub Pull RequestsandGitHub Discussionssections.Commits
895766143-feat(seo): add PR and Discussion news routes (#12646)