# How Workspace Root Resolution and Project Detection Work in Codex Plugin CC

> Learn how the Codex Plugin CC resolves workspace roots by checking Git and falling back to your current directory for reliable project detection and state management.

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

---

**TLDR:** The Codex Plugin CC resolves the workspace root by first executing `git rev-parse --show-toplevel` via `ensureGitRepository`, falling back to the current working directory when no repository exists, then canonicalizing the path with `fs.realpathSync.native` to ensure stable project detection and state storage.

The openai/codex-plugin-cc repository implements a Git-centric strategy for identifying project boundaries on disk. This mechanism ensures that every script—from hook handlers to state managers—operates within a deterministic directory scope. The resolution logic lives primarily in `plugins/codex/scripts/lib/workspace.mjs` and gracefully degrades when version control is unavailable.

## Git-Based Workspace Root Resolution

The primary detection mechanism relies on Git metadata to establish an authoritative project root. When the plugin initializes, it invokes the resolution pipeline to determine where the current project lives.

### Locating the Repository Top-Level

The `ensureGitRepository` function defined in `plugins/codex/scripts/lib/git.mjs` executes the following shell command:

```bash
git rev-parse --show-toplevel

```

This returns the absolute path to the top-level directory of the current Git repository. When successful, this path becomes the canonical workspace root for all subsequent file operations and state management.

### The resolveWorkspaceRoot Fallback

If the Git command fails—indicating execution outside a repository—the `resolveWorkspaceRoot` function catches the error and returns the original `cwd` parameter. This implementation in `plugins/codex/scripts/lib/workspace.mjs` ensures the plugin never fails due to missing Git configuration:

```javascript
export function resolveWorkspaceRoot(cwd) {
  try {
    return ensureGitRepository(cwd);
  } catch {
    return cwd;
  }
}

```

This fallback provides a deterministic root even for non-Git projects, allowing the plugin to function in any directory context.

## Canonicalization and Path Stability

Many callers immediately normalize the resolved root to resolve symlinks and obtain a stable identifier for the workspace. As implemented in `plugins/codex/scripts/lib/state.mjs`, the code uses `fs.realpathSync.native` for this purpose:

```javascript
const workspaceRoot = resolveWorkspaceRoot(cwd);
let canonicalWorkspaceRoot = workspaceRoot;
try {
  canonicalWorkspaceRoot = fs.realpathSync.native(workspaceRoot);
} catch {
  // If realpath fails (e.g., permission issues) we keep the original path.
}

```

This canonicalization prevents duplicate state entries when the same directory is accessed through different symbolic link paths.

## Project Detection and State Management

The presence of a Git repository serves as the primary signal for project detection, triggering additional metadata inference and isolated state storage.

### Detecting Default Branch Metadata

When a repository is detected, the plugin infers repository metadata via `detectDefaultBranch`, also located in `plugins/codex/scripts/lib/git.mjs`. This function identifies the default branch (e.g., **main** or **master**) to contextualize operations within the correct branch context.

### Per-Project State Storage

The resolved workspace root scopes all persistent data. State management modules—including `state.mjs`, `tracked-jobs.mjs`, and `job-control.mjs`—store per-project data in a hidden `.codex` directory located at the workspace root. This ensures that review states, job logs, and metadata remain isolated to their respective repositories.

## Integration Across the Plugin

Multiple subsystems rely on `resolveWorkspaceRoot` to maintain path consistency:

- **Hook scripts** such as `stop-review-gate-hook.mjs` and `session-lifecycle-hook.mjs` use the root to locate configuration files
- **Runtime helpers** including `collectReviewContext` and `resolveStateDir` store review state in workspace-scoped locations
- **Job tracking** modules store execution logs under the workspace root via `tracked-jobs.mjs`

This centralized resolution prevents path mismatches and ensures state files are correctly scoped per repository.

## Practical Implementation Examples

To resolve the workspace root in your own scripts:

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

// Inside a Git repo
const cwd = process.cwd();               // e.g. /home/user/project/src
const root = resolveWorkspaceRoot(cwd);  // → /home/user/project
console.log(root);

```

For non-Git directories, the function returns the input unchanged:

```javascript
// Outside a Git repo
process.chdir("/tmp");
const fallbackRoot = resolveWorkspaceRoot(process.cwd());
// fallbackRoot is "/tmp"

```

To store state files relative to the detected root:

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

const workspaceRoot = resolveWorkspaceRoot(process.cwd());
const statePath = resolveStateFile(workspaceRoot);
// Points to <workspaceRoot>/.codex/state.json

```

To retrieve repository metadata:

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

const cwd = resolveWorkspaceRoot(process.cwd());
const defaultBranch = detectDefaultBranch(cwd);
console.log(`Default branch is ${defaultBranch}`);

```

## Summary

- The plugin uses `git rev-parse --show-toplevel` via `ensureGitRepository` in `plugins/codex/scripts/lib/git.mjs` to detect Git repositories
- `resolveWorkspaceRoot` in `plugins/codex/scripts/lib/workspace.mjs` provides fallback to the current working directory when Git detection fails
- Paths are canonicalized using `fs.realpathSync.native` to resolve symlinks and ensure stable identifiers across the codebase
- Project state is stored in a `.codex` directory at the workspace root, scoped by `state.mjs`, `job-control.mjs`, and `tracked-jobs.mjs`
- Repository metadata such as the default branch is detected via `detectDefaultBranch` for additional project context

## Frequently Asked Questions

### What happens if I run Codex Plugin CC outside a Git repository?

The `resolveWorkspaceRoot` function catches the Git detection error and returns the current working directory as the workspace root. This allows the plugin to function in non-Git projects, though without repository-specific metadata like default branch detection.

### How does the plugin handle symlinked directories?

After resolving the workspace root, many callers use `fs.realpathSync.native` to canonicalize the path. This resolves symlinks to their physical location, ensuring that state files are stored consistently regardless of which symlink path was used to access the directory.

### Where does the plugin store project-specific state data?

State is stored in a hidden `.codex` directory located at the resolved workspace root. Modules like `state.mjs`, `job-control.mjs`, and `tracked-jobs.mjs` use this location to isolate project data, creating per-project storage that moves with the repository.

### Which source files contain the core workspace resolution logic?

The main entry point is `plugins/codex/scripts/lib/workspace.mjs`, which exports `resolveWorkspaceRoot`. Git-specific detection lives in `plugins/codex/scripts/lib/git.mjs` with `ensureGitRepository` and `detectDefaultBranch`. State management implementation using these roots appears in `plugins/codex/scripts/lib/state.mjs`.