fix(codex): walk billed+nest trees on the no-arg singleton

filterOverlap required an explicit nest path, so export const codex =
createCodexProvider() never deduped. When the resolved dir is a launcher
nest and ~/.codex is a distinct existing tree, discover both and drop nest
sources whose session id is already billed. Unique nest and billed sessions
stay. Soften the Buzz doctor note to a presence heuristic.
This commit is contained in:
Aditya Vikram Singh 2026-08-24 18:15:34 +05:30
parent 7042048608
commit 2c87ee3eaa
3 changed files with 77 additions and 27 deletions

View file

@ -23,8 +23,9 @@ export function sameCodexHome(a: string, b: string): boolean {
}
/** True when `dir` is a Codex home nested under a launcher nest, and a distinct
* primary Codex home exists. Gate for overlap-only filtering on a *second*
* provider instance it does not by itself drop sessions. */
* primary Codex home exists. Gate for overlap-only filtering it does not by
* itself drop sessions. The production no-arg factory uses this to walk both
* trees; an explicit nest factory uses it to drop overlapping nest ids. */
export function isNestedLauncherCodexHome(
dir: string,
opts: { primaryDir: string; launcherRoots: string[] },
@ -125,7 +126,7 @@ export function collectLauncherNotes(home = homedir()): LauncherNote[] {
name: 'buzz',
path: buzz,
billedVia: 'codex',
verdict: 'LAUNCHER (billed via Codex)',
verdict: 'LAUNCHER (heuristic; billed via Codex)',
})
}
const grokStore = join(home, '.grok')

View file

@ -1330,24 +1330,40 @@ export async function parseCodexFileFull(source: SessionSource, seenKeys: Set<st
return { calls, ...(capture.write ? { write: capture.write } : {}) }
}
function rootsFor(home: string): ProbeRoot[] {
return [
{ path: join(home, 'sessions'), label: 'sessions' },
{ path: join(home, 'archived_sessions'), label: 'archived' },
]
}
function dropOverlappingNestSources(sources: SessionSource[], billedHome: string): SessionSource[] {
const billedIds = listRolloutSessionIds(billedHome)
return sources.filter(source => {
const id = rolloutFileSessionId(source.path)
return !(id && billedIds.has(id))
})
}
export function createCodexProvider(
codexDir?: string,
opts?: { primaryDir?: string; launcherRoots?: string[] },
): Provider {
const dir = getCodexDir(codexDir)
// Production singleton (no args) still honors CODEX_HOME / override as the
// one root. An explicit nest path — even without second-factory opts —
// overlap-filters against the default billed home using defaultLauncherRoots
// so a future createCodexProvider(nest) cannot double-count session ids.
const primaryDir = opts?.primaryDir ?? defaultBilledCodexHome()
const launcherRoots = opts?.launcherRoots ?? defaultLauncherRoots()
// Explicit nest factory whose path is a realpath alias of the billed home:
// empty so a second factory cannot double-count the same tree. The no-arg
// singleton must not take this branch — CODEX_HOME may be that alias.
const duplicateHome =
codexDir !== undefined &&
sameCodexHome(dir, primaryDir) &&
resolve(dir) !== resolve(primaryDir)
const filterOverlap =
codexDir !== undefined &&
isNestedLauncherCodexHome(dir, { primaryDir, launcherRoots })
const nestHome = isNestedLauncherCodexHome(dir, { primaryDir, launcherRoots })
// Production `codex` singleton is createCodexProvider() with no args. When
// the resolved dir is a launcher nest and ~/.codex is a distinct existing
// tree, walk BOTH and drop nest sources whose session id is already billed.
const scanBoth = nestHome && codexDir === undefined
return {
name: 'codex',
@ -1364,13 +1380,12 @@ export function createCodexProvider(
return toolNameMap[rawTool] ?? rawTool
},
// Same `dir` discoverSessionsInDir walks: <codexDir>/sessions (dated
// rollout files) and <codexDir>/archived_sessions. Honors CODEX_HOME.
// Trees discoverSessions actually walks. Honors CODEX_HOME; when the
// production singleton scans nest + billed home, both appear here.
async probeRoots(): Promise<ProbeRoot[]> {
return [
{ path: join(dir, 'sessions'), label: 'sessions' },
{ path: join(dir, 'archived_sessions'), label: 'archived' },
]
if (duplicateHome) return []
if (scanBoth) return [...rootsFor(primaryDir), ...rootsFor(dir)]
return rootsFor(dir)
},
async discoverSessions(): Promise<SessionSource[]> {
@ -1378,12 +1393,12 @@ export function createCodexProvider(
// distinct nest. isNestedLauncherCodexHome is false in that case.
if (duplicateHome) return []
const sources = await discoverSessionsInDir(dir)
if (!filterOverlap) return sources
const primaryIds = listRolloutSessionIds(primaryDir)
return sources.filter(source => {
const id = rolloutFileSessionId(source.path)
return !(id && primaryIds.has(id))
})
if (scanBoth) {
const billed = await discoverSessionsInDir(primaryDir)
return [...billed, ...dropOverlappingNestSources(sources, primaryDir)]
}
if (!nestHome) return sources
return dropOverlappingNestSources(sources, primaryDir)
},
createSessionParser(source: SessionSource, seenKeys: Set<string>): SessionParser {

View file

@ -1,4 +1,4 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { mkdtemp, mkdir, writeFile, rm, symlink } from 'fs/promises'
import { join } from 'path'
import { tmpdir } from 'os'
@ -114,6 +114,8 @@ describe('Codex discover overlap-only nest filter', () => {
expect(names(await provider.discoverSessions())).toEqual(['rollout-buzz.jsonl'])
const roots = await provider.probeRoots!()
expect(roots.map(r => r.path)).toEqual([
join(primary, 'sessions'),
join(primary, 'archived_sessions'),
join(nested, 'sessions'),
join(nested, 'archived_sessions'),
])
@ -125,19 +127,51 @@ describe('Codex discover overlap-only nest filter', () => {
}
})
it('default factory with CODEX_HOME=nest does not drop unique nest rollouts when ~/.codex has its own', async () => {
it('no-arg factory discovers billed + unique nest and drops overlapping nest ids', async () => {
const home = root
const primary = join(home, '.codex')
const nested = join(home, '.buzz', '.codex')
await writeCodexSession(primary, 'rollout-primary.jsonl')
await writeCodexSession(nested, 'rollout-buzz.jsonl')
await writeCodexSession(primary, 'rollout-billed.jsonl', 'sess-shared')
await writeCodexSession(primary, 'rollout-billed-only.jsonl', 'sess-billed')
await writeCodexSession(nested, 'rollout-renamed.jsonl', 'sess-shared')
await writeCodexSession(nested, 'rollout-buzz-only.jsonl', 'sess-buzz')
const prevHome = process.env.HOME
const prevCodex = process.env.CODEX_HOME
process.env.HOME = home
process.env.CODEX_HOME = nested
try {
const provider = createCodexProvider()
expect(names(await provider.discoverSessions())).toEqual(['rollout-buzz.jsonl'])
expect(names(await provider.discoverSessions()).sort()).toEqual([
'rollout-billed-only.jsonl',
'rollout-billed.jsonl',
'rollout-buzz-only.jsonl',
])
} finally {
if (prevHome === undefined) delete process.env.HOME
else process.env.HOME = prevHome
if (prevCodex === undefined) delete process.env.CODEX_HOME
else process.env.CODEX_HOME = prevCodex
}
})
it('exported codex singleton dedups nest ids against billed home', async () => {
const home = root
const primary = join(home, '.codex')
const nested = join(home, '.buzz', '.codex')
await writeCodexSession(primary, 'rollout-billed.jsonl', 'sess-shared')
await writeCodexSession(nested, 'rollout-renamed.jsonl', 'sess-shared')
await writeCodexSession(nested, 'rollout-buzz-only.jsonl', 'sess-buzz')
const prevHome = process.env.HOME
const prevCodex = process.env.CODEX_HOME
process.env.HOME = home
process.env.CODEX_HOME = nested
vi.resetModules()
try {
const { codex } = await import('../src/providers/codex.js')
expect(names(await codex.discoverSessions()).sort()).toEqual([
'rollout-billed.jsonl',
'rollout-buzz-only.jsonl',
])
} finally {
if (prevHome === undefined) delete process.env.HOME
else process.env.HOME = prevHome