Skip to content

fix(context): fold submodule cwd sessions into the parent repo memory (closes #2842)#2856

Open
rodboev wants to merge 9 commits into
thedotmack:mainfrom
rodboev:fix/2842-submodule-parent-context
Open

fix(context): fold submodule cwd sessions into the parent repo memory (closes #2842)#2856
rodboev wants to merge 9 commits into
thedotmack:mainfrom
rodboev:fix/2842-submodule-parent-context

Conversation

@rodboev

@rodboev rodboev commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Sessions launched inside a git submodule were resolving to the submodule leaf directory as a brand-new project, so context injection hit the empty-state path even when the parent repo already had substantial memory. This change teaches the .git indirection parser to recognize .git/modules/* layouts and updates getProjectContext() to include the parent repo project for submodule cwd sessions.

Why

detectWorktree() in src/utils/worktree.ts:20-55 only recognized .git/worktrees/*, so a submodule .git file pointing at .git/modules/* always fell through to NOT_A_WORKTREE. getProjectContext() in src/utils/project-name.ts:83-96 then returned allProjects: [cwdProjectName], and generateContext() in src/services/context/ContextBuilder.ts:107-125 queried only that empty leaf project. Recognizing the submodule parent in the same parsing path fixes the lookup without adding route-specific fallback logic.

Scope

This PR is limited to project-context resolution for submodule cwd sessions. It does not add a configurable submodule namespacing mode, and it does not change current worktree naming, context rendering, or search-query semantics outside the project list produced by getProjectContext().

Risk

The behavior change is confined to cwds whose .git file points at .git/modules/*. Ordinary repos and current worktree handling should remain unchanged, but the project-context tests need to keep both branches covered because the parent-project merge logic now has two valid .git indirection shapes instead of one.

Verification / Test plan

  • bun test tests/utils/project-name.test.ts --test-name-pattern "submodule|worktree isolation" — 4 passed; new submodule and existing worktree context coverage are green.
  • bun test tests/utils/project-name.test.ts — 20 passed, 2 failed on pre-existing Windows ~ expectations that currently expect C:\Users\Rod instead of the actual basename Rod; unchanged by this branch.
  • bun test tests/hooks/file-context.test.ts tests/worker/http/routes/search-routes-welcome-hint.test.ts — 13 passed; multi-project context injection still queries both projects for worktrees.
  • npm run build — passed after wiring the worktree to the existing repo node_modules; regenerated plugin/scripts/context-generator.cjs from the project-context source change.
  • npm run lint:hook-io && npm run lint:spawn-env — passed.
  • npm run strip-comments:check — fails on the existing repo-wide baseline (Changed: 301 in check mode), unrelated to this branch.

Closes #2842

@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes context injection for sessions launched inside a git submodule by teaching detectWorktree to recognise .git/modules/* layouts alongside the existing .git/worktrees/* pattern, and updating getProjectContext to return the parent repo as the primary project for submodule cwds. A companion validContextRoot guard prevents unrelated ancestor worktrees (e.g. a dotfiles bare-repo at ~) from polluting project resolution.

  • src/utils/worktree.ts — adds a second regex branch for gitdir: .../\.git/modules/... paths; correctly identifies the parent repo via non-greedy capture and returns kind: 'submodule' with parentProjectName.
  • src/utils/project-name.tsfindNearestGitContextRoot now stops at submodule roots; getProjectContext adds an isSubmodule branch that folds the session into the parent project; the new validContextRoot guard discards ancestor context roots that lie outside the repo's own git tree.
  • src/services/worker/http/routes/SearchRoutes.ts — introduces per-instance projectsKnownNonEmpty memoisation and a TTL-cached settings reader to power the welcome-hint path without synchronous disk reads on every hook callback.

Confidence Score: 5/5

Safe to merge — the change is tightly scoped to the .git-file parsing path and does not touch worktree naming, context rendering, or search semantics.

The submodule regex correctly extracts the outermost parent repo path via non-greedy capture, the validContextRoot guard prevents ancestor-worktree overshoot for both worktree and submodule shapes, and the new test suite covers the root-dir case, the nested-subdirectory case, and the ancestor-worktree isolation case. Existing worktree tests are green. No data is mutated; the change only affects which project name is returned.

No files require special attention.

Important Files Changed

Filename Overview
src/utils/worktree.ts Adds submodule detection: reads the gitdir pointer, strips trailing separators, then matches .git/modules/ with non-greedy capture to extract the parent repo path. Falls back to NOT_A_WORKTREE for unrecognised gitdir shapes.
src/utils/project-name.ts findNearestGitContextRoot now stops at isSubmodule in addition to isWorktree; getProjectContext adds a submodule branch returning the parent project; a validContextRoot guard discards ancestor worktrees that are not within the project's own git tree.
tests/utils/project-name.test.ts Adds two new describe blocks: submodule parent context (root and nested-dir variants) and ancestor worktree isolation.
src/services/worker/http/routes/SearchRoutes.ts Introduces per-instance projectsKnownNonEmpty set and a 5s TTL-cached settings reader for the welcome-hint path; env override is read per-request to bypass the cache.
src/server/queue/redis-config.ts Standalone Redis/BullMQ config module with clear env > file > defaults priority chain, URL validation, and port range checks.
src/shared/SettingsDefaultsManager.ts Adds server-beta settings keys and UTF-8 BOM stripping on settings file load; existing logic unchanged.
tests/worker/http/routes/search-routes-welcome-hint.test.ts New test file covering welcome-hint show/skip, multi-project observation queries, per-instance isolation, and env-override port interpolation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["getProjectContext(cwd)"] --> B["findNearestGitContextRoot(cwd)"]
    B --> C{".git file at current dir?"}
    C -- No --> D["Walk up to parent dir"]
    D --> C
    C -- Yes --> E["detectWorktree(dir)"]
    E --> F{gitdir pattern}
    F -- ".git/worktrees/*" --> G["kind: worktree"]
    F -- ".git/modules/* NEW" --> H["kind: submodule"]
    F -- other --> I["NOT_A_WORKTREE"]
    G --> J["contextRoot = dir"]
    H --> J
    J --> K["validContextRoot check: contextRoot inside repoRoot?"]
    K -- No ancestor overshoot --> L["validContextRoot = null"]
    K -- Yes --> M["validContextRoot = contextRoot"]
    L --> N["detectWorktree(repoRoot ?? cwd)"]
    M --> N
    N --> P{isWorktree?}
    P -- Yes --> Q["primary = parent/worktree\nallProjects = parent + composite"]
    P -- No --> R{isSubmodule NEW}
    R -- Yes --> S["primary = parentProjectName\nallProjects = parentProjectName only"]
    R -- No --> T["primary = cwdProjectName"]
Loading

Reviews (5): Last reviewed commit: "fix(context): reject unrelated ancestor ..." | Re-trigger Greptile

Comment thread src/utils/project-name.ts
Comment thread src/utils/worktree.ts
@rodboev

rodboev commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Reworked getProjectContext() to probe the nearest git-context root before falling back, so nested submodule and worktree subdirectories inherit the parent repo context without the redundant early git rev-parse. I also kept parent: null for submodule sessions and added nested-submodule coverage.

Comment thread src/utils/project-name.ts
@rodboev

rodboev commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the ancestor-worktree false positive Greptile flagged.

getProjectContext() now only trusts the nearest .git file root when it matches the repo root, lives inside it, or git rev-parse cannot resolve a repo root at all, so nested submodules still inherit the parent repo context while unrelated home-directory worktrees no longer take over ordinary repos below them.

Focused checks after the patch:

  • bun test tests/utils/project-name.test.ts — new ancestor-worktree regression passes; the only remaining red is the unchanged Windows ~ expectation baseline already present on this branch
  • bun test tests/hooks/file-context.test.ts tests/worker/http/routes/search-routes-welcome-hint.test.ts
  • npm run build
  • npm run lint:hook-io
  • npm run lint:spawn-env

@thedotmack

Copy link
Copy Markdown
Owner

Review finding that blocks this one: relative gitdir: paths break the parse — and create a cross-project memory leak. Real submodule .git files usually contain relative paths (gitdir: ../.git/modules/sub), and the new regex in src/utils/worktree.ts doesn't resolve them against cwd, yielding parentRepoPath: \"..\" and parentProjectName: \"..\". Consequence: the issue stays unfixed for git's default layout, and worse — every submodule session in every unrelated repo on the machine collapses into one shared project literally named .., so observations from repo A's submodule get injected into repo B's submodule sessions. The PR's tests only cover an absolute gitdir: path, which is why this slipped through.\n\nThe concept is sound (the gitdir pointer does guarantee the parent is the superproject, and the repoRoot-bounded ancestor walk in project-name.ts is good). Fix: path.resolve(dirname(gitFilePath), gitdirPath) before matching, plus a relative-path test case. Happy to merge with that.

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.

Submodule cwd resolves to empty leaf project — "no memory yet" despite parent repo history

2 participants