---
id: 11516
title: Repair DevIndex users/tracker reconciliation and rename recovery
state: CLOSED
labels:
  - bug
  - ai
  - architecture
assignees:
  - neo-gpt
createdAt: '2026-05-17T01:34:36Z'
updatedAt: '2026-05-17T19:40:35Z'
githubUrl: 'https://github.com/neomjs/neo/issues/11516'
author: neo-gpt
commentsCount: 1
parentIssue: 10117
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-17T19:40:35Z'
---
# Repair DevIndex users/tracker reconciliation and rename recovery

## Context

Follow-up from #10117 intake/investigation on 2026-05-17. The original #10117 snapshot reported `354` `users.jsonl` records absent from `tracker.json` on 2026-04-19. Fresh V-B-A on current `dev` data shows the invariant is still broken and has worsened.

Current counts from `apps/devindex/resources/data/`:

| Metric | Current value |
|---|---:|
| `users.jsonl` rich-user records | 50,000 |
| `tracker.json` entries | 49,387 |
| Rich-user records absent from tracker | 614 |
| Tracker entries absent from rich users | 1 |

Recent bot data-sync history confirms this is not self-healing:

```text
bc8c7f25d 2026-05-15T23:26Z users=50000 tracker=49394 orphans=606 trackerOnly=0
8383fae69 2026-05-16T04:08Z users=50000 tracker=49393 orphans=607 trackerOnly=0
907240408 2026-05-16T22:18Z users=50000 tracker=49387 orphans=614 trackerOnly=1
```

## The Problem

`tracker.json` is the Updater scheduling index. A rich `users.jsonl` record absent from `tracker.json` is invisible to refresh scheduling, so the profile can remain stale forever. The drift is currently growing slowly across hourly data-sync commits.

The investigation also found a concrete rename / username-reuse hazard:

- Rich record sample: `0xBigBoss` has stored `i: 95193764`, `tc: 17244`, `lu: 2026-05-08T15:18:19.485Z`.
- Live REST `GET /users/0xBigBoss` now returns a different account, `id: 283503690`, created 2026-05-11.
- Live REST `GET /user/95193764` resolves the stored user ID to `alleneubank`.

Therefore a naive cleanup that simply re-adds all rich-user logins into `tracker.json` would requeue stale logins and may overwrite an old identity with a new account that reused the login. The repair must be identity-aware.

A second V-B-A catch: `apps/devindex/services/GitHub.mjs#getLoginByDatabaseId()` currently uses GraphQL `user(databaseId: ...)`, but GitHub GraphQL rejects that argument:

```text
Field 'user' is missing required arguments: login
Field 'user' doesn't accept argument 'databaseId'
```

That means the archived rename-recovery intent from #9137 is not actually reliable today.

## The Architectural Reality

Relevant current surfaces:

- `apps/devindex/services/Storage.mjs#updateUsers()` writes rich records and prunes over-cap users, but does not add tracker entries for inserted rich records. It expects callers to own tracker updates.
- `apps/devindex/services/Updater.mjs#saveCheckpoint()` currently persists in this order: `Storage.updateUsers(results)`, `Storage.updateTracker(indexUpdates)`, `Storage.deleteUsers(prunedLogins)`, then failed-list updates. Interruptions between these operations can leave one store updated without the other.
- `apps/devindex/services/Updater.mjs` rename recovery depends on `GitHub.getLoginByDatabaseId()` to map old stored user IDs to current logins.
- `apps/devindex/services/GitHub.mjs#getLoginByDatabaseId()` uses a GraphQL signature that is invalid against current GitHub GraphQL.
- `apps/devindex/services/Cleanup.mjs` only resurrects allowlisted users into tracker. It does not reconcile non-allowlisted rich-user records missing from tracker, and it must not do so blindly because of login reuse.
- `.github/workflows/data-sync-pipeline.yml` runs `devindex:spider` and `devindex:update`; the data-sync bot commits both `users.jsonl` and `tracker.json` hourly.

## The Fix

Implement a bounded, identity-aware repair and prevention path:

1. Replace or fix `GitHub.getLoginByDatabaseId()` with a current GitHub API-compatible resolver for integer database IDs. If no direct GraphQL lookup exists, use a safe REST fallback (`GET /user/{id}`) or equivalent verified endpoint.
2. Update rename recovery to use the fixed resolver and to avoid leaving stale old-login rich records behind when a user has renamed or when a login has been reused by a different account.
3. Add a reconciliation step or script that audits rich-user records missing from tracker and repairs them safely:
   - If the stored user ID resolves to the same login, restore the tracker entry with the rich record's `lu` timestamp or a deliberate recheck timestamp.
   - If the stored user ID resolves to a different current login, treat it as a rename: migrate/queue the current login and remove or supersede the stale old-login rich record.
   - If the stored user ID no longer resolves, route to failed/penalty handling or prune according to existing data-minimization rules.
   - Never requeue a stale login when live login ownership differs from the stored user ID.
4. Add focused unit coverage for the resolver and reconciliation behavior. Add an offline invariant assertion for `users.jsonl` vs `tracker.json` after repair, or document why it cannot be enforced in CI until the committed data is repaired.
5. After code is fixed, perform the committed data repair in a dedicated data-sync-safe commit or document the operator/data-pipeline step required to perform it.

## Contract Ledger Matrix

| Target Surface | Source of Authority | Proposed Behavior | Fallback | Docs | Evidence |
|---|---|---|---|---|---|
| `GitHub.getLoginByDatabaseId()` | Current GitHub API behavior + archived rename-recovery intent #9137 | Resolves stored integer user IDs to the current login using a verified supported API path | Returns `null` only when the user cannot be resolved; transient API errors still throw | `learn/guides/devindex/data-factory/GitHubAPI.md` | Unit test or isolated API mock proving resolver handles success, missing user, and transient error |
| `Updater` rename recovery | #9137 + current #10117 drift evidence | Old login is removed only after current-login replacement is safely persisted or queued; username reuse does not overwrite the wrong identity | Failed/penalty path preserves retriable state without deleting valid rich history | `learn/guides/devindex/data-factory/Updater.md` | Unit test for rename and username-reuse scenario (`0xBigBoss` class) |
| Rich-store / tracker invariant | #10117 + current data counts | Rich users that should be refreshable have a tracker entry; tracker-only entries are resolved or pruned according to existing rules | Ambiguous identity records are quarantined/failed instead of blindly requeued | `learn/guides/devindex/data-factory/DataHygiene.md` | Offline count assertion shows no unsafe rich-user orphan drift after repair |
| Data-sync pipeline | `.github/workflows/data-sync-pipeline.yml` | Hourly bot runs do not reintroduce the invariant drift | If GitHub API resolver unavailable, data-sync fails loudly or records bounded failed state | Workflow logs + data files | Post-merge data-sync observation confirms orphan count does not grow |

## Acceptance Criteria

- [ ] `GitHub.getLoginByDatabaseId()` no longer uses invalid `user(databaseId: ...)` GraphQL and is covered by tests.
- [ ] Rename recovery distinguishes same-login, renamed-login, missing-user, and username-reuse cases.
- [ ] A reconciliation/repair path handles the existing rich-user orphan set without blindly requeuing stale logins.
- [ ] The `0xBigBoss` / `alleneubank` class is represented in test fixtures or documented as a verified manual sample.
- [ ] A post-fix audit reports current `users.jsonl`, `tracker.json`, rich-user orphan count, tracker-only count, and sample classifications.
- [ ] #10117 receives a closing investigation comment linking this ticket and recording whether #10117 should be closed after the repair path lands.

## Out of Scope

- Changing the `maxUsers` cap or contribution threshold.
- Rewriting DevIndex storage formats.
- Blind one-time JSON surgery that repairs counts without fixing the pipeline path.
- Broad spider/updater performance tuning unrelated to the invariant.

## Avoided Traps

- **Blind resurrection of every rich-user login into tracker.** Rejected because username reuse can map the same login to a different GitHub account than the stored user ID.
- **Treating #10117 as stale because it was filed in April.** Rejected: current data is worse, and recent bot commits show active drift.
- **Only repairing committed data.** Rejected because hourly data-sync would continue recreating drift if the resolver/checkpoint/reconciliation path remains broken.

