From 9f96a89eb2540a7ff2e12c5f27690debd3e6c071 Mon Sep 17 00:00:00 2001 From: Joel Teply Date: Thu, 13 Aug 2026 12:29:12 -0500 Subject: [PATCH] fix(memory-bridge): remember.sh hung silently instead of reporting a down core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The memory tool stored NOTHING and said NOTHING, which is the worst possible combination for a system whose job is preserving corrections. Two defects in one line: if "$CONTINUUM" memory/remember ... 2>/dev/null | grep -q '"appended"...' 1. NO TIMEOUT. `memory/remember` dispatches to a core; with none answering, the CLI cold-STARTS one (a full Rust build via start-server.sh), so this blocked for minutes. Measured 2026-08-13: an agent's `remember` sat 300s and was killed by the caller (exit 143). Nothing stored, nothing reported. 2. `2>/dev/null` DISCARDED THE CAUSE. The fallback could only GUESS ("is the server up?"), throwing away the CLI's own stderr — the one diagnostic that would have named it — on every failure. Together they made the engram path look permanently broken, and it was never investigated because the failure had no voice. An agent believes its correction is durable, drops it from context, and re-learns the same lesson next session. That is precisely the failure this plugin exists to prevent, in the plugin. Now: bounded call (REMEMBER_TIMEOUT, default 20s), stderr captured and REPORTED, and the distinct outcomes named — a timeout says the core did not answer and NOTHING WAS STORED; a non-zero exit prints the CLI's actual error. Both say "nothing was stored" explicitly, because silence is what let this persist. Not a fix for the underlying cause: `continuum start` shells to a build script, so a cold core is minutes away and callers cannot tell that from a hang. That belongs with the core lifecycle work (an install-registered supervisor + a `start` that launches the INSTALLED server binary rather than compiling). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Q4NU4VNiELPQfBpCacDZGc --- .../plugins/memory-bridge/scripts/remember.sh | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/tools/plugins/memory-bridge/scripts/remember.sh b/tools/plugins/memory-bridge/scripts/remember.sh index c2a1bb89e7..d6e0b76662 100644 --- a/tools/plugins/memory-bridge/scripts/remember.sh +++ b/tools/plugins/memory-bridge/scripts/remember.sh @@ -25,9 +25,45 @@ PERSONA="$(resolve_agent_persona)" [ -n "${PERSONA:-}" ] || { echo "remember: could not resolve agent persona (airc peer id)" >&2; exit 1; } SCOPE="$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")" -if "$CONTINUUM" memory/remember --persona_id "$PERSONA" --content "$CONTENT" --scope "$SCOPE" 2>/dev/null | grep -q '"appended"\|"remembered"\|"id"'; then +# BOUND THE CALL AND KEEP THE REAL ERROR. +# +# This was `... 2>/dev/null | grep -q ...` with no timeout, and it had two +# defects that compounded into "the memory system silently does nothing": +# +# 1. NO TIMEOUT. `memory/remember` dispatches to a running core. With no core +# up, the CLI blocks indefinitely, so this script HUNG instead of failing. +# Measured 2026-08-13: an agent's `remember` sat 300s and was killed by the +# caller's timeout (exit 143). Nothing stored, nothing reported, and the +# lesson it was preserving was the one about not hand-rolling around +# governed paths. +# 2. `2>/dev/null` DISCARDED THE CAUSE. The fallback line could only GUESS +# ("is the server up?"), so the one diagnostic that would have named it — +# the CLI's own stderr — was thrown away on every failure. +# +# A memory tool that fails silently is worse than none: the agent believes the +# correction is durable, stops carrying it in context, and re-learns it next +# session. That is the whole failure mode this plugin exists to prevent. +# +# 20s is generous for a local IPC round-trip and short enough that a missing +# core is reported while the operator is still watching. +REMEMBER_TIMEOUT="${REMEMBER_TIMEOUT:-20}" +err_file="$(mktemp)" +trap 'rm -f "$err_file"' EXIT + +if out="$(timeout "$REMEMBER_TIMEOUT" "$CONTINUUM" memory/remember \ + --persona_id "$PERSONA" --content "$CONTENT" --scope "$SCOPE" 2>"$err_file")" \ + && printf '%s' "$out" | grep -q '"appended"\|"remembered"\|"id"'; then echo "remembered (scope: $SCOPE): ${CONTENT:0:80}" else - echo "remember: continuum memory/remember failed (is the server up? try: continuum ping)" >&2 + status=$? + err="$(tr -d '\r' < "$err_file" | tail -3)" + if [ "$status" -eq 124 ]; then + echo "remember: TIMED OUT after ${REMEMBER_TIMEOUT}s — the continuum core did not answer." >&2 + echo " NOTHING WAS STORED. Start the core (npm start), then re-run." >&2 + else + echo "remember: continuum memory/remember FAILED (exit $status) — NOTHING WAS STORED." >&2 + [ -n "$err" ] && echo " cause: $err" >&2 + echo " check the core is up: continuum ping" >&2 + fi exit 1 fi