LearnNewsExamplesServices
Frontmatter
id15130
titleRepair PR archive index drift and divergent duplicate artifacts
stateOpen
labels
bugaitestingarchitecturebuild
assignees[]
createdAt4:13 PM
updatedAt4:13 PM
githubUrlhttps://github.com/neomjs/neo/issues/15130
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
milestonev13.2

Repair PR archive index drift and divergent duplicate artifacts

Open Backlog/active-chunk-5 bugaitestingarchitecturebuild
neo-gpt
neo-gpt commented on 4:13 PM

Context

A source-integrity audit while implementing #15088 falsified the local pull-request corpus contract. The Bird View can bypass the content index and report local divergence, but that containment is not a repair of the generated GitHub-content substrate.

A direct 2026-07-13 census found:

  • resources/content/_index.json: 4,566 pulls entries.
  • Active + archived corpus: 4,593 files representing 4,566 unique PR IDs.
  • 2,015 index entries reference missing paths; every affected ID still exists under archive/pulls/.
  • 27 PR IDs exist twice in the archive; every pair is byte-divergent.

The Problem

PR #13200 added PullRequestSyncer.reconcileClosedPullRequestLocations() and correctly moved the historical terminal-PR backlog out of the active tree. Its persistence boundary is incomplete:

  1. reconcileClosedPullRequestLocations() calls fs.rename() but does not upsert the moved PR's _index.json entry.
  2. Its bucket plan sees the active scan plus delta-only metadata.pulls, not complete existing archive-bucket membership, so it can calculate an ordinal target from a partial collection.
  3. syncPullRequests() later rebuilds metadata.pulls from the fetched delta and upserts only those PRs. A historical archived PR refreshed without a cached prior path can be written at a partial-plan location while its earlier archived copy remains.
  4. No post-pass proves that every indexed path exists or that each PR ID has exactly one corpus artifact.

The prior assumption that “the next sync rebuilds _index.json” is empirically false for archive moves: the delta sync does not enumerate the complete archive, while reconcileActiveChunks() explicitly handles only the active tier.

Historical falsifier: sync commit 657f7f8d13600a01530fb66434e1f5c3401a220f moved 1,325 PR files from active to archive. Representative index entries for descriptive PRs #9537 and #10124 remained byte-identical before/after and continued to name active paths.

The Architectural Reality

ADR 0004 governs this generated-content substrate:

  • §1.3: GitHub is source of truth; resources/content/ is regeneratable.
  • §2.4–§2.5: archive membership is sealed and ordinal paths use complete bucket ordering.
  • §3.2: _index.json is the ID-to-path lookup contract.
  • §3.3: syncers update the index alongside content writes.

Owning surfaces:

  • ai/services/github-workflow/sync/PullRequestSyncer.mjs
    • #planBuckets()
    • reconcileClosedPullRequestLocations()
    • syncPullRequests()
  • ai/services/github-workflow/shared/contentIndex.mjs
  • ai/services/github-workflow/shared/reconcileActiveChunks.mjs
  • ai/services/knowledge-base/source/PullRequestSource.mjs
  • test/playwright/unit/ai/services/github-workflow/PullRequestSyncer.spec.mjs

The Agent OS structure map was run at creation time and confirms GitHub Workflow sync/shared services own the repair. #15088 remains non-blocked because its runtime reader deliberately bypasses the stale index and treats live GitHub as canonical; that safeguard must not become permission to leave the local projection corrupt.

The Fix

One coherent repair lane:

  1. Plan PR placement against a complete PR corpus inventory, including existing archive-bucket membership. Never derive a sealed archive ordinal from the current delta alone.
  2. Preserve an existing unique archived location when refreshing that PR. For a new active→archive move, compute the target from complete bucket membership.
  3. Treat file move/write and _index.json upsert as one sync-pass mutation set. Before the generated-content commit, validate that every resulting indexed path exists.
  4. Detect duplicate PR IDs across active + archive:
    • byte-identical copies may be deterministically collapsed;
    • byte-divergent copies fail loud and are restored from canonical GitHub state or an ADR-0004 clean regeneration—never arbitrary “keep first” deletion.
  5. Repair the current generated corpus through the corrected normal sync/clean-regeneration path. Do not hand-edit 2,015 paths or add a one-off migration script.
  6. Emit a structured integrity result containing stale paths, duplicate IDs, divergent IDs, and repaired counts so the sync cannot silently commit partial integrity.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Evidence
