Skip to content

t2893: Address quality-debt review feedback on pulse-wrapper.sh#2910

Merged
marcusquinn merged 1 commit intomainfrom
bugfix/pulse-wrapper-quality-debt-2893
Mar 5, 2026
Merged

t2893: Address quality-debt review feedback on pulse-wrapper.sh#2910
marcusquinn merged 1 commit intomainfrom
bugfix/pulse-wrapper-quality-debt-2893

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Mar 5, 2026

Summary

  • Rewrites _get_process_tree_cpu with iterative BFS to walk the full descendant tree, fixing incorrect CPU calculations when active processes are nested deeper than one level
  • Deduplicates watchdog kill logic in run_pulse() using a kill_reason variable with a single kill block, replacing two identical kill+force-kill sequences
  • Guards all kill commands with || true across check_dedup, guard_child_processes, and run_pulse to prevent set -e from aborting cleanup when the target process has already exited

Changes

Fix 1: Recursive process tree CPU calculation (line 260)

Problem: _get_process_tree_cpu used pgrep -P which only returns direct children, missing grandchildren and deeper descendants. If active processes were nested deeper (e.g., node -> shell -> language-server), the watchdog would see near-zero CPU and prematurely kill the process tree.

Fix: Iterative BFS that expands pgrep -P at each level until no more children are found. Also extracts the duplicated CPU-parsing logic into a _get_pid_cpu helper function.

Fix 2: Deduplicated kill logic (line 1112)

Problem: The kill+force-kill sequence was duplicated for both the stale threshold check and the idle timeout check in the watchdog loop.

Fix: Both checks now set a kill_reason variable, and a single block at the end performs the kill operation. This improves maintainability and ensures consistent behaviour.

Fix 3: set -e safety for kill commands

Problem: kill commands in cleanup paths were not guarded with || true. If the target process exited between the alive-check and the kill call, set -e would abort the script, preventing subsequent cleanup.

Fix: All kill commands in check_dedup, guard_child_processes, and run_pulse are now guarded. Also switched from ps -p to kill -0 for process-alive checks (more reliable, no stdout).

Verification

  • ShellCheck: zero violations
  • Bash syntax check: passes
  • All existing functions unchanged in behaviour — refactoring only

Closes #2893

Summary by CodeRabbit

  • Bug Fixes

    • Improved process cleanup robustness to handle edge cases where processes exit unexpectedly during cleanup operations.
    • Enhanced CPU usage monitoring to accurately account for child processes in process hierarchies.
  • Chores

    • Streamlined internal monitoring logic to reduce duplication and increase resilience against race conditions.

- Rewrite _get_process_tree_cpu with iterative BFS to walk the full
  descendant tree (not just direct children via pgrep -P). Fixes
  incorrect CPU calculation when active processes are nested deeper
  than one level (e.g., node -> shell -> language-server).

- Extract CPU-parsing logic into _get_pid_cpu helper to eliminate
  duplication between parent and child CPU reads.

- Deduplicate watchdog kill logic in run_pulse() using kill_reason
  variable with a single kill+force-kill block, replacing two
  identical kill sequences.

- Guard all kill commands with || true across check_dedup,
  guard_child_processes, and run_pulse to prevent set -e from
  aborting cleanup when the target process has already exited.

- Switch process-alive checks from ps -p to kill -0 (more reliable,
  no stdout output).

Closes #2893
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the robustness and accuracy of the pulse-wrapper.sh script's process monitoring and termination mechanisms. It resolves issues with incorrect CPU usage calculations for complex process hierarchies, streamlines the watchdog's kill logic, and improves the resilience of cleanup operations by preventing script abortion when attempting to kill already-exited processes.

Highlights

  • Process Tree CPU Calculation: The _get_process_tree_cpu function was rewritten to use an iterative Breadth-First Search (BFS) to correctly walk the full descendant tree, addressing issues where CPU calculations were incorrect for deeply nested processes. A new helper function, _get_pid_cpu, was extracted for single PID CPU retrieval.
  • Watchdog Kill Logic Deduplication: The run_pulse() function's watchdog kill logic was deduplicated by introducing a kill_reason variable, allowing a single block to handle both stale threshold and idle timeout kills, improving maintainability.
  • Robust Kill Commands: All kill commands in check_dedup, guard_child_processes, and run_pulse were guarded with || true to prevent set -e from aborting cleanup if the target process had already exited. Process-alive checks were also switched from ps -p to kill -0 for reliability.
Changelog
  • .agents/scripts/pulse-wrapper.sh
    • Modified check_dedup to guard _kill_tree and _force_kill_tree with || true and switched process existence check from ps -p to kill -0.
    • Refactored _get_process_tree_cpu to use an iterative BFS for comprehensive descendant traversal and introduced _get_pid_cpu for single PID CPU retrieval.
    • Updated guard_child_processes to guard _kill_tree and _force_kill_tree with || true and changed process existence check from ps -p to kill -0.
    • Deduplicated kill logic within run_pulse by using a kill_reason variable and a single kill block, also guarding kill commands with || true and using kill -0 for process checks.
Activity
  • No human activity (comments, reviews, etc.) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ca965e1b-811f-466a-a1a8-02e653d70686

📥 Commits

Reviewing files that changed from the base of the PR and between 85ce94f and 2e80577.

📒 Files selected for processing (1)
  • .agents/scripts/pulse-wrapper.sh

Walkthrough

The pull request hardens .agents/scripts/pulse-wrapper.sh by refactoring kill/cleanup logic into a single guarded path, implementing BFS-based process-tree CPU accounting to capture all descendants, replacing ps -p liveness checks with kill -0, and guarding all termination commands with || true to prevent premature set -e exits during cleanup.

Changes

Cohort / File(s) Summary
Pulse Watchdog Robustness & Process-Tree CPU Accounting
.agents/scripts/pulse-wrapper.sh
Unified kill/cleanup logic into a single guarded path driven by kill_reason variable; implemented BFS-based _get_process_tree_cpu() to sum CPU% across all descendants (replacing non-recursive pgrep -P); extracted _get_pid_cpu() helper; replaced ps -p existence checks with kill -0 liveness checks; guarded all kill commands with || true to prevent early termination under set -e; hardened race-condition handling in check_dedup() and run_pulse watchdog.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • #2853: Modifies pulse-wrapper.sh watchdog and process-tree kill logic, unifying guard patterns and liveness checks.
  • #2855: Updates pulse-wrapper.sh watchdog and process-kill logic with liveness checks and guarded cleanup paths.
  • #2881: Modifies pulse-wrapper.sh process-termination behavior and interacts with _kill_tree/_force_kill_tree helpers.

Suggested labels

enhancement

Poem

⚔️ No more orphans left behind,
BFS descends the process tree,
kill -0 checks liveness true,
Guard with \|\| true so free,
Watchdog sleeps in peace at last. 🛡️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and directly references the PR's main objective: addressing quality-debt review feedback on pulse-wrapper.sh, matching the issue #2893 and the core refactoring work.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from #2893: fixed _get_process_tree_cpu with BFS for full descendants, extracted _get_pid_cpu helper, deduplicated kill logic with kill_reason variable, guarded kill commands with || true, and replaced ps -p with kill -0 checks.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the pulse-wrapper.sh quality-debt objectives; no unrelated modifications to other files or unrelated functionality are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/pulse-wrapper-quality-debt-2893

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.

@github-actions
Copy link

github-actions bot commented Mar 5, 2026

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 107 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Thu Mar 5 13:50:27 UTC 2026: Code review monitoring started
Thu Mar 5 13:50:27 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 107

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 107
  • VULNERABILITIES: 0

Generated on: Thu Mar 5 13:50:30 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 5, 2026

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several high-quality improvements to pulse-wrapper.sh. The rewrite of _get_process_tree_cpu to use a proper BFS traversal is an excellent fix for the CPU calculation bug. Deduplicating the kill logic and adding || true guards significantly improves the script's maintainability and robustness. My review includes a few suggestions to further enhance error handling by removing 2>/dev/null from kill -0 checks, aligning with repository guidelines, and a suggestion to make a loop more robust against word splitting.

