Skip to content

fix(BUG-023): rewrite claude-mem hook probe to materialize+break (cross-OS)#89

Merged
mlorentedev merged 1 commit into
mainfrom
fix/BUG-023-healthcheck-probe-sigpipe
May 22, 2026
Merged

fix(BUG-023): rewrite claude-mem hook probe to materialize+break (cross-OS)#89
mlorentedev merged 1 commit into
mainfrom
fix/BUG-023-healthcheck-probe-sigpipe

Conversation

@mlorentedev
Copy link
Copy Markdown
Owner

Summary

BUG-022 (PR #87) appended head -n1 after the while-loop in the BUG-015 hook-resolution probe, but the form still raced under set -euo pipefail when 2+ candidates matched in ~/.claude/plugins/cache/thedotmack/claude-mem/: the consumer closed after the first line, leftover printfs in the while loop got EPIPE, pipefail propagated 141 to the $(...) and set -e killed healthcheck.sh mid-section 4/12. setup-linux.sh then logged a false-positive WARNING: healthcheck reported one or more FAIL items.

Race-free pattern: materialize candidates into a variable first (no pipe), then iterate in pure bash with break — no consumer-close, no EPIPE, no pipefail propagation. Supersedes BUG-022.

Cross-OS parity: healthcheck.ps1's bash sub-invocation never observed the symptom (its bash -c context doesn't set pipefail), but the race-free form keeps both probes structurally identical. Applies the recently-captured lesson "the detection probe MUST use the race-free pattern, not faithfully reproduce broken upstream behaviour".

Empirical evidence

This user has 6 versions of claude-mem in cache (12.7.413.3.0), so the race fires deterministically on every setup run. Post-fix healthcheck.sh completes all 12/12 sections; probe resolves to 13.3.0.

$ ls ~/.claude/plugins/cache/thedotmack/claude-mem/
12.7.4  13.0.0  13.0.1  13.1.0  13.2.0  13.3.0

# Before:
$ ./scripts/healthcheck.sh; echo "EXIT=$?"
...
[4/12] Key Symlinks
  PASS: claude-mem@thedotmack installed (BUG-014 canonical check)
EXIT=141                                       # SIGPIPE; sections 5-12 never ran

# After:
$ ./scripts/healthcheck.sh | grep BUG-015
  PASS: claude-mem hook path resolves to: /home/manu/.claude/plugins/cache/thedotmack/claude-mem/13.3.0 (BUG-015)
# Healthcheck now completes all 12/12 sections.

SDD skip rationale

Atomic fix to an existing safety-net probe; production diff ~42 LOC (under the 50 LOC spec-gate threshold). Same shape + precedent as PR #87 (BUG-022) and PR #79 (WIN-001b). No public-contract surface touched (probe is an internal helper inside healthcheck.{sh,ps1}). Lesson capture goes to vault post-merge.

Test plan

  • shellcheck scripts/healthcheck.sh clean
  • bash -n scripts/healthcheck.sh clean
  • bats tests/setup-linux.bats --filter "BUG-023|BUG-015" → 3/3 PASS
  • bats tests/ → 765/765 PASS, 0 fail (no regressions)
  • Live healthcheck.sh run completes all 12/12 sections, exit no longer 141
  • Probe resolves to expected path (13.3.0, latest cache version)
  • 2 new parity asserts lock both positive (new pattern present) AND negative (pipe-to-head absent) in both .sh and .ps1

Cross-OS validation

  • Linux (this PR author's primary): empirically validated, SIGPIPE eliminated.
  • Windows: PS1 change is comment + bash one-liner rewrite to mirror Linux form. Symptom never observed there (no pipefail in bash -c sub-context), so this is parity-only. Empirical Windows validation tracked in WIN-002-windows-smoke-sweep spec (full clean-VM sweep).

…ss-OS)

BUG-022 fixed the BUG-015 probe by appending `head -n1` after the while
loop, but the form still raced under `set -euo pipefail` when 2+
candidates matched in `~/.claude/plugins/cache/thedotmack/claude-mem/`:
the consumer closed after the first line, leftover printfs in the while
loop got EPIPE, pipefail propagated 141 to the $() and `set -e` killed
healthcheck.sh mid-section 4/12. setup-linux.sh logged the resulting
exit as a false-positive `WARNING: healthcheck reported one or more
FAIL items`.

Race-free pattern: materialize all candidates into a variable first
(no pipe), then iterate in pure bash with `break` -- no consumer-close,
no EPIPE, no pipefail propagation. Supersedes BUG-022.

Cross-OS parity: the PS1 bash sub-invocation never observed the
symptom (its `bash -c` context doesn't set pipefail), but the
race-free form keeps both probes structurally identical. Applies the
recently-captured lesson "the detection probe MUST use the race-free
pattern, not faithfully reproduce broken upstream behaviour".

Empirical: this user has 6 versions in cache (12.7.4 -> 13.3.0), so
the race fires deterministically on every setup run. Post-fix
healthcheck completes all 12/12 sections; probe resolves to 13.3.0.

Tests: 2 new bats parity asserts in tests/setup-linux.bats lock both
the new materialize+break pattern (positive grep) and the absence of
the pipe-to-head form (negative grep) in BOTH healthcheck.sh and
healthcheck.ps1, so a future "simplification" can't regress us back.
765/765 bats pass.
@mlorentedev mlorentedev merged commit c7096dc into main May 22, 2026
6 checks passed
@mlorentedev mlorentedev deleted the fix/BUG-023-healthcheck-probe-sigpipe branch May 22, 2026 01:02
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.

1 participant