fix(claude-mem-heal): create legacy marketplace junction/symlink (BUG-012)#70
Merged
Merged
Conversation
…-012)
The thedotmack/claude-mem plugin's bundled hooks.json hardcodes the fallback
discovery path `marketplaces/thedotmack/plugin/scripts/...` (the marketplace's
declared `name`), but Claude Code clones the marketplace under the GitHub repo
name `marketplaces/thedotmack-claude-mem/`. When CLAUDE_PLUGIN_ROOT is unset or
stale (the typical Windows case where the plugin cache stays unpopulated),
plugin hooks fall through to that path and fail with `claude-mem: plugin
scripts not found` -> exit 1, blocking UserPromptSubmit. On Windows the
visible symptom is `printf: write error: Permission denied` (Git Bash + hook
subprocess sandbox quirk); on Linux it would fail more cleanly with the
not-found message.
Same root cause silently neuters scripts/claude-mem-heal.{sh,ps1}: both
hardcoded `marketplaces/thedotmack/plugin` -> dir absent -> heal no-ops.
Upstream .mcp.json / missing-zod patches never reach the actual install.
Fix (both heal scripts):
- New ensure_marketplace_compat_symlink() / Repair-MarketplaceCompatJunction
function. Creates symlink (Linux) / Junction (Windows, no admin) from
`marketplaces/thedotmack` -> `marketplaces/thedotmack-claude-mem` when the
source exists and the target is absent. Idempotent: skip otherwise.
- Extend marketplace healing walks to cover both legacy and current path
variants (path-aware backstop in case junction creation fails).
- Bats parity assertions in tests/setup-{linux,windows}.bats lock the
junction-creation block in both heal scripts.
Empirically validated on Windows: junction created on first run (LinkType
= Junction, Target = thedotmack-claude-mem), silent on second run (exit 0,
no output) -- contract preserved. Linux side: bash -n + manual review only;
no Linux test machine available this session.
Specs: specs/BUG-012-claude-mem-marketplace-junction/{proposal,tasks,verification}.md
Discovered during BUG-011 hook-failure diagnosis. Sibling of PR #69.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
Two failures in CI test job for PR #70: 1. `grep -qF '-ItemType Junction'` -> GNU grep parses `-ItemType` as flag set before checking the pattern. Fix: add `--` separator before the pattern. Same convention used by the existing `--ignore-scripts` assert above. 2. `grep -B2 -A6 'ln -s.*thedotmack-claude-mem'` -> the literal `ln -s` line in claude-mem-heal.sh is `ln -s "$actual" "$legacy"` (no marketplace name on that line; the name is in the earlier variable assignment). The regex never matched, so `-B2 -A6` returned empty and the piped grep failed. Rewrite to grep the actual guard expressions: `[ ! -d "$actual" ]` (source-exists check) and `[ -e "$legacy" ]` (target-absent check) -- both required for the idempotence contract. All four patterns verified locally against the heal scripts. Co-Authored-By: Claude Opus 4.7 <[email protected]>
mlorentedev
added a commit
that referenced
this pull request
May 21, 2026
… pattern (cross-OS) (#83) User encountered `/mcp Failed to reconnect to plugin:claude-mem:mcp-search: -32000` in this session AFTER BUG-014 (PR #75) restored the install AND BUG-015 (PR #81) shipped the detection layer. Root cause traced to claude-mem-heal silently no-oping against v13.3.0 installs. scripts/claude-mem-heal.{sh,ps1}::Repair-McpJson was authored in PR #57 against v12.7.4's broken pattern (literal `${_R%/}`). v13.0.0+ ships a different broken pattern: `sh -c` with cascading-printf pipe that triggers the same EPIPE race documented in thedotmack/claude-mem#2607. The original detection regex (`${_R%/}`) doesn't match v13.x at all -> heal exits silent, leaving the .mcp.json broken -> MCP server fails to start -> -32000. Changes: 1. scripts/claude-mem-heal.sh::heal_mcp_json: - Detection extended: triggers on EITHER v12.7.4 `${_R%/}` OR v13.x signature (`"sh".*"-c"` OR `while IFS= read`). - Replacement template: canonical race-free form using `done | head -n1` instead of `done` with inner `break`. This consumes the entire producer pipe (no leftover writes -> no EPIPE -- Option A from upstream issue #2607 applied locally). - Log message distinguishes which signature was patched. 2. scripts/claude-mem-heal.ps1::Repair-McpJson: equivalent on Windows. 3. tests/setup-linux.bats: 3 new asserts: - both heal scripts grep `while IFS=` (v13.x signature) - both heal scripts contain `head -n1` (race-free template) - both reference BUG-016 + claude-mem#2607 Empirical validation on user's daily-driver Windows during implementation: 3 affected .mcp.json files (cache 13.3.0 + marketplace junction + marketplace canonical) patched on first heal run (exit 0). Second run silent (idempotent -- no v12/v13 signature left to detect). Spec at specs/BUG-016-claude-mem-heal-v13-refresh/. Diff: +63 prod (heal scripts) + 28 test = 91 LOC. Production diff at spec-gate threshold; spec folder ships per discipline. Lesson candidate (post-merge): "heal scripts must be versioned against the upstream bug class they paper over; when upstream's bug pattern changes, the heal's detection regex MUST be refreshed in the same PR that discovers the new pattern." Companion to BUG-015 (PR #81 detection layer) and BUG-012 (PR #70 junction fix). Pairs with upstream issue thedotmack/claude-mem#2607 where Option A `head -n1` is recommended as the canonical upstream fix for the same pipe race.
This was referenced May 21, 2026
mlorentedev
added a commit
that referenced
this pull request
May 21, 2026
Move from specs/ to specs/archive/ per SDD lifecycle close (the folder move IS the archive marker; status: archived frontmatter update deferred to per-spec follow-up if needed). This session shipped (today, 2026-05-21): - AI-014-opencode-windows-bootstrap (PR #78) - BUG-014-claude-mem-marketplace-register (PR #75) - BUG-016-claude-mem-heal-v13-refresh (PR #83) - BUG-017-claude-mem-heal-hooks-json-race (PR #84) - BUG-018-userpromptsubmit-continue-directive (PR #85) - REFACTOR-003-diff-check-ps1 (PR #82) Catch-up archive (merged earlier weeks but specs/ folder lingered): - BUG-007-remove-github-plugin-broken (PR #65, 2026-05-19) - BUG-011-mcp-loop-claude-json-guard (PR #69, 2026-05-20) - BUG-012-claude-mem-marketplace-junction (PR #70, 2026-05-20) - SDD-005-github-copilot-instructions-sync (PR #62, 2026-05-19) - SDD-006-vault-integrity-check (PR #63, 2026-05-19) Active specs remaining in specs/ (not yet merged): - REFACTOR-002-paths-in-env-contract (queued, still draft) - WIN-002-windows-smoke-sweep (partial closure via PR #73, full clean-VM sweep still open) 33 file moves total (3 files per spec × 11 specs). Zero content change.
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.
Summary
thedotmack/claude-memplugin's bundledhooks.jsonhardcodes the fallback discovery pathmarketplaces/thedotmack/plugin/scripts/...(the marketplace's declaredname), but Claude Code clones the marketplace under the GitHub repo namemarketplaces/thedotmack-claude-mem/. WhenCLAUDE_PLUGIN_ROOTis unset/stale (typical Windows case — plugin cache stays unpopulated), plugin hooks hit the broken fallback,exit 1, and block UserPromptSubmit.scripts/claude-mem-heal.{sh,ps1}— both hardcodedmarketplaces/thedotmack/plugin, no-op'd on dir-absent, never applied upstream.mcp.json/ missing-zod patches to the actual install.marketplaces/thedotmack→marketplaces/thedotmack-claude-memwhen the source exists and target is absent. Heal walks extended to cover both legacy and current marketplace paths.Why this bug only surfaced on Windows
On Linux, Claude Code typically injects
CLAUDE_PLUGIN_ROOTcorrectly at hook invocation, so the broken fallback is never consulted. On Windows the plugin cache (cache/thedotmack/claude-mem/) stays empty, the env var stays unset or stale, and the fallback IS consulted — landing on the non-existentmarketplaces/thedotmack/plugin/. The visible Windows symptom (printf: write error: Permission denied) is Git Bash + Claude Code's hook subprocess sandbox quirk; the underlyingexit 1is cross-OS. Fix applied to both heal scripts for parity.Empirical validation (Windows)
Second run is silent (exit 0, no output) —
silent-on-healthycontract preserved.Test plan
tests/setup-linux.bats+ 2 intests/setup-windows.bats.shellcheck --severity=error scripts/claude-mem-heal.shclean.pwsh -Command "Invoke-ScriptAnalyzer -Path scripts/claude-mem-heal.ps1 -Severity Error"clean (locally: clean).bash -n scripts/claude-mem-heal.shclean (locally: clean).claude-mem-heal.ps1(locally: clean).Discovered during
BUG-011 hook-failure diagnosis (PR #69). Sibling fix — same plugin, different layer.
Spec
specs/BUG-012-claude-mem-marketplace-junction/{proposal,tasks,verification}.md