Note: Security Review did not run due to the size of the PR.

# Force kill if still alive
if ps -p "$old_pid" >/dev/null; then
_force_kill_tree "$old_pid"
if kill -0 "$old_pid" 2>/dev/null; then

Choose a reason for hiding this comment

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

medium

While using kill -0 is a good improvement for checking process existence, suppressing stderr with 2>/dev/null can hide important system errors, such as 'Operation not permitted'. This could lead the script to incorrectly assume a process is gone when it's actually running but unkillable by the current user. The general rules for this repository advise against suppressing stderr for this reason.

Consider removing the redirection to ensure all potential errors are logged for debugging, even if it adds some 'No such process' noise.

Suggested change
if kill -0 "$old_pid" 2>/dev/null; then
if kill -0 "$old_pid"; then
References
  1. Avoid blanket suppression of command errors with '2>/dev/null' to ensure critical issues like permission denials remain visible for debugging.

Comment on lines +316 to +321
local p
for p in $(printf "%s\n" "${all_pids[@]}" | sort -u); do
local cpu
cpu=$(_get_pid_cpu "$p")
total_cpu=$((total_cpu + cpu))
done

Choose a reason for hiding this comment

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

medium

This for p in $(...) loop is subject to word splitting by the shell, which could cause issues if any of the PIDs contained whitespace (though unlikely for PIDs, it's a robustness concern). A more robust pattern for iterating over lines of output is to use a while read loop, which also tends to be more memory-efficient for large inputs. This change also makes the local p declaration on line 316 unnecessary.

Suggested change
local p
for p in $(printf "%s\n" "${all_pids[@]}" | sort -u); do
local cpu
cpu=$(_get_pid_cpu "$p")
total_cpu=$((total_cpu + cpu))
done
printf "%s\n" "${all_pids[@]}" | sort -u | while IFS= read -r p; do
local cpu
cpu=$(_get_pid_cpu "$p")
total_cpu=$((total_cpu + cpu))
done

sleep 1
if ps -p "$pid" >/dev/null; then
_force_kill_tree "$pid"
if kill -0 "$pid" 2>/dev/null; then

Choose a reason for hiding this comment

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

medium

Suppressing stderr with 2>/dev/null here could mask important system errors like permission denials, which would prevent the script from correctly identifying and handling a process that is alive but not signalable. To align with repository guidelines on error visibility, consider removing the redirection.

Suggested change
if kill -0 "$pid" 2>/dev/null; then
if kill -0 "$pid"; then
References
  1. Avoid blanket suppression of command errors with '2>/dev/null' to ensure critical issues like permission denials remain visible for debugging.

echo "[pulse-wrapper] ${kill_reason} — killing" >>"$LOGFILE"
_kill_tree "$opencode_pid" || true
sleep 2
if kill -0 "$opencode_pid" 2>/dev/null; then

Choose a reason for hiding this comment

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

medium

As with the other kill -0 checks, suppressing stderr can hide important system errors like permission denials, potentially leading to incorrect script behavior. To ensure all errors are visible for debugging as per repository guidelines, it's safer to remove the 2>/dev/null redirection.

Suggested change
if kill -0 "$opencode_pid" 2>/dev/null; then
if kill -0 "$opencode_pid"; then
References
  1. Avoid blanket suppression of command errors with '2>/dev/null' to ensure critical issues like permission denials remain visible for debugging.

@marcusquinn marcusquinn merged commit 58acf5d into main Mar 5, 2026
31 of 32 checks passed
@marcusquinn marcusquinn deleted the bugfix/pulse-wrapper-quality-debt-2893 branch March 5, 2026 14:25
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.

quality-debt: .agents/scripts/pulse-wrapper.sh — PR #2882 review feedback (medium)

1 participant