fix(coding-agents): resolve the project when the working directory is gone (supersedes #3110) - #3410
Merged
Merged
Conversation
… gone Supersedes #3110, filed against the per-agent Claude Code plugin. A hook runs after the fact, so the directory it reports can already be deleted — an ephemeral worktree removed once the task finished, a checkout moved or deleted mid-session. git can only answer about a path that exists, so the probe failed and `basename` of the vanished path became the project identity: a throwaway name like `agent-a33c4d63` that scatters memory into orphan banks. Resolution now walks up to the nearest ancestor that still exists and probes that. This is harness-agnostic on purpose: no harness has to export anything, so it covers all eleven rather than the one that happens to publish a project-root variable. For a live directory the walk returns it unchanged, so the common path is untouched and no existing bank moves. CLAUDE_PROJECT_DIR is kept as a last rescue for the one case the walk cannot reach: a LINKED worktree is a sibling of the repository, not a child, so walking up from it leaves the repository entirely. It is deliberately a list of one rather than a guess at nine names — only Claude Code is known to export such a variable, and inventing the others would register behaviour nothing implements. Also stops project names from being empty. `basename("/")` is "", which produced bank ids like `coding-agent::` naming nothing; both `{gitProject}` and `{project}` now fall back to "unknown". That case was flagged reviewing #3286 and never fixed. Tests use real git against real directories rather than the mocked child_process of bank.test.ts, since what is under test is behaviour against paths that do and do not exist.
Generated artifact missing on main: #3394 added hindsight-docs/docs-integrations/agent-plugin.md without regenerating the docs skill, so skills/.../integrations/agent-plugin.md was never committed. verify-generated-files only runs on PRs, so the drift is invisible on main and surfaces as a failure on the next unrelated PR — this one. Separate commit because it is not part of the project-resolution fix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ports #3110's idea to the Coding Agents plugin, which supersedes the per-agent Claude Code plugin that PR targets.
The failure
A hook runs after the fact, so the directory it reports can already be deleted — an ephemeral worktree removed once its task finished, or a checkout moved mid-session.
gitcan only answer about a path that exists, so the probe fails andbasenameof the vanished path becomes the project identity: a throwaway name likeagent-a33c4d63, scattering memory into orphan banks.The fix, for every harness
@jouve's PR rescues this with
CLAUDE_PROJECT_DIR. That works, but only for the one harness that exports it — we support eleven, and only Claude Code is known to publish such a variable. Inventing names for the other ten would be registering behaviour nothing implements.So the primary mechanism here is harness-agnostic: walk up to the nearest ancestor that still exists and probe that. It needs nothing from the harness, and it covers the reported shape directly (
<repo>/.agent/worktrees/agent-xdeleted →<repo>resolves → the repository's name). For a live directory the walk returns it unchanged, so the common path is untouched and no existing bank moves.CLAUDE_PROJECT_DIRis kept as a last rescue for the one case the walk cannot reach: a linked worktree is a sibling of the repository, not a child, so walking up from it leaves the repository entirely. Deliberately a list of one, easy to extend if another harness turns out to export an equivalent.Also: no more empty project names
basename("/")is"", which produced bank ids likecoding-agent::that name nothing — flagged while reviewing #3286 and never fixed. Both{gitProject}and{project}now fall back to"unknown".Test
453 passed, 21 skipped; tsc and lint clean.
Six new cases in their own file, using real git against real directories rather than the mocked
child_processofbank.test.ts— what is under test is behaviour against paths that do and do not exist, which a mock can't tell you: the deleted leaf resolving to its repository, a live directory keeping its historical answer, the exported root rescuing a deleted linked worktree, a resolvable directory winning over that variable, no empty names, and a plain non-git directory still getting one.