LearnNewsExamplesServices
Frontmatter
id13328
titleActive content chunks drift off ordinal-100 — portal lists wrong PR/issue numbers
stateClosed
labels
bugairegressionarchitecture
assigneesneo-opus-grace
createdAtJun 15, 2026, 12:02 PM
updatedAtJun 21, 2026, 2:17 AM
githubUrlhttps://github.com/neomjs/neo/issues/13328
authorneo-opus-grace
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 15, 2026, 2:02 PM

Active content chunks drift off ordinal-100 — portal lists wrong PR/issue numbers

Closed v13.1.0/archive-v13-1-0-chunk-3 bugairegressionarchitecture
neo-opus-grace
neo-opus-grace commented on Jun 15, 2026, 12:02 PM

Authored by Claude Opus 4.8 (Claude Code), @neo-opus-grace (Grace). Operator prio-0 (broken neomjs.com/news portal).

Context

neomjs.com/news/pulls (and tickets) list wrong PR/issue numbers. The chunk folders should be exactly 100 items (ADR 0004 Universal Ordinal-100; folder labels are position-ranges 1-100, 101-200…), but on-disk active chunks are 148/135/180/100/75 — non-uniform, with overlapping/gapped ranges (e.g. resources/content/pulls/chunk-1 holds 148 PRs spanning 9537-12693). This is in the neo repo content directly; the pages deploy just mirrors it.

Root Cause (V-B-A: ADR 0004 §2.2, contentPath.mjs, PullRequestSyncer.mjs, IssueSyncer.mjs, pulls.mjs)

Ordinal-100 chunking is position-dependent: a item's chunk = Math.floor(rank/100)+1 where rank is its position among all active items sorted ascending by id. But the syncers only ever rank/re-place the delta:

  1. Stale-rank drift (pulls + issues): syncPullRequests/pullFromGitHub call #planBuckets(metadata, <delta-fetch>) and only relocate the delta items. When a release is cut and a batch is archived, the active set shrinks → every surviving active item's rank shifts → its correct chunk changes — but only delta items move; the rest stay in stale folders. reconcileClosed*Locations relocates terminal items to archive but nothing re-ranks the remaining active tier.
  2. Pulls worse (asymmetry): PullRequestSyncer:492 does metadata.pulls = {} (delta-rebuilt, "not a full index" per its own JSDoc), vs IssueSyncer:601 issues: {...metadata.issues} (cumulative). So even delta pulls' itemIndex is computed over a partial set.

The index generators (pulls.mjs/tickets.mjs) faithfully mirror the on-disk folders (getSourceBucket → dirname); they're not independently buggy. contentPath.mjs's floor(itemIndex/100)+1 math is correct. The fix is in the placement.

The Fix

  • Add a full-corpus active-tier re-chunk pass: scan all active pr-*.md/issue-*.md/discussion-*.md, sort ascending by id, recompute chunk-N via contentPath, fs.rename any mis-located file, prune empty dirs. This is the active-tier sibling of reconcileClosed*Locations (which only archives). Idempotent (no-op when already ordinal-correct).
  • Wire it into the sync/rebuild pipeline (rebuildContentIndexesAndSeo.mjs before the index generators) so it runs every sync → no re-drift.
  • Fix the metadata.pulls = {} vs {...metadata.issues} asymmetry.
  • Regenerate the current content + indexes to un-break the portal now.

Acceptance Criteria

  • Every active chunk folder holds exactly 100 items except the last (per content type).
  • The re-chunk pass is idempotent and runs in the sync/rebuild pipeline.
  • The portal Latest list shows the newest PRs/issues first, with contiguous position labels (1-100, 101-200, …).
  • Applies to pulls, issues, and discussions.

Out of Scope

  • Archive-tier chunks (sealed per ADR 0004 §2.6 — immutable, do not re-chunk).

Related

  • ADR 0004 (learn/agentos/decisions/0004-github-content-architecture.md), contentPath.mjs, the github-workflow syncers, buildScripts/docs/index/*.mjs.

Release classification: prio-0 operator-flagged (public portal correctness); boardless (no portal board).

Origin Session ID: 0f5d9f1d-0683-452d-aac1-f467297186ac