Skip to content

Commit c43ea2a

Browse files
authored
Merge pull request #1739 from astandrik/codex/fix-1689-codex-agents-cleanup
fix(codex): replace global guidance with an activation pointer
2 parents 29dbf0b + 76d0382 commit c43ea2a

4 files changed

Lines changed: 460 additions & 40 deletions

File tree

‎README.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ overwrite user-modified agents.
491491
| Agent | Activation | MCP config | Durable context / augmentation |
492492
|-------|------------|------------|--------------------------------|
493493
| Claude Code | Detected | `~/.claude.json` | Skill + three exact-tool graph agents; `SessionStart`, `SubagentStart`, non-blocking `PreToolUse` for `Grep`/`Glob`/`Bash`, and post-`Read` coverage |
494-
| Codex CLI | Detected | `$CODEX_HOME/config.toml` | `AGENTS.md`, skill, three read-only agents; `SessionStart` + `SubagentStart` |
494+
| Codex CLI | Detected | `$CODEX_HOME/config.toml` | Managed `AGENTS.md` activation pointer, skill, three read-only agents; `SessionStart` + `SubagentStart` |
495495
| Gemini CLI | Detected | `.gemini/settings.json` | `GEMINI.md`, three explicit read/graph-tool subagents; `BeforeTool`, `AfterTool` `read_file` coverage, and `SessionStart` |
496496
| Zed | Detected | platform `settings.json` (JSONC) | `AGENTS.md` + shared skill |
497497
| OpenCode | Detected | `$OPENCODE_CONFIG` or resolved global config | `AGENTS.md`, skill, three deny-by-default read-only agents; plugin adds grep/glob graph lookup, post-`read` coverage, first-tool-result session context, and post-compaction reinjection |
@@ -536,6 +536,11 @@ overwrite user-modified agents.
536536
| Oh My Pi (omp) | Detected | Effective agent directory (`OMP_PROFILE` / `PI_CODING_AGENT_DIR`; default `~/.omp/agent/mcp.json`) | Skill and three direct-MCP graph-tool subagents (Scout/Verify/Auditor); preserves user `AGENTS.md` |
537537
| Sourcegraph Cody | Explicit opt-in | Existing `$CBM_CODY_CONFIG_PATH` | MCP only |
538538

539+
For Codex, install keeps only a tiny managed activation pointer in global
540+
`$CODEX_HOME/AGENTS.md`; all detailed behavior lives in the installed `codebase-memory` skill.
541+
Fresh installs create the pointer, upgrades replace the legacy full managed block while preserving
542+
all user-owned bytes, and uninstall removes only the managed pointer.
543+
539544
### Sessions, compaction, and subagents
540545

541546
Hooks installed by this project are fail-open and context-only. Claude Code's

‎scripts/smoke-test.sh‎

Lines changed: 137 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,12 @@ run_no_crash() {
144144

145145
TMPDIR=$(smoke_mktemp_dir)
146146
DRYRUN_HOME=""
147+
CODEX_LIFECYCLE_HOME=""
147148
# On MSYS2/Windows, convert POSIX path to native Windows path for the binary
148149
if command -v cygpath &>/dev/null; then
149150
TMPDIR=$(cygpath -m "$TMPDIR")
150151
fi
151-
trap 'smoke_rmtree "$TMPDIR" "${DRYRUN_HOME:-}"' EXIT
152+
trap 'smoke_rmtree "$TMPDIR" "${DRYRUN_HOME:-}" "${CODEX_LIFECYCLE_HOME:-}"' EXIT
152153

153154
CLI_STDERR=$(smoke_mktemp_file)
154155
# 10 of the cli call sites assign directly (VAR=$(cli ...)). Under
@@ -1432,6 +1433,7 @@ echo '# Personal Rovo guidance' > "$ROVO_INSTRUCTIONS"
14321433
PHASE8_INSTALL_RC=0
14331434
PHASE8_INSTALL_LOG=$(smoke_mktemp_file)
14341435
HOME="$FAKE_HOME" \
1436+
CODEX_HOME="$FAKE_HOME/.codex" \
14351437
XDG_CONFIG_HOME="$FAKE_HOME/.config" \
14361438
APPDATA="$FAKE_HOME/AppData/Roaming" \
14371439
LOCALAPPDATA="$FAKE_HOME/AppData/Local" \
@@ -1704,12 +1706,139 @@ if ! grep -q 'existing_section' "$FAKE_HOME/.codex/config.toml"; then
17041706
fi
17051707
echo "OK 8f-h: Codex TOML (MCP + preserved existing)"
17061708

1707-
# 8i: Codex instructions
1708-
if [ ! -f "$FAKE_HOME/.codex/AGENTS.md" ] || ! grep -q 'codebase-memory-mcp' "$FAKE_HOME/.codex/AGENTS.md"; then
1709-
echo "FAIL 8i: Codex AGENTS.md missing"
1710-
exit 1
1711-
fi
1712-
echo "OK 8i: Codex instructions"
1709+
# 8i: Codex keeps only a tiny global activation pointer; the installed skill
1710+
# owns all detailed behavior. Exercise the complete lifecycle in a second HOME
1711+
# so Codex-only reinstalls cannot perturb the all-agent fixture below.
1712+
CODEX_POINTER_EXPECTED=$(smoke_mktemp_file)
1713+
printf '%s\n' \
1714+
'<!-- codebase-memory-mcp:start -->' \
1715+
'For structural codebase exploration, use the installed `codebase-memory` skill.' \
1716+
'<!-- codebase-memory-mcp:end -->' > "$CODEX_POINTER_EXPECTED"
1717+
if [ ! -f "$FAKE_HOME/.codex/AGENTS.md" ] ||
1718+
[ "$(smoke_file_sha256 "$FAKE_HOME/.codex/AGENTS.md")" != \
1719+
"$(smoke_file_sha256 "$CODEX_POINTER_EXPECTED")" ]; then
1720+
echo "FAIL 8i: fresh Codex install did not create the exact activation pointer"
1721+
exit 1
1722+
fi
1723+
1724+
CODEX_LIFECYCLE_HOME=$(smoke_mktemp_dir)
1725+
CODEX_LIFECYCLE_ROOT="$CODEX_LIFECYCLE_HOME/.codex"
1726+
CODEX_INSTRUCTIONS="$CODEX_LIFECYCLE_ROOT/AGENTS.md"
1727+
mkdir -p "$CODEX_LIFECYCLE_ROOT"
1728+
1729+
CODEX_FRESH_LOG=$(smoke_mktemp_file)
1730+
HOME="$CODEX_LIFECYCLE_HOME" \
1731+
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
1732+
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
1733+
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
1734+
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
1735+
"$BINARY" install --skip-binary --clients=codex -y > "$CODEX_FRESH_LOG" 2>&1
1736+
if [ ! -f "$CODEX_INSTRUCTIONS" ] ||
1737+
[ "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" != \
1738+
"$(smoke_file_sha256 "$CODEX_POINTER_EXPECTED")" ] ||
1739+
! grep -q '\[mcp_servers.codebase-memory-mcp\]' "$CODEX_LIFECYCLE_ROOT/config.toml" ||
1740+
! grep -q 'search_graph' "$CODEX_LIFECYCLE_ROOT/skills/codebase-memory/SKILL.md" ||
1741+
[ ! -s "$CODEX_LIFECYCLE_ROOT/agents/codebase-memory-scout.toml" ] ||
1742+
[ ! -s "$CODEX_LIFECYCLE_ROOT/agents/codebase-memory.toml" ] ||
1743+
[ ! -s "$CODEX_LIFECYCLE_ROOT/agents/codebase-memory-auditor.toml" ] ||
1744+
! grep -q 'SessionStart' "$CODEX_LIFECYCLE_ROOT/config.toml" ||
1745+
! grep -q 'SubagentStart' "$CODEX_LIFECYCLE_ROOT/config.toml"; then
1746+
echo "FAIL 8i: isolated fresh Codex install lost the pointer or another surface"
1747+
exit 1
1748+
fi
1749+
1750+
printf '%s\n' \
1751+
'# Personal Codex guidance' \
1752+
'<!-- codebase-memory-mcp:start -->' \
1753+
'legacy managed guidance' \
1754+
'<!-- codebase-memory-mcp:end -->' \
1755+
'# Keep this line' > "$CODEX_INSTRUCTIONS"
1756+
CODEX_EXPECTED_MIGRATED=$(smoke_mktemp_file)
1757+
printf '%s\n' \
1758+
'# Personal Codex guidance' \
1759+
'<!-- codebase-memory-mcp:start -->' \
1760+
'For structural codebase exploration, use the installed `codebase-memory` skill.' \
1761+
'<!-- codebase-memory-mcp:end -->' \
1762+
'# Keep this line' > "$CODEX_EXPECTED_MIGRATED"
1763+
CODEX_EXPECTED_USER=$(smoke_mktemp_file)
1764+
printf '%s\n' '# Personal Codex guidance' '# Keep this line' > "$CODEX_EXPECTED_USER"
1765+
CODEX_LEGACY_SHA=$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")
1766+
1767+
CODEX_PLAN=$(smoke_mktemp_file)
1768+
HOME="$CODEX_LIFECYCLE_HOME" \
1769+
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
1770+
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
1771+
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
1772+
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
1773+
"$BINARY" install --plan --skip-binary --clients=codex > "$CODEX_PLAN"
1774+
CODEX_INSTRUCTION_PATH=$(json_get "$CODEX_PLAN" \
1775+
"next((str(x) for x in d.get('instruction_files_planned', []) if str(x).replace('\\\\','/').endswith('/.codex/AGENTS.md')), '')")
1776+
CODEX_CLEANUP_COUNT=$(json_get "$CODEX_PLAN" "len(d.get('cleanup_actions_planned', []))")
1777+
if ! exact_path_match "$CODEX_INSTRUCTION_PATH" "$CODEX_INSTRUCTIONS" ||
1778+
[ "$CODEX_CLEANUP_COUNT" != "0" ] ||
1779+
[ "$CODEX_LEGACY_SHA" != "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" ]; then
1780+
echo "FAIL 8i: Codex plan did not describe a non-mutating pointer upsert"
1781+
exit 1
1782+
fi
1783+
1784+
CODEX_DRY_LOG=$(smoke_mktemp_file)
1785+
HOME="$CODEX_LIFECYCLE_HOME" \
1786+
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
1787+
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
1788+
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
1789+
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
1790+
"$BINARY" install --dry-run --skip-binary --clients=codex -y > "$CODEX_DRY_LOG" 2>&1
1791+
if ! grep -q 'managed activation pointer' "$CODEX_DRY_LOG" ||
1792+
[ "$CODEX_LEGACY_SHA" != "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" ]; then
1793+
echo "FAIL 8i: Codex dry-run did not preview a byte-identical pointer migration"
1794+
exit 1
1795+
fi
1796+
1797+
CODEX_MIGRATE_LOG=$(smoke_mktemp_file)
1798+
HOME="$CODEX_LIFECYCLE_HOME" \
1799+
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
1800+
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
1801+
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
1802+
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
1803+
"$BINARY" install --skip-binary --clients=codex -y > "$CODEX_MIGRATE_LOG" 2>&1
1804+
if [ ! -f "$CODEX_INSTRUCTIONS" ] ||
1805+
[ "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" != \
1806+
"$(smoke_file_sha256 "$CODEX_EXPECTED_MIGRATED")" ]; then
1807+
echo "FAIL 8i: Codex migration did not replace only the legacy managed block"
1808+
exit 1
1809+
fi
1810+
1811+
HOME="$CODEX_LIFECYCLE_HOME" \
1812+
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
1813+
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
1814+
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
1815+
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
1816+
"$BINARY" install --skip-binary --clients=codex -y > /dev/null 2>&1
1817+
if [ ! -f "$CODEX_INSTRUCTIONS" ] ||
1818+
[ "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" != \
1819+
"$(smoke_file_sha256 "$CODEX_EXPECTED_MIGRATED")" ] ||
1820+
[ "$(grep -c '<!-- codebase-memory-mcp:start -->' "$CODEX_INSTRUCTIONS")" -ne 1 ]; then
1821+
echo "FAIL 8i: Codex reinstall changed or duplicated the activation pointer"
1822+
exit 1
1823+
fi
1824+
CODEX_UNINSTALL_LOG=$(smoke_mktemp_file)
1825+
if ! HOME="$CODEX_LIFECYCLE_HOME" \
1826+
CODEX_HOME="$CODEX_LIFECYCLE_ROOT" \
1827+
XDG_CONFIG_HOME="$CODEX_LIFECYCLE_HOME/.config" \
1828+
APPDATA="$CODEX_LIFECYCLE_HOME/AppData/Roaming" \
1829+
LOCALAPPDATA="$CODEX_LIFECYCLE_HOME/AppData/Local" \
1830+
"$BINARY" uninstall -y -n > "$CODEX_UNINSTALL_LOG" 2>&1; then
1831+
echo "FAIL 8i: Codex uninstall returned nonzero"
1832+
cat "$CODEX_UNINSTALL_LOG"
1833+
exit 1
1834+
fi
1835+
if [ ! -f "$CODEX_INSTRUCTIONS" ] ||
1836+
[ "$(smoke_file_sha256 "$CODEX_INSTRUCTIONS")" != \
1837+
"$(smoke_file_sha256 "$CODEX_EXPECTED_USER")" ]; then
1838+
echo "FAIL 8i: Codex uninstall did not remove only the activation pointer"
1839+
exit 1
1840+
fi
1841+
echo "OK 8i: isolated Codex activation-pointer lifecycle"
17131842

17141843
# 8j-l: Gemini MCP + hooks + merge
17151844
CMD=$(json_get "$FAKE_HOME/.gemini/settings.json" "d['mcpServers']['codebase-memory-mcp']['command']")
@@ -2728,6 +2857,7 @@ if [[ "$BINARY" == *.exe ]]; then
27282857
UNINSTALL_BINARY="$SELF_PATH"
27292858
fi
27302859
HOME="$FAKE_HOME" \
2860+
CODEX_HOME="$FAKE_HOME/.codex" \
27312861
XDG_CONFIG_HOME="$FAKE_HOME/.config" \
27322862
APPDATA="$FAKE_HOME/AppData/Roaming" \
27332863
LOCALAPPDATA="$FAKE_HOME/AppData/Local" \

‎src/cli/cli.c‎

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,18 +1393,7 @@ static const char skill_content[] =
13931393
"5. `search_graph` results default to 50 per page — check `has_more` and use `offset`.\n";
13941394

13951395
static const char codex_instructions_content[] =
1396-
"# Codebase Knowledge Graph\n"
1397-
"\n"
1398-
"This project uses codebase-memory-mcp to maintain a knowledge graph of the codebase.\n"
1399-
"Use the MCP tools to explore and understand the code:\n"
1400-
"\n"
1401-
"- `search_graph` — find functions, classes, routes by pattern\n"
1402-
"- `trace_path` — trace who calls a function or what it calls\n"
1403-
"- `get_code_snippet` — read function source code\n"
1404-
"- `query_graph` — run Cypher queries for complex patterns\n"
1405-
"- `get_architecture` — high-level project summary\n"
1406-
"\n"
1407-
"Always prefer graph tools over grep for code discovery.\n";
1396+
"For structural codebase exploration, use the installed `codebase-memory` skill.\n";
14081397

14091398
/* Old skill names — cleaned up during install to remove stale directories. */
14101399
static const char *old_skill_names[] = {
@@ -8054,6 +8043,24 @@ static bool install_generic_agent_config(const char *label, const char *binary_p
80548043
return mcp_installed;
80558044
}
80568045

8046+
static bool install_codex_activation_pointer(const char *path, bool dry_run) {
8047+
if (!path) {
8048+
return false;
8049+
}
8050+
if (g_install_plan) {
8051+
plan_record("Codex CLI", "instructions", path);
8052+
return true;
8053+
}
8054+
return dry_run || cbm_upsert_instructions(path, codex_instructions_content) == CLI_OK;
8055+
}
8056+
8057+
static void report_codex_activation_pointer_install(const char *path, bool installed) {
8058+
printf(" instructions: %s (managed activation pointer)\n", path);
8059+
if (!installed) {
8060+
record_agent_config_error(false, "Codex CLI", "instructions_install", path);
8061+
}
8062+
}
8063+
80578064
static void install_windsurf_config(const char *binary_path, const char *config_path,
80588065
const char *rules_path, bool dry_run) {
80598066
if (g_install_plan) {
@@ -8997,6 +9004,8 @@ static void install_cli_agent_configs(const cbm_detected_agents_t *agents, const
89979004
snprintf(ip, sizeof(ip), "%s/AGENTS.md", config_dir);
89989005
snprintf(skills_dir, sizeof(skills_dir), "%s/skills", config_dir);
89999006
snprintf(ap, sizeof(ap), "%s/agents/codebase-memory.toml", config_dir);
9007+
/* A broken hook config must not keep legacy full guidance active. */
9008+
bool pointer_installed = install_codex_activation_pointer(ip, dry_run);
90009009
char command[CLI_BUF_8K];
90019010
char command_windows[CLI_BUF_8K];
90029011
char hooks_json[CLI_BUF_1K];
@@ -9024,10 +9033,16 @@ static void install_cli_agent_configs(const cbm_detected_agents_t *agents, const
90249033
record_agent_config_error_with_reason(
90259034
false, "Codex CLI", commands_ok ? "hook_preflight" : "hook_command_build", cp,
90269035
reason);
9036+
if (!g_install_plan) {
9037+
report_codex_activation_pointer_install(ip, pointer_installed);
9038+
}
90279039
goto codex_install_done;
90289040
}
9029-
install_generic_agent_config("Codex CLI", binary_path, cp, ip, dry_run,
9041+
install_generic_agent_config("Codex CLI", binary_path, cp, NULL, dry_run,
90309042
cbm_upsert_codex_mcp);
9043+
if (!g_install_plan) {
9044+
report_codex_activation_pointer_install(ip, pointer_installed);
9045+
}
90319046
install_agent_skill("Codex CLI", skills_dir, force, dry_run);
90329047
install_tiered_agent_profiles(
90339048
(cbm_tiered_profile_set_t){
@@ -10953,6 +10968,17 @@ static void uninstall_agent_mcp_instr(mcp_uninstall_args_t paths, bool dry_run,
1095310968
}
1095410969
}
1095510970

10971+
static bool uninstall_codex_activation_pointer(const char *path, bool dry_run) {
10972+
return path && (dry_run || cbm_remove_instructions(path) == CLI_OK);
10973+
}
10974+
10975+
static void report_codex_activation_pointer_uninstall(const char *path, bool removed) {
10976+
printf(" instructions: removed managed activation pointer\n");
10977+
if (!removed) {
10978+
record_agent_config_error(true, "Codex CLI", "instructions_uninstall", path);
10979+
}
10980+
}
10981+
1095610982
static void uninstall_agent_skill(const char *label, const char *skills_dir, bool dry_run) {
1095710983
int removed = cbm_remove_skills(skills_dir, dry_run);
1095810984
printf(" %s skill: %d removed\n", label, removed);
@@ -11387,6 +11413,7 @@ static void uninstall_cli_agents(const cbm_detected_agents_t *agents, const char
1138711413
snprintf(ip, sizeof(ip), "%s/AGENTS.md", config_dir);
1138811414
snprintf(skills_dir, sizeof(skills_dir), "%s/skills", config_dir);
1138911415
snprintf(ap, sizeof(ap), "%s/agents/codebase-memory.toml", config_dir);
11416+
bool pointer_removed = uninstall_codex_activation_pointer(ip, dry_run);
1139011417
cbm_agent_installed_binary_path(home, installed_binary, sizeof(installed_binary));
1139111418
char hook_command[CLI_BUF_8K];
1139211419
char hook_command_windows[CLI_BUF_8K];
@@ -11407,10 +11434,12 @@ static void uninstall_cli_agents(const cbm_detected_agents_t *agents, const char
1140711434
? NULL
1140811435
: cbm_toml_codex_hook_failure_name(preflight_failure);
1140911436
record_agent_config_error_with_reason(true, "Codex CLI", "hook_preflight", cp, reason);
11437+
report_codex_activation_pointer_uninstall(ip, pointer_removed);
1141011438
goto codex_toml_done;
1141111439
}
11412-
uninstall_agent_mcp_instr((mcp_uninstall_args_t){"Codex CLI", cp, ip}, dry_run,
11440+
uninstall_agent_mcp_instr((mcp_uninstall_args_t){"Codex CLI", cp, NULL}, dry_run,
1141311441
cbm_remove_codex_mcp_owned);
11442+
report_codex_activation_pointer_uninstall(ip, pointer_removed);
1141411443
if (!dry_run &&
1141511444
cbm_reconcile_codex_hooks_command(cp, hook_command, hook_command_windows,
1141611445
CBM_TOML_CODEX_HOOK_REMOVE, false) != CLI_OK) {

0 commit comments

Comments
 (0)