LearnNewsExamplesServices
Frontmatter
titlefeat(portal): emit chunked ticket indexes (#12217)
authorneo-gpt
stateMerged
createdAtMay 31, 2026, 9:52 PM
updatedAtMay 31, 2026, 10:51 PM
closedAtMay 31, 2026, 10:51 PM
mergedAtMay 31, 2026, 10:51 PM
branchesdevcodex/12217-tickets-chunk-index
urlhttps://github.com/neomjs/neo/pull/12271
Merged
neo-gpt
neo-gpt commented on May 31, 2026, 9:52 PM

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]

Evidence: L2 (focused unit test + real generator output-shape verifier + unchanged legacy tickets.json diff) → L3 required (post-merge portal lazy-load consumer proof in #12218/#12219). Residual: #12218/#12219.

Does this PR resolve an issue? (Required)

Resolves #12217 Resolves #12220 Related: #12204

What kind of change does this PR introduce? (check at least one)

  • Bugfix
  • Feature
  • Code style update
  • Refactor
  • Build-related changes
  • Other, please describe:

Does this PR introduce a breaking change? (check one)

  • Yes
  • No

If yes, please describe the impact and migration path for existing applications:

The PR fulfills this requirement:

  • It's submitted to the dev branch, not the main branch

If adding a new feature, the PR's description includes:

  • A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

Other information:

Summary

  • Extends the portal ticket index generator to emit both the current compatibility feed (apps/portal/resources/data/tickets.json) and the future chunked lazy-load surface (apps/portal/resources/data/tickets/index.json plus leaf chunks).
  • Adds a deterministic crawler manifest so downstream work can discover every chunk without loading leaf payloads into the root index.
  • Updates the data-sync staging path so nested portal data files are detected and committed by the hourly pipeline.
  • Adds focused unit coverage for the generator contract: legacy full tree, chunked root, path-free leaves, and manifest shape.

Deltas

  • Preserves tickets.json instead of replacing the current Portal store input in this PR.
  • Emits tickets/index.json, tickets/manifest.json, and deterministic chunk files as the additive future surface for #12218/#12219.
  • Folds #12220 into this PR because the generator JSDoc now documents the actual dual-output contract and the chunked JSON file structure is implemented.

Compatibility

The existing tickets.json route remains present and unchanged for the current Portal consumer. The new chunked surface is intentionally additive so #12218/#12219 can switch the runtime store and route behavior without destabilizing /news/tickets in this PR.

Scope Notes

  • This PR does not close the parent epic #12204.
  • #12220 is resolved here because the generator documentation now describes the actual dual-output contract and the chunked JSON structure is emitted as real data.
  • The chunk leaf records intentionally contain only id, parentId, and title; markdown paths stay out of the eagerly loaded chunk payload.

Test Evidence

  • node --check buildScripts/docs/index/tickets.mjs
  • node --check test/playwright/unit/ai/buildScripts/docs/index/TicketIndex.spec.mjs
  • npm run test-unit -- test/playwright/unit/ai/buildScripts/docs/index/TicketIndex.spec.mjs -> 1 passed
  • node buildScripts/docs/index/tickets.mjs -> 8772 ticket leaves, 1002 groups, 1032 chunk files plus root index and manifest
  • Shape verifier -> 2034 root nodes, 1032 chunks, 8772 leaves, no malformed leaf payload, sample leaf keys id,parentId,title
  • git diff -- apps/portal/resources/data/tickets.json -> no output after generation
  • git diff --check

Post-Merge Validation

  • Verify the hourly data-sync pipeline stages nested apps/portal/resources/data/tickets/** changes.
  • Continue with #12218/#12219 to switch the runtime store to tickets/index.json and verify first paint only loads the root index plus requested chunks.
  • Use Neural Link on the Portal /news/tickets route after #12218/#12219 land to prove the lazy-load consumer is stable.
github-actions commented on May 31, 2026, 9:52 PM

🚨 Agent PR Body Lint Violation

@neo-gpt — your PR body on PR #12271 does not match the pull-request template structure.

Required action: read .agents/skills/pull-request/SKILL.md BEFORE editing the PR body. The skill points at:

  • Minimum-viable PR body structure: .agents/skills/pull-request/references/pull-request-workflow.md §9
  • Self-Identification mandate: .agents/skills/pull-request/references/pull-request-workflow.md §5

Do NOT compose a substitute template or hallucinate section headings. The validator checks more structural anchors than this comment names. The only reliable path to passing is reading the actual workflow file and following its structure.

Diagnostic hint: at least one recognized anchor like ## Post-Merge Validation is missing.

Visible anchors missing (full list)
  • ## Post-Merge Validation

This is the CI tool-boundary lint companion to PR #11494's MCP manage_pr_review validator and PR #11502's agent-pr-review-body-lint.yml reviewer-side lint. Resolves #11501.


neo-opus-ada
neo-opus-ada APPROVED reviewed on May 31, 2026, 10:50 PM

PR Review Summary

Status: Approved

🪜 Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back:

  • Decision: Approve
  • Rationale: Lands the Track-B chunked surface with the byte-identical contract to the just-merged #12268, while keeping the legacy tickets.json feed byte-unchanged so /news/tickets is not destabilized before #12218/#12219 switch the consumer. Correctly folds #12220 and adds the crawler manifest #12217 required. No blocking defects. Approve.

Peer-Review Opening: This is the linchpin for cross-type consistency and you nailed it — I verified the on-disk leaf shape is byte-identical to #12268's generators ({id,parentId,title}, no path), and the chunk/root nodes match too. The dual-output split (buildLegacyFlatTree / buildChunkedIndex) is a clean refactor, and the legacy feed regenerates with zero diff. Ran the suite + regenerated locally; notes below are non-blocking.


🕸️ Context & Graph Linking

  • Target Issues: Resolves #12217, Resolves #12220 (both leaf subs — verified labels, neither epic)
  • Related Graph Nodes: Epic #12204, sibling #12268 (merged — shared shape), consumers #12218 / #12219, #11372 Phase 2

🔬 Depth Floor

Challenge (non-blocking, but the one worth tracking): The "identical leaf shape across all three types" contract is now realized across three sibling generators (tickets/pulls/discussions), but it's enforced only by convention + my manual cross-check — there's no shared schema or shape-assertion test. With #12268 merged and this PR matching, the contract is consistent today; the drift risk is future edits to one generator not mirrored in the others. Since the ticket deliberately chose sibling-duplication over a generalized generator (correct), the right enforcement is a shared shape-assertion test (not shared code) that runs all three generators against a fixture and asserts identical root/chunk/leaf keys. Worth a small follow-up ticket (flagging, not filing — operator's 3:1 rule).

Secondary: the spec is a single comprehensive test covering legacy + chunked + leaf + manifest + exclusion + ordering. Coverage is genuinely thorough; splitting it would only improve failure-localization, not coverage. Minor.

Rhetorical-Drift Audit: Pass. PR body ("Preserves tickets.json instead of replacing", "chunk leaf records intentionally contain only id, parentId, title") matches the diff and the on-disk output exactly; the git diff tickets.json → no output claim reproduces (I regenerated and confirmed zero diff).


🧠 Graph Ingestion Notes

  • [RETROSPECTIVE]: The additive dual-output (legacy full-tree retained + chunked surface emitted) is the right migration shape — it lets #12218/#12219 switch the runtime store and route without a flag-day on /news/tickets. The crawler manifest.json ({indexUrl, chunks:[...]}) closes the SEO-discoverability gap the chunk split would otherwise open.
  • [KB_GAP]: none.
  • [TOOLING_GAP]: none.

N/A Audits — 📡 🛂

N/A across listed dimensions: no openapi.yaml; no major novel abstraction (sibling-of-pulls/discussions pattern; refactor of an existing generator).


🎯 Close-Target Audit

  • Resolves #12217 + Resolves #12220 — both leaf subs (#12217: enhancement/ai/performance/build; #12220: documentation/enhancement/ai), each syntax-exact on its own line. #12220 fold is correct per its "best folded into sub 12" disposition.
  • Whole-body prose scan (grep -iE "(close|fix|resolve)[sd]? +#?N"): only the two intended Resolves lines. "This PR does not close the parent epic #12204" does not match (words between close and #12204).
  • closingIssuesReferences = [12217, 12220] — confirmed it will not auto-close epic #12204.
  • Findings: Pass.

📑 Contract Completeness Audit

  • Epic #12204 ledger: leaf {id, parentId, title} with path reconstructed from parentId+id+chunk. Implemented: chunked leaf = {id,parentId,title} (on-disk verified, no path); chunk node carries contentDir/filePrefix/childrenUrl for reconstruction. No drift; byte-identical to merged #12268.
  • Findings: Pass.

🔗 Cross-Skill Integration Audit

  • Consumers #12218 (lazy TreeList) + #12219 (lazy-aware controller) will switch to tickets/index.json + chunk loads; PR's Post-Merge Validation names that gate. SEO ingestion gets the new manifest.json.
  • data-sync-pipeline.yml: the staging-broadening (apps/portal/resources/data/*.json → the dir) is identical to what #12268 already merged → 3-way-merges as a no-op (confirmed mergeStateStatus: CLEAN). tickets.mjs is already invoked in prepare.mjs + the pipeline, so no new generator wiring needed.
  • Findings: Integration dependencies surfaced + handled; no conflict with the merged #12268.

🧪 Test-Execution & Location Audit

  • Checked out PR head (detached); ran npm run test-unit -- test/playwright/unit/ai/buildScripts/docs/index/TicketIndex.spec.mjs1 passed (522ms).
  • Regenerated the real index (node buildScripts/docs/index/tickets.mjs → 8772 leaves / 1002 groups / 1032 chunks) and confirmed git diff apps/portal/resources/data/tickets.json is empty (legacy feed byte-unchanged).
  • Spot-checked a real on-disk leaf: {"id":"1203","parentId":"v1.4.10/archive-v1-4-10-chunk-1","title":"…"} — matches #12268's shape, no path.
  • Test asserts the chunked leaf not.toHaveProperty('path') (path de-dup) + manifest shape; location canonical (unit/ai/buildScripts/docs/index/).
  • CI: green (unit, integration-unified, CodeQL, Analyze, lint-pr-body).
  • Findings: Tests pass.

📋 Required Actions

No required actions — eligible for human merge.

(Follow-up watch, not a blocker: a shared cross-generator shape-assertion test to lock the tickets/pulls/discussions leaf+chunk contract against future drift.)


📊 Evaluation Metrics

  • [ARCH_ALIGNMENT]: 98 — byte-identical shape to merged #12268 (cross-PR consistency verified on-disk); legacy feed preserved byte-unchanged; additive migration; SEO manifest. 2 deducted: cross-generator shape consistency is convention-enforced, not schema-enforced (future-drift risk).
  • [CONTENT_COMPLETENESS]: 98 — module JSDoc rewritten to document the dual-output contract (folds #12220), per-function JSDoc, comprehensive Fat-Ticket body. 2 deducted: minor.
  • [EXECUTION_QUALITY]: 97 — 1/1 test pass (ran locally); regenerated + verified legacy byte-unchanged + on-disk leaf shape; clean buildLegacyFlatTree/buildChunkedIndex extraction. 3 deducted: single comprehensive test (failure-localization) + unenforced cross-generator drift.
  • [PRODUCTIVITY]: 100 — both #12217 ACs verified (root group-index + per-chunk path-free leaves + crawler manifest; leaf shape identical to pulls/discussions) AND #12220's AC (JSDoc reflects the chunked reality).
  • [IMPACT]: 65 — enables the Track-B lazy-load perf win (realized in #12218/#12219); foundational portal-news perf data substrate.
  • [COMPLEXITY]: 55 — moderate-high: dual-output generator refactor (legacy + chunked + manifest) + 1032-chunk corpus + the cross-type shape contract.
  • [EFFORT_PROFILE]: Heavy Lift — contract-critical refactor with a large generated corpus and a cross-PR consistency requirement.

Strong work — the three-type contract is consistent. Approving — over to @tobiu for merge.

— Reviewed by Opus 4.8 (Claude Code), session af888fcb-6a68-45bd-8a34-13be477733b1.