fix(hooks): harden memory/hook helpers β real timeout, signal cleanup, truncation transparency, cross-platform slug#2389
Open
tjaiyen wants to merge 3 commits into
Open
fix(hooks): harden memory/hook helpers β real timeout, signal cleanup, truncation transparency, cross-platform slug#2389tjaiyen wants to merge 3 commits into
tjaiyen wants to merge 3 commits into
Conversation
β¦ cross-platform slug)
FIX 1 β runWithTimeout was inert: it called fn() then clearTimeout()
immediately, so an async callee's hang was never caught (the timer was
cleared before the pending promise settled). Reimplement as a real
Promise.race between the work and the timeout. Document that a synchronous
blocking callee cannot be preempted in-process (the real guard is the
file-size cap in intelligence.cjs). Applied to both hook-handler.cjs copies.
FIX 2 β auto-memory-hook.mjs swallowed ALL unhandled rejections process-wide
via `() => {}`. Keep hooks exit-0 but log the reason under RUFLO_DEBUG/DEBUG
so genuine async bugs are visible.
FIX 3 β no SIGTERM/SIGINT cleanup in the db-touching helpers. Track the active
backend and flush it (JSON persist / SQLite close + WAL flush) on signal,
avoiding half-written stores and stale agentdb.rvf.lock.
FIX 4 β silent value truncation (intelligence.cjs 500/100 chars). Add clip():
appends an ellipsis and warns under debug when it actually cuts.
FIX 5 β projectSlug only handled POSIX '/', so on Windows it never matched
Claude Code's real ~/.claude/projects/<slug> dir and memory bootstrap silently
found nothing. Slugify every non-alphanumeric to '-' to match Claude's
convention (verified: G:\My Drive\...\ruflo-fix -> G--My-Drive-...-ruflo-fix).
Also: export runWithTimeout behind a require.main guard so it is unit-testable,
and add tests/hook-handler-runwithtimeout.test.cjs (node:test, 5 cases incl.
the decisive slow-async timeout case).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apply the analogous fixes to the package-distributed copies under v3/@claude-flow/cli/.claude/helpers so `ruflo init` in new projects ships the fixed behaviour: - intelligence.cjs: cross-platform projectSlug (FIX 5) + clip() truncation transparency on memory-content (FIX 4). - auto-memory-hook.mjs: scoped/debug-gated unhandledRejection (FIX 2) + SIGTERM/SIGINT backend flush (FIX 3). FIX 1 was already applied to this copy's hook-handler.cjs in the previous commit. context-persistence-hook.mjs has only one (root) copy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code syncs via this fork; durable memory (MEMORY.md / Obsidian vault) syncs via Google Drive; the .claude-flow local cache is rebuilt per device from that Markdown (works on Windows now via FIX 5). No servers required. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
Five hardening fixes to the
.claude/helpershook/memory scripts, all reproduced against currentmain. Each is small, isolated, and verified with Node's built-in test runner (no new deps).Fixes
runWithTimeout()never enforced its timeout (hook-handler.cjs). It calledfn()thenclearTimeout(timer)immediately, so an asyncfnreturned a pending promise that resolved through the race β the timeout protected nothing (the intelligence-init guard for Hooks causing ~20s latency on every Claude Code CLI interactionΒ #1530/[BUG] Intelligence hooks cause indefinite hang β PageRank on 150MB JSON blocks every CLI interactionΒ #1531 was inert). Reimplemented as a realPromise.racebetween the work and the timeout. Documented that a synchronous blocking callee can't be preempted in-process (the real guard there is the file-size cap inintelligence.cjs). Applied to bothhook-handler.cjscopies (root +v3/@claude-flow/cli).Global silent swallow of unhandled rejections (
auto-memory-hook.mjs).process.on('unhandledRejection', () => {})hid all async failures process-wide. Kept exit-0 behaviour but log the reason underRUFLO_DEBUG/DEBUGso genuine bugs are visible.No SIGTERM/SIGINT cleanup in db-touching helpers (
auto-memory-hook.mjs,context-persistence-hook.mjs). A signal mid-write skippedbackend.shutdown(), risking an unflushed SQLite WAL / staleagentdb.rvf.lock. Track the active backend and flush it on signal.Silent value truncation (
intelligence.cjs). Memory content was cut at 500 chars with no signal. Added aclip()helper that appends an ellipsis and warns under debug when it actually truncates.Cross-platform
projectSlugbug (intelligence.cjs). The slug usedcwd().replace(/^\//,'').replace(/\//g,'-')β POSIX-only. On Windows it kept:and\, so it never matched Claude Code's real~/.claude/projects/<slug>dir and memory bootstrap silently found nothing. Now slugifies every non-alphanumeric to-, matching Claude Code's actual convention (verified:G:\My Drive\...\ruflo-fix->G--My-Drive-...-ruflo-fix).Testing
node --test tests/hook-handler-runwithtimeout.test.cjsβ 5/5, including the decisive case: a slow async fn now resolvesnullat ~3s (old code returned the late value at ~5s).node scripts/smoke-pre-bash-hook.mjsβ passes for both dispatcher copies (no regression).node --checkon all 8 touched files.π€ Generated with Claude Code