# How the Codex Plugin Resolves Workspace Root and Configuration Precedence

> Discover how the Codex plugin resolves workspace root and configuration precedence using a three-tier hierarchy for seamless integration and customization in your projects.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: internals
- Published: 2026-07-17

---

**The Codex plugin uses `resolveWorkspaceRoot` in `workspace.mjs` to locate the Git repository root or fall back to the current directory, then applies a three-tier configuration hierarchy: explicit overrides via `setConfig`, workspace-specific `.codexrc` files, and built-in defaults.**

The openai/codex-plugin-cc repository provides a plugin system that requires a deterministic way to identify project boundaries and load user preferences. Understanding how the Codex plugin resolves workspace root and configuration precedence is essential for developers customizing the behavior of hooks and companion scripts across different repositories.

## Workspace Root Resolution

The plugin establishes a canonical directory representing the project workspace through the `resolveWorkspaceRoot` function. This ensures that all subsequent configuration and state operations reference a consistent base path, regardless of which subdirectory a command originates from.

### The `resolveWorkspaceRoot` Function

In `workspace.mjs`, the `resolveWorkspaceRoot` function implements a Git-aware discovery mechanism:

```javascript
import { ensureGitRepository } from "./git.mjs";

export function resolveWorkspaceRoot(cwd) {
  try {
    // Walk up the filesystem until a `.git` folder is found.
    // If successful, return the absolute path of that Git repository root.
    return ensureGitRepository(cwd);
  } catch {
    // If `cwd` is not inside a Git repo, fall back to the supplied cwd.
    return cwd;
  }
}

```

### Git Repository Detection with `ensureGitRepository`

The `ensureGitRepository` helper, defined in `git.mjs`, climbs the directory tree searching for a `.git` folder. When found, the containing directory becomes the workspace root. If no Git repository is detected, the function throws an error that `resolveWorkspaceRoot` catches, causing it to return the original `cwd` unchanged.

All plugin scripts—including `stop-review-gate-hook.mjs`, `session-lifecycle-hook.mjs`, and `codex-companion.mjs`—invoke `resolveWorkspaceRoot(cwd)` to obtain a reliable root path before performing workspace-specific operations.

## Configuration Loading and Precedence

Configuration values persist per-workspace in a JSON state file. The central API for accessing these values resides in `state.mjs` through the `getConfig` and `setConfig` functions.

### The `getConfig` and `setConfig` API

As implemented in `state.mjs`, these functions manage the configuration object within the workspace state:

```javascript
export function getConfig(cwd) {
  // Load the JSON‑encoded state for the given workspace and return its `config` key.
  return loadState(cwd).config;
}

export function setConfig(cwd, key, value) {
  return updateState(cwd, (state) => {
    state.config = { ...state.config, [key]: value };
  });
}

```

### Three-Tier Configuration Hierarchy

The Codex plugin applies configuration values using a strict precedence order, from highest to lowest priority:

1. **Explicit overrides** – Runtime calls to `setConfig` during a session take precedence over all other sources.
2. **Workspace-specific file** – User-provided configuration files (such as `.codexrc` or any file the plugin parses) located at the resolved workspace root are merged into the state, overriding built-in defaults.
3. **Built-in defaults** – Hard-coded values supplied by the plugin code provide fallback values when no user configuration exists.

When `loadState` executes, it reads the workspace state file and applies any `.codexrc` values before returning the configuration object. Subsequent calls to `setConfig` modify this state for the duration of the process, ensuring that runtime overrides remain effective.

## Practical Implementation Examples

The following patterns demonstrate how to integrate workspace root resolution and configuration access into plugin scripts.

### Resolving the Workspace Root

```javascript
import { resolveWorkspaceRoot } from "./lib/workspace.mjs";

const cwd = process.cwd();                 // e.g. "/home/user/my‑project/src"
const workspaceRoot = resolveWorkspaceRoot(cwd);
// workspaceRoot → "/home/user/my-project" (Git repo root) or cwd if not a repo

```

### Reading Configuration Values

```javascript
import { getConfig } from "./lib/state.mjs";

const config = getConfig(workspaceRoot);
console.log(config.reviewTimeout); // prints the effective timeout (user > file > default)

```

### Setting Runtime Overrides

```javascript
import { setConfig } from "./lib/state.mjs";

setConfig(workspaceRoot, "reviewTimeout", 30_000); // 30 seconds for this run

```

### Complete Workflow in a Hook

```javascript
import { resolveWorkspaceRoot } from "./lib/workspace.mjs";
import { getConfig } from "./lib/state.mjs";

export async function stopReviewGateHook(cwd) {
  const workspaceRoot = resolveWorkspaceRoot(cwd);
  const config = getConfig(workspaceRoot);

  // config now reflects: defaults ← .codexrc ← any runtime overrides
  // ... hook logic using `config` …
}

```

## Summary

- **`resolveWorkspaceRoot`** in `workspace.mjs` locates the Git repository root by walking up the filesystem via `ensureGitRepository`, falling back to the current working directory if no `.git` folder exists.
- **`getConfig`** and **`setConfig`** in `state.mjs` provide the primary interface for reading and writing configuration values stored per-workspace.
- Configuration precedence follows a three-tier hierarchy: explicit runtime overrides (highest), workspace-specific `.codexrc` files (middle), and built-in defaults (lowest).
- Hook scripts such as `stop-review-gate-hook.mjs` and `codex-companion.mjs` rely on this consistent pattern to ensure configuration accuracy across the plugin lifecycle.

## Frequently Asked Questions

### What happens if the current directory is not inside a Git repository?

If `ensureGitRepository` fails to locate a `.git` folder while traversing upward from the current working directory, `resolveWorkspaceRoot` catches the error and returns the original `cwd` unchanged. This ensures the plugin continues operation using the invocation directory as the workspace root.

### How does the plugin prioritize conflicting configuration values?

The plugin uses a strict precedence order: explicit calls to `setConfig` override values from `.codexrc` files, which in turn override the built-in defaults. This hierarchy ensures that runtime customizations take precedence while maintaining sensible fallbacks.

### Which source files contain the core workspace and configuration logic?

The key files are `workspace.mjs` for workspace root resolution, `git.mjs` for Git repository detection, and `state.mjs` for configuration management. These are consumed by hook scripts including `stop-review-gate-hook.mjs`, `session-lifecycle-hook.mjs`, and `codex-companion.mjs`.

### Can runtime configuration changes persist across sessions?

Yes, the `setConfig` function writes changes to the workspace state file via `updateState`, meaning modifications persist beyond the current process. Subsequent sessions loading the same workspace will inherit the updated configuration unless overridden again.