# How Codex Plugin Resolves Workspace Roots Across Different Project Structures

> Learn how the Codex plugin resolves workspace roots across diverse project structures. Discover its efficient Git repository detection and fallback mechanisms for seamless development.

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

---

**The Codex plugin resolves workspace roots by walking up the directory tree to find the top-level Git repository via `git rev-parse --show-toplevel`, falling back to the current working directory when no Git repository exists.**

The **workspace root** is the canonical anchor point from which the Codex plugin loads configuration files, persists session state, and routes job logs. According to the `openai/codex-plugin-cc` source code, this resolution is handled by a dedicated utility that abstracts away project-specific layouts, enabling consistent behavior across monorepos, nested repositories, and plain directories.

## The Resolution Algorithm

The entry point for workspace discovery is `resolveWorkspaceRoot` in `plugins/codex/scripts/lib/workspace.mjs`. This exported function wraps Git detection with a graceful fallback:

```typescript
// plugins/codex/scripts/lib/workspace.mjs
export function resolveWorkspaceRoot(cwd) {
  try {
    return ensureGitRepository(cwd);
  } catch {
    // Not inside a Git repo – fall back to the supplied cwd
    return cwd;
  }
}

```

The function attempts to locate a Git repository starting from `cwd`. If successful, it returns the repository's top-level directory; otherwise, it treats `cwd` itself as the workspace root.

### Git Detection Implementation

The actual Git traversal lives in `plugins/codex/scripts/lib/git.mjs` within the `ensureGitRepository` function:

```typescript
// plugins/codex/scripts/lib/git.mjs (lines 78-88)
function ensureGitRepository(cwd) {
  const result = spawnSync('git', ['rev-parse', '--show-toplevel'], {
    cwd,
    encoding: 'utf-8',
    stdio: ['pipe', 'pipe', 'ignore']
  });
  
  if (result.status !== 0) {
    throw new Error('Not a git repository');
  }
  
  return result.stdout.trim();
}

```

This executes `git rev-parse --show-toplevel`, which Git resolves by walking upward until finding a `.git` directory. The command returns the **absolute path** of the outermost repository containing `cwd`.

## Behavior Across Project Structures

Because Git's traversal always moves **up** the filesystem hierarchy, `resolveWorkspaceRoot` produces predictable results for common project layouts:

| Project Structure | Resolution Result |
|-------------------|-------------------|
| **Single repository** | Returns the repository root (same as `cwd` if already at root) |
| **Monorepo** | Returns the monorepo root regardless of which package subdirectory you run from |
| **Nested Git repositories** | Returns the **innermost** repository root where `.git` exists |
| **Non-Git directory** | Returns `cwd` unchanged as fallback |

### Monorepo Example

Running from any package within a monorepo resolves to the shared root:

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

const cwd = "/Users/alex/company-monorepo/packages/api/src";
const root = resolveWorkspaceRoot(cwd);
// → "/Users/alex/company-monorepo"

```

This ensures all Codex plugin operations—configuration loading, state persistence, and log routing—use a consistent root regardless of which package triggered the command.

### Nested Repository Example

When repositories are nested, resolution stops at the first `.git` encountered:

```typescript
const cwd = "/Users/alex/outer-project/subrepo/src/components";
const root = resolveWorkspaceRoot(cwd);
// → "/Users/alex/outer-project/subrepo" (inner repo root)

```

The inner repository boundary takes precedence, isolating the subrepo as its own workspace.

### Non-Git Fallback

For directories outside version control:

```typescript
const cwd = "/tmp/experiment";
const root = resolveWorkspaceRoot(cwd);
// → "/tmp/experiment" (original cwd preserved)

```

The plugin functions normally, using the supplied directory as its operational root.

## Consumers of Workspace Root Resolution

Higher-level plugin scripts consistently import and invoke `resolveWorkspaceRoot` to establish their working context.

### Configuration Loading

In `plugins/codex/scripts/stop-review-gate-hook.mjs` (lines 145–148), the workspace root locates session-specific configuration:

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

const workspaceRoot = resolveWorkspaceRoot(process.cwd());
const configPath = join(workspaceRoot, '.codex', 'config.json');

```

### State File Persistence

The `session-lifecycle-hook.mjs` script (lines 47–53) resolves state storage relative to the workspace root:

```typescript
const workspaceRoot = resolveWorkspaceRoot(cwd);
const stateDir = join(workspaceRoot, '.codex', 'state');
await ensureDir(stateDir);
const stateFile = join(stateDir, `${sessionId}.json`);

```

This pattern ensures state files and logs are co-located with their associated project, not scattered across arbitrary filesystem locations.

## Key Files in the Resolution Chain

| File | Responsibility |
|------|----------------|
| `plugins/codex/scripts/lib/workspace.mjs` | Exports `resolveWorkspaceRoot`; defines fallback behavior |
| `plugins/codex/scripts/lib/git.mjs` | Implements `ensureGitRepository`; executes `git rev-parse --show-toplevel` |
| `plugins/codex/scripts/stop-review-gate-hook.mjs` | Consumes workspace root for configuration resolution |
| `plugins/codex/scripts/session-lifecycle-hook.mjs` | Consumes workspace root for state file paths |

## Summary

- **`resolveWorkspaceRoot`** in `plugins/codex/scripts/lib/workspace.mjs` is the canonical workspace discovery function.
- Resolution relies on **`git rev-parse --show-toplevel`** via `ensureGitRepository` in `plugins/codex/scripts/lib/git.mjs`.
- Git's upward directory traversal enables **automatic monorepo support** and **nested repository isolation**.
- **Non-Git directories gracefully fall back** to the supplied `cwd`.
- All plugin operations—configuration, state, and logs—reference the **consistently resolved root** regardless of invocation location.

## Frequently Asked Questions

### What happens if Git is not installed on the system?

The `spawnSync` call in `ensureGitRepository` will fail with a non-zero status or throw if the `git` binary is missing. This error propagates to `resolveWorkspaceRoot`'s catch block, which returns the original `cwd` as fallback. The plugin continues operating without Git-based root detection.

### Can I force a specific workspace root instead of auto-detection?

The source code does not expose an override mechanism. `resolveWorkspaceRoot` always attempts Git detection first. To use a custom root, you would need to modify `plugins/codex/scripts/lib/workspace.mjs` or set `cwd` to your desired root before invoking the plugin.

### Why does the monorepo package resolve to the root rather than the package directory?

Git's `--show-toplevel` command walks upward until finding the `.git` folder, which in a monorepo exists only at the repository root. This is intentional design: shared configuration, state, and logs remain centralized rather than fragmented across packages.

### Does resolution performance vary with directory depth?

The Git command executes in milliseconds regardless of depth, as Git's internal traversal is highly optimized. The `spawnSync` overhead dominates, making the operation effectively constant-time for practical project sizes.