fix(observer): repair daemon boot (stale resolver name) and close analysis stdin (#2452)#2464
fix(observer): repair daemon boot (stale resolver name) and close analysis stdin (#2452)#2464mc856 wants to merge 1 commit into
Conversation
…lysis stdin (affaan-m#2452) Two continuous-learning-v2 observer regressions reported in affaan-m#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 affaan-m#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 affaan-m#2452
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📜 Recent review details⏰ Context from checks skipped due to timeout. (28)
🧰 Additional context used📓 Path-based instructions (16)**/*.sh📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
skills/**📄 CodeRabbit inference engine (AGENTS.md)
Files:
{skills,commands,agents,rules}/**⚙️ CodeRabbit configuration file
Files:
**/*.{js,ts,jsx,tsx,py,java,cs,go,rb,php,scala,kt}📄 CodeRabbit inference engine (.cursor/rules/common-coding-style.md)
Files:
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,swift,kt,rs,c,cpp,h,hpp}📄 CodeRabbit inference engine (.cursor/rules/common-security.md)
Files:
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php}📄 CodeRabbit inference engine (.cursor/rules/common-security.md)
Files:
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,sql}📄 CodeRabbit inference engine (.cursor/rules/common-security.md)
Files:
**/*.{js,ts,jsx,tsx,html,php,java,cs,rb,go}📄 CodeRabbit inference engine (.cursor/rules/common-security.md)
Files:
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,swift,kt,rs,c,cpp,h,hpp,properties,yml,yaml,json,env,config}📄 CodeRabbit inference engine (.cursor/rules/common-security.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (.cursor/rules/typescript-coding-style.md)
Files:
**/*.{test,spec}.{js,ts,jsx,tsx}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
**/*.{js,ts,jsx,tsx}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
**/*.{js,ts,jsx,tsx,json,env*}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
**/*.{js,ts}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
**/*.{jsx,tsx,js,ts}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
**/*.{js,ts,env*}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
🧠 Learnings (1)📚 Learning: 2026-06-27T23:49:19.839ZApplied to files:
🪛 ast-grep (0.44.1)tests/hooks/hooks.test.js[warning] 3173-3173: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use. (detect-non-literal-fs-filename) [warning] 3184-3184: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use. (detect-non-literal-fs-filename) [warning] 3196-3196: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use. (detect-non-literal-fs-filename) 🔇 Additional comments (4)
📝 WalkthroughSummary by CodeRabbit
WalkthroughFixes two Windows/Git Bash regressions in the continuous-learning-v2 observer: start-observer.sh now calls the correct ChangesObserver daemon and analysis fixes
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
| Filename | Overview |
|---|---|
| skills/continuous-learning-v2/agents/start-observer.sh | Single-line fix: renames stale _ecc_resolve_homunculus_dir call to _clv2_resolve_homunculus_dir, matching the actual function name in homunculus-dir.sh and eliminating the exit-127 boot failure under set -e. |
| skills/continuous-learning-v2/agents/observer-loop.sh | Adds < /dev/null to the backgrounded claude invocation to prevent Git Bash/MSYS2 from leaving the child process waiting on inherited stdin; change is behavior-neutral on POSIX systems. |
| tests/hooks/hooks.test.js | Adds two regression-guard tests: one verifying resolver-name consistency between caller scripts and homunculus-dir.sh, and one checking that observer-loop.sh closes stdin; the stdin test uses a literal substring match that could miss semantically equivalent reformulations. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["start-observer.sh\n(set -e)"] --> B["source homunculus-dir.sh"]
B --> C["_clv2_resolve_homunculus_dir()\n✅ fixed: was _ecc_resolve_homunculus_dir"]
C --> D["CONFIG_DIR resolved"]
D --> E["Spawn observer-loop.sh &"]
E --> F["analyze_observations()"]
F --> G["Build prompt_content in-memory"]
G --> H["claude -p prompt_content\n< /dev/null >> LOG 2>&1 &\n✅ fixed: stdin closed"]
H --> I["claude_pid captured"]
I --> J["Watchdog subshell\nsleep timeout_seconds &"]
J --> K["wait_for_claude_analysis claude_pid"]
K --> L{"exit_code == 0?"}
L -->|Yes| M["Archive observations.jsonl"]
L -->|No| N["Retain for retry"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["start-observer.sh\n(set -e)"] --> B["source homunculus-dir.sh"]
B --> C["_clv2_resolve_homunculus_dir()\n✅ fixed: was _ecc_resolve_homunculus_dir"]
C --> D["CONFIG_DIR resolved"]
D --> E["Spawn observer-loop.sh &"]
E --> F["analyze_observations()"]
F --> G["Build prompt_content in-memory"]
G --> H["claude -p prompt_content\n< /dev/null >> LOG 2>&1 &\n✅ fixed: stdin closed"]
H --> I["claude_pid captured"]
I --> J["Watchdog subshell\nsleep timeout_seconds &"]
J --> K["wait_for_claude_analysis claude_pid"]
K --> L{"exit_code == 0?"}
L -->|Yes| M["Archive observations.jsonl"]
L -->|No| N["Retain for retry"]
Reviews (1): Last reviewed commit: "fix(observer): repair daemon boot (stale..." | Re-trigger Greptile
| 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'); |
There was a problem hiding this comment.
The test guards against removing
< /dev/null, but it checks for a very specific token order (-p "$prompt_content" < /dev/null on a single line). A semantically equivalent reformulation — e.g. moving the redirect before the env-var prefix, or splitting the line differently — would cause this test to fail even though the guard is actually in place. Consider anchoring only on the presence of < /dev/null in the same command block rather than on exact positional text.
| 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'); | |
| // Match the backgrounded claude line and assert it redirects stdin from /dev/null. | |
| // Anchoring on the whole command block rather than a positional substring keeps the | |
| // guard robust to minor reformatting while still catching accidental removal. | |
| const bgClaudeLineMatch = observerLoopSource.match(/-p "\$prompt_content"[^\n]*&/); | |
| assert.ok(bgClaudeLineMatch, 'observer-loop should have the backgrounded claude call with -p flag'); | |
| assert.ok(bgClaudeLineMatch[0].includes('< /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'); |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
daltino
left a comment
There was a problem hiding this comment.
This PR resolves critical issues in the observer scripts, including fixing a stale resolver name and properly closing stdin to prevent analysis errors. The changes look minimal yet impactful, adding robustness to daemon boot and maintaining compatibility with shared lib-defined resolvers. Additionally, the comprehensive new test ensures future reliability. Nice work!
Summary
start-observer.sh:40still called_ecc_resolve_homunculus_dir, but the shared lib was renamed to_clv2_resolve_homunculus_dir(observe.sh, detect-project.sh, and migrate-homunculus.sh were updated; start-observer.sh was missed) — underset -eevery launch dies with exit 127, so this breaks daemon boot everywhere, not only on Windows< /dev/null) on the backgrounded analysisclaudecall inobserver-loop.sh: on Git Bash/MSYS2 the child inherits an open stdin, waits on it, warns "no stdin data received", and exits 1 before reading the analysis file; this is the only backgrounded claude call site in the repo-pprompt flag rather than reverting to a stdin redirect, preserving the Windows-compat decision from [Windows] Observer Haiku agent cannot read temp files and never writes instinct files #842 (the fix suggested in continuous-learning-v2 observer broken on Windows/Git Bash: daemon boot + analysis both fail (two regressions) #2452 would undo it)tests/hooks/hooks.test.js: every*_resolve_homunculus_dircall site must match a function the shared lib defines, and the backgrounded claude call must close stdinVerification
start-observer.sh statusdies withline 40: _ecc_resolve_homunculus_dir: command not found, exit 127CLV2_HOMUNCULUS_DIRnode tests/run-all.json node 22: 2973 passed, 0 failedFixes #2452