Skip to content

fix: background observer fails closed on confirmation/permission prompts (#400)#403

Merged
affaan-m merged 4 commits into
affaan-m:mainfrom
swarnika-cmd:main
Mar 13, 2026
Merged

fix: background observer fails closed on confirmation/permission prompts (#400)#403
affaan-m merged 4 commits into
affaan-m:mainfrom
swarnika-cmd:main

Conversation

@swarnika-cmd

@swarnika-cmd swarnika-cmd commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

Problem

Fixes #400

The background observer does not fail closed when it encounters confirmation/permission flows. Instead it:

  • Emits prompts like "Can you confirm I should proceed?"
  • Hits the 120s timeout
  • Retries repeatedly, consuming usage
  • Continues spawning sessions even after the usage cap is exhausted

Changes

skills/continuous-learning-v2/hooks/observe.sh

  • Move SENTINEL_FILE definition to after detect-project.sh so PROJECT_DIR is set
  • Add circuit-breaker: skip run immediately if sentinel file exists from a previous abort
  • Detect confirmation/permission language in parsed observer output and exit 2
  • Fix Windows Git Bash Python detection to bypass the Microsoft Store alias

skills/continuous-learning-v2/agents/start-observer.sh

  • Redirect observer output to a temp log on startup
  • Check temp log for confirmation/permission language immediately after observer starts
  • Fail closed with exit 2 if detected, preventing the timeout/retry loop

Testing

  • Ran observe.sh with simulated tool input — exits 0, writes clean observation to log
  • Confirmed sentinel file blocks subsequent runs
  • Confirmed confirmation-language detection fires correctly on mock output

Summary by cubic

Make the background observer fail closed on confirmation/permission prompts to stop timeouts, retries, and wasted usage. Adds a shared prompt guard, a sentinel-based circuit breaker, early log scanning at startup, and a Windows Git Bash Python fix.

  • Bug Fixes
    • Detect confirmation/permission language via shared CLV2_OBSERVER_PROMPT_PATTERN; exit 2 and write .observer.lock in the project root (use start-observer.sh --reset to clear).
    • Export CLV2_OBSERVER_PROMPT_PATTERN and CLV2_OBSERVER_SENTINEL_FILE from detect-project.sh so observe.sh and start-observer.sh share the same config.
    • Skip runs when the sentinel exists (circuit breaker) and avoid false positives on generic “Awaiting …” messages.
    • On startup, append to observer.log, scan newly written lines immediately for prompt language, and fail closed before continuing.
    • Fix Python resolution in Windows Git Bash by probing known install paths instead of the Microsoft Store alias.

Written for commit 1c1a9ef. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Adds a sentinel-based guard to detect/record confirmation prompts, start/stop/status controls, and a --reset option; exports a shared prompt-pattern and sentinel-file env vars.
  • Bug Fixes / Stability

    • Observers now validate startup, avoid duplicate instances, notify running observers, and abort cleanly on prompt detection; improved Windows Python detection and safer secret scrubbing.
  • Tests

    • New integration tests covering observer guard, reset behavior, prompt detection, lock handling, and secret-redaction.

…-m#400)

- Redirect observer output to temp log before appending to main log
- Check temp log for confirmation/permission language immediately after start
- Fail closed with exit 2 if detected, preventing retry loops
@coderabbitai

coderabbitai Bot commented Mar 12, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds sentinel-based guard and prompt-detection to the continuous-learning-v2 observer: new sentinel file handling, CLI --reset/status/stop controls, prompt-pattern scanning on start, observer PID/USR1 signaling, Windows Python detection, secret-scrubbing improvements, and tests for sentinel/guard behavior.

Changes

Cohort / File(s) Summary
Observer start script
skills/continuous-learning-v2/agents/start-observer.sh
Adds CLI parsing for `start
Observer hook / loop
skills/continuous-learning-v2/hooks/observe.sh
Adds sentinel (observer.lock) support and write_guard_sentinel; Windows Git Bash Python detection; early-circuit when sentinel present; prompt detection that writes sentinel and exits with code 2; improved observation construction and secret-scrubbing; sends USR1 to active pid files on sentinel events.
Project detection exports
skills/continuous-learning-v2/scripts/detect-project.sh
Exports CLV2_OBSERVER_PROMPT_PATTERN and CLV2_OBSERVER_SENTINEL_FILE (derived from PROJECT_ROOT/PROJECT_DIR), making prompt pattern and sentinel file available to observer scripts.
Tests
tests/hooks/hooks.test.js
Adds tests covering prompt-pattern export, start-observer --reset, sentinel creation and contents, observer lock behavior, fallbacks for null tool responses, and end-to-end script behaviors around prompts and lock handling.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant CLI as Start CLI
participant Start as start-observer.sh
participant Log as Temp Log File
participant Observer as observer-loop (nohup)
participant Hook as observe.sh
participant FS as Filesystem (sentinel, pid, logs)
CLI->>Start: start (or stop/status) with flags
Start->>FS: check PID file, create log, capture start_line
Start->>Observer: launch (nohup) with env including CLV2_OBSERVER_PROMPT_PATTERN
Observer->>Log: append output
Start->>Log: tail from start_line looking for prompt pattern
Log-->>Start: prompt detected
Start->>Observer: stop (kill PID)
Start->>FS: write sentinel file with scrubbed message
Hook->>FS: observe.sh sees sentinel -> exit/skip processing
Hook->>Observer: send USR1 to pid files when writing sentinel

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I sniffed the logs by moonlit light,
A sentinel stands to halt the night.
No pleading prompts shall loop and roam,
I tuck the observer safe at home.
Hooray — no retries, just peaceful code 🌙

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main objective: making the background observer fail closed on confirmation/permission prompts, which directly addresses issue #400.
Linked Issues check ✅ Passed All coding requirements from issue #400 are addressed: detect confirmation/permission language in observer output [observe.sh, start-observer.sh], implement sentinel-based circuit breaker [observe.sh, detect-project.sh], and fix Windows Python detection [observe.sh].
Out of Scope Changes check ✅ Passed All changes directly support the issue #400 objectives: sentinel guards, prompt detection, Windows Python fixes, and comprehensive testing of these features. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
skills/continuous-learning-v2/agents/start-observer.sh (1)

156-171: ⚠️ Potential issue | 🟡 Minor

Temp log file not cleaned up on successful start.

OBSERVER_LOG_TMP is only removed on abort (line 180). On successful startup, the temp file persists indefinitely and output continues writing to it instead of LOG_FILE. Either merge the temp log into the main log and delete it, or redirect subsequent output to LOG_FILE after the check passes.

🔧 Proposed fix: Clean up temp log on success

Add after line 182 (before the PID file check):

     fi
+
+    # Merge temp log into main log and clean up
+    if [ -f "$OBSERVER_LOG_TMP" ]; then
+      cat "$OBSERVER_LOG_TMP" >> "$LOG_FILE"
+      rm -f "$OBSERVER_LOG_TMP"
+    fi

     if [ -f "$PID_FILE" ]; then
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/continuous-learning-v2/agents/start-observer.sh` around lines 156 -
171, The temp log OBSERVER_LOG_TMP created before launching OBSERVER_LOOP_SCRIPT
is left behind and continues receiving output; after verifying the background
process started (the existing PID_FILE/PID check), append or move the contents
of OBSERVER_LOG_TMP into the intended LOG_FILE, remove OBSERVER_LOG_TMP, and
ensure future output goes to LOG_FILE (i.e., update redirection so nohup's
stdout/stderr ultimately target LOG_FILE or rotate the file and reopen
descriptors). Locate the startup block using OBSERVER_LOG_TMP,
OBSERVER_LOOP_SCRIPT, LOG_FILE and PID_FILE and add the merge/delete and
redirection fix immediately after the startup check so no temp file persists.
🧹 Nitpick comments (2)
skills/continuous-learning-v2/hooks/observe.sh (1)

36-46: Windows Python paths are limited to specific versions.

The hardcoded paths only check Python 3.10, 3.11, and 3.12. Consider using a glob pattern or loop to discover Python installations more dynamically, which would handle future Python versions (e.g., 3.13+) without code changes.

💡 Optional: Use glob pattern for more flexible detection
-  for win_py in \
-    "/c/Users/$USER/AppData/Local/Programs/Python/Python311/python" \
-    "/c/Users/$USER/AppData/Local/Programs/Python/Python312/python" \
-    "/c/Users/$USER/AppData/Local/Programs/Python/Python310/python"; do
-    if [ -x "$win_py" ]; then
-      printf '%s\n' "$win_py"
-      return 0
-    fi
-  done
+  # Check Windows Python install paths with glob pattern
+  for win_py in /c/Users/"$USER"/AppData/Local/Programs/Python/Python3*/python; do
+    if [ -x "$win_py" ]; then
+      printf '%s\n' "$win_py"
+      return 0
+    fi
+  done
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/continuous-learning-v2/hooks/observe.sh` around lines 36 - 46, Replace
the hardcoded version-specific list in the Windows-check loop with a glob-based
discovery so future Python versions are found automatically: modify the loop
that iterates over win_py (currently enumerating Python310/311/312) to iterate
over the pattern "/c/Users/$USER/AppData/Local/Programs/Python/Python*/python"
(or similar glob) and keep the existing -x check and printf/return behavior in
the same loop in observe.sh so any matching Python executable is picked up
without updating versions.
skills/continuous-learning-v2/agents/start-observer.sh (1)

131-147: No recovery path from circuit-breaker state.

Once observe.sh writes the sentinel file (.observer.lock), the observer is permanently disabled. The start command doesn't check for or offer to remove the sentinel file, requiring users to manually delete it. Consider adding a --reset flag or checking the sentinel on explicit start.

💡 Suggested: Add sentinel check and optional reset
   start)
     # Check if observer is disabled in config
     if [ "$OBSERVER_ENABLED" != "true" ]; then
       echo "Observer is disabled in config.json (observer.enabled: false)."
       echo "Set observer.enabled to true in config.json to enable."
       exit 1
     fi

+    # Check for circuit-breaker sentinel
+    SENTINEL_FILE="${PROJECT_DIR}/.observer.lock"
+    if [ -f "$SENTINEL_FILE" ]; then
+      echo "Observer was previously disabled due to confirmation/permission prompt."
+      echo "Sentinel file: $SENTINEL_FILE"
+      if [ "${2:-}" = "--reset" ]; then
+        echo "Resetting circuit-breaker..."
+        rm -f "$SENTINEL_FILE"
+      else
+        echo "Run '$0 start --reset' to clear the sentinel and retry."
+        exit 1
+      fi
+    fi
+
     # Check if already running
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/continuous-learning-v2/agents/start-observer.sh` around lines 131 -
147, The start branch currently never checks or clears the circuit-breaker
sentinel (e.g., .observer.lock), so once written the observer cannot be
restarted; update the start command logic in start-observer.sh to detect the
sentinel file (introduce/inspect a variable like SENTINEL_FILE or OBSERVER_LOCK
alongside PID_FILE), exit with a clear message if the sentinel exists, and
support an explicit override flag (e.g., --reset passed to the script) that
safely removes the sentinel before continuing startup; ensure the start case
honors the flag, performs safe existence checks and removal of the sentinel
file, and then proceeds with the existing PID_FILE/running checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@skills/continuous-learning-v2/agents/start-observer.sh`:
- Around line 176-182: The abort branch exits without stopping the background
observer, leaving an orphaned process; capture or use the observer PID (the
variable set when launching the observer, e.g., the PID saved from $!—refer to
where the observer is started) and terminate that process before exiting: send a
graceful kill (SIGTERM) to the observer PID, wait for it to exit and if it
doesn’t, escalate to SIGKILL, then append the tmp log to LOG_FILE, clean up
OBSERVER_LOG_TMP, and finally exit 2; ensure you handle the case where the PID
is empty or the process is already gone to avoid errors.

In `@skills/continuous-learning-v2/hooks/observe.sh`:
- Around line 216-224: The grep pattern used to detect confirmation/permission
prompts is inconsistent between observe.sh (the pattern in the echo "$PARSED" |
grep -E -i -q "once granted access|grant.*access" branch) and start-observer.sh
which omits those tokens; unify them by extracting the regex into a single
shared constant (e.g., export OBSERVER_PROMPT_PATTERN in a common sourced file
or environment variable) and replace both inline grep calls to use that constant
(reference PARSED, the grep invocation, and SENTINEL_FILE for the abort flow) so
both scripts detect the same prompts and the sentinel/circuit-breaker behavior
stays consistent.

---

Outside diff comments:
In `@skills/continuous-learning-v2/agents/start-observer.sh`:
- Around line 156-171: The temp log OBSERVER_LOG_TMP created before launching
OBSERVER_LOOP_SCRIPT is left behind and continues receiving output; after
verifying the background process started (the existing PID_FILE/PID check),
append or move the contents of OBSERVER_LOG_TMP into the intended LOG_FILE,
remove OBSERVER_LOG_TMP, and ensure future output goes to LOG_FILE (i.e., update
redirection so nohup's stdout/stderr ultimately target LOG_FILE or rotate the
file and reopen descriptors). Locate the startup block using OBSERVER_LOG_TMP,
OBSERVER_LOOP_SCRIPT, LOG_FILE and PID_FILE and add the merge/delete and
redirection fix immediately after the startup check so no temp file persists.

---

Nitpick comments:
In `@skills/continuous-learning-v2/agents/start-observer.sh`:
- Around line 131-147: The start branch currently never checks or clears the
circuit-breaker sentinel (e.g., .observer.lock), so once written the observer
cannot be restarted; update the start command logic in start-observer.sh to
detect the sentinel file (introduce/inspect a variable like SENTINEL_FILE or
OBSERVER_LOCK alongside PID_FILE), exit with a clear message if the sentinel
exists, and support an explicit override flag (e.g., --reset passed to the
script) that safely removes the sentinel before continuing startup; ensure the
start case honors the flag, performs safe existence checks and removal of the
sentinel file, and then proceeds with the existing PID_FILE/running checks.

In `@skills/continuous-learning-v2/hooks/observe.sh`:
- Around line 36-46: Replace the hardcoded version-specific list in the
Windows-check loop with a glob-based discovery so future Python versions are
found automatically: modify the loop that iterates over win_py (currently
enumerating Python310/311/312) to iterate over the pattern
"/c/Users/$USER/AppData/Local/Programs/Python/Python*/python" (or similar glob)
and keep the existing -x check and printf/return behavior in the same loop in
observe.sh so any matching Python executable is picked up without updating
versions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ba0dbe6b-ef06-4256-b5ab-d5906d24a3df

📥 Commits

Reviewing files that changed from the base of the PR and between da4db99 and 96708e5.

📒 Files selected for processing (2)
  • skills/continuous-learning-v2/agents/start-observer.sh
  • skills/continuous-learning-v2/hooks/observe.sh

Comment on lines +176 to +182
# Check for confirmation-seeking output in the observer log
if grep -E -i -q "Can you confirm|requires permission|Awaiting|confirm I should proceed" "$OBSERVER_LOG_TMP"; then
echo "OBSERVER_ABORT: Confirmation or permission prompt detected in observer output. Failing closed."
cat "$OBSERVER_LOG_TMP" >> "$LOG_FILE"
rm -f "$OBSERVER_LOG_TMP"
exit 2
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Observer process not terminated on abort.

When a confirmation prompt is detected, the script exits with code 2 but the backgrounded observer process (launched at line 171) continues running. This leaves an orphaned process that may continue consuming resources or producing unwanted behavior.

🔧 Proposed fix: Kill the observer process on abort
     # Check for confirmation-seeking output in the observer log
     if grep -E -i -q "Can you confirm|requires permission|Awaiting|confirm I should proceed" "$OBSERVER_LOG_TMP"; then
       echo "OBSERVER_ABORT: Confirmation or permission prompt detected in observer output. Failing closed."
       cat "$OBSERVER_LOG_TMP" >> "$LOG_FILE"
       rm -f "$OBSERVER_LOG_TMP"
+      # Kill the backgrounded observer process
+      if [ -f "$PID_FILE" ]; then
+        pid=$(cat "$PID_FILE")
+        kill "$pid" 2>/dev/null || true
+        rm -f "$PID_FILE"
+      fi
       exit 2
     fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/continuous-learning-v2/agents/start-observer.sh` around lines 176 -
182, The abort branch exits without stopping the background observer, leaving an
orphaned process; capture or use the observer PID (the variable set when
launching the observer, e.g., the PID saved from $!—refer to where the observer
is started) and terminate that process before exiting: send a graceful kill
(SIGTERM) to the observer PID, wait for it to exit and if it doesn’t, escalate
to SIGKILL, then append the tmp log to LOG_FILE, clean up OBSERVER_LOG_TMP, and
finally exit 2; ensure you handle the case where the PID is empty or the process
is already gone to avoid errors.

Comment on lines +216 to +224
# FIX: Detect confirmation/permission prompts in observer output and fail closed.
# A non-interactive background observer must never ask for user confirmation.
# If detected: log once, write sentinel to suppress all future retries, exit non-zero.
if echo "$PARSED" | grep -E -i -q "Can you confirm|requires permission|Awaiting|confirm I should proceed|once granted access|grant.*access"; then
echo "[observe] OBSERVER_ABORT: Confirmation or permission prompt detected in observer output. This observer run is non-actionable." >&2
echo "[observe] Writing sentinel to suppress retries: ${SENTINEL_FILE}" >&2
echo "$PARSED" > "$SENTINEL_FILE"
exit 2
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the pattern mismatch between the two files

echo "=== Pattern in observe.sh ==="
grep -n "Can you confirm" skills/continuous-learning-v2/hooks/observe.sh

echo ""
echo "=== Pattern in start-observer.sh ==="
grep -n "Can you confirm" skills/continuous-learning-v2/agents/start-observer.sh

echo ""
echo "=== Diff of patterns ==="
# Extract and compare the patterns
obs_pattern=$(grep -oP 'grep -E -i -q "\K[^"]+' skills/continuous-learning-v2/hooks/observe.sh | head -1)
start_pattern=$(grep -oP 'grep -E -i -q "\K[^"]+' skills/continuous-learning-v2/agents/start-observer.sh | head -1)
echo "observe.sh: $obs_pattern"
echo "start-observer.sh: $start_pattern"

Repository: affaan-m/everything-claude-code

Length of output: 646


Pattern mismatch between observe.sh and start-observer.sh creates detection gaps.

The grep pattern at observe.sh:219 includes "once granted access|grant.*access", but the corresponding check in start-observer.sh:177 omits these patterns. This inconsistency means start-observer.sh will miss prompts that observe.sh would catch, leaving gaps in the circuit-breaker coverage.

Extract the pattern to a shared constant or synchronize both files to use identical patterns.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/continuous-learning-v2/hooks/observe.sh` around lines 216 - 224, The
grep pattern used to detect confirmation/permission prompts is inconsistent
between observe.sh (the pattern in the echo "$PARSED" | grep -E -i -q "once
granted access|grant.*access" branch) and start-observer.sh which omits those
tokens; unify them by extracting the regex into a single shared constant (e.g.,
export OBSERVER_PROMPT_PATTERN in a common sourced file or environment variable)
and replace both inline grep calls to use that constant (reference PARSED, the
grep invocation, and SENTINEL_FILE for the abort flow) so both scripts detect
the same prompts and the sentinel/circuit-breaker behavior stays consistent.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="skills/continuous-learning-v2/agents/start-observer.sh">

<violation number="1" location="skills/continuous-learning-v2/agents/start-observer.sh:171">
P1: Observer output is permanently redirected to the temp file `.observer.tmp.log` instead of `observer.log`. On the success path (no confirmation detected), neither `cat` nor `rm` is called on the temp file, so the running observer process writes all its output to a file nobody monitors. The `status` command tells users to check `$LOG_FILE`, which will be empty.

Consider keeping the original `>> "$LOG_FILE"` redirect and grepping `$LOG_FILE` for the confirmation check instead.</violation>

<violation number="2" location="skills/continuous-learning-v2/agents/start-observer.sh:177">
P1: The abort path doesn't kill the background observer process. When confirmation language is detected, the code exits 2 but the `nohup`'d process keeps running as an orphan. Capture the PID after launch and kill it before exiting.</violation>

<violation number="3" location="skills/continuous-learning-v2/agents/start-observer.sh:177">
P1: Duplicate confirmation pattern regex differs between observe.sh and start-observer.sh</violation>
</file>

<file name="skills/continuous-learning-v2/hooks/observe.sh">

<violation number="1" location="skills/continuous-learning-v2/hooks/observe.sh:219">
P1: The bare keyword `Awaiting` in this grep pattern is far too broad and will cause false positives on normal tool output (e.g., "Awaiting build completion", "awaiting API response"). Since a match writes a sentinel file that **permanently disables the observer**, a false positive silently breaks the feature until a user manually removes the lock file. Consider using a more specific phrase like `Awaiting.*confirmation` or `Awaiting.*permission`.</violation>

<violation number="2" location="skills/continuous-learning-v2/hooks/observe.sh:222">
P1: The raw `$PARSED` JSON (containing unscrubbed tool input/output) is written to the sentinel file, bypassing the secret-redaction logic that the rest of the script carefully applies before persisting to `observations.jsonl`. If the tool I/O that triggered detection contained credentials, they'd be written in plaintext. Consider writing only a short marker message or scrubbing the content first.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread skills/continuous-learning-v2/agents/start-observer.sh Outdated
Comment thread skills/continuous-learning-v2/agents/start-observer.sh Outdated
Comment thread skills/continuous-learning-v2/agents/start-observer.sh Outdated
Comment thread skills/continuous-learning-v2/hooks/observe.sh Outdated
Comment thread skills/continuous-learning-v2/hooks/observe.sh Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
skills/continuous-learning-v2/agents/start-observer.sh (1)

51-69: ⚠️ Potential issue | 🟠 Major

The startup fail-closed path can still miss and leak a prompted observer.

It samples the log once after a fixed 2-second delay and can only abort through PID_FILE. If the prompt appears after that window, it is missed; if it appears before observer-loop.sh writes the pid file, the freshly launched child keeps running in the background. Capture $! from the nohup launch and keep watching the appended log for a bounded startup window before returning success.

Also applies to: 191-219

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/continuous-learning-v2/agents/start-observer.sh` around lines 51 - 69,
When starting the observer (the nohup launch of observer-loop.sh), capture the
child PID via $! immediately, write that PID to PID_FILE, then monitor/tail the
observer log (LOG_FILE) for the specific prompt string within a bounded timeout
(e.g., 5–10s); if the prompt appears return success, otherwise kill the captured
PID, remove PID_FILE and return failure. Update the start logic that currently
relies solely on PID_FILE and a fixed sleep to: use nohup ... & pid=$!; echo
"$pid" > "$PID_FILE"; follow the log (tail -n +1 -F "$LOG_FILE" | timeout ...)
watching for the prompt, and always cleanup PID_FILE and the process on timeout
or error. Ensure the same pattern is applied to the other start path mentioned
(lines 191-219) so both branches use $!, immediate pid-file write, bounded
log-watch, and reliable cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@skills/continuous-learning-v2/hooks/observe.sh`:
- Around line 36-43: The Git Bash-specific Python probe currently only exists in
observe.sh and must be moved into the shared resolver used by
start-observer.sh/detect-project.sh so both scripts get a real Python path;
update the resolver in skills/continuous-learning-v2/scripts/detect-project.sh
(the code that sets CLV2_PYTHON_CMD) to include the same Windows Git Bash loop
that searches /c/Users/"$USER"/AppData/Local/Programs/Python/Python3*/python,
assign the result to CLV2_PYTHON_CMD (and export it if other scripts read it),
and remove the duplicate probe from observe.sh so start-observer.sh no longer
falls back to the Microsoft Store alias.
- Around line 263-270: The check is scanning the whole PARSED payload (which may
include tool_input) and falsely matching user commands; modify the
prompt-detection to extract only the observer "output" field from PARSED and run
the grep against that value (instead of full PARSED). Update the conditional
that uses PARSED and CLV2_OBSERVER_PROMPT_PATTERN so it pipes/parses only the
.output (or equivalent observer output variable) into grep, keep the same error
messages and call to write_guard_sentinel on match, and ensure the variable you
parse is empty-safe (use a default empty string) to avoid crashes in
write_guard_sentinel and exit 2 paths.

---

Duplicate comments:
In `@skills/continuous-learning-v2/agents/start-observer.sh`:
- Around line 51-69: When starting the observer (the nohup launch of
observer-loop.sh), capture the child PID via $! immediately, write that PID to
PID_FILE, then monitor/tail the observer log (LOG_FILE) for the specific prompt
string within a bounded timeout (e.g., 5–10s); if the prompt appears return
success, otherwise kill the captured PID, remove PID_FILE and return failure.
Update the start logic that currently relies solely on PID_FILE and a fixed
sleep to: use nohup ... & pid=$!; echo "$pid" > "$PID_FILE"; follow the log
(tail -n +1 -F "$LOG_FILE" | timeout ...) watching for the prompt, and always
cleanup PID_FILE and the process on timeout or error. Ensure the same pattern is
applied to the other start path mentioned (lines 191-219) so both branches use
$!, immediate pid-file write, bounded log-watch, and reliable cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6aba866e-2d31-4c19-b6cf-31258d645015

📥 Commits

Reviewing files that changed from the base of the PR and between 96708e5 and 1c1a9ef.

📒 Files selected for processing (4)
  • skills/continuous-learning-v2/agents/start-observer.sh
  • skills/continuous-learning-v2/hooks/observe.sh
  • skills/continuous-learning-v2/scripts/detect-project.sh
  • tests/hooks/hooks.test.js

Comment on lines +36 to +43
# FIX: Windows Git Bash — probe Python install paths directly because
# `command -v python` can hit the Microsoft Store alias instead.
for win_py in /c/Users/"$USER"/AppData/Local/Programs/Python/Python3*/python; do
if [ -x "$win_py" ]; then
printf '%s\n' "$win_py"
return 0
fi
done

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Move the Git Bash Python probe into the shared resolver.

These lines only fix observe.sh. start-observer.sh still reads CLV2_PYTHON_CMD from skills/continuous-learning-v2/scripts/detect-project.sh, whose resolver still falls through to the Microsoft Store alias. On Git Bash that leaves the launcher without a Python interpreter, so it never reads config.json and treats the observer as disabled.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/continuous-learning-v2/hooks/observe.sh` around lines 36 - 43, The Git
Bash-specific Python probe currently only exists in observe.sh and must be moved
into the shared resolver used by start-observer.sh/detect-project.sh so both
scripts get a real Python path; update the resolver in
skills/continuous-learning-v2/scripts/detect-project.sh (the code that sets
CLV2_PYTHON_CMD) to include the same Windows Git Bash loop that searches
/c/Users/"$USER"/AppData/Local/Programs/Python/Python3*/python, assign the
result to CLV2_PYTHON_CMD (and export it if other scripts read it), and remove
the duplicate probe from observe.sh so start-observer.sh no longer falls back to
the Microsoft Store alias.

Comment on lines +263 to +270
# Detect confirmation/permission prompts in observer output and fail closed.
# A non-interactive background observer must never ask for user confirmation.
if echo "$PARSED" | grep -E -i -q "$CLV2_OBSERVER_PROMPT_PATTERN"; then
echo "[observe] OBSERVER_ABORT: Confirmation or permission prompt detected in observer output. This observer run is non-actionable." >&2
echo "[observe] Writing sentinel to suppress retries: ${SENTINEL_FILE}" >&2
write_guard_sentinel
exit 2
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Only scan the parsed output for prompt text.

PARSED also includes tool_input on pre-hook events. Any user command or file body containing requires permission or confirm I should proceed will currently create the sentinel even though no observer prompt occurred.

🎯 Proposed fix
-# Detect confirmation/permission prompts in observer output and fail closed.
-# A non-interactive background observer must never ask for user confirmation.
-if echo "$PARSED" | grep -E -i -q "$CLV2_OBSERVER_PROMPT_PATTERN"; then
+# Detect confirmation/permission prompts in observer output and fail closed.
+# A non-interactive background observer must never ask for user confirmation.
+parsed_output=$(printf '%s' "$PARSED" | "$PYTHON_CMD" -c 'import json,sys; print(json.load(sys.stdin).get("output") or "")' 2>/dev/null || true)
+if [ "$HOOK_PHASE" = "post" ] && [ -n "$parsed_output" ] && printf '%s' "$parsed_output" | grep -E -i -q "$CLV2_OBSERVER_PROMPT_PATTERN"; then
   echo "[observe] OBSERVER_ABORT: Confirmation or permission prompt detected in observer output. This observer run is non-actionable." >&2
   echo "[observe] Writing sentinel to suppress retries: ${SENTINEL_FILE}" >&2
   write_guard_sentinel
   exit 2
 fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/continuous-learning-v2/hooks/observe.sh` around lines 263 - 270, The
check is scanning the whole PARSED payload (which may include tool_input) and
falsely matching user commands; modify the prompt-detection to extract only the
observer "output" field from PARSED and run the grep against that value (instead
of full PARSED). Update the conditional that uses PARSED and
CLV2_OBSERVER_PROMPT_PATTERN so it pipes/parses only the .output (or equivalent
observer output variable) into grep, keep the same error messages and call to
write_guard_sentinel on match, and ensure the variable you parse is empty-safe
(use a default empty string) to avoid crashes in write_guard_sentinel and exit 2
paths.

@affaan-m affaan-m merged commit bfc8022 into affaan-m:main Mar 13, 2026
3 checks passed
FrancescoRosciano pushed a commit to FRosciano-Mambo/everything-claude-code that referenced this pull request Jun 1, 2026
fix: background observer fails closed on confirmation/permission prompts (affaan-m#400)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

background observer should fail closed on permission prompts instead of retrying/looping

2 participants