PullRequestSyncer.reconcileClosedPullRequestLocations() ADR 0004 §3.3 Every successful active→archive move contributes its index upsert in the same sync pass Move/index mismatch fails the generated-content pass before commit No-metadata and multi-move fixtures
PullRequestSyncer.syncPullRequests() ADR 0004 §2.4–§2.5 Historical refreshes update their unique existing archive location; new placement uses complete membership Missing/ambiguous prior location routes to integrity repair, not a second write Existing-archive refresh fixture
resources/content/_index.json ADR 0004 §3.2 Exactly one PR entry per ID and every indexed path exists Stale entry is an explicit integrity failure Full-corpus validation fixture
PR artifact uniqueness GitHub source of truth + ADR 0004 §1.3 Exactly one local artifact per PR ID Divergent duplicates fail loud and restore from GitHub/clean regeneration Equal-copy and divergent-copy fixtures
PullRequestSource input Knowledge Base source config Active+archive extraction observes unique PR identities only Integrity failure blocks duplicate projection from masquerading as distinct evidence Duplicate logical-name falsifier

Decision Record impact

aligned-with ADR 0004. This repairs enforcement of the accepted content-index and ordinal archive contracts; it does not amend them.

Acceptance Criteria

  • A terminal PR moved from active to archive with no metadata.pulls entry receives a correct _index.json upsert in the same sync pass.
  • Multiple PRs moved in one pass are planned against complete bucket membership; ordinal targets and index entries remain collision-free.
  • Refreshing an already archived historical PR updates its unique existing artifact instead of creating a second copy.
  • A corpus validation pass proves every pulls index path exists and that index version, chunkNumber, and path match the artifact.
  • The existing 2,015 stale PR index paths reconcile through the corrected sync/clean-regeneration path; post-repair stale count is zero.
  • Duplicate PR IDs are detected across active and archive roots. Byte-divergent duplicates fail loud and never resolve by arbitrary first-copy deletion.
  • The existing 27 duplicate PR IDs restore from canonical GitHub state or a clean regeneration; post-repair duplicate/divergent counts are zero.
  • Focused tests cover no-metadata move, multiple moves, existing archived refresh, stale indexed path, byte-identical duplicate, byte-divergent duplicate, and post-pass path validation.
  • A PullRequestSource falsifier proves the repaired corpus emits no duplicate logical pr-N#... names.
  • The PR body reports before/after corpus counts and the exact integrity result.

Out of Scope

  • The resolved-PR Bird View runtime feature in #15088.
  • Generalizing the repair to issues or discussions without equivalent empirical evidence.
  • Re-chunking otherwise valid sealed archives.
  • A one-off migration script or manual generated-file cleanup.

Avoided Traps

  • Do not treat delta-only metadata.pulls as the full PR corpus.
  • Do not compute sealed archive ordinals from only the current delta or active backlog.
  • Do not copy reconcileActiveChunks()'s “keep first, unlink rest” behavior onto divergent archive artifacts.
  • Do not merely warn and continue committing a stale index.
  • Do not treat #15088's index bypass as resolution of this defect.

Related

Follow-up: #13001 / PR #13200
Discovered by, non-blocking: #15088
Prior index substrate: #11390 / PR #11403
Adjacent active-tier repair: #13328 / PR #13332
Authority: learn/agentos/decisions/0004-github-content-architecture.md

Live duplicate sweep: latest 20 open issues, all open PRs, recent all-state A2A claims, targeted local history, and semantic Knowledge Base tickets checked at 2026-07-13T14:12Z; no equivalent open ticket, PR, or in-flight claim found.

Origin Session ID: 837ad74b-c2d2-413d-9aab-b7165a93a82a

Retrieval Hint: "PullRequestSyncer archive rename stale _index 2015 paths 27 divergent duplicate PR artifacts #13001 #13200"