# How Workspace Root Resolution Works in Codex-Companion: Git-Aware Detection and Fallback Logic

> Discover how Codex-Companion finds your project root using Git-aware detection and fallback logic. Learn the secrets behind accurate workspace resolution.

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

---

**Workspace root resolution in Codex-Companion uses Git-aware detection to walk up the directory tree until it finds a `.git` folder, falling back to the current working directory if no repository is found.**

The openai/codex-plugin-cc repository implements deterministic **workspace root resolution** to ensure consistent state management across subdirectories. This mechanism anchors all plugin operations to a stable base directory, whether inside a Git repository or a standalone folder. The resolution logic lives in `plugins/codex/scripts/lib/workspace.mjs` and depends on Git repository detection to establish canonical project boundaries.

## The Entry Point: `resolveWorkspaceRoot` in `workspace.mjs`

The `resolveWorkspaceRoot` function serves as the primary entry point for determining the workspace base directory. Located in `plugins/codex/scripts/lib/workspace.mjs`, this function implements a try-catch wrapper around Git detection logic that balances reliability with graceful degradation.

```javascript
export function resolveWorkspaceRoot(cwd) {
  try {
    return ensureGitRepository(cwd);
  } catch {
    // Not a Git repo – fall back to the directory that was passed in
    return cwd;
  }
}

```

### Git-Aware Resolution via `ensureGitRepository`

The actual directory traversal happens in `ensureGitRepository`, implemented in `plugins/codex/scripts/lib/git.mjs`. This function walks up the file tree from the provided `cwd` parameter until it locates a `.git` directory. When found, the containing directory is returned as the canonical workspace root. If the walk reaches the filesystem root without finding `.git`, it throws an error that triggers the fallback mechanism.

### Fallback Behavior When Outside Git Repositories

When `ensureGitRepository` throws—indicating the current path is not inside a Git repository—`resolveWorkspaceRoot` catches the exception and returns the original `cwd` argument. This ensures the plugin degrades gracefully in standalone directories while maintaining predictable behavior.

```javascript
// Assume the current directory is not inside a Git repository
const cwd = "/tmp/some-standalone-dir";
const root = resolveWorkspaceRoot(cwd);
console.log(root);   // → "/tmp/some-standalone-dir"

```

## Canonicalization and State Management

After determining the workspace root, other modules like `plugins/codex/scripts/lib/state.mjs` often call `fs.realpathSync.native` to resolve symlinks. However, the decision about which directory constitutes the workspace is made solely by `resolveWorkspaceRoot`. Most Codex-Companion subsystems store data under paths derived from this root, ensuring project state remains consistent even when commands are invoked from subdirectories.

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

const cwd = process.cwd();               // e.g. invoked from a sub-folder
const workspaceRoot = resolveWorkspaceRoot(cwd);
const state = loadState(workspaceRoot);

```

## Implementation Examples in Hook Scripts

Real-world usage appears in hook scripts like `stop-review-gate-hook.mjs` and `session-lifecycle-hook.mjs`. These demonstrate how to leverage the workspace root for file operations.

**Getting the workspace root in a hook:**

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

export async function runHook({ cwd }) {
  const workspaceRoot = resolveWorkspaceRoot(cwd);
  // Now use `workspaceRoot` for state files, job directories, etc.
}

```

## Summary

- **Workspace root resolution** is handled by `resolveWorkspaceRoot` in `plugins/codex/scripts/lib/workspace.mjs`
- **Git detection** via `ensureGitRepository` in `plugins/codex/scripts/lib/git.mjs` walks up the directory tree looking for `.git` folders
- **Graceful fallback** to `cwd` occurs when no Git repository is found
- **Downstream modules** use the resolved path for state management and job tracking
- **Consistent behavior** is ensured regardless of which subdirectory invokes the command

## Frequently Asked Questions

### What happens if I run Codex-Companion outside a Git repository?

When no `.git` directory is found, `ensureGitRepository` throws an error that `resolveWorkspaceRoot` catches, returning the current working directory as the workspace root. This allows the plugin to function in standalone directories without requiring Git initialization.

### Where is the workspace root resolution logic implemented?

The main logic resides in `plugins/codex/scripts/lib/workspace.mjs` for the entry point and `plugins/codex/scripts/lib/git.mjs` for the Git-aware directory traversal. These files handle the detection and fallback mechanisms used throughout the codebase.

### Does workspace root resolution follow symbolic links?

The resolution itself does not follow symlinks during the search phase, but downstream modules like `state.mjs` typically call `fs.realpathSync.native` on the resolved path to canonicalize it after the root is determined.

### How does this ensure consistent state across subdirectories?

By anchoring to the Git repository root (or the invocation directory as fallback), all state files and job directories are stored relative to a single stable base path. This prevents fragmentation when commands are run from different subdirectories within the same project.