From a1bf029cbf270f7772ce088ebc65435ee51ca335 Mon Sep 17 00:00:00 2001 From: Jun <39075334+mc856@users.noreply.github.com> Date: Thu, 23 Jul 2026 00:17:19 +0800 Subject: [PATCH] fix(observer): repair daemon boot (stale resolver name) and close analysis stdin (#2452) (#2464) Two continuous-learning-v2 observer regressions reported in #2452: - start-observer.sh still called _ecc_resolve_homunculus_dir, but the shared lib was renamed to _clv2_resolve_homunculus_dir (with observe.sh and detect-project.sh updated, start-observer.sh missed). Under set -e every launch dies with exit 127 at line 40 - daemon boot is broken on all platforms, not just Windows. - observer-loop.sh backgrounds the analysis claude call with stdin left open; on Git Bash/MSYS2 the child inherits it, waits, warns 'no stdin data received', and exits 1 before reading the analysis file. Close stdin with > "$LOG_FILE" 2>&1 & + -p "$prompt_content" < /dev/null >> "$LOG_FILE" 2>&1 & claude_pid=$! ( diff --git a/tests/hooks/hooks.test.js b/tests/hooks/hooks.test.js index 2830fbed3..82724226a 100644 --- a/tests/hooks/hooks.test.js +++ b/tests/hooks/hooks.test.js @@ -3175,6 +3175,40 @@ async function runTests() { passed++; else failed++; + if ( + test('observer scripts only call homunculus resolvers the shared lib defines (#2452)', () => { + const skillRoot = path.join(__dirname, '..', '..', 'skills', 'continuous-learning-v2'); + const libSource = fs.readFileSync(path.join(skillRoot, 'scripts', 'lib', 'homunculus-dir.sh'), 'utf8'); + const definedResolvers = new Set([...libSource.matchAll(/^([A-Za-z_][A-Za-z0-9_]*_resolve_homunculus_dir)\(\)/gm)].map((m) => m[1])); + assert.ok(definedResolvers.size > 0, 'homunculus-dir.sh should define a homunculus resolver function'); + + const callers = [ + ['agents', 'start-observer.sh'], + ['hooks', 'observe.sh'], + ['scripts', 'detect-project.sh'], + ['scripts', 'migrate-homunculus.sh'] + ]; + for (const rel of callers) { + const callerSource = fs.readFileSync(path.join(skillRoot, ...rel), 'utf8'); + for (const match of callerSource.matchAll(/([A-Za-z_][A-Za-z0-9_]*_resolve_homunculus_dir)\b/g)) { + assert.ok(definedResolvers.has(match[1]), `${rel.join('/')} calls ${match[1]}, which homunculus-dir.sh does not define (stale name breaks daemon boot under set -e)`); + } + } + }) + ) + passed++; + else failed++; + + if ( + test('observer-loop closes stdin on the backgrounded claude analysis call (#2452)', () => { + const observerLoopSource = fs.readFileSync(path.join(__dirname, '..', '..', 'skills', 'continuous-learning-v2', 'agents', 'observer-loop.sh'), 'utf8'); + + assert.ok(observerLoopSource.includes('-p "$prompt_content" < /dev/null'), 'observer-loop should close stdin on the backgrounded claude call so Git Bash children do not hang on inherited stdin and exit 1'); + }) + ) + passed++; + else failed++; + if (SKIP_BASH) { console.log(' ⊘ detect-project exports the resolved Python command (skipped on Windows)'); passed++;