mirror of
https://github.com/stablyai/orca.git
synced 2026-10-04 08:02:09 +00:00
* fix(codex): stop re-scanning all Codex session history on every launch (#16251) A launch deleted the backfill completion marker, and a marker could never be written while a Codex pane was open, so every launch re-derived "needs full scan" and walked the entire .codex/sessions tree — on Windows with a large history that read as a hung window. - v4 marker keeps a durable full-history baseline plus a bounded set of pending dates. v3 is read as a baseline, so upgrades pay no full scan. - A launch now marks dates pending instead of deleting the marker, and a full pass certifies the baseline even while a pane is still running; the live pane's own date just stays pending. - Pending dates are persisted, so an abnormal exit or a cross-midnight pane recovers a bounded window instead of a full walk. - A date-limited pass can only extend an existing baseline, never create one, so it can no longer certify history it never looked at. - Marker and index-heal target roots compare through normalizeRuntimePathForComparison, so Windows spellings of one directory stop invalidating each other. - Both append-only ledgers stream instead of readFileSync + whole-file JSON.parse, keeping the main thread responsive on large histories. * fix(codex): keep the backfill marker's full-scan demand durable Review follow-ups on the v4 backfill marker: - markCodexSessionBackfillMarkerPending no longer erases a persisted needsFullScan; the demand survives until a generation-current full walk retires it, and the function now reports it so the launch path folds it into its own in-memory flag (as @rumoii's #16252 does). - A full pass settles the whole pending set instead of subtracting the empty set, so a date a full walk provably covered stops forcing an extra bounded pass on every startup. - isCodexSessionBackfillDate does a real calendar check, so a corrupted marker cannot carry 2026/99/99. No age or future bound: the same guard gates rollout publication and a clock-skewed directory holds real sessions. - 'scans only the current date once a baseline exists' now has a second date directory, so it fails on a full walk instead of passing either way.
118 lines
4.4 KiB
TypeScript
118 lines
4.4 KiB
TypeScript
import type * as NodeFs from 'node:fs'
|
|
import type * as NodeFsPromises from 'node:fs/promises'
|
|
|
|
// Fault-injection doubles for the backfill tests. Kept out of the spec files so
|
|
// several suites can drive the same failure modes from one switchboard.
|
|
|
|
export const fsMockState = {
|
|
failLink: false,
|
|
failLinkTransiently: false,
|
|
failLinkPermission: false,
|
|
raceTargetIntoExistence: false,
|
|
failMarkerRm: false,
|
|
failMarkerReplacement: false,
|
|
failAuditMkdirOnce: false,
|
|
failAuditWrites: false,
|
|
failMkdirPath: null as string | null,
|
|
failDirectoryPath: null as string | null,
|
|
failLstatPath: null as string | null
|
|
}
|
|
|
|
export function resetCodexSessionBackfillFsMocks(): void {
|
|
fsMockState.failLink = false
|
|
fsMockState.failLinkTransiently = false
|
|
fsMockState.failLinkPermission = false
|
|
fsMockState.raceTargetIntoExistence = false
|
|
fsMockState.failMarkerRm = false
|
|
fsMockState.failMarkerReplacement = false
|
|
fsMockState.failAuditMkdirOnce = false
|
|
fsMockState.failAuditWrites = false
|
|
fsMockState.failMkdirPath = null
|
|
fsMockState.failDirectoryPath = null
|
|
fsMockState.failLstatPath = null
|
|
}
|
|
|
|
function errnoError(message: string, code: string): NodeJS.ErrnoException {
|
|
const error = new Error(message) as NodeJS.ErrnoException
|
|
error.code = code
|
|
return error
|
|
}
|
|
|
|
function isMarkerPath(value: unknown): boolean {
|
|
return (
|
|
String(value).includes('codex-session-backfill') &&
|
|
String(value).endsWith('backfill-complete.json')
|
|
)
|
|
}
|
|
|
|
export function createNodeFsMock(actual: typeof NodeFs): typeof NodeFs {
|
|
return {
|
|
...actual,
|
|
existsSync: (...args: Parameters<typeof actual.existsSync>) =>
|
|
args[0] === fsMockState.failLstatPath ? false : actual.existsSync(...args),
|
|
rmSync: (...args: Parameters<typeof actual.rmSync>) => {
|
|
if (fsMockState.failMarkerRm && isMarkerPath(args[0])) {
|
|
throw errnoError('EACCES: marker removal failed', 'EACCES')
|
|
}
|
|
return actual.rmSync(...args)
|
|
},
|
|
renameSync: (...args: Parameters<typeof actual.renameSync>) => {
|
|
if (fsMockState.failMarkerReplacement && isMarkerPath(args[1])) {
|
|
throw errnoError('EACCES: marker replacement failed', 'EACCES')
|
|
}
|
|
return actual.renameSync(...args)
|
|
}
|
|
}
|
|
}
|
|
|
|
export function createNodeFsPromisesMock(actual: typeof NodeFsPromises): typeof NodeFsPromises {
|
|
return {
|
|
...actual,
|
|
mkdir: (...args: Parameters<typeof actual.mkdir>) => {
|
|
if (args[0] === fsMockState.failMkdirPath) {
|
|
throw errnoError('EACCES: target directory inaccessible', 'EACCES')
|
|
}
|
|
if (fsMockState.failAuditMkdirOnce && String(args[0]).includes('codex-session-backfill')) {
|
|
fsMockState.failAuditMkdirOnce = false
|
|
throw errnoError('EACCES: transient audit directory failure', 'EACCES')
|
|
}
|
|
return actual.mkdir(...args)
|
|
},
|
|
appendFile: (...args: Parameters<typeof actual.appendFile>) => {
|
|
if (fsMockState.failAuditWrites && String(args[0]).includes('codex-session-backfill')) {
|
|
throw errnoError('ENOSPC: audit write failed', 'ENOSPC')
|
|
}
|
|
return actual.appendFile(...args)
|
|
},
|
|
lstat: (...args: Parameters<typeof actual.lstat>) => {
|
|
if (args[0] === fsMockState.failLstatPath) {
|
|
throw errnoError('EACCES: path inaccessible', 'EACCES')
|
|
}
|
|
return actual.lstat(...args)
|
|
},
|
|
link: async (...args: Parameters<typeof actual.link>) => {
|
|
if (fsMockState.raceTargetIntoExistence && String(args[0]).includes('codex-runtime-home')) {
|
|
fsMockState.raceTargetIntoExistence = false
|
|
await actual.writeFile(args[1], 'concurrent target\n', 'utf-8')
|
|
throw errnoError('EEXIST: concurrent target', 'EEXIST')
|
|
}
|
|
if (fsMockState.failLink && String(args[0]).includes('codex-runtime-home')) {
|
|
throw errnoError('EXDEV: cross-device link', 'EXDEV')
|
|
}
|
|
if (fsMockState.failLinkTransiently && String(args[0]).includes('codex-runtime-home')) {
|
|
throw errnoError('EIO: transient hardlink failure', 'EIO')
|
|
}
|
|
if (fsMockState.failLinkPermission && String(args[0]).includes('codex-runtime-home')) {
|
|
throw errnoError('EACCES: hardlink permission denied', 'EACCES')
|
|
}
|
|
return actual.link(...args)
|
|
},
|
|
opendir: (...args: Parameters<typeof actual.opendir>) => {
|
|
if (args[0] === fsMockState.failDirectoryPath) {
|
|
throw errnoError('EACCES: directory unreadable', 'EACCES')
|
|
}
|
|
return actual.opendir(...args)
|
|
}
|
|
} as typeof NodeFsPromises
|
|
}
|