fix(codex): preserve ordinary managed-home settings

This commit is contained in:
Brennan Benson
2026-08-23 16:05:25 -07:00
parent 9009863af4
commit 6bb4f49f2e
10 changed files with 922 additions and 59 deletions
+6 -5
View File
@@ -87,7 +87,9 @@ export function syncSystemConfigIntoManagedCodexHome(
// baseline at all, and promotion stays inert until one exists — bootstrap
// it, since nothing is promotable yet and so nothing can be stranded.
if (!readCodexSettingsBaseline(homes.runtimeHomePath)) {
snapshotCodexRuntimeSettingsBaseline(homes.runtimeHomePath)
snapshotCodexRuntimeSettingsBaseline(homes.runtimeHomePath, new Map(), {
sourceIsAuthoritative: false
})
}
return
}
@@ -97,7 +99,8 @@ export function syncSystemConfigIntoManagedCodexHome(
homes.runtimeHomePath,
new Map(
[...promotionPlan.conflicts].filter(([key]) => mirrorResult.preservedConflictKeys.has(key))
)
),
{ sourceIsAuthoritative: true }
)
}
@@ -179,9 +182,7 @@ function syncSystemConfigIntoManagedCodexHomeUnsafe(
// it would erase every ordinary setting from an existing managed runtime, and
// a 0-byte file is what a half-written or unhydrated cloud-synced home shows.
if (rawSystemConfig.trim() === '') {
return runtimeConfigExists
? { status: 'skipped-missing-source' }
: { status: 'mirrored', preservedConflictKeys: new Set() }
return { status: 'skipped-missing-source' }
}
const sourceConfigDir = resolveCodexConfigMirrorSourceDirectory(systemHomePath)
@@ -53,6 +53,10 @@ function runtimeConfigPath(): string {
return join(runtimeHomeDir(), 'config.toml')
}
function baselinePath(): string {
return join(runtimeHomeDir(), '.orca-config-settings-baseline.json')
}
function writeSystemConfig(content: string): void {
mkdirSync(join(tmpHome, '.codex'), { recursive: true })
writeFileSync(systemConfigPath(), content, 'utf-8')
@@ -127,6 +131,30 @@ describe('ordinary Codex settings survive a managed-home remirror', () => {
expect(existsSync(systemConfigPath())).toBe(true)
})
it('keeps a second runtime edit local instead of promoting an unlisted key', () => {
writeSystemConfig('model = "gpt-5"\n')
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig('model = "gpt-5"\npersonality = "nerdy"\n')
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig('model = "gpt-5"\npersonality = "pragmatic"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('personality = "pragmatic"')
expect(readSystemConfig()).not.toContain('personality')
})
it('mirrors a brand-new system ordinary key after an aligned snapshot', () => {
writeSystemConfig('model = "gpt-5"\n')
syncSystemConfigIntoManagedCodexHome()
writeSystemConfig('model = "gpt-5"\npersonality = "nerdy"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('personality = "nerdy"')
expect(readSystemConfig()).toContain('personality = "nerdy"')
})
it('carries an unlisted [tui] neighbor through a remirror', () => {
writeSystemConfig('model = "gpt-5"\n\n[tui]\ntheme = "dark"\n')
syncSystemConfigIntoManagedCodexHome()
@@ -137,4 +165,177 @@ describe('ordinary Codex settings survive a managed-home remirror', () => {
expect(readRuntimeConfig()).toContain('animations = false')
expect(readSystemConfig()).not.toContain('animations')
})
it('accepts a system-only change to an aligned unlisted key', () => {
writeSystemConfig('model = "gpt-5"\npersonality = "nerdy"\n')
syncSystemConfigIntoManagedCodexHome()
writeSystemConfig('model = "gpt-5"\npersonality = "pragmatic"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('personality = "pragmatic"')
})
it('keeps a runtime deletion local until the system value changes', () => {
writeSystemConfig('model = "gpt-5"\npersonality = "nerdy"\n')
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig('model = "gpt-5"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).not.toContain('personality')
expect(readSystemConfig()).toContain('personality = "nerdy"')
writeSystemConfig('model = "gpt-5"\npersonality = "pragmatic"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('personality = "pragmatic"')
})
it('re-anchors an existing local conflict when the runtime value is deleted', () => {
writeSystemConfig('model = "gpt-5"\npersonality = "system"\n')
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig('model = "gpt-5"\npersonality = "runtime"\n')
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig('model = "gpt-5"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).not.toContain('personality')
expect(JSON.parse(readFileSync(baselinePath(), 'utf-8')).conflicts.personality).toEqual({
runtime: null,
system: '"system"'
})
})
it('keeps simultaneous unlisted divergence local and content-anchored', () => {
writeSystemConfig('model = "gpt-5"\npersonality = "ancestor"\n')
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig('model = "gpt-5"\npersonality = "runtime"\n')
writeSystemConfig('model = "gpt-5"\npersonality = "system"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('personality = "runtime"')
expect(readSystemConfig()).toContain('personality = "system"')
expect(JSON.parse(readFileSync(baselinePath(), 'utf-8')).conflicts.personality).toEqual({
runtime: '"runtime"',
system: '"system"'
})
})
it('restores promoted values deleted from the runtime', () => {
writeSystemConfig('model = "gpt-5"\n\n[tui]\ntheme = "dark"\n')
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig('[tui]\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('model = "gpt-5"')
expect(readRuntimeConfig()).toContain('theme = "dark"')
expect(readSystemConfig()).toContain('model = "gpt-5"')
expect(readSystemConfig()).toContain('theme = "dark"')
})
it('keeps legacy-baseline unlisted edits local while promoted edits write through', () => {
writeSystemConfig('model = "gpt-5"\npersonality = "system"\n')
syncSystemConfigIntoManagedCodexHome()
writeFileSync(
baselinePath(),
`${JSON.stringify({ version: 2, settings: { model: '"gpt-5"' } })}\n`,
'utf-8'
)
setRuntimeConfig('model = "o4"\npersonality = "runtime"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readSystemConfig()).toContain('model = "o4"')
expect(readSystemConfig()).toContain('personality = "system"')
expect(readRuntimeConfig()).toContain('personality = "runtime"')
setRuntimeConfig('model = "o4"\npersonality = "runtime-2"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readSystemConfig()).not.toContain('personality = "runtime-2"')
expect(readRuntimeConfig()).toContain('personality = "runtime-2"')
})
it('bootstraps missing source without treating unlisted runtime state as authoritative', () => {
setRuntimeConfig('model = "seeded"\npersonality = "local"\n')
syncSystemConfigIntoManagedCodexHome()
expect(JSON.parse(readFileSync(baselinePath(), 'utf-8'))).toMatchObject({ version: 2 })
expect(JSON.parse(readFileSync(baselinePath(), 'utf-8')).sourceAuthority).toBeUndefined()
writeSystemConfig('model = "seeded"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('personality = "local"')
expect(readSystemConfig()).not.toContain('personality')
expect(JSON.parse(readFileSync(baselinePath(), 'utf-8'))).toMatchObject({
version: 3,
sourceAuthority: 'mirrored'
})
})
it('stalls absent and blank sources after an authoritative mirror', () => {
writeSystemConfig('model = "gpt-5"\npersonality = "system"\n')
syncSystemConfigIntoManagedCodexHome()
const baseline = readFileSync(baselinePath(), 'utf-8')
setRuntimeConfig('model = "o4"\npersonality = "local"\n')
rmSync(systemConfigPath())
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('personality = "local"')
expect(readFileSync(baselinePath(), 'utf-8')).toBe(baseline)
writeSystemConfig(' \n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('model = "o4"')
expect(readFileSync(baselinePath(), 'utf-8')).toBe(baseline)
writeSystemConfig('model = "gpt-5"\npersonality = "system"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readSystemConfig()).toContain('model = "o4"')
expect(readRuntimeConfig()).toContain('personality = "local"')
})
it('keeps supported unlisted values local when seeding a missing system config', () => {
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig(
'model = "o4"\npersonality = "local"\n"quoted-local" = true\nmultiline = """\nvalue\n"""\n\n[features]\nhooks = true\n\n[mcp_servers.local]\ncommand = "run"\n'
)
syncSystemConfigIntoManagedCodexHome()
expect(readSystemConfig()).toContain('model = "o4"')
expect(readSystemConfig()).not.toContain('personality')
expect(readSystemConfig()).toContain('"quoted-local" = true')
expect(readSystemConfig()).toContain('multiline = """')
expect(readSystemConfig()).toContain('[features]')
expect(readSystemConfig()).toContain('[mcp_servers.local]')
expect(readRuntimeConfig()).toContain('personality = "local"')
})
it('excludes structured shapes and quoted unknown keys while preserving flat arrays', () => {
writeSystemConfig('model = "gpt-5"\n')
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig(
'model = "gpt-5"\ninline = { enabled = true }\nnested = [{ enabled = true }]\nflat = ["a", "b"]\n"quoted-local" = true\n'
)
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).not.toContain('inline =')
expect(readRuntimeConfig()).not.toContain('nested =')
expect(readRuntimeConfig()).not.toContain('quoted-local')
expect(readRuntimeConfig()).toContain('flat = ["a", "b"]')
expect(readSystemConfig()).not.toContain('flat =')
})
it('lets table and dotted descendants win over colliding runtime scalars', () => {
writeSystemConfig('model = "gpt-5"\nfoo.bar = true\n\n[tui.theme]\nvariant = "dark"\n')
syncSystemConfigIntoManagedCodexHome()
setRuntimeConfig('model = "gpt-5"\nfoo = "scalar"\n\n[tui]\ntheme = "scalar"\n')
syncSystemConfigIntoManagedCodexHome()
expect(readRuntimeConfig()).toContain('foo.bar = true')
expect(readRuntimeConfig()).not.toContain('foo = "scalar"')
expect(readRuntimeConfig()).toContain('[tui.theme]')
expect(readRuntimeConfig()).not.toContain('theme = "scalar"')
})
})
@@ -73,6 +73,8 @@ function readBaseline(): {
version: number
settings: Record<string, string | null>
conflicts?: Record<string, { runtime: string | null; system: string | null }>
knownPromotedKeys?: string[]
sourceAuthority?: string
} {
return JSON.parse(readFileSync(baselinePath(), 'utf-8'))
}
@@ -85,9 +87,11 @@ describe('Codex settings baseline schema upgrade', () => {
syncSystemConfigIntoManagedCodexHome()
expect(readBaseline()).toMatchObject({
version: 2,
settings: { model: '"gpt-5"', 'tui.theme': '"dark"' }
version: 3,
settings: { model: '"gpt-5"', 'tui.theme': '"dark"' },
sourceAuthority: 'mirrored'
})
expect(readBaseline().knownPromotedKeys).toContain('tui.theme')
expect(readBaseline().conflicts).toBeUndefined()
})
@@ -233,6 +237,57 @@ describe('Codex settings baseline schema upgrade', () => {
})
})
it('migrates a promoted key omitted from an older v3 promoted-key set', () => {
prepareLegacyState(
'model = "gpt-5"\napproval_policy = "never"\n',
'model = "gpt-5"\napproval_policy = "on-request"\n'
)
writeFileSync(
baselinePath(),
`${JSON.stringify({
version: 3,
sourceAuthority: 'mirrored',
knownPromotedKeys: ['model'],
settings: { model: '"gpt-5"', approval_policy: '"never"' }
})}\n`,
'utf-8'
)
syncSystemConfigIntoManagedCodexHome()
expect(readFileSync(systemConfigPath(), 'utf-8')).toContain('approval_policy = "never"')
expect(readFileSync(runtimeConfigPath(), 'utf-8')).toContain('approval_policy = "on-request"')
expect(readBaseline().conflicts?.approval_policy).toEqual({
runtime: '"on-request"',
system: '"never"'
})
writeFileSync(runtimeConfigPath(), 'model = "gpt-5"\napproval_policy = "chosen"\n', 'utf-8')
syncSystemConfigIntoManagedCodexHome()
expect(readFileSync(systemConfigPath(), 'utf-8')).toContain('approval_policy = "chosen"')
expect(readBaseline().conflicts).toBeUndefined()
})
it('retries a promoted conflict after its authoritative source disappears', () => {
prepareLegacyState('model = "gpt-5"\n', 'model = "gpt-5"\napproval_policy = "on-request"\n')
syncSystemConfigIntoManagedCodexHome()
const anchoredBaseline = readFileSync(baselinePath(), 'utf-8')
rmSync(systemConfigPath())
writeFileSync(runtimeConfigPath(), 'model = "gpt-5"\napproval_policy = "never"\n', 'utf-8')
syncSystemConfigIntoManagedCodexHome()
expect(existsSync(systemConfigPath())).toBe(false)
expect(readFileSync(baselinePath(), 'utf-8')).toBe(anchoredBaseline)
writeFileSync(systemConfigPath(), 'model = "gpt-5"\n', 'utf-8')
syncSystemConfigIntoManagedCodexHome()
expect(readFileSync(systemConfigPath(), 'utf-8')).toContain('approval_policy = "never"')
expect(readBaseline().conflicts).toBeUndefined()
})
it('lets an incompatible system TOML shape win instead of stranding a conflict', () => {
prepareLegacyState(
'model = "gpt-5"\ntui = { animations = false }\n',
@@ -0,0 +1,210 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import type * as CodexFsUtils from '../codex-accounts/fs-utils'
const { atomicState } = vi.hoisted(() => ({
atomicState: { fail: false, guardedCalls: 0 }
}))
vi.mock('../codex-accounts/fs-utils', async (importOriginal) => {
const actual = await importOriginal<typeof CodexFsUtils>()
return {
...actual,
writeFileAtomically: (...args: Parameters<typeof actual.writeFileAtomically>) => {
if (atomicState.fail) {
throw new Error('injected baseline rename failure')
}
return actual.writeFileAtomically(...args)
},
writeFileAtomicallyIfUnchanged: (
...args: Parameters<typeof actual.writeFileAtomicallyIfUnchanged>
) => {
atomicState.guardedCalls += 1
return actual.writeFileAtomicallyIfUnchanged(...args)
}
}
})
import { MAX_AGENT_STATE_FILE_BYTES } from '../agent-state-file-reader'
import {
getCodexSettingsBaselinePath,
readCodexSettingsBaseline,
writeCodexSettingsBaseline,
type CodexSettingsBaseline
} from './config-settings-baseline'
let runtimeHomePath: string
beforeEach(() => {
runtimeHomePath = mkdtempSync(join(tmpdir(), 'orca-codex-baseline-v3-'))
atomicState.fail = false
atomicState.guardedCalls = 0
})
afterEach(() => {
rmSync(runtimeHomePath, { recursive: true, force: true })
vi.clearAllMocks()
})
function baselinePath(): string {
return getCodexSettingsBaselinePath(runtimeHomePath)
}
function makeBaseline(
settings: ReadonlyMap<string, string | null>,
conflicts: CodexSettingsBaseline['conflicts'] = new Map()
): CodexSettingsBaseline {
return {
settings,
conflicts,
knownPromotedKeys: new Set(['model', 'tui.theme']),
sourceIsAuthoritative: true,
tracksAllOrdinarySettings: true
}
}
function readStoredBaseline(): {
version: number
settings: Record<string, string | null>
sourceAuthority?: string
} {
return JSON.parse(readFileSync(baselinePath(), 'utf-8'))
}
describe('Codex settings baseline v3 persistence', () => {
it.each([
{
name: 'setting',
value: {
version: 3,
sourceAuthority: 'mirrored',
knownPromotedKeys: ['model'],
settings: { model: 42 }
}
},
{
name: 'conflict',
value: {
version: 3,
sourceAuthority: 'mirrored',
knownPromotedKeys: ['model'],
settings: {},
conflicts: { model: { runtime: '"r"', system: false } }
}
},
{
name: 'known promoted key',
value: {
version: 3,
sourceAuthority: 'mirrored',
knownPromotedKeys: ['model', 42],
settings: {}
}
},
{
name: 'structured key',
value: {
version: 3,
sourceAuthority: 'mirrored',
knownPromotedKeys: ['model'],
settings: { '': '"invalid"' }
}
},
{
name: 'conflict container',
value: {
version: 3,
sourceAuthority: 'mirrored',
knownPromotedKeys: ['model'],
settings: {},
conflicts: []
}
}
])('rejects the whole baseline when a v3 $name is malformed', ({ value }) => {
writeFileSync(baselinePath(), JSON.stringify(value), 'utf-8')
expect(readCodexSettingsBaseline(runtimeHomePath)).toBeNull()
})
it('falls back to readable v2 when an escaped promoted value exceeds the byte limit', () => {
const raw = `"${'\\'.repeat(MAX_AGENT_STATE_FILE_BYTES / 2 + 64)}"`
writeCodexSettingsBaseline(runtimeHomePath, makeBaseline(new Map([['model', raw]])))
expect(statSync(baselinePath()).size).toBeLessThanOrEqual(MAX_AGENT_STATE_FILE_BYTES)
expect(readStoredBaseline()).toMatchObject({
version: 2,
settings: {},
sourceAuthority: 'mirrored'
})
expect(readCodexSettingsBaseline(runtimeHomePath)).not.toBeNull()
})
it('keeps promoted ancestors when an unlisted value forces v2 fallback', () => {
const settings = new Map<string, string | null>([
['model', '"gpt-5"'],
['personality', `"${'x'.repeat(MAX_AGENT_STATE_FILE_BYTES)}"`]
])
writeCodexSettingsBaseline(runtimeHomePath, makeBaseline(settings))
expect(readStoredBaseline()).toMatchObject({
version: 2,
settings: { model: '"gpt-5"' },
sourceAuthority: 'mirrored'
})
})
it('bounds baselines with many distinct ordinary keys', () => {
const settings = new Map<string, string | null>()
for (let index = 0; index < 48_000; index += 1) {
settings.set(`ordinary_${index}`, `"${'x'.repeat(64)}"`)
}
settings.set('model', '"gpt-5"')
writeCodexSettingsBaseline(runtimeHomePath, makeBaseline(settings))
expect(readStoredBaseline()).toMatchObject({ version: 2, settings: { model: '"gpt-5"' } })
expect(statSync(baselinePath()).size).toBeLessThanOrEqual(MAX_AGENT_STATE_FILE_BYTES)
})
it('uses UTF-8 byte length rather than JavaScript string length', () => {
const multibyte = '界'.repeat(Math.floor(MAX_AGENT_STATE_FILE_BYTES / 2))
expect(multibyte.length).toBeLessThan(MAX_AGENT_STATE_FILE_BYTES)
writeCodexSettingsBaseline(
runtimeHomePath,
makeBaseline(
new Map([
['model', '"gpt-5"'],
['personality', `"${multibyte}"`]
])
)
)
expect(readStoredBaseline()).toMatchObject({ version: 2, settings: { model: '"gpt-5"' } })
})
it.skipIf(process.platform === 'win32')(
'atomically writes baselines with owner-only mode',
() => {
writeCodexSettingsBaseline(runtimeHomePath, makeBaseline(new Map([['model', '"gpt-5"']])))
expect(statSync(baselinePath()).mode & 0o777).toBe(0o600)
expect(atomicState.guardedCalls).toBe(0)
}
)
it('keeps the prior readable baseline when replacement fails', () => {
writeCodexSettingsBaseline(runtimeHomePath, makeBaseline(new Map([['model', '"gpt-5"']])))
const before = readFileSync(baselinePath(), 'utf-8')
atomicState.fail = true
expect(() =>
writeCodexSettingsBaseline(runtimeHomePath, makeBaseline(new Map([['model', '"o4"']])))
).toThrow('injected baseline rename failure')
expect(readFileSync(baselinePath(), 'utf-8')).toBe(before)
})
})
+131 -19
View File
@@ -1,9 +1,13 @@
import { writeFileSync } from 'node:fs'
import { isDefinitiveAbsence } from '../../shared/definitive-filesystem-absence'
import { JsonTextStructureCapacityError } from '../../shared/json-text-structure-limit'
import { NodeFileReadTooLargeError } from '../../shared/node-bounded-file-reader'
import { join } from 'node:path'
import { readAgentStateFileSync, readAgentStateJsonFileSync } from '../agent-state-file-reader'
import {
MAX_AGENT_STATE_FILE_BYTES,
readAgentStateFileSync,
readAgentStateJsonFileSync
} from '../agent-state-file-reader'
import { writeFileAtomically } from '../codex-accounts/fs-utils'
const SETTINGS_BASELINE_FILE = '.orca-config-settings-baseline.json'
@@ -15,12 +19,17 @@ export type CodexSettingsConflict = {
export type CodexSettingsBaseline = {
settings: ReadonlyMap<string, string | null>
conflicts: ReadonlyMap<string, CodexSettingsConflict>
knownPromotedKeys: ReadonlySet<string>
sourceIsAuthoritative: boolean
tracksAllOrdinarySettings: boolean
}
type StoredSettingsBaseline = {
version: 1 | 2
version: 1 | 2 | 3
settings: Record<string, string | null>
conflicts?: Record<string, CodexSettingsConflict>
knownPromotedKeys?: string[]
sourceAuthority?: 'mirrored'
}
/**
@@ -59,11 +68,15 @@ function readParsedCodexSettingsBaseline(
if (!isStoredSettingsBaseline(parsed)) {
return null
}
const settings = new Map(
Object.entries(parsed.settings).filter((entry): entry is [string, string | null] => {
return typeof entry[1] === 'string' || entry[1] === null
})
)
if (parsed.version === 3 && !hasStrictV3Members(parsed)) {
return null
}
const settings = new Map<string, string | null>()
for (const [key, raw] of Object.entries(parsed.settings)) {
if (typeof raw === 'string' || raw === null) {
settings.set(key, raw)
}
}
const conflicts = new Map<string, CodexSettingsConflict>()
for (const [key, conflict] of Object.entries(parsed.conflicts ?? {})) {
if (
@@ -74,7 +87,13 @@ function readParsedCodexSettingsBaseline(
conflicts.set(key, conflict)
}
}
return { settings, conflicts }
return {
settings,
conflicts,
knownPromotedKeys: new Set(parsed.knownPromotedKeys ?? []),
sourceIsAuthoritative: parsed.sourceAuthority === 'mirrored',
tracksAllOrdinarySettings: parsed.version === 3
}
} catch (error) {
// Why: invalid baseline state is still `null` — resetting it is the intent,
// and only a read that FAILED must be preserved.
@@ -95,15 +114,9 @@ export function writeCodexSettingsBaseline(
runtimeHomePath: string,
baseline: CodexSettingsBaseline
): void {
const file: StoredSettingsBaseline = {
version: 2,
settings: Object.fromEntries(baseline.settings)
}
if (baseline.conflicts.size > 0) {
file.conflicts = Object.fromEntries(baseline.conflicts)
}
const file = buildStoredSettingsBaseline(baseline)
const baselinePath = getCodexSettingsBaselinePath(runtimeHomePath)
const serialized = `${JSON.stringify(file, null, 2)}\n`
const serialized = serializeBaseline(file)
let existing: string | null = null
try {
existing = readAgentStateFileSync(baselinePath)
@@ -117,7 +130,7 @@ export function writeCodexSettingsBaseline(
if (existing === serialized) {
return
}
writeFileSync(baselinePath, serialized, { encoding: 'utf-8', mode: 0o600 })
writeFileAtomically(baselinePath, serialized, { mode: 0o600 })
}
export function getCodexSettingsBaselinePath(runtimeHomePath: string): string {
@@ -130,9 +143,108 @@ function isStoredSettingsBaseline(value: unknown): value is StoredSettingsBaseli
}
const candidate = value as Partial<StoredSettingsBaseline>
return (
(candidate.version === 1 || candidate.version === 2) &&
(candidate.version === 1 || candidate.version === 2 || candidate.version === 3) &&
!!candidate.settings &&
typeof candidate.settings === 'object' &&
!Array.isArray(candidate.settings)
)
}
function hasStrictV3Members(baseline: StoredSettingsBaseline): boolean {
if (
baseline.sourceAuthority !== 'mirrored' ||
!Array.isArray(baseline.knownPromotedKeys) ||
(baseline.conflicts !== undefined &&
(!baseline.conflicts ||
typeof baseline.conflicts !== 'object' ||
Array.isArray(baseline.conflicts))) ||
baseline.knownPromotedKeys.some(
(key) => typeof key !== 'string' || !isStoredStructuredKey(key)
) ||
new Set(baseline.knownPromotedKeys).size !== baseline.knownPromotedKeys.length
) {
return false
}
if (
Object.entries(baseline.settings).some(
([key, raw]) => !isStoredStructuredKey(key) || (typeof raw !== 'string' && raw !== null)
)
) {
return false
}
return Object.entries(baseline.conflicts ?? {}).every(
([key, conflict]) =>
!Object.hasOwn(baseline.settings, key) &&
isStoredStructuredKey(key) &&
isCodexSettingsConflict(conflict)
)
}
function isStoredStructuredKey(key: string): boolean {
return /^(?:[A-Za-z0-9_-]+|tui\.[A-Za-z0-9_-]+)$/.test(key)
}
function isCodexSettingsConflict(value: unknown): value is CodexSettingsConflict {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return false
}
const conflict = value as Partial<CodexSettingsConflict>
return (
(typeof conflict.runtime === 'string' || conflict.runtime === null) &&
(typeof conflict.system === 'string' || conflict.system === null)
)
}
function buildStoredSettingsBaseline(baseline: CodexSettingsBaseline): StoredSettingsBaseline {
const preferred: StoredSettingsBaseline = {
version: baseline.tracksAllOrdinarySettings ? 3 : 2,
settings: Object.fromEntries(baseline.settings)
}
if (baseline.conflicts.size > 0) {
preferred.conflicts = Object.fromEntries(baseline.conflicts)
}
if (baseline.sourceIsAuthoritative) {
preferred.sourceAuthority = 'mirrored'
}
if (preferred.version === 3) {
preferred.knownPromotedKeys = [...baseline.knownPromotedKeys]
}
if (fitsBaselineReader(preferred)) {
return preferred
}
return buildBoundedLegacyBaseline(baseline)
}
function buildBoundedLegacyBaseline(baseline: CodexSettingsBaseline): StoredSettingsBaseline {
const fallback: StoredSettingsBaseline = { version: 2, settings: {} }
if (baseline.sourceIsAuthoritative) {
fallback.sourceAuthority = 'mirrored'
}
if (baseline.conflicts.size > 0) {
fallback.conflicts = Object.fromEntries(baseline.conflicts)
}
if (!fitsBaselineReader(fallback)) {
throw new NodeFileReadTooLargeError(
Buffer.byteLength(serializeBaseline(fallback), 'utf-8'),
MAX_AGENT_STATE_FILE_BYTES
)
}
for (const key of baseline.knownPromotedKeys) {
if (!baseline.settings.has(key)) {
continue
}
fallback.settings[key] = baseline.settings.get(key) ?? null
if (!fitsBaselineReader(fallback)) {
delete fallback.settings[key]
}
}
return fallback
}
function fitsBaselineReader(file: StoredSettingsBaseline): boolean {
return Buffer.byteLength(serializeBaseline(file), 'utf-8') <= MAX_AGENT_STATE_FILE_BYTES
}
function serializeBaseline(file: StoredSettingsBaseline): string {
return `${JSON.stringify(file, null, 2)}\n`
}
@@ -33,3 +33,25 @@ export function resolveUntrackedCodexSetting(
}
return { action: 'preserve', conflict: existingConflict }
}
export function resolveUnlistedCodexSetting(
runtime: string | null,
system: string | null,
existingConflict?: CodexSettingsConflict
): CodexSettingsConflictResolution {
if (runtime === system) {
return { action: 'aligned' }
}
if (!existingConflict) {
return { action: 'preserve', conflict: { runtime, system } }
}
const runtimeChanged = runtime !== existingConflict.runtime
const systemChanged = system !== existingConflict.system
if (!runtimeChanged && systemChanged) {
return { action: 'use-system' }
}
if (runtimeChanged) {
return { action: 'preserve', conflict: { runtime, system } }
}
return { action: 'preserve', conflict: existingConflict }
}
@@ -201,7 +201,10 @@ describe('codex settings write-back promotion', () => {
simulateCodexSettingWrite('model', '"o4"')
syncSystemConfigIntoManagedCodexHome()
expect(readSystemConfig()).toBe('model = "gpt-5"\n')
expect(JSON.parse(readFileSync(baselinePath(), 'utf-8'))).toMatchObject({ version: 2 })
expect(JSON.parse(readFileSync(baselinePath(), 'utf-8'))).toMatchObject({
version: 3,
sourceAuthority: 'mirrored'
})
simulateCodexSettingWrite('model', '"o4"')
syncSystemConfigIntoManagedCodexHome()
+119 -21
View File
@@ -6,15 +6,21 @@ import { writeFileAtomically } from '../codex-accounts/fs-utils'
import { parseWslUncPath } from '../../shared/wsl-paths'
import { getOrcaManagedCodexHomePath, getSystemCodexHomePath } from './codex-home-paths'
import { upsertPromotedSettingsInContent } from './codex-config-settings-upsert'
import { removePromotedSettingsFromContent } from './codex-config-settings-removal'
import {
observeCodexSettingsBaseline,
writeCodexSettingsBaseline,
type CodexSettingsBaseline,
type CodexSettingsConflict
} from './config-settings-baseline'
import { resolveUntrackedCodexSetting } from './config-settings-conflict-resolution'
import {
resolveUnlistedCodexSetting,
resolveUntrackedCodexSetting
} from './config-settings-conflict-resolution'
import {
collectOrdinaryCodexSettingKeys,
isOrdinarySettingBlocked,
isPromotedStructuredKey,
parseOrdinaryCodexSettingValues,
PROMOTED_STRUCTURED_KEYS,
type OrdinaryCodexSettingValue
@@ -23,7 +29,7 @@ import { extractOrdinaryCodexSettings } from './config-toml-runtime-owned-sectio
// Why: the mirror reverts in-Codex config changes each launch; promotion salvages them by diffing the last baseline.
function readPromotedSettingValues(configPath: string): Map<string, OrdinaryCodexSettingValue> {
function readOrdinarySettingValues(configPath: string): Map<string, OrdinaryCodexSettingValue> {
// Why: an unreadable config held no settings only in the sense that we could
// not read them. Returning an empty map says the user cleared every promoted
// value, and the write below then acts on that.
@@ -38,27 +44,43 @@ function readPromotedSettingValues(configPath: string): Map<string, OrdinaryCode
}
/**
* Records the promotable settings the runtime config.toml holds after a mirror, so the next
* promotion can tell "value Orca mirrored" from "value Codex wrote for the user".
* Records supported ordinary settings after a mirror so the next pass can identify runtime edits.
* Call after a successful mirror only — advancing past an unpromoted change strands it forever.
*/
export function snapshotCodexRuntimeSettingsBaseline(
runtimeHomePath = getOrcaManagedCodexHomePath(),
conflicts: ReadonlyMap<string, CodexSettingsConflict> = new Map()
conflicts: ReadonlyMap<string, CodexSettingsConflict> = new Map(),
options: { sourceIsAuthoritative?: boolean } = {}
): void {
try {
const runtimeTomlPath = join(runtimeHomePath, 'config.toml')
// Why: record an empty baseline even for a missing runtime config, so Codex's first write still diffs and promotes.
const runtimeValues = readPromotedSettingValues(runtimeTomlPath)
const runtimeValues = readOrdinarySettingValues(runtimeTomlPath)
const settings = new Map<string, string | null>()
for (const key of PROMOTED_STRUCTURED_KEYS) {
const sourceIsAuthoritative = options.sourceIsAuthoritative ?? true
const keys = sourceIsAuthoritative ? runtimeValues.keys() : PROMOTED_STRUCTURED_KEYS
for (const key of keys) {
const value = runtimeValues.get(key)
if (!conflicts.has(key) && !value?.multiline) {
if (!conflicts.has(key) && !value?.multiline && !value?.unsupported) {
// Why: explicit nulls distinguish a schema-aware absence from a key added by a later schema.
settings.set(key, value?.raw ?? null)
}
}
writeCodexSettingsBaseline(runtimeHomePath, { settings, conflicts })
for (const key of PROMOTED_STRUCTURED_KEYS) {
if (!conflicts.has(key) && !settings.has(key)) {
const value = runtimeValues.get(key)
if (!value?.multiline && !value?.unsupported) {
settings.set(key, value?.raw ?? null)
}
}
}
writeCodexSettingsBaseline(runtimeHomePath, {
settings,
conflicts,
knownPromotedKeys: new Set(PROMOTED_STRUCTURED_KEYS),
sourceIsAuthoritative,
tracksAllOrdinarySettings: sourceIsAuthoritative
})
} catch (error) {
console.warn('[codex-settings-promotion] failed to snapshot settings baseline', error)
}
@@ -129,8 +151,26 @@ function promoteCodexRuntimeSettingsToSystemUnsafe(
return emptyPromotionPlan()
}
const baseline = baselineObservation.baseline
const runtimeValues = readPromotedSettingValues(runtimeTomlPath)
const systemValues = readPromotedSettingValues(systemTomlPath)
const runtimeValues = readOrdinarySettingValues(runtimeTomlPath)
const systemTomlObservation = observeAgentStateFile(systemTomlPath)
if (systemTomlObservation.kind === 'indeterminate') {
throw systemTomlObservation.error
}
const systemSourceIsBlank =
systemTomlObservation.kind === 'present' && systemTomlObservation.value.trim() === ''
if (
baseline.sourceIsAuthoritative &&
(systemTomlObservation.kind === 'absent' || systemSourceIsBlank)
) {
throw new Error('Authoritative Codex config source is unavailable')
}
if (systemSourceIsBlank) {
return emptyPromotionPlan()
}
const systemValues =
systemTomlObservation.kind === 'present'
? parseOrdinaryCodexSettingValues(systemTomlObservation.value)
: new Map<string, OrdinaryCodexSettingValue>()
const updates = new Map<string, string>()
const conflicts = new Map<string, CodexSettingsConflict>()
const runtimeValuesToPreserve = new Map<string, string | null>()
@@ -155,7 +195,7 @@ function promoteCodexRuntimeSettingsToSystemUnsafe(
// the canonical config with settings derived from Orca's runtime copy. One
// read replaces the old existsSync + read pair and its TOCTOU gap.
// The indeterminate arm is a backstop rather than the live guard: an
// unreadable system config already refused in readPromotedSettingValues,
// unreadable system config already refused above,
// because `writeTarget.path` always resolves to the same file as
// `systemTomlPath` (its realpath, its dangling-link target, or itself).
const writeTargetObservation = observeAgentStateFile(writeTarget.path)
@@ -170,7 +210,7 @@ function promoteCodexRuntimeSettingsToSystemUnsafe(
const systemContent =
writeTargetObservation.kind === 'present'
? writeTargetObservation.value
: extractOrdinaryCodexSettings(runtimeTomlObservation.value)
: getMissingSystemSeedContent(runtimeTomlObservation.value, runtimeValues)
const nextContent = upsertPromotedSettingsInContent(systemContent, updates)
if (nextContent === systemContent) {
return { conflicts, runtimeValuesToPreserve }
@@ -200,6 +240,12 @@ function collectPromotionChanges(context: PromotionCollectionContext): void {
...context.baseline.settings.keys(),
...context.baseline.conflicts.keys()
])) {
if (
isOrdinarySettingBlocked(context.runtimeValues, key) ||
isOrdinarySettingBlocked(context.systemValues, key)
) {
continue
}
const runtimeRaw = getComparableRaw(context.runtimeValues.get(key))
const systemRaw = getComparableRaw(context.systemValues.get(key))
if (runtimeRaw === undefined || systemRaw === undefined) {
@@ -207,10 +253,16 @@ function collectPromotionChanges(context: PromotionCollectionContext): void {
}
const existingConflict = context.baseline.conflicts.get(key)
if (existingConflict || !context.baseline.settings.has(key)) {
const resolution = resolveUntrackedCodexSetting(runtimeRaw, systemRaw, existingConflict)
const promoted = isPromotedStructuredKey(key)
const tracked = isTrackedSetting(context.baseline, key, promoted)
if (existingConflict || !tracked) {
const resolution = promoted
? resolveUntrackedCodexSetting(runtimeRaw, systemRaw, existingConflict)
: resolveUnlistedCodexSetting(runtimeRaw, systemRaw, existingConflict)
if (resolution.action === 'promote-runtime') {
context.updates.set(key, resolution.raw)
if (promoted) {
context.updates.set(key, resolution.raw)
}
} else if (resolution.action === 'preserve') {
// Why: a schema-new key has no three-way ancestor; preserve both values until content changes one side.
context.conflicts.set(key, resolution.conflict)
@@ -219,22 +271,68 @@ function collectPromotionChanges(context: PromotionCollectionContext): void {
continue
}
if (runtimeRaw === null || runtimeRaw === context.baseline.settings.get(key)) {
const ancestor = context.baseline.settings.get(key) ?? null
if (runtimeRaw === ancestor) {
continue
}
// Why: ~/.codex remains source of truth when both sides changed from a known baseline.
if (systemRaw !== context.baseline.settings.get(key)) {
if (systemRaw !== ancestor) {
if (!promoted && runtimeRaw !== systemRaw) {
preserveLocalValue(context, key, runtimeRaw, systemRaw)
}
continue
}
context.updates.set(key, runtimeRaw)
if (promoted) {
if (runtimeRaw !== null) {
context.updates.set(key, runtimeRaw)
}
} else {
preserveLocalValue(context, key, runtimeRaw, systemRaw)
}
}
}
function isTrackedSetting(
baseline: CodexSettingsBaseline,
key: string,
promoted: boolean
): boolean {
if (promoted && baseline.tracksAllOrdinarySettings && !baseline.knownPromotedKeys.has(key)) {
return false
}
return baseline.settings.has(key) || (baseline.tracksAllOrdinarySettings && !promoted)
}
function preserveLocalValue(
context: PromotionCollectionContext,
key: string,
runtime: string | null,
system: string | null
): void {
context.conflicts.set(key, { runtime, system })
context.runtimeValuesToPreserve.set(key, runtime)
}
function getMissingSystemSeedContent(
runtimeContent: string,
runtimeValues: ReadonlyMap<string, OrdinaryCodexSettingValue>
): string {
const localOnlyKeys = new Set<string>()
for (const [key, value] of runtimeValues) {
if (!isPromotedStructuredKey(key) && !value.multiline && !value.unsupported) {
localOnlyKeys.add(key)
}
}
return removePromotedSettingsFromContent(
extractOrdinaryCodexSettings(runtimeContent),
localOnlyKeys
)
}
function getComparableRaw(value: OrdinaryCodexSettingValue | undefined): string | null | undefined {
if (!value) {
return null
}
return value.multiline ? undefined : value.raw
return value.multiline || value.unsupported ? undefined : value.raw
}
function emptyPromotionPlan(): CodexSettingsPromotionPlan {
+157 -7
View File
@@ -12,6 +12,7 @@ export type OrdinaryCodexSettingValue = {
raw: string
// Why: a multiline string/array value can't be replaced line-by-line, so it's excluded from promotion.
multiline: boolean
unsupported?: boolean
}
// Why: these are user preferences the Codex TUI persists. Pre-registering them
@@ -46,6 +47,8 @@ export function parseOrdinaryCodexSettingValues(
let inPreamble = true
let tuiTableSeen = false
let tuiBodyActive = false
let genericTuiBodyActive = false
const blockedKeys = new Set<string>()
for (const line of lines) {
if (isTomlStructuralLine(state)) {
const header = getTomlTableHeader(line)
@@ -57,19 +60,29 @@ export function parseOrdinaryCodexSettingValues(
table.segments.length === 1 &&
table.segments[0] === 'tui' &&
!tuiTableSeen
genericTuiBodyActive = tuiBodyActive && header.trim() === '[tui]'
if (tuiBodyActive) {
tuiTableSeen = true
}
blockTableNamespace(table, blockedKeys)
inPreamble = false
state = updateTomlLineScanState(state, line)
continue
}
const matched = matchOrdinaryStructuredKey(line, inPreamble, tuiBodyActive)
blockDottedNamespace(line, inPreamble, tuiBodyActive, blockedKeys)
blockTuiScalarNamespace(line, inPreamble, blockedKeys)
const matched = matchOrdinaryStructuredKey(
line,
inPreamble,
tuiBodyActive,
genericTuiBodyActive
)
if (matched) {
const nextState = updateTomlLineScanState(state, line)
result.set(matched.structuredKey, {
raw: matched.raw,
multiline: !isTomlStructuralLine(nextState)
multiline: !isTomlStructuralLine(nextState),
unsupported: containsInlineTable(matched.raw)
})
state = nextState
continue
@@ -77,6 +90,9 @@ export function parseOrdinaryCodexSettingValues(
}
state = updateTomlLineScanState(state, line)
}
for (const key of blockedKeys) {
result.set(key, { raw: '', multiline: false, unsupported: true })
}
return result
}
@@ -98,13 +114,15 @@ export function collectOrdinaryCodexSettingKeys(
function matchOrdinaryStructuredKey(
line: string,
inPreamble: boolean,
tuiBodyActive: boolean
tuiBodyActive: boolean,
genericTuiBodyActive: boolean
): { structuredKey: string; raw: string } | null {
const parsed = parseTomlKeyPath(line)
if (!parsed || line[parsed.end] !== '=') {
return null
}
const raw = line.slice(parsed.end + 1).trim()
const keySource = line.slice(0, parsed.end).trim()
const topLevelKey = parsed.segments.length === 1 ? parsed.segments[0] : null
// Why: `tui = { ... }` already defines the tui table; scanning it as a scalar
// would strand a conflict against a `[tui]` body the upsert cannot place.
@@ -112,23 +130,155 @@ function matchOrdinaryStructuredKey(
inPreamble &&
topLevelKey &&
topLevelKey !== 'tui' &&
(isPromotedStructuredKey(topLevelKey) || isBareTomlKey(keySource)) &&
!isOrcaRewrittenCodexPreambleKey(topLevelKey)
) {
return { structuredKey: topLevelKey, raw }
}
const tuiKey = matchTuiStructuredKey(parsed.segments, inPreamble, tuiBodyActive)
const tuiKey = matchTuiStructuredKey(
parsed.segments,
keySource,
inPreamble,
tuiBodyActive,
genericTuiBodyActive
)
return tuiKey ? { structuredKey: tuiKey, raw } : null
}
function matchTuiStructuredKey(
keyPath: string[],
keySource: string,
inPreamble: boolean,
tuiBodyActive: boolean
tuiBodyActive: boolean,
genericTuiBodyActive: boolean
): string | null {
if (inPreamble) {
const tuiKey = keyPath.length === 2 && keyPath[0] === 'tui' ? keyPath[1] : null
return tuiKey ? tuiStructuredKey(tuiKey) : null
if (!tuiKey) {
return null
}
const structuredKey = tuiStructuredKey(tuiKey)
return isPromotedStructuredKey(structuredKey) || isBareDottedTuiKey(keySource)
? structuredKey
: null
}
const tuiKey = keyPath.length === 1 ? keyPath[0] : null
return tuiBodyActive && tuiKey ? tuiStructuredKey(tuiKey) : null
if (!tuiBodyActive || !tuiKey) {
return null
}
const structuredKey = tuiStructuredKey(tuiKey)
return isPromotedStructuredKey(structuredKey) ||
(genericTuiBodyActive && isBareTomlKey(keySource))
? structuredKey
: null
}
export function isPromotedStructuredKey(key: string): boolean {
return PROMOTED_STRUCTURED_KEYS.includes(key)
}
export function isOrdinarySettingBlocked(
values: ReadonlyMap<string, OrdinaryCodexSettingValue>,
key: string
): boolean {
return (
!!values.get(key)?.unsupported || (key.startsWith('tui.') && !!values.get('tui')?.unsupported)
)
}
function isBareTomlKey(source: string): boolean {
return /^[A-Za-z0-9_-]+$/.test(source)
}
function isBareDottedTuiKey(source: string): boolean {
return /^tui\s*\.\s*[A-Za-z0-9_-]+$/.test(source)
}
function blockTableNamespace(
table: ReturnType<typeof parseTomlTableHeaderPath>,
blockedKeys: Set<string>
): void {
if (!table || table.segments.length === 0) {
return
}
if (table.segments[0] === 'tui') {
if (table.segments.length > 1) {
const key = table.segments[1]
if (key) {
blockedKeys.add(tuiStructuredKey(key))
}
}
return
}
const root = table.segments[0]
if (root) {
blockedKeys.add(root)
}
}
function blockDottedNamespace(
line: string,
inPreamble: boolean,
tuiBodyActive: boolean,
blockedKeys: Set<string>
): void {
const parsed = parseTomlKeyPath(line)
if (!parsed || line[parsed.end] !== '=') {
return
}
if (inPreamble && parsed.segments[0] === 'tui' && parsed.segments.length > 2) {
const key = parsed.segments[1]
if (key) {
blockedKeys.add(tuiStructuredKey(key))
}
} else if (inPreamble && parsed.segments.length > 1 && parsed.segments[0] !== 'tui') {
const root = parsed.segments[0]
if (root) {
blockedKeys.add(root)
}
} else if (tuiBodyActive && parsed.segments.length > 1) {
const key = parsed.segments[0]
if (key) {
blockedKeys.add(tuiStructuredKey(key))
}
}
}
function blockTuiScalarNamespace(
line: string,
inPreamble: boolean,
blockedKeys: Set<string>
): void {
if (!inPreamble) {
return
}
const parsed = parseTomlKeyPath(line)
if (parsed?.segments.length === 1 && parsed.segments[0] === 'tui' && line[parsed.end] === '=') {
blockedKeys.add('tui')
}
}
function containsInlineTable(raw: string): boolean {
let quote: '"' | "'" | null = null
for (let index = 0; index < raw.length; index += 1) {
const char = raw[index]
if (quote === '"' && char === '\\') {
index += 1
continue
}
if (quote) {
if (char === quote) {
quote = null
}
continue
}
if (char === '"' || char === "'") {
quote = char
} else if (char === '#') {
return false
} else if (char === '{') {
return true
}
}
return false
}
@@ -368,7 +368,10 @@ describe('STA-4823 D26 — an unreadable settings baseline must stall the mirror
// Asserted against the file rather than the new observation API, so this
// anchor still means something when the fix is reverted.
expect(JSON.parse(realFs.readFileSync(baselinePath(), 'utf-8'))).toMatchObject({ version: 2 })
expect(JSON.parse(realFs.readFileSync(baselinePath(), 'utf-8'))).toMatchObject({
version: 3,
sourceAuthority: 'mirrored'
})
})
it('still replaces a fully-read baseline rejected by the JSON structure limit', () => {
@@ -377,7 +380,10 @@ describe('STA-4823 D26 — an unreadable settings baseline must stall the mirror
snapshotCodexRuntimeSettingsBaseline(runtimeHomePath)
expect(JSON.parse(realFs.readFileSync(baselinePath(), 'utf-8'))).toMatchObject({ version: 2 })
expect(JSON.parse(realFs.readFileSync(baselinePath(), 'utf-8'))).toMatchObject({
version: 3,
sourceAuthority: 'mirrored'
})
})
it('rebuilds an oversized baseline previously produced from a bounded runtime config', () => {
@@ -387,8 +393,13 @@ describe('STA-4823 D26 — an unreadable settings baseline must stall the mirror
realFs.writeFileSync(join(runtimeHomePath, 'config.toml'), oversizedRuntimeConfig, 'utf-8')
snapshotCodexRuntimeSettingsBaseline(runtimeHomePath)
expect(realFs.statSync(baselinePath()).size).toBeGreaterThan(MAX_AGENT_STATE_FILE_BYTES)
expect(observeCodexSettingsBaseline(runtimeHomePath)).toEqual({ kind: 'absent' })
expect(realFs.statSync(baselinePath()).size).toBeLessThan(MAX_AGENT_STATE_FILE_BYTES)
expect(JSON.parse(realFs.readFileSync(baselinePath(), 'utf-8'))).toMatchObject({
version: 2,
sourceAuthority: 'mirrored',
settings: {}
})
expect(observeCodexSettingsBaseline(runtimeHomePath).kind).toBe('present')
realFs.writeFileSync(join(runtimeHomePath, 'config.toml'), 'model = "recovered"\n', 'utf-8')
snapshotCodexRuntimeSettingsBaseline(runtimeHomePath)