Skip to content

t1423: Add priority-class worker reservations for per-repo concurrency fairness#3966

Merged
marcusquinn merged 2 commits intomainfrom
feature/t1423-priority-class-reservations
Mar 9, 2026
Merged

t1423: Add priority-class worker reservations for per-repo concurrency fairness#3966
marcusquinn merged 2 commits intomainfrom
feature/t1423-priority-class-reservations

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Mar 9, 2026

Summary

  • Product repos get a guaranteed minimum share of worker slots (default 60%), preventing tooling hygiene from starving user-facing work
  • Soft reservation: when one class has no pending work, the other can use freed slots
  • Merges and CI fixes are exempt from class checks (always proceed)

Changes

  • pulse-wrapper.sh: Add PRODUCT_RESERVATION_PCT config, calculate_priority_allocations() function, _append_priority_allocations() state output
  • pulse.md: Add priority-class enforcement section in Step 1, update priority order item 5, update Hard Rule 6
  • t1423-brief.md: Task brief

Testing

  • ShellCheck clean (SC1091 only — pre-existing, info-level)
  • 6 unit tests passing: normal case, small pool, 1 worker edge case, no tooling repos, no product repos, real config
  • Integration test: sourced functions, verified allocation file and state output

Current allocation (4 product, 4 tooling repos, ~6 workers)

Product repos: 4 reserved slots (60% minimum)
Tooling repos: 2 slots (remainder)

Closes #3965

Summary by CodeRabbit

  • New Features

    • Implemented priority-class enforcement for worker dispatch, ensuring product work receives minimum guaranteed resource allocation.
    • Added configurable resource reservation system to intelligently balance product and tooling work distribution.
  • Documentation

    • Enhanced dispatch guidance to document priority-class enforcement and dynamic resource allocation behavior.

… fairness (t1423)

Product repos get a guaranteed minimum share of worker slots (default 60%).
Tooling repos get the remainder. Soft reservation — when one class has no
pending work, the other can use freed slots.

- Add PRODUCT_RESERVATION_PCT config (default 60%)
- Add calculate_priority_allocations() to pulse-wrapper.sh
- Add _append_priority_allocations() to surface data in pulse state
- Update pulse.md Step 1 with class enforcement guidance
- Update priority order and Hard Rule 6

Closes #3965
@github-actions github-actions bot added the enhancement Auto-created from TODO.md tag label Mar 9, 2026
@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 introduces a sophisticated worker slot allocation mechanism within the pulse supervisor, designed to ensure fair concurrency between product and tooling development efforts. By reserving a minimum percentage of worker slots for product repositories, it prevents critical user-facing work from being stalled by high volumes of internal hygiene tasks. The system incorporates a flexible soft reservation approach, allowing dynamic reallocation of unused slots, and prioritizes essential operations like merges and CI fixes to maintain continuous integration and delivery pipelines.

Highlights

  • Worker Slot Reservation: Implemented a priority-class worker reservation system to guarantee a minimum share of worker slots (default 60%) for product repositories, preventing tooling work from monopolizing resources.
  • Soft Reservation Logic: Introduced soft reservation, allowing worker slots reserved for one class to be utilized by the other class if the reserved class has no pending work.
  • Exemptions for Critical Tasks: Established exemptions for merges and CI fixes, ensuring these critical tasks always proceed regardless of priority-class worker slot allocations.
  • Configuration and Allocation Logic: Added PRODUCT_RESERVATION_PCT configuration to pulse-wrapper.sh and developed calculate_priority_allocations() and _append_priority_allocations() functions to manage and report worker slot distribution.
  • Documentation Updates: Updated pulse.md to include detailed priority-class enforcement rules, revised priority order item 5, and modified Hard Rule 6 to reflect the new reservation system.
