LearnNewsExamplesServices
Frontmatter
id15452
titleIssueSyncer + DiscussionSyncer: adopt COMPLETE-membership ordinal placement (buildContentInventory) — the #15354 audit''s named follow-up
stateClosed
labels
bug
assigneesneo-opus-grace
createdAtJul 18, 2026, 11:22 AM
updatedAtJul 18, 2026, 1:22 PM
githubUrlhttps://github.com/neomjs/neo/issues/15452
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 18, 2026, 1:22 PM

IssueSyncer + DiscussionSyncer: adopt COMPLETE-membership ordinal placement (buildContentInventory) — the #15354 audit's named follow-up

neo-opus-grace
neo-opus-grace commented on Jul 18, 2026, 11:22 AM

Context

ADR-0004 §2.2.1 (amended via #15354 / PR #15451) now states the precondition: an ordinal is only meaningful against COMPLETE membership — a partial collection yields a different ordinal, not an approximate one, producing valid-looking duplicate artifacts in plausible chunks (the #15130 / #15319 damage: 2,015 stale index entries, 27 divergent duplicates).

#15354's AC-5 audit (verified against the code, recorded on #15354) found two of three syncers do NOT satisfy the precondition. This is that named follow-up.

The Problem

  • DiscussionSyncer#planBuckets(metadata, fetchedDiscussions) (ai/services/github-workflow/sync/DiscussionSyncer.mjs:105) builds combined from metadata.discussions + the delta fetchedDiscussions, then buckets + computes itemIndex (the ordinal) against it. No contentInventory.
  • IssueSyncer#planBuckets(metadata, fetchedIssues) (ai/services/github-workflow/sync/IssueSyncer.mjs:385) builds combined from metadata.issues + fetchedIssues; called at 5 sites including [] and [issue] (metadata-only / single-item — definitionally partial). Uses contentIndex.mjs, never contentInventory.mjs.

Both compute placement from partial membership by construction and are structurally free to reproduce the duplicate-artifact defect on the next sync that lands an item whose complete-membership ordinal differs from the partial one.

The Fix

Adopt complete membership in both #planBuckets paths, mirroring #15319's PullRequestSyncer repair: read the complete on-disk corpus via buildContentInventory (ai/services/github-workflow/shared/contentInventory.mjs — active tier + every archive bucket, recursive) as the ordering source, instead of metadata.{type} + the delta fetch.

Acceptance Criteria

  • IssueSyncer ordinal placement reads complete membership (via buildContentInventory or an equivalent full-corpus scan), not metadata.issues + delta.
  • DiscussionSyncer ordinal placement reads complete membership, not metadata.discussions + delta.
  • A red-proof witness per syncer: a partial-input case that lands an item in the wrong chunk before the fix, correct after (mirror #15319's test shape).
  • No behavior change to the active-flat fallback for closed-post-latest-release items (already matches across all three syncers).

Out of Scope

  • Re-opening ordinal-100 / sealed chunks / §1.3 (ADR-0004 unchanged beyond the §2.2.1 precondition).
  • ReleaseNotesSyncer (no archive tier; assess separately if it plans chunks).

Refs #15354, #15319, #15130. Governing design: ADR-0004 §2.2.1 (PR #15451).

Authored by Grace (Claude Opus 4.8, Claude Code) — the named follow-up per #15354 AC-5.