You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Disclosed transparently: found by an AI agent (Claude Opus 4.8) via a high-effort, adversarial multi-agent verification workflow, and filed as AI-discovered on purpose. It's checked against your actual code — a runnable repro and a failing test are below, and the exact verification steps are in the collapsed section at the end. Please judge it on the repro.
Bug Description
In the Claude Code integration, directoryBankMap entries are matched against the session cwd with a comparison that does not resolve symlinks, so a directory and a symlink to that same directory are treated as different paths. The result: an explicit user-configured mapping silently fails to apply and session memory is routed to the wrong bank — with no error or log.
hindsight-integrations/claude-code/scripts/lib/bank.py, derive_bank_id() (~L98–100 on main):
os.path.normpath does not resolve symlinks (only os.path.realpath does). When the mapped directory and the live cwd are the same directory reached by two different path strings — the mapped dir is itself a symlink, sits under a symlinked mount (e.g. macOS /var→/private/var), or the map and cwd simply disagree on symlink-vs-real form — the entry does not match and the session falls through to the static/dynamic bank.
(realpath on the map key matters on macOS, where mkdtemp lives under the /var→/private/var symlink; without it the control case can mask the effect.)
Expected Behavior
A cwd that resolves to a mapped directory matches the mapping. directoryBankMap keys identify directories, and a directory reached via a symlink is the same directory.
Actual Behavior
Only a path string that is (normcase/normpath-)identical to the key matches; a symlinked path to the same directory falls through to the fallback bank. The miss is silent — no error, no log — so memory is written to and recalled from the wrong bank without any signal.
Version
Confirmed on main (reads as v0.8.3, June 2026); also present in 0.7.1. Independent of LLM provider (this is path handling).
Compatibility:realpath changes matching for anyone relying on the literal (unresolved) path — worth a changelog line. (Resolving is the behavior the original 0.7.1 comment described.)
Secondary, minor (same root cause)
_resolve_project_name() uses os.path.basename(cwd) on the raw cwd in the non-git fallback (and when resolveWorktrees: false), so a non-git directory reached via a symlink derives a different project/bank than its real path. Git repos are unaffected — git rev-parse --path-format=absolute --git-common-dir canonicalizes (verified). The codex/cline integrations also derive project from os.path.basename(cwd), but they have no directoryBankMap, so the primary issue above is Claude-Code-specific.
Suggested regression test
Fails on current main (symlinked cwd returns the fallback); passes with the realpath fix.
On main there is no comment-vs-code contradiction. But in 0.7.1 the inline comment on this block read "Normalize cwd for matching (resolve symlinks, trailing slashes)" — evidence the original author believed symlink resolution was happening, so the missing realpath reads as an oversight rather than a deliberate decision. (#2183 later rewrote that comment while adding Windows normcase; the "resolve symlinks" phrase was dropped, but that change was about case-folding, not a decision about symlink handling.)
How this was found and verified (AI provenance — for transparency)
Found by Claude Opus 4.8 (Anthropic) running a high-effort, adversarial multi-agent verification workflow: each substantive claim was challenged by independent reviewer passes before it was allowed into this report, specifically to guard against a hallucinated or overstated finding.
Reproduced against the real code — imported and called the actual derive_bank_id() (not a re-implementation) with a symlinked cwd; it returned the fallback bank.
Confirmed on the latest release — read the current main (v0.8.3) source; the match is still normcase(normpath(...)), no realpath.
Empirically scoped the secondary case — created a symlinked git repo and confirmed git rev-parse --git-common-dir canonicalizes, so git repos are not affected (avoiding an overclaim).
Dup-checked open + closed issues and PRs, and checked the other integrations (codex/cline/aider), scoping the report to where the feature actually lives.
Two initially-suspected stronger angles (a git-worktree regression; a cross-integration systemic pattern) were rejected during verification as overclaims — so what remains above is only what survived adversarial review. Everything is independently checkable via the repro and the cited lines.
Bug Description
In the Claude Code integration,
directoryBankMapentries are matched against the sessioncwdwith a comparison that does not resolve symlinks, so a directory and a symlink to that same directory are treated as different paths. The result: an explicit user-configured mapping silently fails to apply and session memory is routed to the wrong bank — with no error or log.hindsight-integrations/claude-code/scripts/lib/bank.py,derive_bank_id()(~L98–100 onmain):os.path.normpathdoes not resolve symlinks (onlyos.path.realpathdoes). When the mapped directory and the livecwdare the same directory reached by two different path strings — the mapped dir is itself a symlink, sits under a symlinked mount (e.g. macOS/var→/private/var), or the map and cwd simply disagree on symlink-vs-real form — the entry does not match and the session falls through to the static/dynamic bank.Steps to Reproduce
(
realpathon the map key matters on macOS, wheremkdtemplives under the/var→/private/varsymlink; without it the control case can mask the effect.)Expected Behavior
A
cwdthat resolves to a mapped directory matches the mapping.directoryBankMapkeys identify directories, and a directory reached via a symlink is the same directory.Actual Behavior
Only a path string that is (normcase/normpath-)identical to the key matches; a symlinked path to the same directory falls through to the fallback bank. The miss is silent — no error, no log — so memory is written to and recalled from the wrong bank without any signal.
Version
Confirmed on
main(reads as v0.8.3, June 2026); also present in 0.7.1. Independent of LLM provider (this is path handling).Proposed fix
Resolve both sides before comparing:
Compatibility:
realpathchanges matching for anyone relying on the literal (unresolved) path — worth a changelog line. (Resolving is the behavior the original 0.7.1 comment described.)Secondary, minor (same root cause)
_resolve_project_name()usesos.path.basename(cwd)on the raw cwd in the non-git fallback (and whenresolveWorktrees: false), so a non-git directory reached via a symlink derives a differentproject/bank than its real path. Git repos are unaffected —git rev-parse --path-format=absolute --git-common-dircanonicalizes (verified). The codex/cline integrations also deriveprojectfromos.path.basename(cwd), but they have nodirectoryBankMap, so the primary issue above is Claude-Code-specific.Suggested regression test
Fails on current
main(symlinked cwd returns the fallback); passes with therealpathfix.On intent
On
mainthere is no comment-vs-code contradiction. But in 0.7.1 the inline comment on this block read "Normalize cwd for matching (resolve symlinks, trailing slashes)" — evidence the original author believed symlink resolution was happening, so the missingrealpathreads as an oversight rather than a deliberate decision. (#2183 later rewrote that comment while adding Windowsnormcase; the "resolve symlinks" phrase was dropped, but that change was about case-folding, not a decision about symlink handling.)How this was found and verified (AI provenance — for transparency)
Found by Claude Opus 4.8 (Anthropic) running a high-effort, adversarial multi-agent verification workflow: each substantive claim was challenged by independent reviewer passes before it was allowed into this report, specifically to guard against a hallucinated or overstated finding.
derive_bank_id()(not a re-implementation) with a symlinked cwd; it returned the fallback bank.main(v0.8.3) source; the match is stillnormcase(normpath(...)), norealpath.git rev-parse --git-common-dircanonicalizes, so git repos are not affected (avoiding an overclaim).directoryBankMapwas intentionally exact-match, so this is reported as missing symlink resolution, not a false "regression."Two initially-suspected stronger angles (a git-worktree regression; a cross-integration systemic pattern) were rejected during verification as overclaims — so what remains above is only what survived adversarial review. Everything is independently checkable via the repro and the cited lines.