mirror of
https://github.com/Jeuners/ECC.git
synced 2026-09-09 15:02:30 +02:00
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 </dev/null while keeping the -p prompt flag, preserving the Windows-compat decision from #842 instead of reverting to a stdin redirect. Adds two source invariant guards to tests/hooks/hooks.test.js: every *_resolve_homunculus_dir call site must match a function the shared lib defines, and the backgrounded claude call must close stdin. Fixes #2452
This commit is contained in:
parent
cd39df154c
commit
a1bf029cbf
2 changed files with 38 additions and 1 deletions
|
|
@ -248,13 +248,16 @@ PROMPT
|
||||||
# Pass prompt via -p flag instead of stdin redirect for Windows compatibility (#842).
|
# Pass prompt via -p flag instead of stdin redirect for Windows compatibility (#842).
|
||||||
# prompt_content is already loaded in-memory so this no longer depends on the
|
# prompt_content is already loaded in-memory so this no longer depends on the
|
||||||
# mktemp absolute path continuing to resolve after cwd changes (#1296).
|
# mktemp absolute path continuing to resolve after cwd changes (#1296).
|
||||||
|
# stdin is explicitly closed with </dev/null: on Git Bash/MSYS2 the backgrounded
|
||||||
|
# child otherwise inherits an open stdin, and claude waits on it, warns
|
||||||
|
# "no stdin data received", and exits 1 before reading the analysis file (#2452).
|
||||||
# Model is configurable via ECC_OBSERVER_MODEL (defaults to haiku for cost efficiency);
|
# Model is configurable via ECC_OBSERVER_MODEL (defaults to haiku for cost efficiency);
|
||||||
# e.g. ECC_OBSERVER_MODEL=opus for higher-quality instinct extraction. Heavier models are
|
# e.g. ECC_OBSERVER_MODEL=opus for higher-quality instinct extraction. Heavier models are
|
||||||
# slower — consider raising ECC_OBSERVER_TIMEOUT_SECONDS (default 120s) so the watchdog
|
# slower — consider raising ECC_OBSERVER_TIMEOUT_SECONDS (default 120s) so the watchdog
|
||||||
# doesn't kill the analysis mid-run.
|
# doesn't kill the analysis mid-run.
|
||||||
ECC_SKIP_OBSERVE=1 ECC_HOOK_PROFILE=minimal claude --model "${ECC_OBSERVER_MODEL:-haiku}" --max-turns "$max_turns" --print \
|
ECC_SKIP_OBSERVE=1 ECC_HOOK_PROFILE=minimal claude --model "${ECC_OBSERVER_MODEL:-haiku}" --max-turns "$max_turns" --print \
|
||||||
--allowedTools "Read,Write" \
|
--allowedTools "Read,Write" \
|
||||||
-p "$prompt_content" >> "$LOG_FILE" 2>&1 &
|
-p "$prompt_content" < /dev/null >> "$LOG_FILE" 2>&1 &
|
||||||
claude_pid=$!
|
claude_pid=$!
|
||||||
|
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -3175,6 +3175,40 @@ async function runTests() {
|
||||||
passed++;
|
passed++;
|
||||||
else failed++;
|
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) {
|
if (SKIP_BASH) {
|
||||||
console.log(' ⊘ detect-project exports the resolved Python command (skipped on Windows)');
|
console.log(' ⊘ detect-project exports the resolved Python command (skipped on Windows)');
|
||||||
passed++;
|
passed++;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue