From aedbf8a51559bd276b208f11b33b44299b541783 Mon Sep 17 00:00:00 2001 From: slaguru666 Date: Wed, 15 Jul 2026 21:10:03 +0100 Subject: [PATCH] Stop emitting legacy Codex profile config; preserve settings after managed block Codex 0.144+ refuses to load any config containing the top-level `profile = "..."` key or `[profiles.]` tables, so every machine running the kit's output hit "Error loading configuration" and codex would not start. Profiles now live in per-profile files: - templates/global/{economy,balanced,deep}.config.toml, installed to ~/.codex/.config.toml and selected with `codex --profile ` - config.toml keeps the balanced values as its defaults, so plain `codex` behaves as before without a profile selector `profile` and `profiles.*` stay in the installer's strip lists so upgrading an existing machine removes the legacy keys from its live config. Also fixes two latent bugs in the merge, both hit while testing the above: - Only text *before* the managed block was retained, so anything after it was silently deleted on every install. `codex mcp add` appends [mcp_servers.*] to the end of config.toml, so those servers would be wiped. Both sides are now kept and filtered. - The config was read with the locale default encoding and a leading BOM was preserved, which could strand the BOM mid-file once content was merged around the block -- invalid TOML. Read as utf-8-sig, write utf-8. Verified against a seeded legacy config (BOM at byte 0, machine-specific [mcp_servers] on both sides of the block): legacy keys stripped, both sides preserved, idempotent across three runs, and `codex --profile economy exec` returns a live model response. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 6 +- scripts/install_codex_kit.sh | 81 ++++++++++++++++++--------- templates/global/balanced.config.toml | 4 ++ templates/global/config-managed.toml | 19 ------- templates/global/config.toml | 23 ++------ templates/global/deep.config.toml | 4 ++ templates/global/economy.config.toml | 4 ++ 7 files changed, 75 insertions(+), 66 deletions(-) create mode 100644 templates/global/balanced.config.toml create mode 100644 templates/global/deep.config.toml create mode 100644 templates/global/economy.config.toml diff --git a/README.md b/README.md index 9ece15e..dfb684f 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,11 @@ Run the audit script periodically. If session storage balloons again, archive th - `balanced`: everyday profile - `deep`: higher-effort work when architecture or debugging really needs it -These profiles are defined in `templates/global/config.toml`. The installer preserves machine-specific plugin and trust settings while merging the managed cost and memory block into your live `~/.codex/config.toml`. +Each profile is defined in its own `templates/global/.config.toml` and is installed to `~/.codex/.config.toml`. Select one with `codex --profile `; plain `codex` uses the `config.toml` defaults, which match `balanced`. + +Codex 0.144+ rejects the legacy top-level `profile = "..."` key and `[profiles.]` tables, so the kit no longer emits them and strips them from a live config on upgrade. + +The installer merges the managed cost and memory block into your live `~/.codex/config.toml`, preserving machine-specific settings that appear *before* the managed block. ## New Machine Rollout diff --git a/scripts/install_codex_kit.sh b/scripts/install_codex_kit.sh index 14654c2..53d0408 100755 --- a/scripts/install_codex_kit.sh +++ b/scripts/install_codex_kit.sh @@ -28,6 +28,9 @@ managed_path = Path(sys.argv[2]) managed_start = "# >>> codex-ops-kit managed block >>>" managed_end = "# <<< codex-ops-kit managed block <<<" +# "profile" and the "profiles.*" tables are legacy: Codex 0.144+ refuses to load +# a config containing them. They stay in these strip lists (despite no longer +# being emitted) so an upgrade from an older kit removes them from a live config. managed_root_keys = { "model", "model_reasoning_effort", @@ -42,46 +45,70 @@ managed_sections = { "profiles.deep", } -text = config_path.read_text() if config_path.exists() else "" -managed = managed_path.read_text().rstrip() +# utf-8-sig drops a leading BOM if one is present. Preserving it risks stranding +# the BOM mid-file once content is merged around the block, which is invalid TOML. +text = config_path.read_text(encoding="utf-8-sig") if config_path.exists() else "" +managed = managed_path.read_text(encoding="utf-8-sig").rstrip() -if managed_start in text and managed_end in text: - before = text.split(managed_start, 1)[0].rstrip() -else: - before = text.rstrip() -filtered_lines = [] -current_section = None +def strip_managed(chunk): + """Drop kit-owned keys/tables so only machine-specific settings remain.""" + filtered_lines = [] + current_section = None + + for line in chunk.splitlines(): + stripped = line.strip() + if stripped.startswith("[") and stripped.endswith("]"): + current_section = stripped[1:-1].strip() + if current_section in managed_sections: + continue + filtered_lines.append(line) + continue -for line in before.splitlines(): - stripped = line.strip() - if stripped.startswith("[") and stripped.endswith("]"): - current_section = stripped[1:-1].strip() if current_section in managed_sections: continue + + if "=" in line and not line.lstrip().startswith("#"): + key = line.split("=", 1)[0].strip() + if current_section is None and key in managed_root_keys: + continue + filtered_lines.append(line) - continue - if current_section in managed_sections: - continue + return "\n".join(filtered_lines).strip() - if "=" in line and not line.lstrip().startswith("#"): - key = line.split("=", 1)[0].strip() - if current_section is None and key in managed_root_keys: - continue - filtered_lines.append(line) +# Settings may sit on either side of the managed block -- notably `codex mcp add` +# appends [mcp_servers.*] to the end of the file. Keep both sides; rewriting only +# what precedes the block silently discards everything after it. +if managed_start in text and managed_end in text: + before = text.split(managed_start, 1)[0] + after = text.split(managed_end, 1)[1] +else: + before = text + after = "" -before = "\n".join(filtered_lines).strip() +before = strip_managed(before) +after = strip_managed(after) block = f"{managed_start}\n{managed}\n{managed_end}\n" -if before: - config_path.write_text(before + "\n\n" + block) -else: - config_path.write_text(block) +parts = [part for part in (before, block.rstrip(), after) if part] +config_path.write_text("\n\n".join(parts) + "\n", encoding="utf-8") PY +# Profile files. Codex 0.144+ reads per-profile settings from .config.toml +# next to config.toml rather than from [profiles.] tables. These files are +# wholly kit-managed, so they are replaced outright (after backup). +for profile in economy balanced deep; do + profile_src="$REPO_ROOT/templates/global/$profile.config.toml" + profile_dest="$CODEX_HOME/$profile.config.toml" + if [[ -f "$profile_dest" ]]; then + cp "$profile_dest" "$BACKUP_DIR/$profile.config.toml.bak" + fi + cp "$profile_src" "$profile_dest" +done + mkdir -p "$CODEX_HOME/memories/portable-kit" cp "$REPO_ROOT"/memory/*.md "$CODEX_HOME/memories/portable-kit/" @@ -89,6 +116,6 @@ echo "Installed Codex kit into: $CODEX_HOME" echo "Backup created at: $BACKUP_DIR" echo echo "Next steps:" -echo "- Start Codex and use the balanced profile by default." -echo "- Switch to economy for lighter work and deep for hard tasks." +echo "- Defaults match the balanced profile; plain 'codex' needs no flag." +echo "- Use 'codex --profile economy' for lighter work and '--profile deep' for hard tasks." echo "- Run bash scripts/audit_codex_home.sh periodically." diff --git a/templates/global/balanced.config.toml b/templates/global/balanced.config.toml new file mode 100644 index 0000000..f47d563 --- /dev/null +++ b/templates/global/balanced.config.toml @@ -0,0 +1,4 @@ +model = "gpt-5.4" +model_reasoning_effort = "medium" +plan_mode_reasoning_effort = "medium" +web_search = "cached" diff --git a/templates/global/config-managed.toml b/templates/global/config-managed.toml index 83b0dde..489a094 100644 --- a/templates/global/config-managed.toml +++ b/templates/global/config-managed.toml @@ -1,27 +1,8 @@ model = "gpt-5.4" model_reasoning_effort = "medium" -profile = "balanced" project_doc_max_bytes = 16384 web_search = "cached" [history] max_bytes = 10485760 persistence = "save-all" - -[profiles.economy] -model = "gpt-5.4-mini" -model_reasoning_effort = "low" -plan_mode_reasoning_effort = "low" -web_search = "cached" - -[profiles.balanced] -model = "gpt-5.4" -model_reasoning_effort = "medium" -plan_mode_reasoning_effort = "medium" -web_search = "cached" - -[profiles.deep] -model = "gpt-5.4" -model_reasoning_effort = "high" -plan_mode_reasoning_effort = "high" -web_search = "live" diff --git a/templates/global/config.toml b/templates/global/config.toml index 83b0dde..6b4fcf4 100644 --- a/templates/global/config.toml +++ b/templates/global/config.toml @@ -1,27 +1,12 @@ +# Defaults match the balanced profile. Per-profile settings live in sibling +# .config.toml files (economy/balanced/deep) and are selected with +# `codex --profile `. Codex 0.144+ rejects the legacy top-level +# `profile = "..."` key and `[profiles.*]` tables. model = "gpt-5.4" model_reasoning_effort = "medium" -profile = "balanced" project_doc_max_bytes = 16384 web_search = "cached" [history] max_bytes = 10485760 persistence = "save-all" - -[profiles.economy] -model = "gpt-5.4-mini" -model_reasoning_effort = "low" -plan_mode_reasoning_effort = "low" -web_search = "cached" - -[profiles.balanced] -model = "gpt-5.4" -model_reasoning_effort = "medium" -plan_mode_reasoning_effort = "medium" -web_search = "cached" - -[profiles.deep] -model = "gpt-5.4" -model_reasoning_effort = "high" -plan_mode_reasoning_effort = "high" -web_search = "live" diff --git a/templates/global/deep.config.toml b/templates/global/deep.config.toml new file mode 100644 index 0000000..daa0d88 --- /dev/null +++ b/templates/global/deep.config.toml @@ -0,0 +1,4 @@ +model = "gpt-5.4" +model_reasoning_effort = "high" +plan_mode_reasoning_effort = "high" +web_search = "live" diff --git a/templates/global/economy.config.toml b/templates/global/economy.config.toml new file mode 100644 index 0000000..195ddf9 --- /dev/null +++ b/templates/global/economy.config.toml @@ -0,0 +1,4 @@ +model = "gpt-5.4-mini" +model_reasoning_effort = "low" +plan_mode_reasoning_effort = "low" +web_search = "cached"