-
Notifications
You must be signed in to change notification settings - Fork 9
t1453: enforce pulse-wrapper utilization invariants #4198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -83,6 +83,8 @@ | |||||||||||||||||||||||||||||||||||||
| DAILY_PR_CAP="${DAILY_PR_CAP:-5}" # Max PRs created per repo per day (GH#3821) | ||||||||||||||||||||||||||||||||||||||
| PRODUCT_RESERVATION_PCT="${PRODUCT_RESERVATION_PCT:-60}" # % of worker slots reserved for product repos (t1423) | ||||||||||||||||||||||||||||||||||||||
| QUALITY_DEBT_CAP_PCT="${QUALITY_DEBT_CAP_PCT:-$(config_get "orchestration.quality_debt_cap_pct" "30")}" # % cap for quality-debt dispatch share | ||||||||||||||||||||||||||||||||||||||
| PULSE_BACKFILL_MAX_ATTEMPTS="${PULSE_BACKFILL_MAX_ATTEMPTS:-3}" # Additional pulse passes when below utilization target (t1453) | ||||||||||||||||||||||||||||||||||||||
| PULSE_LAUNCH_GRACE_SECONDS="${PULSE_LAUNCH_GRACE_SECONDS:-20}" # Grace window for worker process to appear after dispatch (t1453) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Process guard limits (t1398) | ||||||||||||||||||||||||||||||||||||||
| CHILD_RSS_LIMIT_KB="${CHILD_RSS_LIMIT_KB:-2097152}" # 2 GB default — kill child if RSS exceeds this | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -106,6 +108,8 @@ | |||||||||||||||||||||||||||||||||||||
| if [[ "$QUALITY_DEBT_CAP_PCT" -gt 100 ]]; then | ||||||||||||||||||||||||||||||||||||||
| QUALITY_DEBT_CAP_PCT=100 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
| PULSE_BACKFILL_MAX_ATTEMPTS=$(_validate_int PULSE_BACKFILL_MAX_ATTEMPTS "$PULSE_BACKFILL_MAX_ATTEMPTS" 3 0) | ||||||||||||||||||||||||||||||||||||||
| PULSE_LAUNCH_GRACE_SECONDS=$(_validate_int PULSE_LAUNCH_GRACE_SECONDS "$PULSE_LAUNCH_GRACE_SECONDS" 20 5) | ||||||||||||||||||||||||||||||||||||||
| CHILD_RSS_LIMIT_KB=$(_validate_int CHILD_RSS_LIMIT_KB "$CHILD_RSS_LIMIT_KB" 2097152 1) | ||||||||||||||||||||||||||||||||||||||
| CHILD_RUNTIME_LIMIT=$(_validate_int CHILD_RUNTIME_LIMIT "$CHILD_RUNTIME_LIMIT" 1800 1) | ||||||||||||||||||||||||||||||||||||||
| SHELLCHECK_RSS_LIMIT_KB=$(_validate_int SHELLCHECK_RSS_LIMIT_KB "$SHELLCHECK_RSS_LIMIT_KB" 1048576 1) | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -1668,6 +1672,212 @@ | |||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| # Count active worker processes | ||||||||||||||||||||||||||||||||||||||
| # Returns: count via stdout | ||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| count_active_workers() { | ||||||||||||||||||||||||||||||||||||||
| local count | ||||||||||||||||||||||||||||||||||||||
| count=$(ps axo command | grep '[/]full-loop' | grep -c '[.]opencode') || count=0 | ||||||||||||||||||||||||||||||||||||||
| echo "$count" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| # Get current max workers from pulse-max-workers file | ||||||||||||||||||||||||||||||||||||||
| # Returns: numeric value via stdout (defaults to 1) | ||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| get_max_workers_target() { | ||||||||||||||||||||||||||||||||||||||
| local max_workers_file="${HOME}/.aidevops/logs/pulse-max-workers" | ||||||||||||||||||||||||||||||||||||||
| local max_workers | ||||||||||||||||||||||||||||||||||||||
| max_workers=$(cat "$max_workers_file" 2>/dev/null || echo "1") | ||||||||||||||||||||||||||||||||||||||
| [[ "$max_workers" =~ ^[0-9]+$ ]] || max_workers=1 | ||||||||||||||||||||||||||||||||||||||
| if [[ "$max_workers" -lt 1 ]]; then | ||||||||||||||||||||||||||||||||||||||
| max_workers=1 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
| echo "$max_workers" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| # Count runnable backlog candidates across pulse scope | ||||||||||||||||||||||||||||||||||||||
| # Heuristic for t1453 utilization loop: | ||||||||||||||||||||||||||||||||||||||
| # - open unassigned, non-blocked issues | ||||||||||||||||||||||||||||||||||||||
| # - open PRs with failing checks or changes requested | ||||||||||||||||||||||||||||||||||||||
| # Returns: count via stdout | ||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| count_runnable_candidates() { | ||||||||||||||||||||||||||||||||||||||
| local repos_json="${REPOS_JSON}" | ||||||||||||||||||||||||||||||||||||||
| if [[ ! -f "$repos_json" ]] || ! command -v jq &>/dev/null; then | ||||||||||||||||||||||||||||||||||||||
| echo "0" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| local total=0 | ||||||||||||||||||||||||||||||||||||||
| while IFS='|' read -r slug _path; do | ||||||||||||||||||||||||||||||||||||||
| [[ -n "$slug" ]] || continue | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| local issue_json | ||||||||||||||||||||||||||||||||||||||
| issue_json=$(gh issue list --repo "$slug" --state open --json assignees,labels --limit 100 2>/dev/null) || issue_json="[]" | ||||||||||||||||||||||||||||||||||||||
| local issue_count | ||||||||||||||||||||||||||||||||||||||
| issue_count=$(echo "$issue_json" | jq '[.[] | select((.assignees | length) == 0 and (.labels | map(.name) | index("status:blocked") | not))] | length' 2>/dev/null) || issue_count=0 | ||||||||||||||||||||||||||||||||||||||
| [[ "$issue_count" =~ ^[0-9]+$ ]] || issue_count=0 | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| local pr_json | ||||||||||||||||||||||||||||||||||||||
| pr_json=$(gh pr list --repo "$slug" --state open --json reviewDecision,statusCheckRollup --limit 100 2>/dev/null) || pr_json="[]" | ||||||||||||||||||||||||||||||||||||||
| local pr_count | ||||||||||||||||||||||||||||||||||||||
| pr_count=$(echo "$pr_json" | jq '[.[] | select(.reviewDecision == "CHANGES_REQUESTED" or ((.statusCheckRollup // []) | any((.conclusion // .state) == "FAILURE")))] | length' 2>/dev/null) || pr_count=0 | ||||||||||||||||||||||||||||||||||||||
| [[ "$pr_count" =~ ^[0-9]+$ ]] || pr_count=0 | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| total=$((total + issue_count + pr_count)) | ||||||||||||||||||||||||||||||||||||||
| done < <(jq -r '.initialized_repos[] | select(.pulse == true and (.local_only // false) == false and .slug != "") | "\(.slug)|\(.path)"' "$repos_json" 2>/dev/null) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| echo "$total" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| # Count queued issues that do not have an active worker process | ||||||||||||||||||||||||||||||||||||||
| # This is a launch-validation signal: queued labels imply dispatch, | ||||||||||||||||||||||||||||||||||||||
| # but no matching worker indicates startup failure or immediate exit. | ||||||||||||||||||||||||||||||||||||||
| # Returns: count via stdout | ||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| count_queued_without_worker() { | ||||||||||||||||||||||||||||||||||||||
| local repos_json="${REPOS_JSON}" | ||||||||||||||||||||||||||||||||||||||
| if [[ ! -f "$repos_json" ]] || ! command -v jq &>/dev/null; then | ||||||||||||||||||||||||||||||||||||||
| echo "0" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| local total=0 | ||||||||||||||||||||||||||||||||||||||
| while IFS= read -r slug; do | ||||||||||||||||||||||||||||||||||||||
| [[ -n "$slug" ]] || continue | ||||||||||||||||||||||||||||||||||||||
| local queued_numbers | ||||||||||||||||||||||||||||||||||||||
| queued_numbers=$(gh issue list --repo "$slug" --state open --label "status:queued" --json number --jq '.[].number' --limit 100 2>/dev/null) || queued_numbers="" | ||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
References
|
||||||||||||||||||||||||||||||||||||||
| if [[ -z "$queued_numbers" ]]; then | ||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| while IFS= read -r issue_num; do | ||||||||||||||||||||||||||||||||||||||
| [[ "$issue_num" =~ ^[0-9]+$ ]] || continue | ||||||||||||||||||||||||||||||||||||||
| if ! pgrep -fal "issue-${issue_num}|Issue #${issue_num}:" >/dev/null; then | ||||||||||||||||||||||||||||||||||||||
| total=$((total + 1)) | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1761
to
+1765
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The
Since output is discarded to 🔧 Proposed fix for portability while IFS= read -r issue_num; do
[[ "$issue_num" =~ ^[0-9]+$ ]] || continue
- if ! pgrep -fal "issue-${issue_num}|Issue #${issue_num}:" >/dev/null; then
+ if ! pgrep -f "issue-${issue_num}|Issue #${issue_num}:" >/dev/null 2>&1; then
total=$((total + 1))
fi
done <<<"$queued_numbers"🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| done <<<"$queued_numbers" | ||||||||||||||||||||||||||||||||||||||
| done < <(jq -r '.initialized_repos[] | select(.pulse == true and (.local_only // false) == false and .slug != "") | .slug' "$repos_json" 2>/dev/null) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| echo "$total" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| # Launch validation gate for pulse dispatches (t1453) | ||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||
| # Arguments: | ||||||||||||||||||||||||||||||||||||||
| # $1 - issue number | ||||||||||||||||||||||||||||||||||||||
| # $2 - repo slug (owner/repo) | ||||||||||||||||||||||||||||||||||||||
| # $3 - optional grace timeout in seconds | ||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||
| # Exit codes: | ||||||||||||||||||||||||||||||||||||||
| # 0 - worker launch appears valid (process observed, no CLI usage output marker) | ||||||||||||||||||||||||||||||||||||||
| # 1 - launch invalid (no process within grace window or usage output detected) | ||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| check_worker_launch() { | ||||||||||||||||||||||||||||||||||||||
| local issue_number="$1" | ||||||||||||||||||||||||||||||||||||||
| local repo_slug="$2" | ||||||||||||||||||||||||||||||||||||||
| local grace_seconds="${3:-$PULSE_LAUNCH_GRACE_SECONDS}" | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if [[ ! "$issue_number" =~ ^[0-9]+$ ]] || [[ -z "$repo_slug" ]]; then | ||||||||||||||||||||||||||||||||||||||
| echo "[pulse-wrapper] check_worker_launch: invalid arguments issue='$issue_number' repo='$repo_slug'" >>"$LOGFILE" | ||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
| [[ "$grace_seconds" =~ ^[0-9]+$ ]] || grace_seconds="$PULSE_LAUNCH_GRACE_SECONDS" | ||||||||||||||||||||||||||||||||||||||
| if [[ "$grace_seconds" -lt 1 ]]; then | ||||||||||||||||||||||||||||||||||||||
| grace_seconds=1 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| local safe_slug | ||||||||||||||||||||||||||||||||||||||
| safe_slug=$(echo "$repo_slug" | tr '/:' '--') | ||||||||||||||||||||||||||||||||||||||
| local -a log_candidates=( | ||||||||||||||||||||||||||||||||||||||
| "/tmp/pulse-${safe_slug}-${issue_number}.log" | ||||||||||||||||||||||||||||||||||||||
| "/tmp/pulse-${issue_number}.log" | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| local elapsed=0 | ||||||||||||||||||||||||||||||||||||||
| local poll_seconds=2 | ||||||||||||||||||||||||||||||||||||||
| while [[ "$elapsed" -lt "$grace_seconds" ]]; do | ||||||||||||||||||||||||||||||||||||||
| if pgrep -fal "issue-${issue_number}|Issue #${issue_number}:" >/dev/null; then | ||||||||||||||||||||||||||||||||||||||
| local candidate | ||||||||||||||||||||||||||||||||||||||
| for candidate in "${log_candidates[@]}"; do | ||||||||||||||||||||||||||||||||||||||
| if [[ -f "$candidate" ]] && rg -q '^opencode run \[message\.\.\]|^run opencode with a message|^Options:' "$candidate"; then | ||||||||||||||||||||||||||||||||||||||
| echo "[pulse-wrapper] Launch validation failed for issue #${issue_number} (${repo_slug}) — CLI usage output detected in ${candidate}" >>"$LOGFILE" | ||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1809
to
+1817
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify if ripgrep (rg) is used elsewhere in the codebase or if there's a documented dependency
# Check for rg usage in shell scripts
echo "=== rg usage in .agents/scripts/ ==="
rg -l '\brg\b' .agents/scripts/*.sh 2>/dev/null || echo "No other rg usage found"
# Check if there's a dependency list
echo ""
echo "=== Checking for documented dependencies ==="
fd -t f -e md 'INSTALL|SETUP|DEPENDENCY|README' . --exec head -50 {} 2>/dev/null | grep -i 'ripgrep\|rg' || echo "No ripgrep dependency documented"Repository: marcusquinn/aidevops Length of output: 3487 🏁 Script executed: #!/bin/bash
# 1. Check for ripgrep in dependency/setup files
echo "=== Ripgrep in dependency/setup files ==="
fd -t f \( -name 'setup.sh' -o -name '.tool-versions' -o -name 'Brewfile' -o -name 'requirements*.txt' -o -name 'package.json' -o -name 'Dockerfile' \) . --exec grep -l 'ripgrep\|rg' {} \; 2>/dev/null || echo "No explicit ripgrep in common dependency files found"
# 2. Check if README or INSTALL documents ripgrep as requirement
echo ""
echo "=== README/INSTALL mentioning ripgrep ==="
fd -t f \( -name 'README*' -o -name 'INSTALL*' -o -name 'SETUP*' \) . --exec grep -i 'ripgrep\|rg' {} + 2>/dev/null || echo "Not found in README/INSTALL"
# 3. Check pgrep usage pattern in .agents/scripts/
echo ""
echo "=== pgrep usage in .agents/scripts/ ==="
rg -n 'pgrep' .agents/scripts/*.sh | head -20
# 4. Read the actual lines 1809-1817 in pulse-wrapper.sh
echo ""
echo "=== Lines 1809-1817 in pulse-wrapper.sh ==="
sed -n '1809,1817p' .agents/scripts/pulse-wrapper.shRepository: marcusquinn/aidevops Length of output: 2840 🏁 Script executed: #!/bin/bash
# 1. Check for pgrep -fal specifically
echo "=== pgrep -fal usage in codebase ==="
rg 'pgrep.*-.*a.*l' .agents/scripts/*.sh
# 2. Check for any tools documentation
echo ""
echo "=== Tool requirements documentation ==="
cat .tool-versions 2>/dev/null || echo "No .tool-versions found"
echo ""
cat .github/workflows/*.yml 2>/dev/null | grep -A5 'ripgrep\|setup-' | head -20 || echo "No tool setup in workflows"
# 3. Check if there's any comment about tool dependencies in the scripts themselves
echo ""
echo "=== Tool requirement comments in pulse-wrapper.sh ==="
head -50 .agents/scripts/pulse-wrapper.sh | grep -i 'require\|depend\|need\|install'
# 4. Check what the -a and -l flags do (look at pgrep man page context or comments)
echo ""
echo "=== Context around pgrep usage in pulse-wrapper.sh ==="
sed -n '1800,1825p' .agents/scripts/pulse-wrapper.shRepository: marcusquinn/aidevops Length of output: 3538 Remove non-portable Line 1809 uses Additionally, Suggested fixes while [[ "$elapsed" -lt "$grace_seconds" ]]; do
- if pgrep -fal "issue-${issue_number}|Issue #${issue_number}:" >/dev/null; then
+ if pgrep -f "issue-${issue_number}|Issue #${issue_number}:" >/dev/null 2>&1; then
local candidate
for candidate in "${log_candidates[@]}"; do
- if [[ -f "$candidate" ]] && rg -q '^opencode run \[message\.\.\]|^run opencode with a message|^Options:' "$candidate"; then
+ if [[ -f "$candidate" ]] && grep -Eq '^opencode run \[message\.\.\]|^run opencode with a message|^Options:' "$candidate"; then
echo "[pulse-wrapper] Launch validation failed for issue #${issue_number} (${repo_slug}) — CLI usage output detected in ${candidate}" >>"$LOGFILE"
return 1
fiNote: 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
| sleep "$poll_seconds" | ||||||||||||||||||||||||||||||||||||||
| elapsed=$((elapsed + poll_seconds)) | ||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| echo "[pulse-wrapper] Launch validation failed for issue #${issue_number} (${repo_slug}) — no active worker process within ${grace_seconds}s" >>"$LOGFILE" | ||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| # Enforce utilization invariants post-pulse (t1453) | ||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||
| # Invariant: keep dispatching pulse cycles until either: | ||||||||||||||||||||||||||||||||||||||
| # - active workers >= MAX_WORKERS, OR | ||||||||||||||||||||||||||||||||||||||
| # - no runnable work remains in scope | ||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||
| # Launch validation integration: | ||||||||||||||||||||||||||||||||||||||
| # queued issues with no active worker are treated as launch failures, | ||||||||||||||||||||||||||||||||||||||
| # which trigger an immediate backfill cycle. | ||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| enforce_utilization_invariants() { | ||||||||||||||||||||||||||||||||||||||
| local attempts=0 | ||||||||||||||||||||||||||||||||||||||
| local max_attempts="$PULSE_BACKFILL_MAX_ATTEMPTS" | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| while [[ "$attempts" -lt "$max_attempts" ]]; do | ||||||||||||||||||||||||||||||||||||||
| if [[ -f "$STOP_FLAG" ]]; then | ||||||||||||||||||||||||||||||||||||||
| echo "[pulse-wrapper] Stop flag present during utilization enforcement — exiting" >>"$LOGFILE" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| local max_workers active_workers runnable_count queued_without_worker | ||||||||||||||||||||||||||||||||||||||
| max_workers=$(get_max_workers_target) | ||||||||||||||||||||||||||||||||||||||
| active_workers=$(count_active_workers) | ||||||||||||||||||||||||||||||||||||||
| runnable_count=$(count_runnable_candidates) | ||||||||||||||||||||||||||||||||||||||
| queued_without_worker=$(count_queued_without_worker) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| [[ "$max_workers" =~ ^[0-9]+$ ]] || max_workers=1 | ||||||||||||||||||||||||||||||||||||||
| [[ "$active_workers" =~ ^[0-9]+$ ]] || active_workers=0 | ||||||||||||||||||||||||||||||||||||||
| [[ "$runnable_count" =~ ^[0-9]+$ ]] || runnable_count=0 | ||||||||||||||||||||||||||||||||||||||
| [[ "$queued_without_worker" =~ ^[0-9]+$ ]] || queued_without_worker=0 | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if [[ "$active_workers" -ge "$max_workers" && "$queued_without_worker" -eq 0 ]]; then | ||||||||||||||||||||||||||||||||||||||
| echo "[pulse-wrapper] Utilization invariant satisfied: active workers ${active_workers}/${max_workers}" >>"$LOGFILE" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if [[ "$runnable_count" -eq 0 && "$queued_without_worker" -eq 0 ]]; then | ||||||||||||||||||||||||||||||||||||||
| echo "[pulse-wrapper] Utilization invariant satisfied: no runnable work remains (active ${active_workers}/${max_workers})" >>"$LOGFILE" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| attempts=$((attempts + 1)) | ||||||||||||||||||||||||||||||||||||||
| echo "[pulse-wrapper] Backfill attempt ${attempts}/${max_attempts}: active=${active_workers}/${max_workers}, runnable=${runnable_count}, queued_without_worker=${queued_without_worker}" >>"$LOGFILE" | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Refresh prompt state before each backfill cycle so pulse sees latest context. | ||||||||||||||||||||||||||||||||||||||
| prefetch_state || true | ||||||||||||||||||||||||||||||||||||||
| run_pulse | ||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| echo "[pulse-wrapper] Reached backfill attempt cap (${max_attempts}) before utilization invariant converged" >>"$LOGFILE" | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ####################################### | ||||||||||||||||||||||||||||||||||||||
| # Main | ||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -1719,6 +1929,7 @@ | |||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| run_pulse | ||||||||||||||||||||||||||||||||||||||
| enforce_utilization_invariants | ||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
@@ -1787,7 +1998,7 @@ | |||||||||||||||||||||||||||||||||||||
| local mb=$((rss / 1024)) | ||||||||||||||||||||||||||||||||||||||
| killed=$((killed + 1)) | ||||||||||||||||||||||||||||||||||||||
| total_mb=$((total_mb + mb)) | ||||||||||||||||||||||||||||||||||||||
| done < <(ps axo pid,tty,etime,rss,command | grep 'node.*opencode' | grep -v '[.]opencode') | ||||||||||||||||||||||||||||||||||||||
|
Check warning on line 2001 in .agents/scripts/pulse-wrapper.sh
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if [[ "$killed" -gt 0 ]]; then | ||||||||||||||||||||||||||||||||||||||
| echo "[pulse-wrapper] Cleaned up $killed orphaned opencode processes (freed ~${total_mb}MB)" >>"$LOGFILE" | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
ghCLI commands are limited to fetching 100 items by default. If a repository has more than 100 open issues or pull requests, this count of runnable candidates will be incomplete, potentially causing the backfill loop to terminate prematurely even when there is more work available. To ensure all items are retrieved, use the--paginateflag.References
ghcommand, use the--paginateflag to ensure all items are retrieved, not just the first page.