Skip to content

fix(codex): write hook config to the location codex expects#1729

Closed
entire[bot] wants to merge 3 commits into
mainfrom
fix/842-codex-config-location
Closed

fix(codex): write hook config to the location codex expects#1729
entire[bot] wants to merge 3 commits into
mainfrom
fix/842-codex-config-location

Conversation

@entire

@entire entire Bot commented Jul 12, 2026

Copy link
Copy Markdown

Trail: https://entire.io/gh/entireio/cli/trails/836

Summary

  • entire enable --agent codex writes a project-local .codex/config.toml to enable the hooks feature. When the repo lives inside <CODEX_HOME>/agents/... (the tree Codex recursively scans for custom agent-role TOML files), that config.toml gets picked up by Codex's role-file scanner and rejected at startup as a malformed agent role definition (missing developer_instructions).
  • Scope the feature flag into the global config.toml instead, under [projects."<repoRoot>".features], whenever repoRoot falls inside that reserved tree, so no TOML file is ever written there. hooks.json (JSON, not scanned by Codex's role discovery) keeps installing at the normal project-local path.
  • Self-heals: a stale project-local .codex/config.toml left behind by an older entire version is removed once it contains nothing but the feature-flag content this package manages; files with unrelated content are left alone.

Testing

Live-reproduced and verified against the real codex binary (codex-cli 0.139.0), using an isolated temp HOME/CODEX_HOME for every run — real ~/.codex was never touched (confirmed unchanged before/after).

Before fix — repo at ~/.codex/agents/repos/project, run entire enable --agent codex, then codex exec:

warning: Ignoring malformed agent role definition: agent role file at
.../.codex/agents/repos/project/.codex/config.toml must define `developer_instructions`

After fix — same repo layout and same codex exec invocation: no .codex/config.toml is created (only .codex/hooks.json), the flag lands in <CODEX_HOME>/config.toml under [projects."...".features], and the warning is gone.

Also verified:

  • Enable against a pre-existing global config.toml carrying unrelated custom keys and another project's own scoped section — both preserved untouched.
  • Re-enable is idempotent — no duplicate sections or lines.
  • A stale local config.toml left by an older entire version (the exact bug artifact from the issue) is cleaned up on next enable; a local config.toml carrying unrelated user content is left alone.

New regression tests cover all of the above in cmd/entire/cli/agent/codex/hooks_test.go.

  • gofmt -l clean
  • golangci-lint run (full repo) — 0 issues
  • go test -race ./... — all packages pass
  • go test ./cmd/entire/cli/agent/codex/...

Audit hardening

A follow-up audit pass tightened three sharp edges, each with mutation-verified tests:

  • No TOML injection into the global config. repoRoot is a filesystem path embedded into [projects."<repoRoot>".features]. The key now escapes every TOML-significant character (quote, backslash, newline, other control chars). Previously a path containing a " or a newline could terminate the quoted key early and inject arbitrary lines into the shared global config.toml; a newline also split the header across physical lines and broke idempotency. Reproduced live: a repo directory whose name contained a newline injected a rogue line into the global config before the fix, and no longer does.
  • No lost updates or torn writes on the global config. The read-modify-write is now serialized under a cross-process advisory lock (internal/flock) and swapped in atomically (jsonutil.WriteFileAtomic). Two concurrent entire enable runs for different repos under CODEX_HOME/agents could previously clobber each other's edits; a concurrent stress test reproduces the lost update without the lock and passes with it (green under -race).
  • Self-heal never deletes user data. Stale-config detection is now line-anchored (every non-blank line must be exactly a managed feature line or the [features] header) instead of substring stripping, which could collapse unrelated content to whitespace and delete a file this package never wrote.
  • Also fixed isUnderCodexAgentsDir so a directory literally named ..foo under the agents tree is correctly detected as inside it.

Re-verified end-to-end against codex-cli 0.139.0 in an isolated temp HOME/CODEX_HOME: fresh install, existing-keys-preserved, idempotent re-enable, stale-heal, and unrelated-content-preserved all behave correctly, and the real ~/.codex was confirmed untouched (byte-identical config.toml/agents tree before and after).

Fixes #842

entire enable --agent codex wrote a project-local .codex/config.toml to
flip on the hooks feature. When the repo lives inside <CODEX_HOME>/agents
(the reserved tree Codex recursively scans for custom agent-role TOML
files), that config.toml is picked up by Codex's role-file scanner and
rejected at startup as a malformed agent role definition.

Scope the feature flag into the global config.toml instead, under
[projects."<repoRoot>".features], whenever repoRoot falls inside that
reserved tree, so no TOML file is ever written there. hooks.json (JSON,
not scanned) keeps installing at the normal project-local path. A stale
project-local config.toml left behind by an older entire version is
removed once it contains nothing but the feature-flag content this
package manages.

Fixes #842
@suhaanthayyil
suhaanthayyil marked this pull request as ready for review July 12, 2026 01:55
@suhaanthayyil
suhaanthayyil requested a review from a team as a code owner July 12, 2026 01:55
Copilot AI review requested due to automatic review settings July 12, 2026 01:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts how the Codex agent hook feature flag is written so entire enable --agent codex doesn’t create a repo-local .codex/config.toml in the reserved <CODEX_HOME>/agents/... tree (which Codex scans for agent-role TOML files), avoiding Codex’s “malformed agent role definition” startup warning.

Changes:

  • Route feature-flag writes to <CODEX_HOME>/config.toml under a per-project [projects."<repoRoot>".features] table when the repo lives under <CODEX_HOME>/agents.
  • Keep installing .codex/hooks.json project-locally, and add a self-heal cleanup for stale .codex/config.toml that contains only Entire-managed flag content.
  • Add regression tests covering reserved-tree behavior, idempotency, preservation of unrelated config, and stale-file cleanup.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
cmd/entire/cli/agent/codex/hooks.go Adds reserved-tree detection and global scoped config write path; adds cleanup of stale local config; introduces small TOML/section-edit helpers.
cmd/entire/cli/agent/codex/hooks_test.go Adds tests for the reserved agents tree scenario and cleanup/idempotency behaviors.

Comment thread cmd/entire/cli/agent/codex/hooks.go
Comment thread cmd/entire/cli/agent/codex/hooks.go Outdated
Comment thread cmd/entire/cli/agent/codex/hooks.go Outdated
Follow-up hardening on the #842 fix after audit.

- Escape all TOML-significant characters (quote, backslash, newline,
  control chars) when embedding repoRoot into the scoped
  [projects."<repoRoot>".features] key. A path containing a quote or a
  newline could otherwise terminate the quoted key early and inject
  arbitrary lines into the shared global config.toml; a newline also
  split the header across physical lines and defeated the line-based
  idempotency check.

- Serialize the global config.toml read-modify-write under a
  cross-process advisory lock and write it atomically. Two concurrent
  `entire enable` runs for different repos under CODEX_HOME/agents could
  otherwise lose each other's edits or expose a truncated file.

- Detect the stale project-local config.toml with a line-anchored match
  instead of substring stripping, so self-heal never deletes a file
  carrying any content this package did not write.

- Fix isUnderCodexAgentsDir so a directory literally named "..foo" under
  the agents tree is correctly detected as inside it.

Verified end-to-end against codex-cli 0.139.0: a repo under
CODEX_HOME/agents no longer triggers the "malformed agent role
definition" startup warning, the scoped hooks feature is honored, and
existing global keys are preserved.
@gtrrz-victor

Copy link
Copy Markdown
Contributor

Rethinking the approach here: per the current Codex docs (https://developers.openai.com/codex/hooks), hooks are now enabled by default[features] hooks = true is no longer required, and the flag only exists as an opt-out (hooks = false). The flag-writing in this package dates back to when hooks were experimental (hence the legacy codex_hooks = true line).

So what we really need is much simpler: stop creating .codex/config.toml altogether. That fixes #842 at the root — no TOML file is ever written into Codex's reserved agents tree — without any of the global-config machinery this PR adds (scoped [projects...] sections, file locking, TOML-key escaping). It also avoids the portability problem of the scoped approach: the global flag is keyed to an absolute path on one machine, so a clone on another laptop (or a moved repo) would look enabled but silently never fire hooks.

What to keep from this PR: the stale-file self-heal (cleanupStaleReservedTreeConfig), applied everywhere rather than only under the reserved tree, so files written by older entire versions get cleaned up.

Two things to verify before landing that simplification:

  1. Which codex-cli version flipped the default — if we support older versions, we may need a version check or just accept the flag is a no-op there.
  2. A live check that hooks actually fire with only .codex/hooks.json present and no feature flag anywhere (the testing here verified the warning is gone and the flag lands in the global config, but not that the flag is still required at all).

@gtrrz-victor

Copy link
Copy Markdown
Contributor

Closing as duplicate — superseded by #1760, which implements the simpler approach from the last review comment: hooks are enabled by default since codex-cli 0.124.0 (openai/codex#19012), so the flag write is dropped entirely instead of being scoped into the global config. The stale-file self-heal from this PR carries over (applied everywhere, line-anchored detection). Both open verification items were resolved there: the default flipped in rust-v0.124.0, and hooks were live-verified to fire on codex-cli 0.144.4 with only .codex/hooks.json and no feature flag anywhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

entire enable --agent codex can create .codex/config.toml under ~/.codex/agents and trigger Codex warning

3 participants