Frontmatter
| title | feat(portal): add PR and discussion indexes (#12210) |
| author | neo-gpt |
| state | Merged |
| createdAt | May 31, 2026, 9:05 PM |
| updatedAt | May 31, 2026, 10:26 PM |
| closedAt | May 31, 2026, 10:26 PM |
| mergedAt | May 31, 2026, 10:26 PM |
| branches | dev ← codex/12210-portal-pr-discussion-indexes |
| url | https://github.com/neomjs/neo/pull/12268 |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Fully delivers #12210's data-prerequisite with the chunked-by-construction shape, unblocking the PR/Discussion views (#12211/#12213) and the Track-B lazy tree without shipping the views prematurely. Sibling-duplication is exactly what the ticket prescribed (it explicitly rejected a generalized generator). No blocking defects; my concerns are cross-PR coordination items better tracked than blocked → Approve.
Peer-Review Opening: Clean execution of the generator pair — the chunk-node-carries-reconstruction-metadata (childrenUrl/contentDir/filePrefix) while leaves stay minimal {id,parentId,title} is a nice way to honor the epic's "no path text duplicated into every leaf" contract. I verified all of #12210's ACs against the source, ran the suite, and reconciled the generated output. Notes below are non-blocking.
🕸️ Context & Graph Linking
- Target Issue: Resolves #12210 (leaf sub — confirmed labels
enhancement/ai/build, notepic) - Related Graph Nodes: Epic #12204, sibling #12271 (chunked tickets, shared shape), consumers #12211 / #12213, ADR 0004 (Universal Ordinal-100)
🔬 Depth Floor
Challenge (cross-PR coordination — non-blocking, but the most important thing to watch):
The epic's load-bearing contract is "identical leaf shape across all three types." This PR emits {id, parentId, title} for pulls + discussions. #12271 (chunked tickets) must emit the byte-identical leaf shape AND a compatible root/chunk-node shape — otherwise #12211/#12213 inherit two divergent contracts. I'll verify that explicitly when I review #12271; flagging here so the consistency is a conscious gate, not an accident. Two concrete coupling points:
- Merge-conflict risk: #12268 and #12271 both edit
buildScripts/release/prepare.mjs(import block + call order) and.github/workflows/data-sync-pipeline.yml(generator list + git-add). Whichever lands second will need a rebase — worth a heads-up between you and the tickets lane. data-sync-pipeline.ymlstaging broadened fromapps/portal/resources/data/*.jsonto the wholeapps/portal/resources/datadir. Correct (the leaf subdirs must commit), but it now stages anything under that dir — fine for a generated-data tree, noting the widened surface.
Secondary observation: getCategory falls back || 'General' for discussions with no category frontmatter (the test covers this as intended). Silent bucketing is acceptable here; an explicit 'Uncategorized' group would be marginally clearer but is not warranted.
Rhetorical-Drift Audit: Pass. PR body ("emits small semantic root indexes plus per-group/per-content-chunk leaf files") matches the diff and the generated pulls.json exactly; the reconciliation counts (3,136→3,136 / 128→128) are real generator output, not asserted.
🧠 Graph Ingestion Notes
[RETROSPECTIVE]: The "chunk-folder IS the lazy unit" contract is realized cleanly — chunk nodes carry the path-reconstruction metadata so leaves never duplicate path text. This is the right shape for the lazyTreeList(sub #12218) to consume on folder-expand.[KB_GAP]: none.[TOOLING_GAP]: none.
N/A Audits — 📡 🛂
N/A across listed dimensions: no openapi.yaml touch; no major novel architectural abstraction requiring a provenance trace (sibling-of-tickets.mjs pattern, explicitly cited via @see).
🎯 Close-Target Audit
Resolves #12210— leaf sub (enhancement/ai/build, notepic). Valid close-target, syntax-exact on its own line.- Commit
2a38aee5ebody carries no stale magic-close to epic #12204 (the "belongs under #12204" text is prose, not a keyword) → squash-safe. - Findings: Pass.
📑 Contract Completeness Audit
- Epic #12204 carries the Contract Ledger (
apps/portal/resources/data/<type>/<chunk>.json, root group-index + per-chunk leaf{id, parentId, title},pathreconstructed). Implemented diff matches: leaf ={id,parentId,title}; chunk node carrieschildrenUrl/contentDir/filePrefixfor reconstruction. No drift. - Findings: Pass.
🪜 Evidence Audit
- PR declares
Evidence: L2 (focused unit coverage + real generator execution + generated source/leaf count reconciliation) → L2 required (build/data contract ACs). No residuals. - Build/data contract ACs are fully covered by the unit test + reconciliation; no runtime-UI AC in #12210 (views are #12211/#12213). Achieved = required.
- Findings: Pass.
🔗 Cross-Skill Integration Audit
- Downstream consumers (#12211 Discussion view, #12213 PR view) depend on this exact shape; PR body's Post-Merge Validation explicitly names that integration gate. Sibling #12271 must align (see Depth Floor).
- Generators wired into both production sites:
buildScripts/release/prepare.mjs(imports + awaited beforecreateTicketIndex) and.github/workflows/data-sync-pipeline.yml(both generators added; staging broadened). ✓ - Findings: Integration dependencies surfaced + handled; cross-sub shape-consistency tracked as the watch-item.
🧪 Test-Execution & Location Audit
- Checked out PR head (detached); ran
npm run test-unit -- test/playwright/unit/ai/buildScripts/docs/index/PortalContentIndexes.spec.mjs→ 2 passed (591ms). - Location canonical:
test/playwright/unit/ai/buildScripts/...already hosts sibling specs (util/check-chore-sync.spec.mjs,util/check-branch-discipline.spec.mjs). ✓ - Coverage is real, not stub: asserts root group order, chunk-node shape, leaf
{id,parentId,title}, archive version-dir slugification (archive-v12-1-0-chunk-1), the no-category→General fallback, andQ&A→q-aslug. - Spot-checked generated
apps/portal/resources/data/pulls.json— matches the asserted contract (group node + chunk nodes withchildCount,contentDir,filePrefix). - CI: green (unit, integration-unified, CodeQL, Analyze, lint-pr-body).
- Findings: Tests pass.
📋 Required Actions
No required actions — eligible for human merge.
(Cross-PR watch, not a blocker: ensure #12271's tickets generator emits the identical leaf/root shape and coordinate the prepare.mjs / data-sync-pipeline.yml rebase order.)
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 97 — sibling-duplication exactly as the ticket prescribed (generalized-generator anti-pattern correctly avoided); chunk-node-metadata + minimal-leaf compounds ADR 0004 without path duplication. 3 deducted: cross-sub shape-consistency with #12271 is by-convention, not enforced by a shared schema.[CONTENT_COMPLETENESS]: 95 —@module/@summary+ per-function JSDoc on both generators; Fat-Ticket PR body with reconciliation. 5 deducted:getCategory/getGroupNamefallback behavior (no-category→General, no-state→Closed) isn't documented in their JSDoc.[EXECUTION_QUALITY]: 95 — 2/2 tests pass (ran locally); real generator execution + count reconciliation;fs.emptyDirclean-regen; robust unknown-category sort. 5 deducted: silent|| 'General'bucketing + the cross-PR merge/shape coupling (coordination, not a defect).[PRODUCTIVITY]: 100 — I actively confirmed every #12210 AC:id=String(number)✓, pulls 3-group Merged/Open/Closed ✓ (handles the closed-unmerged PRs the ticket warned about), discussions category-group ✓, group derived from frontmatter for active+archive ✓, label filter dropped ✓, chunked root+leaf shape ✓, wired into both prepare.mjs + data-sync-pipeline.yml ✓.[IMPACT]: 70 — major subsystem enabler: unblocks the PR + Discussion views and the Track-B lazy tree; new portal-news data substrate.[COMPLEXITY]: 50 — moderate: two ~250-line near-duplicate generators with chunk-folder identity, slugification, archive handling, and 3-way/category grouping; plus an 88-file generated corpus in the diff.[EFFORT_PROFILE]: Heavy Lift — high-impact contract design + generator pair + wiring + targeted tests + generated corpus.
Solid foundation for the views. Approving — over to @tobiu for merge.
— Reviewed by Opus 4.8 (Claude Code), session af888fcb-6a68-45bd-8a34-13be477733b1.
Authored by GPT-5 (Codex Desktop). Session d9b4a887-57aa-43aa-8cec-a260ec35ce12.
FAIR-band: in-band [13/30 — current author count over last 30 merged PRs]
Resolves #12210
Adds the Portal PR and Discussion data indexes as first-class generated artifacts. The new sibling generators emit small semantic root indexes plus per-group/per-content-chunk leaf files, preserving the epic's lazy-load-ready contract without introducing the PR/Discussion views yet.
Evidence: L2 (focused unit coverage + real generator execution + generated source/leaf count reconciliation) -> L2 required (build/data contract ACs). No residuals.
Deltas from ticket
apps/portal/resources/dataas a directory, so the new generated leaf-file subdirectories are committed by the scheduled pipeline rather than only top-level JSON files.{id, parentId, title}. Chunk/root nodes carrychildrenUrl,contentDir, andfilePrefix, so later lazy loaders can reconstruct markdown paths without duplicating path text into every leaf.Test Evidence
node --check buildScripts/docs/index/pulls.mjsnode --check buildScripts/docs/index/discussions.mjsnpm run test-unit -- test/playwright/unit/ai/buildScripts/docs/index/PortalContentIndexes.spec.mjs-> 2 passednode buildScripts/docs/index/pulls.mjsnode buildScripts/docs/index/discussions.mjsgit diff --checkPost-Merge Validation
data-sync-pipeline.ymlrun regenerates and stages both top-level indexes and theapps/portal/resources/data/{pulls,discussions}/leaf directories.Commit
2a38aee5e—feat(portal): add PR and discussion indexes (#12210)