## Related

- Parent investigation: #10117
- Historical rename-recovery intent: #9137
- Safe purge / fallen-hero context: #9135
- Current data-sync commits sampled: `bc8c7f25d`, `8383fae69`, `907240408`

Origin Session ID: c934160e-e886-455a-b41e-4bb2dd1f2732

Handoff Retrieval Hints:
- `DevIndex users.jsonl tracker.json orphan invariant 614`
- `0xBigBoss alleneubank username reuse database id 95193764`
- `GitHub getLoginByDatabaseId user(databaseId) invalid GraphQL`
- `Updater saveCheckpoint Storage.updateUsers updateTracker deleteUsers order`

## Timeline

- 2026-05-17T01:34:37Z @neo-gpt added the `bug` label
- 2026-05-17T01:34:38Z @neo-gpt added the `ai` label
- 2026-05-17T01:34:38Z @neo-gpt added the `architecture` label
- 2026-05-17T01:34:59Z @neo-gpt added parent issue #10117
- 2026-05-17T01:35:01Z @neo-gpt cross-referenced by #10117
- 2026-05-17T01:35:45Z @neo-gpt assigned to @neo-gpt
- 2026-05-17T01:42:12Z @neo-gpt cross-referenced by PR #11517
- 2026-05-17T01:43:09Z @neo-opus-ada cross-referenced by PR #11518
- 2026-05-17T02:19:25Z @neo-gpt cross-referenced by PR #11522
- 2026-05-17T06:15:44Z @tobiu referenced in commit `5468a10` - "feat(ai): document withHeavyMaintenanceLease release-timing + reference-impl + spec (#11515) (#11518)

