Skip to content

fix(clv2): resolve cwd to git root before project detection#831

Merged
affaan-m merged 1 commit into
affaan-m:mainfrom
dani-mezei:fix/clv2-subdirectory-project-detection
Mar 30, 2026
Merged

fix(clv2): resolve cwd to git root before project detection#831
affaan-m merged 1 commit into
affaan-m:mainfrom
dani-mezei:fix/clv2-subdirectory-project-detection

Conversation

@dani-mezei

@dani-mezei dani-mezei commented Mar 23, 2026

Copy link
Copy Markdown

Summary

  • When observe.sh captures tool events, it extracts cwd from the hook JSON and sets CLAUDE_PROJECT_DIR. Previously it used the raw cwd, so subdirectories (e.g. my-repo/docs/) created bogus project entries in projects.json instead of being attributed to the parent repo.
  • Now resolves cwd to the git repo root via git rev-parse --show-toplevel before setting CLAUDE_PROJECT_DIR. Falls back to raw cwd for non-git directories.

Root Cause

In observe.sh lines 72-74, CLAUDE_PROJECT_DIR was set to the raw cwd from hook JSON. Since detect-project.sh uses CLAUDE_PROJECT_DIR with highest priority (before git detection), subdirectories became separate "projects."

Changes

  • skills/continuous-learning-v2/hooks/observe.sh — 3-line change: resolve cwd to git root, fall back to raw cwd
  • tests/hooks/observe-subdirectory-detection.test.js — 6 new tests (content checks, behavior, E2E)

Test plan

  • New tests pass (6/6)
  • Existing test suite unaffected (pre-existing failures from missing ajv dep unchanged)
  • Manual verification: run a session from a subdirectory of a git repo, confirm observations go to the repo root project

Summary by cubic

Resolve cwd to the git repo root in CLv2 observation so events from subfolders map to the correct project. Prevents bogus entries in projects.json, falling back to raw cwd for non-git dirs.

  • Bug Fixes
    • Updated skills/continuous-learning-v2/hooks/observe.sh to set CLAUDE_PROJECT_DIR to git -C "$STDIN_CWD" rev-parse --show-toplevel (fallback to "$STDIN_CWD").
    • Added 6 tests covering script content checks, git root detection, and E2E behavior for git and non-git directories.

Written for commit 3f6a14a. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed project directory detection to correctly identify and use the Git repository root when initializing, preventing subdirectories from being incorrectly treated as project roots.
  • Tests

    • Added comprehensive test coverage for subdirectory detection scenarios, including code validation, Git behavior verification, and path resolution testing.

@coderabbitai

coderabbitai Bot commented Mar 23, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The observe.sh script now resolves CLAUDE_PROJECT_DIR to the Git repository root using git rev-parse --show-toplevel when STDIN_CWD is provided, preventing subdirectory-based bogus project roots. A comprehensive test suite validates this behavior across static checks, runtime Git operations, and end-to-end simulations.

Changes

Cohort / File(s) Summary
observe.sh hook logic
skills/continuous-learning-v2/hooks/observe.sh
Modified CLAUDE_PROJECT_DIR resolution to derive Git repository root from STDIN_CWD via git rev-parse --show-toplevel, with fallback to original STDIN_CWD if Git operation fails.
Subdirectory detection tests
tests/hooks/observe-subdirectory-detection.test.js
New test suite validating observe.sh's Git root detection through static content inspection, runtime Git behavior verification, and end-to-end simulation of CLAUDE_PROJECT_DIR logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • leegonzales

Poem

🐰 A hop through Git's great halls so deep,
No more false roots in subdirs steep!
With rev-parse, we find the way,
To project roots, both night and day.
Tests ensure our path is right,
Hooray for Git, shining bright! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: resolving the current working directory to git root before project detection to fix subdirectory-based project detection issues.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug in skills/continuous-learning-v2/hooks/observe.sh where tool-use events fired from a subdirectory of a git repository were attributed to a separate bogus project entry (the subdirectory path) instead of the repository root project. The fix resolves STDIN_CWD to the git repo root via git -C "$STDIN_CWD" rev-parse --show-toplevel before setting CLAUDE_PROJECT_DIR, with a correct fallback to the raw cwd for non-git directories. The end-to-end tests in the new file do exercise the real script path, providing meaningful regression coverage.

Confidence Score: 5/5

Safe to merge — the fix is minimal, correct, and all edge cases are handled.

The shell change is a tightly-scoped 3-line addition that correctly resolves the git root before setting CLAUDE_PROJECT_DIR, with graceful fallback for non-git directories and a missing git binary. End-to-end tests actually invoke observe.sh and verify both the success and fallback paths. No P0/P1 issues were found.

No files require special attention.

Important Files Changed

Filename Overview
skills/continuous-learning-v2/hooks/observe.sh Core bug fix: resolves STDIN_CWD to git repo root before exporting CLAUDE_PROJECT_DIR; change is minimal, correct, and handles edge cases (no git, non-git dirs) cleanly.
tests/hooks/observe-subdirectory-detection.test.js 5 new tests added; real E2E tests (Tests 4-5) invoke observe.sh directly, providing strong regression coverage; the non-git E2E test (Test 5) could be flaky on CI environments where os.tmpdir() lives inside a git worktree, since observe.sh does not receive GIT_CEILING_DIRECTORIES.

Reviews (2): Last reviewed commit: "fix(clv2): resolve cwd to git root befor..." | Re-trigger Greptile

Comment on lines +157 to +161
// ──────────────────────────────────────────────────────
// Group 3: E2E — observe.sh cwd extraction with subdirectory
// ──────────────────────────────────────────────────────

console.log('\n--- E2E: observe.sh cwd resolution ---');

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.

P2 "E2E" label is misleading — tests are unit/integration level

The Group 3 tests are labelled E2E: observe.sh cwd resolution, but they never actually invoke observe.sh. Instead they reproduce the relevant shell snippet inline (lines 175-182 and 205-212). A true E2E test would pipe a hook JSON payload into observe.sh and verify the resulting CLAUDE_PROJECT_DIR or project entry in projects.json.

Because the logic is duplicated rather than exercised through the real script, these tests would not catch regressions in observe.sh itself (e.g. if the variable name _GIT_ROOT were accidentally changed in the script but not in the tests). Consider renaming the group to --- Unit: git root resolution snippet --- or, ideally, invoking observe.sh directly via a minimal stub environment.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +144 to +155
test('git rev-parse fails gracefully outside a git repo', () => {
const testDir = createTempDir();

try {
const result = runBash(
`git -C "${toBashPath(testDir)}" rev-parse --show-toplevel 2>/dev/null || echo ""`
);
assert.strictEqual(result, '', 'Should return empty string outside a git repo');
} finally {
cleanupDir(testDir);
}
});

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.

P2 Test may be flaky if TMPDIR is inside a git repository

createTempDir() uses os.tmpdir() to create the temp directory. On some CI environments (Docker-in-Docker, devcontainer, etc.) TMPDIR or /tmp can itself reside within a git worktree, in which case git rev-parse --show-toplevel would succeed and return a non-empty string — causing this "should return empty" assertion to fail.

A more robust approach is to ensure the temp path is not inside a git repo by using GIT_CEILING_DIRECTORIES to cap traversal:

const result = runBash(
  `GIT_CEILING_DIRECTORIES="${toBashPath(testDir)}" git -C "${toBashPath(testDir)}" rev-parse --show-toplevel 2>/dev/null || echo ""`
);

Alternatively, use --git-dir pointing to a definitely-absent path to force failure.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
tests/hooks/observe-subdirectory-detection.test.js (2)

42-47: Avoid silently swallowing cleanup errors.

Line 45-Line 47 suppresses all cleanup failures, which can mask temp-dir leakage in CI. Please handle/report the error explicitly while keeping cleanup best-effort.

