Skip to content

t1314: Fix batch concurrency bypass in supervisor dispatch pipeline#2236

Merged
marcusquinn merged 2 commits intomainfrom
feature/t1314
Feb 25, 2026
Merged

t1314: Fix batch concurrency bypass in supervisor dispatch pipeline#2236
marcusquinn merged 2 commits intomainfrom
feature/t1314

Conversation

@marcusquinn
Copy link
Copy Markdown
Owner

Summary

Fixes five contributing causes of batch concurrency bypass when cmd_pulse() runs without --batch flag.

  • Batch auto-detection in pulse Phase 2: When cmd_pulse() runs without --batch, Phase 2 now queries active batches from DB and dispatches through each one respecting their concurrency limits
  • Batch affinity in auto-absorption: Tasks only absorbed into batches containing tasks from the same repo, preventing cross-repo serialization under concurrency=1
  • Sibling limit capping: cmd_next() now caps SUPERVISOR_MAX_PARALLEL_SIBLINGS to the batch's concurrency setting when a subtask belongs to a batch
  • Blocked-by enforcement at dispatch: cmd_dispatch() re-checks blocked-by: dependencies before the concurrency check, not just during auto-pickup
  • Human review gate for auto-merge: merge_pr action requires APPROVED review decision unless SUPERVISOR_AUTO_MERGE_ENABLED=true (default: false)

Files Changed

File Fix
pulse.sh Phase 2 batch auto-detection
cron.sh Batch affinity + syntax fix
state.sh Sibling limit capping
dispatch.sh Blocked-by enforcement
ai-lifecycle.sh Human review gate

Testing

  • All 5 files pass bash -n syntax check
  • All 5 files pass ShellCheck (only pre-existing SC2016 info-level on unrelated echo statements)

Ref: GH#2163

When cmd_pulse() runs without --batch flag (e.g., from cron), Phase 2
now auto-detects active batches and dispatches through them, respecting
each batch's concurrency limit. Previously, all queued tasks were
dispatched via global SUPERVISOR_MAX_CONCURRENCY (default: 4), bypassing
batch concurrency=1 settings entirely.

Unbatched tasks still use global concurrency as a fallback.
…e (t1314)

- Fix batch affinity in auto-batch absorption (cron.sh): tasks only absorbed
  into batches containing tasks from the same repo, preventing cross-repo
  serialization. Remaining unbatched tasks get a new auto-batch.
- Cap cmd_next() sibling limits to batch concurrency (state.sh): when a
  subtask belongs to a batch with concurrency=1, sibling parallelism is
  capped to that value instead of using SUPERVISOR_MAX_PARALLEL_SIBLINGS.
- Enforce blocked-by at dispatch time (dispatch.sh): re-check blocked-by
  dependencies in cmd_dispatch() before the concurrency check, not just
  during auto-pickup. Blocked tasks return to queued state.
- Add human review gate for AI lifecycle auto-merge (ai-lifecycle.sh):
  merge_pr action now requires APPROVED review decision unless
  SUPERVISOR_AUTO_MERGE_ENABLED=true. Tasks without approval transition
  to review_waiting state instead of merging.

Ref: GH#2163
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 24, 2026

Warning

Rate limit exceeded

@marcusquinn has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 20 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 5859f3c and a94c64f.

📒 Files selected for processing (5)
  • .agents/scripts/supervisor/ai-lifecycle.sh
  • .agents/scripts/supervisor/cron.sh
  • .agents/scripts/supervisor/dispatch.sh
  • .agents/scripts/supervisor/pulse.sh
  • .agents/scripts/supervisor/state.sh
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/t1314

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.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @marcusquinn, 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 supervisor's batch processing pipeline by addressing several scenarios where batch concurrency limits and intended behaviors were inadvertently bypassed. The changes ensure that tasks are dispatched according to their batch configurations, maintain repository affinity within batches, enforce dependency checks more rigorously, and introduce a human review gate for automated PR merges. These improvements lead to more predictable and controlled task execution, especially in concurrent environments.

Highlights

  • Batch Auto-Detection in Pulse Phase 2: Implemented logic in cmd_pulse() to automatically detect active batches and dispatch tasks through them, respecting their individual concurrency limits, even when the --batch flag is not explicitly used.
  • Batch Affinity in Auto-Absorption: Modified auto-pickup to ensure tasks are only absorbed into existing batches if those batches contain tasks from the same repository, preventing unintended cross-repo serialization.
  • Sibling Limit Capping: Introduced a mechanism in cmd_next() to cap the SUPERVISOR_MAX_PARALLEL_SIBLINGS setting to a batch's concurrency limit when a subtask belongs to that batch, ensuring batch concurrency is honored.
  • Blocked-by Enforcement at Dispatch: Added a pre-dispatch check in cmd_dispatch() to re-verify blocked-by: dependencies, preventing tasks from being dispatched if their dependencies are not yet resolved, thus avoiding wasted resources.
  • Human Review Gate for Auto-Merge: Configured the merge_pr action to require an APPROVED review decision before merging, unless SUPERVISOR_AUTO_MERGE_ENABLED is explicitly set to true, preventing AI from bypassing human review.
Changelog
  • .agents/scripts/supervisor/ai-lifecycle.sh
    • Added a human review gate for PR merges, requiring an 'APPROVED' review decision unless auto-merge is enabled.
    • Updated decision rules to reflect the new human review requirement for PRs in 'CLEAN' or 'UNSTABLE' states.
  • .agents/scripts/supervisor/cron.sh
    • Implemented batch affinity logic during auto-absorption, ensuring tasks are batched with other tasks from the same repository.
    • Modified the query for unbatched tasks to include repository information.
    • Updated logging for batch creation to reflect the number of still unbatched tasks.
  • .agents/scripts/supervisor/dispatch.sh
    • Introduced a pre-dispatch check to enforce 'blocked-by' dependencies, preventing dispatch of tasks with unresolved blockers.
  • .agents/scripts/supervisor/pulse.sh
    • Refactored Phase 2 dispatch to auto-detect and iterate through active batches, respecting their concurrency limits, when cmd_pulse() is called without an explicit --batch flag.
    • Added logic to dispatch any remaining unbatched queued tasks using global concurrency.
  • .agents/scripts/supervisor/state.sh
    • Implemented capping of sibling parallelism based on the batch's concurrency setting for subtasks, overriding the global SUPERVISOR_MAX_PARALLEL_SIBLINGS if the batch limit is lower.
Activity
  • No human activity (comments, reviews) has been recorded on 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.

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Tue Feb 24 20:54:51 UTC 2026: Code review monitoring started
Tue Feb 24 20:54:52 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 58

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 58
  • VULNERABILITIES: 0

Generated on: Tue Feb 24 20:54:54 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@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 crucial fixes to address concurrency bypass issues within the supervisor pipeline, significantly improving its robustness and reliability. The changes to auto-detect and respect batch concurrency, enforce batch affinity for repositories, and cap sibling parallelism are well-implemented and directly tackle the core problems. The addition of a pre-dispatch dependency check and a human review gate for pull request merges are also valuable enhancements for safety and correctness. My review primarily identifies opportunities to improve debuggability by removing error suppression and to enhance maintainability by reducing code duplication. Overall, this is a strong set of improvements.

local current_review_decision="NONE"
if [[ -n "$pr_number" && -n "$pr_repo_slug" ]] && command -v gh &>/dev/null; then
current_review_decision=$(gh pr view "$pr_number" --repo "$pr_repo_slug" \
--json reviewDecision --jq '.reviewDecision // "NONE"' 2>/dev/null || echo "NONE")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The use of 2>/dev/null here suppresses potentially useful error messages from the gh command. If the command fails due to authentication issues, an invalid repository slug, or other problems, these errors will be hidden, making debugging more difficult. It's better to let stderr be printed so that such issues are visible in the logs. The || echo "NONE" construct already safely handles the command failing without exiting the script.

Suggested change
--json reviewDecision --jq '.reviewDecision // "NONE"' 2>/dev/null || echo "NONE")
--json reviewDecision --jq '.reviewDecision // "NONE"' || echo "NONE")
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

local still_unbatched_count
still_unbatched_count=$(echo "$still_unbatched" | wc -l | tr -d ' ')
local auto_batch_id
auto_batch_id=$(cmd_batch "$auto_batch_name" --concurrency "$auto_base_concurrency" --tasks "$task_csv" 2>/dev/null)
Copy link
Copy Markdown

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 can hide important diagnostic information from the cmd_batch command. If there's an issue creating the batch, the error message will be lost, making it harder to debug why auto_batch_id might be empty. It's better to allow errors to be printed to stderr.

Suggested change
auto_batch_id=$(cmd_batch "$auto_batch_name" --concurrency "$auto_base_concurrency" --tasks "$task_csv" 2>/dev/null)
auto_batch_id=$(cmd_batch "$auto_batch_name" --concurrency "$auto_base_concurrency" --tasks "$task_csv")
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

# is_task_blocked() was only called during auto-pickup (cron.sh), not here.
# A task could become blocked between pickup and dispatch (e.g., a dependency
# was re-queued or failed). Re-check now to avoid wasting an Opus session.
if declare -f is_task_blocked &>/dev/null && [[ -f "$dispatch_todo_file" ]]; then
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using &>/dev/null suppresses both stdout and stderr. For declare -f, it's better to only suppress stdout with >/dev/null. This allows potential syntax errors or other issues from the shell to be visible on stderr, which is useful for debugging, while still hiding the function definition output.

Suggested change
if declare -f is_task_blocked &>/dev/null && [[ -f "$dispatch_todo_file" ]]; then
if declare -f is_task_blocked >/dev/null && [[ -f "$dispatch_todo_file" ]]; then
References
  1. In shell scripts, avoid blanket suppression of errors with '2>/dev/null'. For 'declare -f', redirecting only stdout ('>/dev/null') is sufficient as it does not output to stderr, allowing potential syntax errors to remain visible.

# was re-queued or failed). Re-check now to avoid wasting an Opus session.
if declare -f is_task_blocked &>/dev/null && [[ -f "$dispatch_todo_file" ]]; then
local todo_line
todo_line=$(grep -E "^[[:space:]]*- \[.\] $task_id( |$)" "$dispatch_todo_file" 2>/dev/null | head -1 || true)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The 2>/dev/null here is redundant because the existence of $dispatch_todo_file is already checked in the preceding if condition. Suppressing stderr can hide other potential grep errors, such as issues with the regex pattern. It's better to remove the suppression and let || true handle any non-zero exit code from grep if no match is found.

Suggested change
todo_line=$(grep -E "^[[:space:]]*- \[.\] $task_id( |$)" "$dispatch_todo_file" 2>/dev/null | head -1 || true)
todo_line=$(grep -E "^[[:space:]]*- \[. \] $task_id( |$)" "$dispatch_todo_file" | head -1 || true)
References
  1. Avoid using 2>/dev/null to suppress errors on file operations if the file's existence has already been verified by a preceding check (e.g., [[ -f "$file" ]] or an early return). This practice is redundant for 'file not found' errors and can mask other important issues like permissions problems.

local dispatch_blockers
if dispatch_blockers=$(is_task_blocked "$todo_line" "$dispatch_todo_file"); then
log_warn "Task $task_id blocked by unresolved dependencies ($dispatch_blockers) at dispatch — returning to queued (t1314)"
cmd_unclaim "$task_id" "${trepo:-.}" --force 2>/dev/null || true
Copy link
Copy Markdown

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 for cmd_unclaim can hide important errors related to file permissions, git operations, or other issues within the unclaim logic. Since the command is followed by || true, the script won't exit on failure, so it's safer to let stderr be visible for debugging purposes.

Suggested change
cmd_unclaim "$task_id" "${trepo:-.}" --force 2>/dev/null || true
cmd_unclaim "$task_id" "${trepo:-.}" --force || true
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

AND t.status = 'queued'
)
ORDER BY b.created_at ASC;
" 2>/dev/null || echo "")
Copy link
Copy Markdown

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 can hide important diagnostic information, such as syntax errors in the SQL query or problems with the database file itself. Since the command already uses || echo "" to prevent script termination on failure, it's better to allow errors to be printed to stderr for easier debugging. This pattern appears multiple times in this file.

Suggested change
" 2>/dev/null || echo "")
" || echo "")
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

Comment on lines +1959 to +1968
next_tasks=$(db -separator $'\t' "$SUPERVISOR_DB" "
SELECT id, repo, description, model
FROM tasks
WHERE status = 'queued'
AND retries < max_retries
AND (rate_limit_until IS NULL OR rate_limit_until <= strftime('%Y-%m-%dT%H:%M:%SZ','now'))
AND NOT EXISTS (SELECT 1 FROM batch_tasks bt WHERE bt.task_id = id)
ORDER BY retries ASC, created_at ASC
LIMIT 10;
" 2>/dev/null || echo "")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This database query duplicates the logic already present in the cmd_next function when called with an empty batch ID. To improve maintainability and avoid code duplication, you should call cmd_next "" 10 here instead of re-implementing the query.

Suggested change
next_tasks=$(db -separator $'\t' "$SUPERVISOR_DB" "
SELECT id, repo, description, model
FROM tasks
WHERE status = 'queued'
AND retries < max_retries
AND (rate_limit_until IS NULL OR rate_limit_until <= strftime('%Y-%m-%dT%H:%M:%SZ','now'))
AND NOT EXISTS (SELECT 1 FROM batch_tasks bt WHERE bt.task_id = id)
ORDER BY retries ASC, created_at ASC
LIMIT 10;
" 2>/dev/null || echo "")
next_tasks=$(cmd_next "" 10)

WHERE bt.task_id = '$(sql_escape "$cid")'
AND b.status IN ('active', 'running')
LIMIT 1;
" 2>/dev/null || echo "")
Copy link
Copy Markdown

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 can hide important diagnostic information, such as syntax errors in the SQL query or problems with the database file itself. Since the command already uses || echo "" to prevent script termination on failure, it's better to allow errors to be printed to stderr for easier debugging.

Suggested change
" 2>/dev/null || echo "")
" || echo "")
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

@marcusquinn marcusquinn merged commit fb96524 into main Feb 25, 2026
19 checks passed
@marcusquinn marcusquinn deleted the feature/t1314 branch February 25, 2026 00:30
marcusquinn added a commit that referenced this pull request Feb 25, 2026
 and obsoleted by supervisor refactor #2291

The entire bash supervisor (37K lines) was replaced with a 123-line AI pulse
system. All 5 contributing causes (cmd_pulse batch detection, auto-batch
affinity, cmd_next sibling limits, blocked-by enforcement, auto-merge gate)
no longer exist in the codebase. Closes GH#2163.
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.

1 participant