Changelog
  • .agents/scripts/commands/pulse.md
    • Added a new section detailing priority-class worker enforcement, including rules for dispatching tooling and product workers, and exemptions for merges and CI fixes.
    • Updated the priority order to emphasize that product repos are prioritized over tooling, enforced by the new reservation system.
    • Modified Hard Rule 6 to explicitly prohibit exceeding MAX_WORKERS or violating priority-class reservations.
  • .agents/scripts/pulse-wrapper.sh
    • Introduced PRODUCT_RESERVATION_PCT configuration variable with a default value of 60% for product worker slot reservation.
    • Implemented calculate_priority_allocations() to determine product and tooling repo counts and compute PRODUCT_MIN and TOOLING_MAX based on the reservation percentage.
    • Added _append_priority_allocations() to format and output the calculated allocation data into the state file for the pulse agent.
    • Integrated the calculate_priority_allocations function into the main script's execution flow.
  • todo/tasks/t1423-brief.md
    • Created a new markdown document outlining the summary, rationale, implementation steps, acceptance criteria, and context for the t1423 task, which introduces priority-class worker reservations.
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 9, 2026

Warning

Rate limit exceeded

@marcusquinn has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 32 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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9d3dc7c0-e569-4008-9439-7689d1ac331d

📥 Commits

Reviewing files that changed from the base of the PR and between 60558ea and 8f83366.

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

Walkthrough

This change introduces a priority-class worker reservation system (t1423) to enforce fair per-repo concurrency. It adds configuration-driven allocation calculations in the wrapper script and enforcement logic in dispatch documentation, reserving product-repo slots via a configurable percentage while allowing soft sharing when one class has no pending work.

Changes

Cohort / File(s) Summary
Documentation Updates
.agents/scripts/commands/pulse.md
Enhanced dispatch logic narrative with new Priority-class enforcement (t1423) section. Updated hard rules to include priority-class reservation constraints. Clarified product-over-tooling prioritization and added explicit guidance for using pre-fetched state data in dispatch decisions.
Allocation Calculation & Integration
.agents/scripts/pulse-wrapper.sh
Added PRODUCT_RESERVATION_PCT configuration variable (default 60%), new calculate_priority_allocations() function to compute per-class slot reservations, and _append_priority_allocations() to surface allocations in state output. Integrated into pulse setup workflow to expose priority allocations for agent dispatch enforcement.

Sequence Diagram(s)

sequenceDiagram
    participant PulseWrapper as pulse-wrapper.sh
    participant State as State File
    participant PulseAgent as Pulse Agent
    participant Dispatch as Dispatch Logic

    PulseWrapper->>PulseWrapper: calculate_priority_allocations()
    Note over PulseWrapper: Read repos.json, MAX_WORKERS,<br/>PRODUCT_RESERVATION_PCT
    PulseWrapper->>State: Write pulse-priority-allocations
    PulseWrapper->>State: _append_priority_allocations()<br/>to STATE_FILE
    
    PulseAgent->>State: Read prefetched state
    Note over PulseAgent: Extract PRODUCT_MIN<br/>and TOOLING_MAX
    
    PulseAgent->>Dispatch: Check target repo class
    alt Tooling Worker & Product Below Minimum
        Dispatch->>Dispatch: Reserve slots, skip tooling
        Dispatch->>Dispatch: Dispatch product work
    else Product Worker or Class Has No Pending
        Dispatch->>Dispatch: Proceed with dispatch
    end
    Dispatch->>State: Update worker allocation state
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

🎯 Fair queues align,
Product slots reserved divine,
Tooling gets the rest—
When product takes a rest,
Priority harmonies combine! ✨

🚥 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 accurately describes the main change: introducing priority-class worker reservations for concurrency fairness, with clear reference to the task ID (t1423).
Linked Issues check ✅ Passed The PR fully implements the requirements from issue #3965: priority-class reservations with 60% default product allocation, soft reservation behavior, exemptions for merges/CI fixes, and documentation updates.
Out of Scope Changes check ✅ Passed All changes are scoped to the stated objective: pulse-wrapper.sh configuration and allocation functions, pulse.md documentation updates, and t1423 task brief—no unrelated modifications present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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
  • Commit unit tests in branch feature/t1423-priority-class-reservations

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 9, 2026

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Mon Mar 9 06:16:54 UTC 2026: Code review monitoring started
Mon Mar 9 06:16:55 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 398

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 398
  • VULNERABILITIES: 0

Generated on: Mon Mar 9 06:16:57 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

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 a priority-class worker reservation system, which is a great improvement for ensuring fairness. My review focuses on improving the implementation's efficiency and robustness. I've identified a high-severity logic bug in an edge case, an opportunity to make the jq processing more efficient, and a couple of places where error suppression should be removed to aid debugging, in line with repository guidelines.

Comment on lines +3203 to +3205
if [[ "$product_min" -ge "$max_workers" && "$tooling_repos" -gt 0 ]]; then
product_min=$((max_workers - 1))
fi

Choose a reason for hiding this comment

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

high

There's a logic issue in this block for the edge case where max_workers is 1. If there is one worker slot and at least one tooling repo, the current logic will set product_min to 0, giving the only available slot to a tooling repo. This defeats the purpose of guaranteeing a minimum share for product repos. The fix is to ensure this logic only applies when there is more than one worker to distribute.

Suggested change
if [[ "$product_min" -ge "$max_workers" && "$tooling_repos" -gt 0 ]]; then
product_min=$((max_workers - 1))
fi
if [[ "$max_workers" -gt 1 && "$product_min" -ge "$max_workers" && "$tooling_repos" -gt 0 ]]; then
product_min=$((max_workers - 1))
fi

Comment on lines +48 to +49
PRODUCT_MIN=$(grep '^PRODUCT_MIN=' ~/.aidevops/logs/pulse-priority-allocations 2>/dev/null | cut -d= -f2 || echo 0)
TOOLING_MAX=$(grep '^TOOLING_MAX=' ~/.aidevops/logs/pulse-priority-allocations 2>/dev/null | cut -d= -f2 || echo "$MAX_WORKERS")

Choose a reason for hiding this comment

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

medium

In alignment with repository guidelines to avoid blanket error suppression, it's better to remove 2>/dev/null. This change will make potential issues like file-not-found or permission errors visible in the logs, which is valuable for debugging, while the || construct will still correctly handle default values when the file is missing or a key isn't found.

Suggested change
PRODUCT_MIN=$(grep '^PRODUCT_MIN=' ~/.aidevops/logs/pulse-priority-allocations 2>/dev/null | cut -d= -f2 || echo 0)
TOOLING_MAX=$(grep '^TOOLING_MAX=' ~/.aidevops/logs/pulse-priority-allocations 2>/dev/null | cut -d= -f2 || echo "$MAX_WORKERS")
PRODUCT_MIN=$(grep '^PRODUCT_MIN=' ~/.aidevops/logs/pulse-priority-allocations | cut -d= -f2 || echo 0)
TOOLING_MAX=$(grep '^TOOLING_MAX=' ~/.aidevops/logs/pulse-priority-allocations | cut -d= -f2 || echo "$MAX_WORKERS")
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 +3179 to +3182
product_repos=$(jq '[.initialized_repos[] | select(.pulse == true and (.local_only // false) == false and .slug != "" and .priority == "product")] | length' "$repos_json" 2>/dev/null) || product_repos=0
tooling_repos=$(jq '[.initialized_repos[] | select(.pulse == true and (.local_only // false) == false and .slug != "" and .priority == "tooling")] | length' "$repos_json" 2>/dev/null) || tooling_repos=0
[[ "$product_repos" =~ ^[0-9]+$ ]] || product_repos=0
[[ "$tooling_repos" =~ ^[0-9]+$ ]] || tooling_repos=0

Choose a reason for hiding this comment

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

medium

These four lines can be replaced with a more efficient and robust approach. The current implementation calls jq twice, parsing the same large JSON file each time. A single jq process can calculate both counts at once. This also provides an opportunity to remove 2>/dev/null, which aligns with repository guidelines to not suppress stderr, making debugging easier. The suggested code consolidates the counting, removes the error suppression, and uses a cleaner way to set default values.

Suggested change
product_repos=$(jq '[.initialized_repos[] | select(.pulse == true and (.local_only // false) == false and .slug != "" and .priority == "product")] | length' "$repos_json" 2>/dev/null) || product_repos=0
tooling_repos=$(jq '[.initialized_repos[] | select(.pulse == true and (.local_only // false) == false and .slug != "" and .priority == "tooling")] | length' "$repos_json" 2>/dev/null) || tooling_repos=0
[[ "$product_repos" =~ ^[0-9]+$ ]] || product_repos=0
[[ "$tooling_repos" =~ ^[0-9]+$ ]] || tooling_repos=0
read -r product_repos tooling_repos < <(jq -r '
.initialized_repos |
map(select(.pulse == true and (.local_only // false) == false and .slug != "")) |
[
(map(select(.priority == "product")) | length),
(map(select(.priority == "tooling")) | length)
] | @tsv
' "$repos_json")
product_repos=${product_repos:-0}
tooling_repos=${tooling_repos:-0}
References
  1. Consolidate multiple 'jq' calls into a single pass where possible to improve performance and script efficiency.
  2. 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.

- Fix 1-worker edge case: product keeps the only slot (was giving it to tooling)
- Consolidate two jq calls into single pass for efficiency
- Keep 2>/dev/null on jq (repos.json may not exist during first run)
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/scripts/commands/pulse.md:
- Line 1049: Update the hard rule that currently reads "NEVER exceed MAX_WORKERS
or violate priority-class reservations" to mirror Step 1 exemptions: explicitly
allow merges and priority-2 CI-fix dispatches to bypass class reservations and
consume product-reserved slots when those specific conditions apply; modify the
sentence in the hard-rules section (the rule text containing "NEVER exceed
MAX_WORKERS or violate priority-class reservations") to include an exception
clause referencing "merges" and "priority-2 CI-fix dispatches" so the stronger
wording does not block the intended bypass behavior described in Step 1.
- Around line 62-65: Update the predicate that determines "product repos have
pending work" (used in the tooling dispatch rule where it checks product_active
< PRODUCT_MIN) to include not only open issues and failing-CI PRs but also
mission features, salvage work, review-fix work, approved debt tasks, and any
other dispatchable product work types (e.g., approved/ready backlog items and
orphaned PRs); ensure the documentation and the soft-reservation rule (when
product repos have no pending work their reserved slots become available)
reference the expanded set so tooling dispatch correctly skips when any of these
product work categories exist.

In @.agents/scripts/pulse-wrapper.sh:
- Around line 3202-3205: The branch that forces product_min down to
max_workers-1 incorrectly runs when max_workers==1 and tooling_repos>0, allowing
tooling to steal the only slot; update the conditional around product_min
adjustment (the block referencing product_min, max_workers, tooling_repos) so it
only reduces product_min when max_workers is greater than 1 (e.g. add a
"$max_workers" -gt 1 check) so you don't lower product_min to 0 when there's a
single worker.
- Around line 3168-3170: The conditional that checks if "$repos_json" exists or
jq is available returns early without touching the previous allocation state;
update this branch so that before returning it truncates or overwrites
"$alloc_file" (the pulse-priority-allocations state file) to an empty/clean
state and logs that action to "$LOGFILE" (same context as the existing message),
ensuring stale reservations are removed when repos.json or jq is missing; locate
the check referencing "$repos_json", "jq", "$LOGFILE" and "$alloc_file" and add
the truncation/overwrite step immediately before the return.
- Around line 3184-3217: Clamp PRODUCT_RESERVATION_PCT to a maximum of 100
before using it to compute product_min and before persisting it to the
allocation file: introduce a local variable (e.g., pct_clamped) or overwrite
PRODUCT_RESERVATION_PCT with its min(PRODUCT_RESERVATION_PCT,100) value, use
that clamped value in the product_min calculation (the block that computes
product_min and tooling_max) and write the clamped value to the alloc_file
instead of the raw PRODUCT_RESERVATION_PCT so the allocation math and output
cannot exceed 100%.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 504a3728-5eff-4d5f-a538-75f9a690400b

📥 Commits

Reviewing files that changed from the base of the PR and between 762ca46 and 60558ea.

⛔ Files ignored due to path filters (1)
  • todo/tasks/t1423-brief.md is excluded by !todo/tasks/*-brief.md
📒 Files selected for processing (2)
  • .agents/scripts/commands/pulse.md
  • .agents/scripts/pulse-wrapper.sh

Comment on lines +62 to +65
3. **If dispatching a tooling worker:** check whether product-class workers are using fewer than `PRODUCT_MIN` slots. If `product_active < PRODUCT_MIN` AND product repos have pending work (open issues or failing PRs), the remaining product slots are **reserved** — skip the tooling dispatch and look for product work instead.
4. **If dispatching a product worker:** always proceed — product has no ceiling (only a floor).
5. **Exemptions:** Merges (priority 1) and CI-fix dispatches (priority 2) are exempt from class checks — they always proceed regardless of class.
6. **Soft reservation:** When product repos have no pending work (no open issues, no failing-CI PRs, no orphaned PRs), their reserved slots become available for tooling. The reservation protects product work when it exists, not when it doesn't.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reserve product slots for any dispatchable product work, not just issues/failing PRs.

This check treats product repos as idle when they only have mission features, salvage work, review-fix work, or approved debt tasks pending. In that case tooling can still consume the reserved pool, which defeats the fairness guarantee for product repos.

Suggested fix
-3. **If dispatching a tooling worker:** check whether product-class workers are using fewer than `PRODUCT_MIN` slots. If `product_active < PRODUCT_MIN` AND product repos have pending work (open issues or failing PRs), the remaining product slots are **reserved** — skip the tooling dispatch and look for product work instead.
+3. **If dispatching a tooling worker:** check whether product-class workers are using fewer than `PRODUCT_MIN` slots. If `product_active < PRODUCT_MIN` AND product repos have any dispatchable pending work (for example: open issues, failing/review-fix PRs, orphaned/salvage PRs, active mission features, or approved debt tasks), the remaining product slots are **reserved** — skip the tooling dispatch and look for product work instead.
@@
-6. **Soft reservation:** When product repos have no pending work (no open issues, no failing-CI PRs, no orphaned PRs), their reserved slots become available for tooling. The reservation protects product work when it exists, not when it doesn't.
+6. **Soft reservation:** When product repos have no dispatchable pending work, their reserved slots become available for tooling. The reservation protects product work when it exists, not when it doesn't.
🧰 Tools
🪛 LanguageTool

[style] ~63-~63: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...and look for product work instead. 4. If dispatching a product worker: always ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

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

In @.agents/scripts/commands/pulse.md around lines 62 - 65, Update the predicate
that determines "product repos have pending work" (used in the tooling dispatch
rule where it checks product_active < PRODUCT_MIN) to include not only open
issues and failing-CI PRs but also mission features, salvage work, review-fix
work, approved debt tasks, and any other dispatchable product work types (e.g.,
approved/ready backlog items and orphaned PRs); ensure the documentation and the
soft-reservation rule (when product repos have no pending work their reserved
slots become available) reference the expanded set so tooling dispatch correctly
skips when any of these product work categories exist.

4. **NEVER use `claude` CLI.** Always `opencode run`.
5. **NEVER include private repo names** in public issue titles/bodies/comments.
6. **NEVER exceed MAX_WORKERS.** Count before dispatching.
6. **NEVER exceed MAX_WORKERS or violate priority-class reservations.** Count before dispatching. Check class allocations (Step 1) — tooling workers must not consume product-reserved slots when product work is pending.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mirror the Step 1 exemptions in the hard rule.

Step 1 explicitly allows merges and priority-2 CI-fix dispatches to bypass class reservations, but this hard rule reads like an absolute prohibition. Because the hard-rules section has stronger wording, the agent can end up blocking the exempt cases you intended to keep moving.

Suggested fix
-6. **NEVER exceed MAX_WORKERS or violate priority-class reservations.** Count before dispatching. Check class allocations (Step 1) — tooling workers must not consume product-reserved slots when product work is pending.
+6. **NEVER exceed MAX_WORKERS.** For normal dispatches, do not violate priority-class reservations. Count before dispatching. Check class allocations (Step 1) — tooling workers must not consume product-reserved slots when product work is pending. **Exemption:** merges and priority-2 CI-fix dispatches still proceed as described in Step 1.
🧰 Tools
🪛 LanguageTool

[style] ~1049-~1049: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...blic issue titles/bodies/comments. 6. **NEVER exceed MAX_WORKERS or violate priority-...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

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

In @.agents/scripts/commands/pulse.md at line 1049, Update the hard rule that
currently reads "NEVER exceed MAX_WORKERS or violate priority-class
reservations" to mirror Step 1 exemptions: explicitly allow merges and
priority-2 CI-fix dispatches to bypass class reservations and consume
product-reserved slots when those specific conditions apply; modify the sentence
in the hard-rules section (the rule text containing "NEVER exceed MAX_WORKERS or
violate priority-class reservations") to include an exception clause referencing
"merges" and "priority-2 CI-fix dispatches" so the stronger wording does not
block the intended bypass behavior described in Step 1.

Comment on lines +3168 to +3170
if [[ ! -f "$repos_json" ]] || ! command -v jq &>/dev/null; then
echo "[pulse-wrapper] repos.json or jq not available — skipping priority allocations" >>"$LOGFILE"
return 0
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Delete stale allocation state on the skip path.

If repos.json or jq is unavailable here, the function returns without touching the previous pulse-priority-allocations file. Step 1 in pulse.md reads that file directly, so the next pulse can enforce stale reservations instead of falling back to the flat-pool behavior. Clear or overwrite "$alloc_file" before returning. As per coding guidelines, automation scripts should focus on reliability and robustness.

Suggested fix
 if [[ ! -f "$repos_json" ]] || ! command -v jq &>/dev/null; then
+	rm -f "$alloc_file"
 	echo "[pulse-wrapper] repos.json or jq not available — skipping priority allocations" >>"$LOGFILE"
 	return 0
 fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [[ ! -f "$repos_json" ]] || ! command -v jq &>/dev/null; then
echo "[pulse-wrapper] repos.json or jq not available — skipping priority allocations" >>"$LOGFILE"
return 0
if [[ ! -f "$repos_json" ]] || ! command -v jq &>/dev/null; then
rm -f "$alloc_file"
echo "[pulse-wrapper] repos.json or jq not available — skipping priority allocations" >>"$LOGFILE"
return 0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/pulse-wrapper.sh around lines 3168 - 3170, The conditional
that checks if "$repos_json" exists or jq is available returns early without
touching the previous allocation state; update this branch so that before
returning it truncates or overwrites "$alloc_file" (the
pulse-priority-allocations state file) to an empty/clean state and logs that
action to "$LOGFILE" (same context as the existing message), ensuring stale
reservations are removed when repos.json or jq is missing; locate the check
referencing "$repos_json", "jq", "$LOGFILE" and "$alloc_file" and add the
truncation/overwrite step immediately before the return.

Comment on lines +3184 to +3217
# Calculate reservations
# product_min = ceil(max_workers * PRODUCT_RESERVATION_PCT / 100)
# Using integer arithmetic: ceil(a/b) = (a + b - 1) / b
local product_min tooling_max
if [[ "$product_repos" -eq 0 ]]; then
# No product repos — all slots available for tooling
product_min=0
tooling_max="$max_workers"
elif [[ "$tooling_repos" -eq 0 ]]; then
# No tooling repos — all slots available for product
product_min="$max_workers"
tooling_max=0
else
product_min=$(((max_workers * PRODUCT_RESERVATION_PCT + 99) / 100))
# Ensure product_min doesn't exceed max_workers
if [[ "$product_min" -gt "$max_workers" ]]; then
product_min="$max_workers"
fi
# Ensure at least 1 slot for tooling when tooling repos exist
if [[ "$product_min" -ge "$max_workers" && "$tooling_repos" -gt 0 ]]; then
product_min=$((max_workers - 1))
fi
tooling_max=$((max_workers - product_min))
fi

# Write allocation file (key=value, readable by pulse.md)
{
echo "MAX_WORKERS=${max_workers}"
echo "PRODUCT_REPOS=${product_repos}"
echo "TOOLING_REPOS=${tooling_repos}"
echo "PRODUCT_MIN=${product_min}"
echo "TOOLING_MAX=${tooling_max}"
echo "PRODUCT_RESERVATION_PCT=${PRODUCT_RESERVATION_PCT}"
} >"$alloc_file"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clamp reservation percentages above 100 before persisting them.

A value like PRODUCT_RESERVATION_PCT=150 currently produces impossible state output (150% minimum) and allocation math that operators will not expect. Clamp to 100 before computing and writing the allocation file.

Suggested fix
-	local product_min tooling_max
+	local reservation_pct="$PRODUCT_RESERVATION_PCT"
+	local product_min tooling_max
+	if [[ "$reservation_pct" -gt 100 ]]; then
+		reservation_pct=100
+	fi
 	if [[ "$product_repos" -eq 0 ]]; then
 		# No product repos — all slots available for tooling
 		product_min=0
 		tooling_max="$max_workers"
 	elif [[ "$tooling_repos" -eq 0 ]]; then
@@
-		product_min=$(((max_workers * PRODUCT_RESERVATION_PCT + 99) / 100))
+		product_min=$(((max_workers * reservation_pct + 99) / 100))
@@
-		echo "PRODUCT_RESERVATION_PCT=${PRODUCT_RESERVATION_PCT}"
+		echo "PRODUCT_RESERVATION_PCT=${reservation_pct}"
 	} >"$alloc_file"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Calculate reservations
# product_min = ceil(max_workers * PRODUCT_RESERVATION_PCT / 100)
# Using integer arithmetic: ceil(a/b) = (a + b - 1) / b
local product_min tooling_max
if [[ "$product_repos" -eq 0 ]]; then
# No product repos — all slots available for tooling
product_min=0
tooling_max="$max_workers"
elif [[ "$tooling_repos" -eq 0 ]]; then
# No tooling repos — all slots available for product
product_min="$max_workers"
tooling_max=0
else
product_min=$(((max_workers * PRODUCT_RESERVATION_PCT + 99) / 100))
# Ensure product_min doesn't exceed max_workers
if [[ "$product_min" -gt "$max_workers" ]]; then
product_min="$max_workers"
fi
# Ensure at least 1 slot for tooling when tooling repos exist
if [[ "$product_min" -ge "$max_workers" && "$tooling_repos" -gt 0 ]]; then
product_min=$((max_workers - 1))
fi
tooling_max=$((max_workers - product_min))
fi
# Write allocation file (key=value, readable by pulse.md)
{
echo "MAX_WORKERS=${max_workers}"
echo "PRODUCT_REPOS=${product_repos}"
echo "TOOLING_REPOS=${tooling_repos}"
echo "PRODUCT_MIN=${product_min}"
echo "TOOLING_MAX=${tooling_max}"
echo "PRODUCT_RESERVATION_PCT=${PRODUCT_RESERVATION_PCT}"
} >"$alloc_file"
# Calculate reservations
# product_min = ceil(max_workers * PRODUCT_RESERVATION_PCT / 100)
# Using integer arithmetic: ceil(a/b) = (a + b - 1) / b
local reservation_pct="$PRODUCT_RESERVATION_PCT"
local product_min tooling_max
if [[ "$reservation_pct" -gt 100 ]]; then
reservation_pct=100
fi
if [[ "$product_repos" -eq 0 ]]; then
# No product repos — all slots available for tooling
product_min=0
tooling_max="$max_workers"
elif [[ "$tooling_repos" -eq 0 ]]; then
# No tooling repos — all slots available for product
product_min="$max_workers"
tooling_max=0
else
product_min=$(((max_workers * reservation_pct + 99) / 100))
# Ensure product_min doesn't exceed max_workers
if [[ "$product_min" -gt "$max_workers" ]]; then
product_min="$max_workers"
fi
# Ensure at least 1 slot for tooling when tooling repos exist
if [[ "$product_min" -ge "$max_workers" && "$tooling_repos" -gt 0 ]]; then
product_min=$((max_workers - 1))
fi
tooling_max=$((max_workers - product_min))
fi
# Write allocation file (key=value, readable by pulse.md)
{
echo "MAX_WORKERS=${max_workers}"
echo "PRODUCT_REPOS=${product_repos}"
echo "TOOLING_REPOS=${tooling_repos}"
echo "PRODUCT_MIN=${product_min}"
echo "TOOLING_MAX=${tooling_max}"
echo "PRODUCT_RESERVATION_PCT=${reservation_pct}"
} >"$alloc_file"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/pulse-wrapper.sh around lines 3184 - 3217, Clamp
PRODUCT_RESERVATION_PCT to a maximum of 100 before using it to compute
product_min and before persisting it to the allocation file: introduce a local
variable (e.g., pct_clamped) or overwrite PRODUCT_RESERVATION_PCT with its
min(PRODUCT_RESERVATION_PCT,100) value, use that clamped value in the
product_min calculation (the block that computes product_min and tooling_max)
and write the clamped value to the alloc_file instead of the raw
PRODUCT_RESERVATION_PCT so the allocation math and output cannot exceed 100%.

@github-actions
Copy link

github-actions bot commented Mar 9, 2026

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Mon Mar 9 06:25:48 UTC 2026: Code review monitoring started
Mon Mar 9 06:25:49 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 398

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 398
  • VULNERABILITIES: 0

Generated on: Mon Mar 9 06:25:51 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 9, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Auto-created from TODO.md tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

t1423: Priority-class worker reservations for per-repo concurrency fairness

1 participant