As per coding guidelines, "Always handle errors explicitly at every level and never silently swallow errors".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/hooks/observe-subdirectory-detection.test.js` around lines 42 - 47, The
cleanupDir function is silently swallowing errors which can hide temp-dir leaks;
change the catch to explicitly report the failure while preserving best-effort
cleanup: in cleanupDir(dir) catch the thrown error from fs.rmSync and log/report
it (e.g., console.error or the test logger) including the dir path and
error.stack/message, but do not rethrow so cleanup remains best-effort; ensure
you reference the function name cleanupDir and include the failing dir for
clarity in the message.

174-182: “E2E” cases currently duplicate implementation logic instead of executing observe.sh.

These tests can drift from the real script behavior. Consider invoking skills/continuous-learning-v2/hooks/observe.sh with controlled stdin/env so assertions validate the actual hook path end-to-end.

Refactor sketch
-    // Simulate the cwd extraction + git root resolution logic from observe.sh
-    const script = [
-      `STDIN_CWD="${toBashPath(subDir)}"`,
-      'if [ -n "$STDIN_CWD" ] && [ -d "$STDIN_CWD" ]; then',
-      '  _GIT_ROOT=$(git -C "$STDIN_CWD" rev-parse --show-toplevel 2>/dev/null || true)',
-      '  CLAUDE_PROJECT_DIR="${_GIT_ROOT:-$STDIN_CWD}"',
-      'fi',
-      'echo "$CLAUDE_PROJECT_DIR"',
-    ].join('\n');
+    // Execute actual observe.sh with hook JSON and inspect resulting project-scoped output
+    const input = JSON.stringify({ cwd: subDir, tool_name: 'Read', tool_input: {} });
+    execFileSync('bash', [observeShPath, 'post'], {
+      input,
+      cwd: repoRoot,
+      env: { ...process.env, CLV2_CONFIG: '/nonexistent-config.json' },
+      stdio: 'pipe',
+    });
+    // Assert via created project artifacts / detection output rather than duplicated inline shell logic

Also applies to: 205-212

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/hooks/observe-subdirectory-detection.test.js` around lines 174 - 182,
The test currently constructs a local "script" string that duplicates observe.sh
logic (setting STDIN_CWD, resolving _GIT_ROOT, and echoing CLAUDE_PROJECT_DIR);
instead, update the test to execute the real hook (observe.sh) with controlled
stdin/env and capture its stdout for assertions: spawn or exec the shell script
(skills/continuous-learning-v2/hooks/observe.sh) passing STDIN_CWD via echo/pipe
or environment, ensure git behavior is deterministic by using a temporary repo
or mocking git -C, then assert on the script's output (CLAUDE_PROJECT_DIR)
instead of the local "script" variable to keep tests end-to-end and avoid drift
from the real implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/hooks/observe-subdirectory-detection.test.js`:
- Around line 42-47: The cleanupDir function is silently swallowing errors which
can hide temp-dir leaks; change the catch to explicitly report the failure while
preserving best-effort cleanup: in cleanupDir(dir) catch the thrown error from
fs.rmSync and log/report it (e.g., console.error or the test logger) including
the dir path and error.stack/message, but do not rethrow so cleanup remains
best-effort; ensure you reference the function name cleanupDir and include the
failing dir for clarity in the message.
- Around line 174-182: The test currently constructs a local "script" string
that duplicates observe.sh logic (setting STDIN_CWD, resolving _GIT_ROOT, and
echoing CLAUDE_PROJECT_DIR); instead, update the test to execute the real hook
(observe.sh) with controlled stdin/env and capture its stdout for assertions:
spawn or exec the shell script (skills/continuous-learning-v2/hooks/observe.sh)
passing STDIN_CWD via echo/pipe or environment, ensure git behavior is
deterministic by using a temporary repo or mocking git -C, then assert on the
script's output (CLAUDE_PROJECT_DIR) instead of the local "script" variable to
keep tests end-to-end and avoid drift from the real implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ad39361-1c96-4eb9-9c84-21ed27bc9126

📥 Commits

Reviewing files that changed from the base of the PR and between df4f2df and 6931f04.

📒 Files selected for processing (2)
  • skills/continuous-learning-v2/hooks/observe.sh
  • tests/hooks/observe-subdirectory-detection.test.js

@cubic-dev-ai cubic-dev-ai Bot 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.

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/hooks/observe-subdirectory-detection.test.js">

<violation number="1" location="tests/hooks/observe-subdirectory-detection.test.js:149">
P2: This assertion is environment-dependent because the command can traverse to a parent git worktree when the temp directory is inside one, which makes the test flaky on some CI setups. Bound git discovery (for example with `GIT_CEILING_DIRECTORIES`) before asserting an empty result.</violation>

<violation number="2" location="tests/hooks/observe-subdirectory-detection.test.js:174">
P2: The E2E tests simulate `observe.sh` logic with inline shell snippets instead of executing the real script, leaving integration regressions in `observe.sh` undetected.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

const subDir = path.join(repoDir, 'src', 'components');
fs.mkdirSync(subDir, { recursive: true });

// Simulate the cwd extraction + git root resolution logic from observe.sh

@cubic-dev-ai cubic-dev-ai Bot Mar 23, 2026

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.

P2: The E2E tests simulate observe.sh logic with inline shell snippets instead of executing the real script, leaving integration regressions in observe.sh undetected.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/hooks/observe-subdirectory-detection.test.js, line 174:

<comment>The E2E tests simulate `observe.sh` logic with inline shell snippets instead of executing the real script, leaving integration regressions in `observe.sh` undetected.</comment>

<file context>
@@ -0,0 +1,237 @@
+    const subDir = path.join(repoDir, 'src', 'components');
+    fs.mkdirSync(subDir, { recursive: true });
+
+    // Simulate the cwd extraction + git root resolution logic from observe.sh
+    const script = [
+      `STDIN_CWD="${toBashPath(subDir)}"`,
</file context>
Fix with Cubic

Comment thread tests/hooks/observe-subdirectory-detection.test.js Outdated
@affaan-m

Copy link
Copy Markdown
Owner

thanks. queued for review.

@affaan-m affaan-m force-pushed the fix/clv2-subdirectory-project-detection branch from 6931f04 to 3f6a14a Compare March 30, 2026 08:46
@affaan-m affaan-m merged commit 0220202 into affaan-m:main Mar 30, 2026
3 checks passed
peiking88 pushed a commit to peiking88/everything-claude-code that referenced this pull request Apr 4, 2026
…-project-detection

fix(clv2): resolve cwd to git root before project detection
FrancescoRosciano pushed a commit to FRosciano-Mambo/everything-claude-code that referenced this pull request Jun 1, 2026
…-project-detection

fix(clv2): resolve cwd to git root before project detection
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants