// A retired repo must not be re-created by the tracker-open path.
//
// services/retired-repos.js lists four resurrection paths. Path 4 is "residual
// references: issue-registry-index.json, tracker autobases" — and it was the
// one still open. RefReconcilerManager.ensureFor() opens a tracker via
// getForRepo() and runs `git init --bare` when the directory is missing, with
// no tombstone check. reconcileAllRepoRefs already gated its readdir on
// isRetired; this path did not.
//
// Observed on the live fleet: after retiring 16 repos and removing their bytes,
// a daemon restart re-created exactly the two that had real tracker autobases
// (nvidia-rtx-godot, pear-git-v2) as empty 124K skeletons — still tombstoned,
// still listed as retired, and back on disk regardless. The other 14 had no
// tracker to open and stayed gone.
import test from 'brittle'
import { mkdtempSync, existsSync, rmSync } from '#node:fs'
import { join } from '#node:path'
import { tmpdir } from '#node:os'
import { RefReconcilerManager } from '../lib/services/ref-reconciler-manager.js'
const tmp = (p) => mkdtempSync(join(tmpdir(), p))
// Minimal stand-ins: enough for `enabled` to be true and for ensureFor to
// reach its `git init --bare`.
function makeManager ({ retired = [] } = {}) {
const reposDir = tmp('pgv3-resurrect-')
const opened = []
const mgr = new RefReconcilerManager({
issueRegistry: {
async getForRepo (name) {
opened.push(name)
return { writable: false, base: { on () {}, off () {} }, async listRefs () { return [] } }
}
},
overlay: {},
reposDir,
isRetired: (name) => retired.includes(name)
})
return { mgr, reposDir, opened }
}
test('ensureFor refuses a RETIRED repo and creates nothing', async (t) => {
const { mgr, reposDir, opened } = makeManager({ retired: ['pear-git-v2'] })
const rec = await mgr.ensureFor('pear-git-v2')
t.is(rec, null, 'no reconciler started for a tombstoned repo')
t.absent(existsSync(join(reposDir, 'pear-git-v2.git')), 'no bare repo was re-created')
t.absent(opened.includes('pear-git-v2'), 'its tracker was never even opened')
})
test('a repo that is NOT retired still works', async (t) => {
const { mgr, reposDir, opened } = makeManager({ retired: ['pear-git-v2'] })
const rec = await mgr.ensureFor('live-repo')
t.ok(rec, 'reconciler started for a live repo')
t.ok(existsSync(join(reposDir, 'live-repo.git')), 'its bare repo was materialized as before')
t.ok(opened.includes('live-repo'), 'and its tracker was opened')
})
test('un-retiring restores normal behaviour', async (t) => {
// The tombstone is reversible, so the gate must be too — it reads the live
// set on every call rather than caching a decision.
const retired = ['pear-git-v2']
const reposDir = tmp('pgv3-resurrect-')
const mgr = new RefReconcilerManager({
issueRegistry: {
async getForRepo () { return { writable: false, base: { on () {}, off () {} }, async listRefs () { return [] } } }
},
overlay: {},
reposDir,
isRetired: (name) => retired.includes(name)
})
t.is(await mgr.ensureFor('pear-git-v2'), null, 'refused while retired')
retired.length = 0 // unretire
const rec = await mgr.ensureFor('pear-git-v2')
t.ok(rec, 'accepted once un-retired')
t.ok(existsSync(join(reposDir, 'pear-git-v2.git')), 'and materialized then')
})
test('with no isRetired supplied, nothing is gated (back-compat)', async (t) => {
const reposDir = tmp('pgv3-resurrect-')
const mgr = new RefReconcilerManager({
issueRegistry: {
async getForRepo () { return { writable: false, base: { on () {}, off () {} }, async listRefs () { return [] } } }
},
overlay: {},
reposDir
})
t.ok(await mgr.ensureFor('anything'), 'default predicate retires nothing')
rmSync(reposDir, { recursive: true, force: true })
})