* feat(ai): document withHeavyMaintenanceLease release-timing semantics + reference-impl + spec test (#11515)

Friction-to-gold follow-up from PR #11509 cycles 1 + 2 (per @neo-gpt routing
recommendation MESSAGE:3c42e809: file a tiny follow-up rather than bundling
into Lane A's backup PR).

## What changed

1. `ai/daemons/services/HeavyMaintenanceLeaseService.mjs` — augmented JSDoc on
   `withHeavyMaintenanceLease`:
   - Explicit release-timing callout naming the finally-based release + the
     5-step await ordering trace
   - 'Right shape' code block (substrate mutation inside an inner finally
     of the wrapped task)
   - 'Wrong shape' code block (substrate mutation after the await — outside
     the lease)
   - Returned-shape table for completed / held / stale statuses
   - Cross-references to runSandman.mjs (canonical consumer), PR #11509
     (empirical anchor), and this ticket #11515
   - No code-semantics changes

2. `test/playwright/unit/ai/daemons/services/HeavyMaintenanceLeaseService.spec.mjs` —
   new test 'withLease release-timing invariant: task inner finally runs INSIDE
   the lease window (#11515)'. Captures lease-file presence at three probe
   points and asserts:
   - probe 1 (inside task body): lease ACTIVE
   - probe 2 (inside task's inner finally): lease STILL ACTIVE -- load-bearing
     assertion for the canonical inner-finally pattern
   - probe 3 (after await settles): lease MISSING
   Strict execution order asserted via call-order array.
   A future refactor that releases the lease before the task's inner finally
   fails probe 2.

## Test evidence

- npm run test-unit -- test/playwright/unit/ai/daemons/services/HeavyMaintenanceLeaseService.spec.mjs
  -> 9/9 passed in 627ms (8 baseline + new release-timing test)
- git diff --check origin/dev...HEAD -> clean (no trailing whitespace)
- Branched from origin/dev at a5c638069 (post-Lane-C merge); fresh branch

## Acceptance criteria coverage

- AC1: JSDoc names finally-based release + post-await-outside-lease consequence
- AC2: JSDoc includes runnable code-block example contrasting right vs wrong shapes
- AC3: Spec test pins task's inner finally runs BEFORE wrapper's release
- AC4: 8/8 existing tests still pass; total 9/9
- AC5: ask_knowledge_base verification deferred until KB re-indexes post-merge

## Coordination

- Lane-claim broadcast: MESSAGE:a1adf7d9 (non-colliding with Claude PR #11514
  Lane A, GPT #10117/#11516 DevIndex lanes, Gemini #11475 memory-core lane)
- Authority anchor: PR #11509 cycles 1+2 review IDs PRR_kwDODSospM8AAAABAJIdTg
  and PRR_kwDODSospM8AAAABAJKF8w

(#11515)

* fix(ai): address PR #11518 cycle 1 — wrapper-return shape table + remove fragile line-self-reference (#11515)

Per @neo-gpt cycle 1 review (PRR_kwDODSospM8AAAABAJO13Q), two contract-text
corrections in the withHeavyMaintenanceLease JSDoc:

1. Returned-shape table previously listed `'stale'` as a wrapper return status,
   but the live code normalizes ALL acquired outcomes (acquired, acquired-after-stale,
   acquired-after-malformed) into `{status: 'completed', acquired: true, ...}` from
   the wrapper's perspective. Restructured into TWO tables:
   - Wrapper return shape: 2 rows (`completed`, `held`) + note on theoretical
     pass-through for non-acquired non-held edge cases
   - Acquisition descriptor passed to `task`: 3 rows enumerating internal
     recovery telemetry (acquired / acquired-after-stale / acquired-after-malformed)
     with previousStatus field semantics
   Closes the contract drift that would have poisoned KB ingestion if the table
   shipped as-was.

2. Removed `line ~272` self-reference from the JSDoc release-timing prose.
   Line numbers shift with nearby edits; replaced with stable "this helper's own
   `finally` block (the one that wraps the `task` invocation below and calls
   `releaseHeavyMaintenanceLease`)" relative pointer.

No runtime behavior change. JSDoc + structural-precision only.

Test evidence:
- npm run test-unit -- test/playwright/unit/ai/daemons/services/HeavyMaintenanceLeaseService.spec.mjs
  -> 9/9 passed in 635ms (unchanged from cycle 0)

(#11515)

---------

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-17T06:18:08Z @tobiu referenced in commit `ce7ccf3` - "fix(devindex): repair database-id login resolver (#11516) (#11517)

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-17T06:19:32Z @tobiu referenced in commit `d3f79dc` - "fix(devindex): protect rename recovery before replacement fetch (#11516) (#11522)

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
### @neo-gpt - 2026-05-17T06:34:56Z

**Input from GPT-5 (Codex):**

> ● ## Session-sunset handoff — GPT #11516 lane
> 
> Context: #11517 and #11522 have merged, so the two GPT DevIndex slices from this session are complete:
> - #11517 fixed database-id login resolution.
> - #11522 protected rename recovery before replacement fetch.
> 
> Current issue state: #11516 remains OPEN and assigned to @neo-gpt. The broader repair still needs fresh ticket-intake after syncing `dev`, because the issue ACs include reconciliation/repair of the existing rich-user/tracker orphan set and post-fix audit/closure handling for #10117.
> 
> Verified sunset state:
> - #11517 MERGED at 2026-05-17T06:18:07Z.
> - #11522 MERGED at 2026-05-17T06:19:31Z.
> - Local checkout is on `dev`, synced with `origin/dev`.
> 
> Pickup protocol:
> 1. Start from fresh `dev`.
> 2. Run `$ticket-intake` for #11516 before any new branch or edits.
> 3. Re-run the current-data V-B-A audit; do not trust older orphan counts if data-sync has moved.
> 4. Next likely slice is the reconciliation/repair path and post-fix audit, not resolver/rename-guard work already merged.
> 5. Preserve the identity-aware constraints from the `0xBigBoss` / `alleneubank` sample; do not blindly requeue stale logins.

- 2026-05-17T18:59:56Z @neo-gpt cross-referenced by PR #11545
- 2026-05-17T19:40:35Z @tobiu referenced in commit `75114c8` - "fix(devindex): reconcile rich-user tracker orphans (#11516) (#11545)

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-17T19:40:35Z @tobiu closed this issue

