Skip to content

feat: multi-agent orchestration & token efficiency (p013/t068)#158

Merged
marcusquinn merged 14 commits intomainfrom
feature/multi-agent-orchestration
Jan 24, 2026
Merged

feat: multi-agent orchestration & token efficiency (p013/t068)#158
marcusquinn merged 14 commits intomainfrom
feature/multi-agent-orchestration

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Jan 24, 2026

Summary

Implements the Multi-Agent Orchestration & Token Efficiency plan (p013/t068) - enabling parallel agent coordination with minimal token overhead.

Changes

  • Custom System Prompt (t068.1): .agent/prompts/build.txt replaces default Claude Code harness, eliminating conflicting instructions (e.g., "use Glob" vs "use git ls-files")
  • TOON Mailbox System (t068.4): mail-helper.sh - inter-agent messaging with per-agent inboxes, TOON format (40-60% token savings vs JSON), archive→remember→prune lifecycle
  • AGENTS.md Compression (t068.3): 778 → 219 lines (72% reduction), content moved to subagent-index.toon and workflows/pre-edit.md
  • Mailbox Awareness in Ralph Loop (t068.5): Agent registration on loop start, mailbox check during re-anchor, status reports to coordinator
  • Stateless Coordinator (t068.6): coordinator-helper.sh - pulse-based orchestration (~20K tokens per pulse), convoy grouping, fswatch/polling triggers
  • Model Routing (t068.7): Per-agent model tier assignments via YAML frontmatter, MODEL_TIERS dict mapping to actual model IDs

New Files

  • .agent/prompts/build.txt - Custom system prompt
  • .agent/scripts/mail-helper.sh - Inter-agent mailbox (656 lines)
  • .agent/scripts/coordinator-helper.sh - Stateless coordinator (397 lines)
  • .agent/subagent-index.toon - Compressed agent/subagent index
  • .agent/workflows/pre-edit.md - Detailed pre-edit workflow

Modified Files

  • .agent/AGENTS.md - Compressed (778 → 219 lines)
  • .agent/scripts/generate-opencode-agents.sh - Prompt + model routing
  • .agent/scripts/loop-common.sh - Mailbox awareness

Remaining (future PRs)

  • t068.2: Compaction Plugin (TypeScript) - now unblocked
  • t068.8: TUI Dashboard - now unblocked

Summary by CodeRabbit

  • New Features

    • Multi-agent coordinator with task dispatch/convoy and status/watch modes
    • Asynchronous mailbox for sending, reading, archiving, and pruning messages
    • Per-agent custom prompts and model-tier routing for agent configs
    • Workspace compaction plugin that gathers agent, memory, mailbox, and Git context
  • Documentation

    • Added comprehensive pre-edit workflow and prompt governance; condensed agent reference and file-discovery guidance
  • Chores

    • Prompt-drift check integrated into setup and prompt build/reference added

✏️ Tip: You can customize this high-level summary in your review settings.

Create prompts/build.txt with tool preferences, security rules, git workflow,
and quality standards that replace the default Claude Code system prompt.

This eliminates the harness conflict where the default prompt says 'use Glob'
while AGENTS.md says 'use git ls-files'. The custom prompt becomes highest
priority, surviving compaction.

Update generate-opencode-agents.sh to add 'prompt' field for Build+, AI-DevOps,
and Sisyphus agents referencing the deployed prompt file.
Create mail-helper.sh with send/check/read/archive/prune/status/register/
deregister/agents commands. Uses TOON format for token-efficient messages.

Features:
- Per-agent inbox directories with unread tracking
- Message types: task_dispatch, status_report, discovery, request, broadcast
- Priority levels (high/normal/low) and convoy grouping
- Agent registry with role, branch, worktree, and last_seen tracking
- Archive-remember-prune lifecycle (captures discoveries to memory before deletion)
- Auto-detects agent identity from worktree/branch name or AIDEVOPS_AGENT_ID env
…068.3)

Lossless compression - all content preserved via:
- Create subagent-index.toon with agents, subagents, workflows, scripts listings
- Move detailed pre-edit git check to workflows/pre-edit.md (keep 15-line trigger)
- Replace large markdown tables with TOON format references
- Compress planning, memory, MCP, git, session sections to pointers
- Add inter-agent mailbox section (new capability from t068.4)

Every removed line exists in a subagent, workflow, or the new index file.
Token savings: ~10K → ~3K tokens per session load.
Add mailbox integration to loop-common.sh:
- Register agent on loop start (role: worker, branch auto-detected)
- Check unread messages during re-anchor (included in context)
- Send status_report to coordinator on task completion

This enables the stateless coordinator to track worker progress
and workers to receive dispatch instructions between iterations.
Create coordinator-helper.sh with pulse/status/dispatch/convoy/watch commands.

Key design: STATELESS pulse pattern (not persistent like Gas Town's Mayor):
- Reads: registry.toon, outbox/*.toon, TODO.md
- Writes: inbox/*.toon (dispatch instructions)
- Stores: notable outcomes to memory
- Exits immediately (~20K token budget per pulse)

Features:
- Pulse: read state, process reports, dispatch to idle workers
- Convoy: group related tasks for batch assignment
- Watch: fswatch-driven or polling trigger
- Status: show agent registry and mailbox state
- Memory integration: stores worker reports for long-term context
Add model tier system for cost-effective agent routing:
- Tiers: haiku (triage), sonnet (code), opus (architecture), flash/pro (Gemini)
- Agents declare tier in YAML frontmatter 'model:' field
- generate-opencode-agents.sh resolves tier to model identifier
- Default tiers assigned per agent (overridable via frontmatter)
- Model tier listing added to subagent-index.toon

This enables the coordinator to dispatch tasks to appropriate
model tiers - cheap models for routing, capable for code.
- SC2155: Declare and assign separately in mail-helper.sh
- SC2034: Prefix unused read variables with underscore
- SC2086: Use array for optional arguments instead of unquoted variable
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

Warning

Rate limit exceeded

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

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a TOON mailbox and stateless pulse coordinator, mailbox/loop integration, agent prompt corpus and model-tier routing, prompt-drift detection, an Aidevops compaction plugin, and condenses agent workflow/docs.

Changes

Cohort / File(s) Summary
Mailbox / Messaging
.agent/scripts/mail-helper.sh
New TOON mailbox CLI (inbox/outbox/archive, registry). Implements send, check, read, archive, prune, status, register, deregister, agents and message lifecycle with TOON headers.
Coordinator / Orchestration
.agent/scripts/coordinator-helper.sh
New stateless pulse coordinator with pulse/status/dispatch/convoy/watch; reads registry/outbox/TODO.md, derives ready tasks, dispatches tasks to idle agents, archives reports and emits pulse summaries.
Agent prompts & model routing
.agent/prompts/build.txt, .agent/scripts/generate-opencode-agents.sh
Adds comprehensive prompt corpus and augments agent generation with AGENT_PROMPTS, MODEL_TIERS, AGENT_MODEL_TIERS. get_agent_config(..., model_tier) resolves model routing and custom prompts; logging added.
Loop integration & mailbox hooks
.agent/scripts/loop-common.sh
Introduces LOOP_* constants (LOOP_MEMORY_HELPER, LOOP_MAIL_HELPER, LOOP_REANCHOR_FILE, etc.). Replaces hardcoded memory-helper usage, registers mailbox at loop start, posts status_report on completion, and surfaces unread mailbox messages in re-anchor/guardrails.
Prompt drift & setup
.agent/scripts/opencode-prompt-drift-check.sh, setup.sh
New drift-check script comparing local build.txt header hash to upstream file via GitHub API; setup.sh runs drift check during setup and registers local aidevops compaction plugin when present.
Aidevops compaction plugin
.agent/plugins/opencode-aidevops/index.mjs, .agent/plugins/opencode-aidevops/package.json
New plugin exporting AidevopsPlugin({ directory }) with experimental.session.compacting handler that collects agent state, guardrails, memories, git and mailbox context; package manifest added.
Docs & workflows / Index data
.agent/AGENTS.md, .agent/workflows/pre-edit.md, .agent/subagent-index.toon
Condensed .agent/AGENTS.md, added detailed .agent/workflows/pre-edit.md, and new declarative .agent/subagent-index.toon metadata for agents, model_tiers, subagents, workflows, and scripts.
Prompt corpus
.agent/prompts/build.txt
New build prompt governing agent behavior, formatting, tool usage, security, and workflow rules.

Sequence Diagram(s)

sequenceDiagram
    participant Coord as Coordinator
    participant Reg as Registry
    participant Mail as Mailbox
    participant TODO as TODO.md
    participant Agent as Worker

    loop Pulse cycle
        Coord->>Reg: get_active_agents()
        Reg-->>Coord: agents list

        Coord->>Mail: get_worker_reports()
        Mail-->>Coord: reports

        Coord->>TODO: get_ready_tasks()
        TODO-->>Coord: ready tasks

        alt ready tasks && idle agent available
            Coord->>Mail: send(task → selected_agent)
            Mail-->>Agent: deliver message (inbox)
            Agent-->>Mail: send(status_report)
            Mail-->>Coord: archived report
        else no dispatch
            Coord-->>Coord: no-op / log
        end

        Coord-->>Coord: emit pulse_summary
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

Mailboxes hum as pulses chime,
Coordinators hand out time.
Prompts align and drift stays clear,
Agents whisper tasks near.
Tiny scripts make DevOps rhyme.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main changes: multi-agent orchestration and token efficiency, matching the PR's core objectives and substantial architectural additions.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

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


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

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 multi-agent orchestration capabilities and token efficiency of the system. It introduces a robust inter-agent communication system, streamlines agent configuration and documentation, and implements a stateless coordinator for dynamic task dispatching. These changes aim to improve the scalability, cost-effectiveness, and overall intelligence of the AI agents by optimizing their operational context and communication overhead.

Highlights

  • Custom System Prompt: Introduced a custom system prompt (.agent/prompts/build.txt) to replace the default Claude Code harness, resolving conflicting instructions and ensuring consistent agent behavior across specific agents like Build+, AI-DevOps, and Sisyphus.
  • TOON Mailbox System: Implemented mail-helper.sh for efficient inter-agent communication using the TOON format, significantly reducing token overhead (40-60% savings compared to JSON). This system includes per-agent inboxes, outboxes, archives, and a message lifecycle.
  • AGENTS.md Compression: Reduced the size of AGENTS.md by 72% (from 778 to 219 lines) by moving detailed content to subagent-index.toon and workflows/pre-edit.md, improving context efficiency for agents.
  • Mailbox Integration in Ralph Loop: Enhanced the Ralph Loop (loop-common.sh) to include agent registration at startup, regular mailbox checks during re-anchor phases, and automated status reporting to the coordinator upon task completion, enabling dynamic agent coordination.
  • Stateless Coordinator: Added coordinator-helper.sh to manage multi-agent orchestration through a pulse-based system. This stateless coordinator processes worker reports, dispatches tasks to idle agents, and supports convoy grouping, designed for minimal token usage (~20K tokens per pulse).
  • Model Routing: Introduced a mechanism for assigning specific model tiers (e.g., Haiku, Sonnet, Opus, Flash, Pro) to agents via YAML frontmatter in their definitions, allowing for cost-effective and performance-optimized model selection during agent execution.
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

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Jan 24 00:02:30 UTC 2026: Code review monitoring started
Sat Jan 24 00:02:31 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 351
Sat Jan 24 00:02:31 UTC 2026: Qlty - 0 issues found, auto-formatting applied
Sat Jan 24 00:02:33 UTC 2026: Codacy analysis completed with auto-fixes
Sat Jan 24 00:02:34 UTC 2026: Applied 2 automatic fixes
Sat Jan 24 00:03:48 UTC 2026: Applied 2 automatic fixes

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 351
  • VULNERABILITIES: 0

Generated on: Sat Jan 24 00:03:48 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 major refactoring for multi-agent orchestration and token efficiency. The changes are well-structured, introducing a new mailbox system, a stateless coordinator, and custom prompts. The documentation has been significantly improved by moving details from AGENTS.md into dedicated files. My review focuses on improving the robustness and efficiency of the new shell scripts. I've identified some critical race conditions in the agent registry management and some high-severity issues with argument parsing that could lead to script failures. I've also suggested a refactoring for performance in the coordinator script and a simplification in the main agent loop.

Comment on lines 507 to 532
# Create or update registry
if [[ ! -f "$REGISTRY_FILE" ]]; then
cat > "$REGISTRY_FILE" << EOF
<!--TOON:agents{id,role,branch,worktree,status,registered,last_seen}:
${agent_id},${role},${branch},${worktree},active,${timestamp},${timestamp}
-->
EOF
else
# Check if agent already registered
if grep -q "^${agent_id}," "$REGISTRY_FILE" 2>/dev/null; then
# Update last_seen and status
if command -v sed &>/dev/null; then
sed -i.bak "s|^${agent_id},.*|${agent_id},${role},${branch},${worktree},active,$(grep "^${agent_id}," "$REGISTRY_FILE" | cut -d',' -f6),${timestamp}|" "$REGISTRY_FILE"
rm -f "${REGISTRY_FILE}.bak"
fi
log_info "Updated agent: $agent_id (last_seen: $timestamp)"
else
# Add new agent before closing -->
if command -v sed &>/dev/null; then
sed -i.bak "/^-->$/i\\
${agent_id},${role},${branch},${worktree},active,${timestamp},${timestamp}" "$REGISTRY_FILE"
rm -f "${REGISTRY_FILE}.bak"
fi
log_success "Registered agent: $agent_id (role: $role, branch: $branch)"
fi
fi

Choose a reason for hiding this comment

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

critical

The cmd_register function modifies the shared $REGISTRY_FILE without any locking mechanism. This can lead to race conditions if multiple agents try to register simultaneously, potentially corrupting the registry file.

For example, the grep on line 516 and the sed on line 519 are not an atomic operation. Another agent could modify the file between these two commands.

You should implement a file-based locking mechanism (e.g., using flock or creating a .lock file with mkdir) to ensure that modifications to $REGISTRY_FILE are atomic and prevent data corruption.

Comment on lines +560 to +564
# Mark as inactive (don't remove - preserves history)
if command -v sed &>/dev/null; then
sed -i.bak "s|^\(${agent_id},.*\),active,|\\1,inactive,|" "$REGISTRY_FILE"
rm -f "${REGISTRY_FILE}.bak"
fi

Choose a reason for hiding this comment

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

critical

The cmd_deregister function modifies the shared $REGISTRY_FILE without any locking mechanism, which can lead to race conditions if multiple agents are registering or deregistering at the same time. This could corrupt the registry file.

A file-based locking mechanism should be used to ensure atomic updates to the registry.

Comment on lines 246 to 254
while [[ $# -gt 0 ]]; do
case "$1" in
--task) task="$2"; shift 2 ;;
--to) to="$2"; shift 2 ;;
--priority) priority="$2"; shift 2 ;;
--convoy) convoy="$2"; shift 2 ;;
*) log_error "Unknown option: $1"; return 1 ;;
esac
done

Choose a reason for hiding this comment

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

high

The argument parsing logic is not robust. If an option like --task is provided without a corresponding value, the script will fail due to shift 2 on an empty $2. You should add checks to ensure that arguments for options are present before trying to access or shift them.

Suggested change
while [[ $# -gt 0 ]]; do
case "$1" in
--task) task="$2"; shift 2 ;;
--to) to="$2"; shift 2 ;;
--priority) priority="$2"; shift 2 ;;
--convoy) convoy="$2"; shift 2 ;;
*) log_error "Unknown option: $1"; return 1 ;;
esac
done
while [[ $# -gt 0 ]]; do
case "$1" in
--task)
if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
task="$2"; shift 2 ;;
--to)
if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
to="$2"; shift 2 ;;
--priority)
if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
priority="$2"; shift 2 ;;
--convoy)
if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
convoy="$2"; shift 2 ;;
*) log_error "Unknown option: $1"; return 1 ;;
esac
done

Comment on lines 294 to 300
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--tasks) tasks="$2"; shift 2 ;;
*) log_error "Unknown option: $1"; return 1 ;;
esac
done

Choose a reason for hiding this comment

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

high

The argument parsing logic is not robust. If an option like --name is provided without a corresponding value, the script will fail. You should add checks to ensure that arguments for options are present.

Suggested change
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--tasks) tasks="$2"; shift 2 ;;
*) log_error "Unknown option: $1"; return 1 ;;
esac
done
while [[ $# -gt 0 ]]; do
case "$1" in
--name)
if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
name="$2"; shift 2 ;;
--tasks)
if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
tasks="$2"; shift 2 ;;
*) log_error "Unknown option: $1"; return 1 ;;
esac
done

Comment on lines 143 to 153
while [[ $# -gt 0 ]]; do
case "$1" in
--to) to="$2"; shift 2 ;;
--type) msg_type="$2"; shift 2 ;;
--payload) payload="$2"; shift 2 ;;
--priority) priority="$2"; shift 2 ;;
--convoy) convoy="$2"; shift 2 ;;
--from) from="$2"; shift 2 ;;
*) log_error "Unknown option: $1"; return 1 ;;
esac
done

Choose a reason for hiding this comment

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

high

The argument parsing logic is not robust. If an option like --to is provided without a corresponding value, the script will fail due to shift 2 on an empty $2. You should add checks to ensure that arguments for options are present before trying to access or shift them. This issue is present in other command functions in this script as well.

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --to)
                if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
                to="$2"; shift 2 ;;
            --type)
                if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
                msg_type="$2"; shift 2 ;;
            --payload)
                if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
                payload="$2"; shift 2 ;;
            --priority)
                if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
                priority="$2"; shift 2 ;;
            --convoy)
                if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
                convoy="$2"; shift 2 ;;
            --from)
                if [[ -z "${2-}" ]]; then log_error "Missing argument for $1"; return 1; fi
                from="$2"; shift 2 ;;
            *) log_error "Unknown option: $1"; return 1 ;;
        esac
    done

Comment on lines 110 to 145
# 2. Process worker reports (archive after reading)
local reports
reports=$(get_worker_reports)
if [[ -n "$reports" ]]; then
log_info "Worker reports:"
echo "$reports" | while IFS= read -r report; do
echo " $report"
done

# Store notable reports to memory
if [[ -x "$MEMORY_HELPER" ]]; then
echo "$reports" | while IFS= read -r report; do
if [[ -n "$report" ]]; then
"$MEMORY_HELPER" store \
--content "Coordinator received: $report" \
--type CONTEXT \
--tags "coordinator,status_report" 2>/dev/null || true
fi
done
fi

# Archive processed outbox messages
local outbox_dir="$MAIL_DIR/outbox"
if [[ -d "$outbox_dir" ]]; then
for msg_file in "$outbox_dir"/*.toon; do
[[ -f "$msg_file" ]] || continue
local msg_type
msg_type=$(grep -A1 'TOON:message{' "$msg_file" 2>/dev/null | tail -1 | cut -d',' -f4)
if [[ "$msg_type" == "status_report" ]]; then
local msg_id
msg_id=$(basename "$msg_file" .toon)
"$MAIL_HELPER" archive "$msg_id" 2>/dev/null || true
fi
done
fi
fi

Choose a reason for hiding this comment

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

medium

The current implementation for processing worker reports is inefficient. It iterates over the outbox files multiple times: once in get_worker_reports to get content, once to store reports to memory, and a third time to archive the messages. This results in reading and parsing the same files repeatedly.

I suggest refactoring this section to process each report file in a single pass. This will improve performance, especially with a large number of reports, and make the code easier to maintain. With this change, you can likely remove the get_worker_reports function (lines 59-77).

    # 2. Process worker reports (archive after reading)
    local reports=""
    local outbox_dir="$MAIL_DIR/outbox"
    if [[ -d "$outbox_dir" ]]; then
        log_info "Processing worker reports..."
        for msg_file in "$outbox_dir"/*.toon; do
            [[ -f "$msg_file" ]] || continue

            local header
            header=$(grep -A1 'TOON:message{' "$msg_file" 2>/dev/null | tail -1)
            if [[ "$(echo "$header" | cut -d',' -f4)" != "status_report" ]]; then
                continue
            fi

            local from payload report_line
            from=$(echo "$header" | cut -d',' -f2)
            payload=$(sed -n '/^-->$/,$ { /^-->$/d; p; }' "$msg_file" | sed '/^$/d')
            report_line="$from: $payload"
            reports+="${report_line}\n"
            echo "  - $report_line"

            if [[ -x "$MEMORY_HELPER" && -n "$report_line" ]]; then
                "$MEMORY_HELPER" store --content "Coordinator received: $report_line" --type CONTEXT --tags "coordinator,status_report" 2>/dev/null || true
            fi

            local msg_id
            msg_id=$(basename "$msg_file" .toon)
            "$MAIL_HELPER" archive "$msg_id" 2>/dev/null || true
        done
    fi

Comment on lines 747 to 754
agent_id=$("$mail_helper" agents 2>/dev/null | grep -o '^[^,]*' | head -1 || echo "")
if [[ -n "$agent_id" ]]; then
"$mail_helper" send \
--to "coordinator" \
--type status_report \
--payload "Task completed: $(loop_get_state ".prompt" | head -c 100). Iterations: $iteration. Branch: $(git branch --show-current 2>/dev/null || echo unknown)" \
2>/dev/null || true
fi

Choose a reason for hiding this comment

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

medium

The logic to retrieve the agent_id here is confusing and potentially incorrect. It gets the ID of the first agent in the registry (head -1), which may not be the current agent in a multi-agent scenario. The agent_id variable is then only used for an if check, but not passed to the send command.

The mail-helper.sh send command already determines the 'from' agent ID internally. You can simplify this by removing the agent_id retrieval and the surrounding if statement, and just call the send command directly.

Suggested change
agent_id=$("$mail_helper" agents 2>/dev/null | grep -o '^[^,]*' | head -1 || echo "")
if [[ -n "$agent_id" ]]; then
"$mail_helper" send \
--to "coordinator" \
--type status_report \
--payload "Task completed: $(loop_get_state ".prompt" | head -c 100). Iterations: $iteration. Branch: $(git branch --show-current 2>/dev/null || echo unknown)" \
2>/dev/null || true
fi
# The mail-helper's send command will determine the 'from' agent ID internally.
"$mail_helper" send \
--to "coordinator" \
--type status_report \
--payload "Task completed: $(loop_get_state ".prompt" | head -c 100). Iterations: $iteration. Branch: $(git branch --show-current 2>/dev/null || echo unknown)" \
2>/dev/null || true

@augmentcode
Copy link

augmentcode bot commented Jan 24, 2026

🤖 Augment PR Summary

Summary: Implements a token-efficient multi-agent orchestration layer for aidevops, centered around TOON-formatted messaging and a stateless “pulse” coordinator.

Changes:

  • Added a custom Build system prompt (.agent/prompts/build.txt) to enforce tool preferences and context-budget rules.
  • Introduced a TOON mailbox CLI (.agent/scripts/mail-helper.sh) with per-agent inboxes plus archive/prune lifecycle.
  • Added a stateless coordinator (.agent/scripts/coordinator-helper.sh) that reads current state and dispatches tasks per pulse/watch cycle.
  • Enhanced the Ralph loop (.agent/scripts/loop-common.sh) to register agents, surface unread mailbox messages during re-anchor, and report completion status.
  • Extended OpenCode agent generation (.agent/scripts/generate-opencode-agents.sh) with custom system prompts and model-tier routing from YAML frontmatter.
  • Compressed .agent/AGENTS.md and moved detailed guidance into .agent/workflows/pre-edit.md and a new TOON index (.agent/subagent-index.toon).

Technical Notes: Messaging and indexing use TOON blocks for reduced token overhead, and model routing maps tier names (e.g., sonnet, flash) to concrete model IDs.

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

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

Review completed. 3 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

local agent_inbox="$INBOX_DIR/$agent_id"
mkdir -p "$agent_inbox"
write_message "$agent_inbox/${msg_id}.toon" "$msg_id" "$from" "$agent_id" "$msg_type" "$priority" "$convoy" "$payload"
((count++))
Copy link

Choose a reason for hiding this comment

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

Because the script runs with set -e, ((count++)) exits with status 1 on the first increment (when count is 0), which can abort broadcasts/checks/pruning unexpectedly. Consider using an increment form that always returns success under set -e (or explicitly neutralize the status).

Other Locations
  • .agent/scripts/mail-helper.sh:271
  • .agent/scripts/mail-helper.sh:273
  • .agent/scripts/mail-helper.sh:372
  • .agent/scripts/mail-helper.sh:393
  • .agent/scripts/mail-helper.sh:439

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

if [[ "$msg_type" == "status_report" ]]; then
local msg_id
msg_id=$(basename "$msg_file" .toon)
"$MAIL_HELPER" archive "$msg_id" 2>/dev/null || true
Copy link

Choose a reason for hiding this comment

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

This attempts to archive processed status_report files from $MAIL_DIR/outbox via mail-helper.sh archive, but archive only searches inbox paths, so these outbox messages will likely remain and be reprocessed every pulse. That can also cause repeated memory writes / duplicate reporting.

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

@@ -0,0 +1,95 @@
<!--TOON:agents[14]{name,file,purpose,model_tier}:
Copy link

Choose a reason for hiding this comment

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

The TOON header declares agents[14], but only 13 agent rows follow before -->; any parser relying on the declared count may misread the index.

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

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: 4

🤖 Fix all issues with AI agents
In @.agent/scripts/coordinator-helper.sh:
- Around line 131-143: The outbox status_report files are never removed because
the script in the outbox loop calls "$MAIL_HELPER" archive which only handles
inbox; update the outbox handling in the block using
outbox_dir/msg_file/msg_type so that when msg_type equals "status_report" you
move the file into the archive (or call an outbox-specific archive helper)
instead of calling the inbox-only archive: e.g. invoke a move of "$msg_file"
into the archive directory (or implement and call MAIL_HELPER outbox-archive
with msg_id) and ensure errors are suppressed like the existing call.
- Around line 147-205: The pulse "dispatched" count is inflated because
dispatch_count is computed from idle_agents rather than actual sends; modify the
dispatch logic (around first_idle, first_task and the "$MAIL_HELPER" send call)
to initialize dispatch_count=0 before use and increment dispatch_count only when
a dispatch actually succeeds (after the send returns success), then use that
dispatch_count in the TOON pulse summary instead of counting commas in
idle_agents; also ensure dispatch_count is exported/visible to the summary
section where echo
"${agent_count},${report_count},${dispatch_count},${ready_count},..." is
produced.

In @.agent/scripts/generate-opencode-agents.sh:
- Around line 312-322: The prompt existence check should use the path value from
AGENT_PROMPTS instead of a hardcoded "prompts/build.txt"; update the block that
sets config["prompt"] so it first looks up AGENT_PROMPTS[display_name], builds
prompt_file = os.path.join(agents_dir, AGENT_PROMPTS[display_name]), checks
os.path.exists(prompt_file), and only then sets config["prompt"] = "{file:" +
AGENT_PROMPTS[display_name] + "}" (keep display_name and config["prompt"] logic
intact so the referenced relative path from AGENT_PROMPTS is what’s validated
and used).
- Around line 201-210: MODEL_TIERS contains invalid Claude identifiers for the
"sonnet" and "opus" tiers causing runtime failures; update the "sonnet" value to
the correct identifier "claude-3-5-sonnet-20241022" and either replace or remove
the "opus" entry (currently set to "claude-opus-4-20250514") because the correct
Opus id was "claude-3-opus-20240229" and that model is retired—choose a
supported Claude alternative or drop the "opus" tier in the MODEL_TIERS map to
prevent runtime errors.
🧹 Nitpick comments (4)
.agent/AGENTS.md (1)

43-153: Trim root AGENTS.md further to stay within the 50–100 instruction budget.
Several long tables and code blocks (install commands, directory tree, workflow snippets) make the root file heavier than “universal rules.” Consider moving those examples into the referenced workflow docs and replacing them with short pointers/file references.

Based on learnings and coding guidelines.

Also applies to: 156-219

.agent/scripts/mail-helper.sh (1)

190-205: Broadcast should target active agents only.
Right now the broadcast branch iterates every registry entry (including inactive agents), which will keep creating inboxes for deregistered workers. Filtering to ,active, keeps dispatch lean.

♻️ Proposed tweak
-            agents=$(grep -v '^<!--' "$REGISTRY_FILE" | grep -v '^$' | grep -v '^-->' | cut -d',' -f1)
+            agents=$(grep ',active,' "$REGISTRY_FILE" 2>/dev/null | cut -d',' -f1)
.agent/prompts/build.txt (1)

21-24: Context budget guidance is sound but could include recovery strategy.

The 100K token limit is sensible. Consider documenting what an agent should do when it realizes it's approaching or has exceeded this budget mid-operation—e.g., checkpoint progress, summarize findings, or request user guidance.

.agent/scripts/generate-opencode-agents.sh (1)

579-583: Duplicate claude-code-mcp configuration blocks.

Lines 579-583 configure claude-code-mcp using a forked GitHub repo, while lines 610-616 configure it again with the upstream npm package. The second block's condition if 'claude-code-mcp' not in config['mcp'] will never trigger because the first block already added it. This appears intentional based on the comment at line 578, but the dead code at 610-616 should be removed or the intent clarified.

♻️ Suggested cleanup
-# Claude Code MCP - run Claude Code one-shot as MCP
-# Docs: https://github.com/steipete/claude-code-mcp
-# Note: this exposes tools as claude-code-mcp_*
-if 'claude-code-mcp' not in config['mcp']:
-    config['mcp']['claude-code-mcp'] = {
-        "type": "local",
-        "command": ["npx", "-y", "@steipete/claude-code-mcp@1.10.12"],
-        "enabled": True
-    }
-    print("  Added claude-code-mcp MCP server")

Also applies to: 610-616

Comment on lines 147 to 205
# 3. Check for idle agents that could take new work
local idle_agents=""
if [[ -n "$active_agents" ]]; then
while IFS=',' read -r agent_id role _branch _worktree _status _registered _last_seen; do
# Check if agent has unread messages (busy) or not (idle)
local inbox_dir="$MAIL_DIR/inbox/$agent_id"
local unread=0
if [[ -d "$inbox_dir" ]]; then
unread=$(grep -rl ',unread$' "$inbox_dir" 2>/dev/null | wc -l | tr -d ' ')
fi
if [[ "$unread" -eq 0 && "$role" == "worker" ]]; then
idle_agents="${idle_agents}${agent_id},"
fi
done <<< "$active_agents"
fi

# 4. Find ready tasks to dispatch
local ready_tasks
ready_tasks=$(get_ready_tasks)

if [[ -n "$ready_tasks" && -n "$idle_agents" ]]; then
log_info "Dispatching tasks to idle workers..."
local first_idle
first_idle=$(echo "$idle_agents" | cut -d',' -f1)
local first_task
first_task=$(echo "$ready_tasks" | head -1)

if [[ -n "$first_idle" && -n "$first_task" ]]; then
"$MAIL_HELPER" send \
--from "$COORDINATOR_ID" \
--to "$first_idle" \
--type task_dispatch \
--payload "$first_task" \
--priority normal 2>/dev/null || true
log_success "Dispatched to $first_idle: $(echo "$first_task" | head -c 80)"
fi
elif [[ -z "$active_agents" ]]; then
log_info "No active agents registered. Nothing to coordinate."
elif [[ -z "$ready_tasks" ]]; then
log_info "No ready tasks to dispatch."
else
log_info "All agents busy. Waiting for reports."
fi

# 5. Summary
local report_count=0 dispatch_count=0 ready_count=0
if [[ -n "$reports" ]]; then
report_count=$(echo "$reports" | grep -c '.' 2>/dev/null || echo 0)
fi
if [[ -n "$idle_agents" ]]; then
dispatch_count=$(echo "$idle_agents" | tr -cd ',' | wc -c | tr -d ' ')
fi
if [[ -n "$ready_tasks" ]]; then
ready_count=$(echo "$ready_tasks" | grep -c '.' 2>/dev/null || echo 0)
fi
echo ""
echo "<!--TOON:pulse_summary{agents,reports,dispatched,ready_tasks,timestamp}:"
echo "${agent_count},${report_count},${dispatch_count},${ready_count},$(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "-->"
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

Pulse summary “dispatched” count is inflated.
dispatch_count is derived from idle agent count, but only one task is dispatched. Track actual dispatches so the TOON summary reflects reality.

🔧 Suggested adjustment
-    # 3. Check for idle agents that could take new work
-    local idle_agents=""
+    # 3. Check for idle agents that could take new work
+    local idle_agents=""
+    local dispatched=0
@@
             log_success "Dispatched to $first_idle: $(echo "$first_task" | head -c 80)"
+            dispatched=1
         fi
@@
-    if [[ -n "$idle_agents" ]]; then
-        dispatch_count=$(echo "$idle_agents" | tr -cd ',' | wc -c | tr -d ' ')
-    fi
+    dispatch_count=$dispatched
🤖 Prompt for AI Agents
In @.agent/scripts/coordinator-helper.sh around lines 147 - 205, The pulse
"dispatched" count is inflated because dispatch_count is computed from
idle_agents rather than actual sends; modify the dispatch logic (around
first_idle, first_task and the "$MAIL_HELPER" send call) to initialize
dispatch_count=0 before use and increment dispatch_count only when a dispatch
actually succeeds (after the send returns success), then use that dispatch_count
in the TOON pulse summary instead of counting commas in idle_agents; also ensure
dispatch_count is exported/visible to the summary section where echo
"${agent_count},${report_count},${dispatch_count},${ready_count},..." is
produced.

Comment on lines +201 to +210
# Model routing tiers (from subagent YAML frontmatter 'model:' field)
# Maps tier names to actual model identifiers
# Agents declare their tier; the coordinator uses this for cost-effective routing
MODEL_TIERS = {
"haiku": "claude-3-5-haiku-20241022", # Triage, routing, simple tasks
"sonnet": "claude-sonnet-4-20250514", # Code, review, implementation
"opus": "claude-opus-4-20250514", # Architecture, complex reasoning
"flash": "gemini-2.5-flash", # Fast, cheap, large context
"pro": "gemini-2.5-pro", # Capable, large context
}
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 | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Claude 3.5 Haiku Sonnet Opus current model identifiers API 2025

💡 Result:

Anthropic Claude API model identifiers relevant to 2025:

  • Claude 3.5 Haiku

    • Alias: claude-3-5-haiku-latest
    • Snapshot (model ID): claude-3-5-haiku-20241022 [1]
  • Claude 3.5 Sonnet

    • Alias: claude-3-5-sonnet-latest
    • Snapshots (model IDs): claude-3-5-sonnet-20241022 (and older claude-3-5-sonnet-20240620) [1]
  • Claude Opus (there was no “Claude 3.5 Opus” public API model)

    • Claude 3 Opus snapshot (model ID): claude-3-opus-20240229 — deprecated June 30, 2025; retired January 5, 2026 [2]

If you want, tell me whether you mean Anthropic API names specifically (above) or Bedrock/Vertex identifiers too (they use different ID formats).

Sources: [1] [2]


🌐 Web query:

Google Gemini 2.5 Flash Pro current model API identifiers January 2026

💡 Result:

There isn’t a single model called “Gemini 2.5 Flash Pro” in Google’s APIs; Flash and Pro are separate Gemini 2.5 models. The current Gemini 2.5 API model identifiers (as of January 2026) are:

  • Gemini 2.5 Pro (stable): gemini-2.5-pro [1][2]
  • Gemini 2.5 Flash (stable): gemini-2.5-flash [1][3]
  • Gemini 2.5 Flash (preview, if you need a pinned preview build): gemini-2.5-flash-preview-09-2025 [1]
  • Gemini 2.5 Flash-Lite (stable): gemini-2.5-flash-lite [1][2]
  • Gemini 2.5 Flash Image (stable): gemini-2.5-flash-image [1][2]

Notes:

  • Google’s docs emphasize that stable Gemini 2.5 names typically have no 3-digit suffix (for example, gemini-2.5-flash). [2]
  • If you’re using Vertex AI, Gemini 2.5 preview models released after June 2025 may require the global location. [2]

Sources
[1] Google Cloud Vertex AI docs – Supported models
[2] Firebase AI Logic docs – Supported Gemini model names / notes
[3] Google AI for Developers – Gemini models list


Fix invalid Claude model identifiers.

The claude-sonnet-4-20250514 and claude-opus-4-20250514 identifiers are incorrect and will fail at runtime:

  • Sonnet should be claude-3-5-sonnet-20241022 (there is no "Claude Sonnet 4")
  • Opus should be claude-3-opus-20240229—though note that Claude 3 Opus was retired January 5, 2026 and is no longer available via the API

The Haiku (claude-3-5-haiku-20241022) and Gemini models (gemini-2.5-flash, gemini-2.5-pro) are valid. Consider whether Opus should be replaced with an alternative Claude model or removed from the routing tiers.

🤖 Prompt for AI Agents
In @.agent/scripts/generate-opencode-agents.sh around lines 201 - 210,
MODEL_TIERS contains invalid Claude identifiers for the "sonnet" and "opus"
tiers causing runtime failures; update the "sonnet" value to the correct
identifier "claude-3-5-sonnet-20241022" and either replace or remove the "opus"
entry (currently set to "claude-opus-4-20250514") because the correct Opus id
was "claude-3-opus-20240229" and that model is retired—choose a supported Claude
alternative or drop the "opus" tier in the MODEL_TIERS map to prevent runtime
errors.

- mail-helper: add --agent flag to read/archive commands (was using
  caller's agent ID instead of target inbox)
- coordinator: fix unbound variable with empty extra_args array (bash
  set -u incompatibility), resolve MAIL_HELPER to script-relative path
- loop-common: resolve mail/memory helpers relative to script location
  instead of hardcoded ~/.aidevops path, quote all variable expansions
- subagent-index.toon: fix row count declarations (agents 14→13,
  subagents 30→31)
@github-actions
Copy link

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Jan 24 00:13:55 UTC 2026: Code review monitoring started
Sat Jan 24 00:13:56 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 351
Sat Jan 24 00:13:56 UTC 2026: Qlty - 0 issues found, auto-formatting applied
Sat Jan 24 00:13:58 UTC 2026: Codacy analysis completed with auto-fixes
Sat Jan 24 00:13:59 UTC 2026: Applied 2 automatic fixes

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 351
  • VULNERABILITIES: 0

Generated on: Sat Jan 24 00:15:13 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

…rides

Refactors the custom system prompt to layer aidevops-specific overrides
on top of the upstream Claude Code prompt rather than replacing it entirely.

Changes:
- Keeps upstream: tone/style, professional objectivity, task management,
  tool usage policy, code references
- Overrides: file discovery (git ls-files), code search (Augment primary),
  git workflow (pre-edit-check.sh), security (credential paths),
  agent framework (subagents, TOON, memory, mailbox)
- Removes all 'OpenCode' branding for OAuth plugin compatibility
- Adds upstream source header (3c41e4e8f12b) for future diffing
- 95 lines (was 88), well under 150 line target

Upstream: anomalyco/opencode@3c41e4e8f12b anthropic.txt
@github-actions
Copy link

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Jan 24 00:22:30 UTC 2026: Code review monitoring started
Sat Jan 24 00:22:31 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 351
Sat Jan 24 00:22:31 UTC 2026: Qlty - 0 issues found, auto-formatting applied
Sat Jan 24 00:22:33 UTC 2026: Codacy analysis completed with auto-fixes
Sat Jan 24 00:22:34 UTC 2026: Applied 2 automatic fixes

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 351
  • VULNERABILITIES: 0

Generated on: Sat Jan 24 00:23:40 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

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: 1

🤖 Fix all issues with AI agents
In @.agent/scripts/coordinator-helper.sh:
- Around line 263-270: The auto-targeting in cmd_dispatch currently picks the
first worker from get_active_agents without ensuring they are idle; update
cmd_dispatch to mirror the idle-selection logic used in cmd_pulse: call
get_active_agents, filter for entries containing ',worker,', then further filter
to only agents marked idle (the same idle-check / pattern used in cmd_pulse),
and select the first idle worker as the default value for to; if no idle worker
exists, fall back to the original registry-first behavior. Ensure you modify the
cmd_dispatch flow that sets to so it references the same idle-filter logic as
cmd_pulse and still respects explicit to values.
♻️ Duplicate comments (7)
.agent/scripts/loop-common.sh (1)

746-758: Simplify status report send (agent_id check is redundant).

mail-helper send already resolves the sender; the agents | grep | head step can return header text and doesn’t affect the send. Sending directly is simpler and more reliable. As per coding guidelines, prefer minimal, robust glue.

♻️ Suggested simplification
-            if [[ -x "$LOOP_MAIL_HELPER" ]]; then
-                local agent_id
-                agent_id=$("$LOOP_MAIL_HELPER" agents 2>/dev/null | grep -o '^[^,]*' | head -1 || echo "")
-                if [[ -n "$agent_id" ]]; then
-                    "$LOOP_MAIL_HELPER" send \
-                        --to "coordinator" \
-                        --type status_report \
-                        --payload "Task completed: $(loop_get_state ".prompt" | head -c 100). Iterations: $iteration. Branch: $(git branch --show-current 2>/dev/null || echo unknown)" \
-                        2>/dev/null || true
-                fi
-            fi
+            if [[ -x "$LOOP_MAIL_HELPER" ]]; then
+                "$LOOP_MAIL_HELPER" send \
+                    --to "coordinator" \
+                    --type status_report \
+                    --payload "Task completed: $(loop_get_state ".prompt" | head -c 100). Iterations: $iteration. Branch: $(git branch --show-current 2>/dev/null || echo unknown)" \
+                    2>/dev/null || true
+            fi
.agent/scripts/mail-helper.sh (3)

143-151: Harden option parsing against missing values.

Several parsers shift 2 without ensuring $2 exists; a flag without a value will terminate under set -e. Add a guard before consuming the value and apply the pattern across all cmd_* parsers. As per coding guidelines, prioritize robust argument handling.

🛠️ Suggested guard (apply to all options)
-            --to) to="$2"; shift 2 ;;
+            --to)
+                [[ -n "${2-}" ]] || { log_error "Missing argument for $1"; return 1; }
+                to="$2"; shift 2 ;;

Also applies to: 228-233, 288-293, 329-334, 371-376, 427-431, 497-504, 562-566, 593-597


196-203: Avoid ((count++)) under set -e.

((count++)) returns 1 when the previous value is 0, which triggers set -e and can abort broadcasts/checks/prunes. Use count=$((count + 1)) or ((count+=1)) instead. As per coding guidelines, keep control-flow robust.

🛠️ Safer increment
-                    ((count++))
+                    count=$((count + 1))

Also applies to: 270-274, 388-412, 452-458


525-549: Protect registry updates with a lock.

Register/deregister perform read‑modify‑write on a shared file; concurrent agents can interleave and corrupt it. Add a lightweight lock (e.g., mkdir-based) around the update block. As per coding guidelines, ensure reliable automation under concurrency.

🔒 Add a lightweight lock around registry updates
+    local lock_dir="$MAIL_DIR/.registry.lock"
+    while ! mkdir "$lock_dir" 2>/dev/null; do sleep 0.05; done
+    trap 'rmdir "$lock_dir"' RETURN
     # Create or update registry

Also applies to: 578-582

.agent/scripts/coordinator-helper.sh (3)

133-144: Status reports never leave outbox.

mail-helper archive only searches inboxes, so outbox status_report files are reprocessed every pulse. Move the outbox file into the archive (or add an outbox-archive command). As per coding guidelines, avoid repeated work and duplicate reporting.

🐛 Move outbox reports into archive
-                    local msg_id
-                    msg_id=$(basename "$msg_file" .toon)
-                    "$MAIL_HELPER" archive "$msg_id" 2>/dev/null || true
+                    local archive_dir
+                    archive_dir="$MAIL_DIR/archive/$(date +%Y-%m)"
+                    mkdir -p "$archive_dir"
+                    mv "$msg_file" "$archive_dir/" 2>/dev/null || true

169-184: Pulse summary overstates dispatch count.

Only one task is sent, but dispatch_count is derived from idle-agent count. Track actual sends for an accurate TOON summary. As per coding guidelines, keep reporting accurate.

🔧 Track actual dispatches
-    local report_count=0 dispatch_count=0 ready_count=0
+    local report_count=0 dispatch_count=0 ready_count=0
+    local dispatched=0
@@
             "$MAIL_HELPER" send \
                 --from "$COORDINATOR_ID" \
                 --to "$first_idle" \
                 --type task_dispatch \
                 --payload "$first_task" \
                 --priority normal 2>/dev/null || true
+            dispatched=1
@@
-    if [[ -n "$idle_agents" ]]; then
-        dispatch_count=$(echo "$idle_agents" | tr -cd ',' | wc -c | tr -d ' ')
-    fi
+    dispatch_count=$dispatched

Also applies to: 193-200


248-255: Guard option parsing against missing values.

shift 2 assumes a value exists; passing --task or --name without a value will abort under set -e. Add missing-arg checks before shifting. As per coding guidelines, make CLI parsing resilient.

🛠️ Add missing-arg checks
-            --task) task="$2"; shift 2 ;;
+            --task)
+                [[ -n "${2-}" ]] || { log_error "Missing argument for $1"; return 1; }
+                task="$2"; shift 2 ;;

Also applies to: 300-305

🧹 Nitpick comments (1)
.agent/scripts/mail-helper.sh (1)

27-35: Resolve MEMORY_HELPER relative to the script.

Hardcoding ~/.aidevops/... makes installs elsewhere fail; coordinator/loop-common already use script-relative paths. Use a local SCRIPT_DIR for portability. As per coding guidelines, keep automation location-agnostic.

♻️ Suggested path normalization
+# Resolve script directory for sibling helpers
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+readonly SCRIPT_DIR
@@
-readonly MEMORY_HELPER="$HOME/.aidevops/agents/scripts/memory-helper.sh"
+readonly MEMORY_HELPER="${SCRIPT_DIR}/memory-helper.sh"

… OpenCode branding

- Add model-specific reinforcement section to build.txt (persistence,
  conventions, library verification, lint/typecheck) - helps weaker models
  while being harmless reinforcement for stronger ones
- Restore 'OpenCode' branding in prompt - the opencode-anthropic-auth plugin
  handles the transformation to 'Claude Code' for OAuth users automatically
- Add opencode-prompt-drift-check.sh to detect when upstream anthropic.txt
  changes vs our synced commit hash (3c41e4e8f12b)
- Integrate drift check into setup.sh flow (non-blocking warning)
- build.txt now 106 lines (was 95), still well under 150 target
@github-actions
Copy link

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Jan 24 00:35:20 UTC 2026: Code review monitoring started
Sat Jan 24 00:35:21 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 351
Sat Jan 24 00:35:21 UTC 2026: Qlty - 0 issues found, auto-formatting applied
Sat Jan 24 00:35:23 UTC 2026: Codacy analysis completed with auto-fixes
Sat Jan 24 00:35:24 UTC 2026: Applied 3 automatic fixes

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 351
  • VULNERABILITIES: 0

Generated on: Sat Jan 24 00:36:37 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

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: 2

🤖 Fix all issues with AI agents
In @.agent/prompts/build.txt:
- Around line 12-15: Locate the "Tone and style" section and replace the phrase
"Github-flavored markdown" with the correctly capitalized "GitHub-flavored
Markdown" (update both words: GitHub and Markdown) so the proper noun casing is
used; ensure any other occurrences of that phrase in the file are updated the
same way.

In `@setup.sh`:
- Around line 1452-1467: The check_opencode_prompt_drift function currently
ignores the drift script's exit status and can incorrectly report "in sync";
change it to capture the exit code when running
.agent/scripts/opencode-prompt-drift-check.sh (use a pattern like output=$(bash
"$drift_script" --quiet 2>/dev/null; echo "$?") or capture $? immediately after
execution), then branch on that status: if non-zero, emit an explicit
error/warning (using print_warning/print_error) indicating the drift check
failed and avoid printing "in sync"; if zero continue to parse output and
distinguish PROMPT_DRIFT vs in-sync as before, using the existing
local_hash/upstream_hash handling to report drift.
🧹 Nitpick comments (2)
.agent/scripts/opencode-prompt-drift-check.sh (2)

34-41: Prefer jq (already required) over python3 for JSON parsing.

python3 isn’t guaranteed by setup requirements, so the drift check can fail on minimal systems. Using jq aligns with existing dependencies and improves portability.

♻️ Proposed update (jq parsing)
-    local hash
-    hash=$(echo "$response" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d[0]['sha'][:12])" 2>/dev/null) || return 1
+    local hash
+    hash=$(echo "$response" | jq -r '.[0].sha[0:12] // empty' 2>/dev/null) || return 1
+    [[ -n "$hash" ]] || return 1

26-42: Add explicit returns to helper functions.

Shell guidelines call for explicit returns in functions; get_local_hash and get_upstream_hash currently rely on implicit exit codes.

♻️ Proposed update (explicit returns)
 get_local_hash() {
     local hash
     hash=$(head -1 "$BUILD_TXT" | grep -oE '[0-9a-f]{12}' || echo "")
     echo "$hash"
+    return 0
 }
@@
 get_upstream_hash() {
     local response
     response=$(curl -sf --max-time 10 \
         "https://api.github.com/repos/${UPSTREAM_REPO}/commits?path=${UPSTREAM_FILE}&sha=${UPSTREAM_BRANCH}&per_page=1" \
         -H "Accept: application/vnd.github.v3+json" 2>/dev/null) || return 1
@@
     local hash
     hash=$(echo "$response" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d[0]['sha'][:12])" 2>/dev/null) || return 1
     echo "$hash"
+    return 0
 }
As per coding guidelines, explicit returns are required.

Implements opencode-aidevops plugin that hooks into OpenCode's
experimental.session.compacting event to inject dynamic context
that should survive session compaction:

- Agent state from registry.toon (active multi-agent orchestration)
- Loop guardrails from loop-state/ (iteration count, objectives)
- Relevant memories via memory-helper.sh (project-specific learnings)
- Git context (current branch, recent commits)
- Pending mailbox messages (inter-agent communications)
- Critical rules reminder (file discovery, git workflow, security)

Static rules (tool preferences, security, quality standards) are already
in the system prompt (build.txt) which never gets compacted. This plugin
handles the dynamic, session-specific context that changes per-session.

Plugin is registered in setup.sh as a file:// local plugin.
Zero dependencies - uses only Node.js builtins (fs, path, child_process).
@github-actions
Copy link

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Jan 24 00:50:09 UTC 2026: Code review monitoring started
Sat Jan 24 00:50:10 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 351
Sat Jan 24 00:50:10 UTC 2026: Qlty - 0 issues found, auto-formatting applied
Sat Jan 24 00:50:12 UTC 2026: Codacy analysis completed with auto-fixes
Sat Jan 24 00:50:13 UTC 2026: Applied 3 automatic fixes

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 351
  • VULNERABILITIES: 0

Generated on: Sat Jan 24 00:51:26 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

Fixes raised by automated reviewers on PR #158:

- Add file locking for registry modifications (race condition prevention)
- Add argument validation before shift 2 (prevents crash on missing values)
- Fix outbox status_reports: move directly to archive instead of using
  inbox-only archive command (prevents reprocessing on every pulse)
- Replace ((count++)) with count=$((count + 1)) throughout (prevents
  exit status 1 under set -e when count starts at 0)
- Fix dispatch_count: track actual dispatches (was counting idle agents)
- Fix cmd_dispatch: check worker inbox for unread messages before selecting
- Fix drift check: distinguish success/drift/failure exit codes properly
- Fix loop-common.sh: identify current agent by worktree path match
  before falling back to first registered agent
@github-actions
Copy link

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Jan 24 01:24:34 UTC 2026: Code review monitoring started
Sat Jan 24 01:24:35 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 362
Sat Jan 24 01:24:35 UTC 2026: Qlty - 0 issues found, auto-formatting applied
Sat Jan 24 01:24:37 UTC 2026: Codacy analysis completed with auto-fixes
Sat Jan 24 01:24:38 UTC 2026: Applied 3 automatic fixes

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 362
  • VULNERABILITIES: 0

Generated on: Sat Jan 24 01:25:52 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

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: 1

🤖 Fix all issues with AI agents
In @.agent/plugins/opencode-aidevops/index.mjs:
- Around line 150-164: getMailboxState is treating any output from running
mail-helper.sh as a numeric unread count, which leaks multiline TOON blocks into
compaction; update getMailboxState to call mail-helper.sh with a dedicated count
mode (e.g., add a --count flag) or parse the runner output for a numeric unread
value, validate that pending is a non-negative integer (use regex like /^\d+$/
or Number parsing), only treat it as pending when numeric > 0, and emit just the
numeric summary line (not the raw multiline output) so the function returns a
small, sanitized message when there are unread messages; locate changes around
getMailboxState, the run(`bash "${mailHelper}" ...`) invocation and the pending
variable handling to implement this.
♻️ Duplicate comments (2)
.agent/scripts/mail-helper.sh (1)

228-233: Add missing option-argument validation to avoid set -u crashes.

Some flag handlers still assume $2 exists (e.g., --agent, --older-than-days, --role, --branch). With set -u, this will exit on malformed input. Mirror the guard pattern used in cmd_send across these handlers.

✅ Example pattern (apply to all flagged options)
-            --agent) agent_id="$2"; shift 2 ;;
+            --agent) [[ $# -lt 2 ]] && { log_error "--agent requires a value"; return 1; }; agent_id="$2"; shift 2 ;;

Also applies to: 371-375, 427-431, 497-503, 579-583

.agent/scripts/coordinator-helper.sh (1)

271-277: Use unread-only checks when auto-selecting idle workers.

cmd_dispatch treats any inbox message as “busy,” but cmd_pulse uses unread-only. This can prevent dispatching to agents whose inbox only has read messages. Align the logic.

🛠️ Suggested fix
-                if [[ -d "$inbox_dir" ]]; then
-                    unread=$(find "$inbox_dir" -name "*.toon" -type f 2>/dev/null | wc -l | tr -d ' ')
-                fi
+                if [[ -d "$inbox_dir" ]]; then
+                    unread=$(grep -rl ',unread$' "$inbox_dir" 2>/dev/null | wc -l | tr -d ' ')
+                fi

- Validate mailbox check output is numeric before using as count
  (prevents TOON block leakage into compaction context)
- Add argument guards to all remaining --agent, --older-than-days,
  --role, --branch, --worktree handlers in mail-helper.sh
@github-actions
Copy link

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Jan 24 01:46:27 UTC 2026: Code review monitoring started
Sat Jan 24 01:46:27 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 362
Sat Jan 24 01:46:27 UTC 2026: Qlty - 0 issues found, auto-formatting applied
Sat Jan 24 01:46:29 UTC 2026: Codacy analysis completed with auto-fixes
Sat Jan 24 01:46:30 UTC 2026: Applied 3 automatic fixes

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 362
  • VULNERABILITIES: 0

Generated on: Sat Jan 24 01:47:48 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

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: 4

🤖 Fix all issues with AI agents
In @.agent/plugins/opencode-aidevops/index.mjs:
- Around line 103-121: The project name extraction in getRelevantMemories is
fragile and unsafe: replace directory.split("/").pop() with a robust basename
extraction (use path.basename or equivalent) to correctly handle trailing
slashes and empty results, then properly escape or avoid interpolating
projectName into a shell string (use child_process spawn/execFile or a
shell-escape utility) when invoking memoryHelper; ensure you reference
getRelevantMemories, memoryHelper, projectName, and run to locate the change,
validate empty projectName before calling recall, and pass the project name as a
safe argument rather than raw string interpolation.
- Around line 16-26: The current run(cmd) executes shell commands with
interpolated directories, which risks shell injection; replace it with a new
runInDir(cmd, cwd) that calls execSync with the cwd option (and no shell
interpolation) and update callers like getGitContext to pass the directory via
cwd instead of embedding it into cmd; additionally validate/normalize the
directory argument (e.g., path.resolve and fs.stat to ensure it is an existing
directory and reject or escape paths containing shell metacharacters) and keep
the same encoding/timeout/stdio behavior and error handling pattern in the new
runInDir function.

In @.agent/scripts/mail-helper.sh:
- Around line 639-647: The agent listing prints ANSI color variables (${CYAN},
${NC}) using plain echo which won't interpret escape sequences; update the print
to use an escape-aware output (e.g., replace the echo call that prints " 
${CYAN}$id${NC} ($role) on $branch - last seen: $last_seen" with an echo -e or a
printf '%b' equivalent) so the ${CYAN} and ${NC} color codes render; ensure any
other similar echoes in the same block (and the initial "Active Agents:" line if
intended to be colored) are updated consistently and that the IFS/while loop
remains unchanged.
- Around line 550-567: Validate and reject unsafe agent_id values before using
them in sed/registry updates: in the cmd_register flow (immediately after its
start, e.g., after the current line ~508) add a check that agent_id matches a
safe character class (e.g., only alphanumeric, underscore, hyphen) and on
failure call log_error and return 1; this prevents sed injection in the sed
invocations that update REGISTRY_FILE (the sed lines that replace or insert
"${agent_id},${role},..." should then only ever see a validated agent_id).
🧹 Nitpick comments (2)
.agent/scripts/mail-helper.sh (2)

120-133: parse_message is defined but not used internally.

This function appears to be intended for external consumers. Consider documenting this in the usage section or adding a brief comment indicating it's part of the public API for other scripts.


526-541: Locking mechanism addresses race condition concerns.

The noclobber-based lock with retry loop is a reasonable approach. However, the aggressive stale lock removal after only 1 second (5 × 0.2s) could cause issues if a legitimate holder is performing a slow operation.

Consider checking lock file age or using a longer timeout before assuming staleness:

♻️ Optional: Add lock age check before removal
     if [[ "$lock_acquired" != "true" ]]; then
-        log_warn "Could not acquire registry lock (stale lock?). Removing and retrying."
-        rm -f "$lock_file"
+        # Only remove lock if it's older than 30 seconds (likely stale from crash)
+        if [[ -f "$lock_file" ]]; then
+            local lock_age
+            lock_age=$(( $(date +%s) - $(stat -c %Y "$lock_file" 2>/dev/null || stat -f %m "$lock_file" 2>/dev/null || echo 0) ))
+            if [[ "$lock_age" -gt 30 ]]; then
+                log_warn "Removing stale lock (age: ${lock_age}s)"
+                rm -f "$lock_file"
+            else
+                log_error "Registry locked by another process"
+                return 1
+            fi
+        fi
         (set -o noclobber; echo $$ > "$lock_file") 2>/dev/null || true
     fi

Comment on lines +16 to +26
function run(cmd) {
try {
return execSync(cmd, {
encoding: "utf-8",
timeout: 5000,
stdio: ["pipe", "pipe", "pipe"],
}).trim();
} catch {
return "";
}
}
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

Shell command injection risk when directory contains metacharacters.

While directory comes from the plugin system rather than direct user input, interpolating it into shell commands without sanitization poses a risk if the path contains shell metacharacters (;, $, backticks, etc.). Consider using cwd option for git commands instead of -C, or validate/escape the directory path.

🛠️ Safer approach using cwd option

For git commands specifically, use cwd instead of shell interpolation:

function runInDir(cmd, cwd) {
  try {
    return execSync(cmd, {
      encoding: "utf-8",
      timeout: 5000,
      cwd,
      stdio: ["pipe", "pipe", "pipe"],
    }).trim();
  } catch {
    return "";
  }
}

Then in getGitContext:

-  const branch = run(`git -C "${directory}" branch --show-current 2>/dev/null`);
+  const branch = runInDir("git branch --show-current", directory);
🧰 Tools
🪛 GitHub Check: Codacy Static Code Analysis

[failure] 18-18: .agent/plugins/opencode-aidevops/index.mjs#L18
Detected calls to child_process from a function argument cmd. This could lead to a command injection if the input is user controllable.

🤖 Prompt for AI Agents
In @.agent/plugins/opencode-aidevops/index.mjs around lines 16 - 26, The current
run(cmd) executes shell commands with interpolated directories, which risks
shell injection; replace it with a new runInDir(cmd, cwd) that calls execSync
with the cwd option (and no shell interpolation) and update callers like
getGitContext to pass the directory via cwd instead of embedding it into cmd;
additionally validate/normalize the directory argument (e.g., path.resolve and
fs.stat to ensure it is an existing directory and reject or escape paths
containing shell metacharacters) and keep the same encoding/timeout/stdio
behavior and error handling pattern in the new runInDir function.

Comment on lines +103 to +121
function getRelevantMemories(directory) {
const memoryHelper = join(SCRIPTS_DIR, "memory-helper.sh");
if (!existsSync(memoryHelper)) return "";

// Get the project name from the directory for context-relevant recall
const projectName = directory.split("/").pop() || "";

// Recall recent memories related to this project
const memories = run(
`bash "${memoryHelper}" recall "${projectName}" --limit 5 2>/dev/null`
);
if (!memories) return "";

return [
"## Relevant Memories",
"Previous session learnings relevant to this project:",
memories,
].join("\n");
}
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

Project name extraction is fragile and poses injection risk.

directory.split("/").pop() returns empty string for trailing slashes and passes unsanitized into a shell command. If directory ends with a malicious segment, it could inject commands.

🛠️ Proposed fix with sanitization
 function getRelevantMemories(directory) {
   const memoryHelper = join(SCRIPTS_DIR, "memory-helper.sh");
   if (!existsSync(memoryHelper)) return "";

   // Get the project name from the directory for context-relevant recall
-  const projectName = directory.split("/").pop() || "";
+  const projectName = (directory.replace(/\/+$/, "").split("/").pop() || "")
+    .replace(/[^a-zA-Z0-9_-]/g, "");
+  if (!projectName) return "";

   // Recall recent memories related to this project
   const memories = run(
     `bash "${memoryHelper}" recall "${projectName}" --limit 5 2>/dev/null`
   );
🤖 Prompt for AI Agents
In @.agent/plugins/opencode-aidevops/index.mjs around lines 103 - 121, The
project name extraction in getRelevantMemories is fragile and unsafe: replace
directory.split("/").pop() with a robust basename extraction (use path.basename
or equivalent) to correctly handle trailing slashes and empty results, then
properly escape or avoid interpolating projectName into a shell string (use
child_process spawn/execFile or a shell-escape utility) when invoking
memoryHelper; ensure you reference getRelevantMemories, memoryHelper,
projectName, and run to locate the change, validate empty projectName before
calling recall, and pass the project name as a safe argument rather than raw
string interpolation.

Comment on lines +550 to +567
# Check if agent already registered
if grep -q "^${agent_id}," "$REGISTRY_FILE" 2>/dev/null; then
# Update last_seen and status
if command -v sed &>/dev/null; then
sed -i.bak "s|^${agent_id},.*|${agent_id},${role},${branch},${worktree},active,$(grep "^${agent_id}," "$REGISTRY_FILE" | cut -d',' -f6),${timestamp}|" "$REGISTRY_FILE"
rm -f "${REGISTRY_FILE}.bak"
fi
log_info "Updated agent: $agent_id (last_seen: $timestamp)"
else
# Add new agent before closing -->
if command -v sed &>/dev/null; then
sed -i.bak "/^-->$/i\\
${agent_id},${role},${branch},${worktree},active,${timestamp},${timestamp}" "$REGISTRY_FILE"
rm -f "${REGISTRY_FILE}.bak"
fi
log_success "Registered agent: $agent_id (role: $role, branch: $branch)"
fi
fi
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

Potential sed injection if agent_id contains special characters.

If agent_id contains characters like |, &, \, or /, the sed pattern could break or behave unexpectedly. Consider sanitizing or validating the agent_id format.

🛡️ Suggested validation for agent_id

Add this validation early in cmd_register (after line 508):

# Validate agent_id contains only safe characters
if [[ ! "$agent_id" =~ ^[a-zA-Z0-9_-]+$ ]]; then
    log_error "Invalid agent_id: must contain only alphanumeric, underscore, or hyphen"
    return 1
fi
🤖 Prompt for AI Agents
In @.agent/scripts/mail-helper.sh around lines 550 - 567, Validate and reject
unsafe agent_id values before using them in sed/registry updates: in the
cmd_register flow (immediately after its start, e.g., after the current line
~508) add a check that agent_id matches a safe character class (e.g., only
alphanumeric, underscore, hyphen) and on failure call log_error and return 1;
this prevents sed injection in the sed invocations that update REGISTRY_FILE
(the sed lines that replace or insert "${agent_id},${role},..." should then only
ever see a validated agent_id).

Comment on lines +639 to +647
if [[ "$active_only" == true ]]; then
echo "Active Agents:"
grep ',active,' "$REGISTRY_FILE" 2>/dev/null | while IFS=',' read -r id role branch _worktree _status _registered last_seen; do
echo " ${CYAN}$id${NC} ($role) on $branch - last seen: $last_seen"
done
else
cat "$REGISTRY_FILE"
fi
}
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

Color codes won't render without echo -e.

Line 642 uses ${CYAN} and ${NC} variables but echo without -e flag won't interpret ANSI escape sequences. The colors will appear as literal escape characters.

🐛 Fix: Use echo -e for color output
     if [[ "$active_only" == true ]]; then
         echo "Active Agents:"
         grep ',active,' "$REGISTRY_FILE" 2>/dev/null | while IFS=',' read -r id role branch _worktree _status _registered last_seen; do
-            echo "  ${CYAN}$id${NC} ($role) on $branch - last seen: $last_seen"
+            echo -e "  ${CYAN}$id${NC} ($role) on $branch - last seen: $last_seen"
         done
     else
📝 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 [[ "$active_only" == true ]]; then
echo "Active Agents:"
grep ',active,' "$REGISTRY_FILE" 2>/dev/null | while IFS=',' read -r id role branch _worktree _status _registered last_seen; do
echo " ${CYAN}$id${NC} ($role) on $branch - last seen: $last_seen"
done
else
cat "$REGISTRY_FILE"
fi
}
if [[ "$active_only" == true ]]; then
echo "Active Agents:"
grep ',active,' "$REGISTRY_FILE" 2>/dev/null | while IFS=',' read -r id role branch _worktree _status _registered last_seen; do
echo -e " ${CYAN}$id${NC} ($role) on $branch - last seen: $last_seen"
done
else
cat "$REGISTRY_FILE"
fi
}
🤖 Prompt for AI Agents
In @.agent/scripts/mail-helper.sh around lines 639 - 647, The agent listing
prints ANSI color variables (${CYAN}, ${NC}) using plain echo which won't
interpret escape sequences; update the print to use an escape-aware output
(e.g., replace the echo call that prints "  ${CYAN}$id${NC} ($role) on $branch -
last seen: $last_seen" with an echo -e or a printf '%b' equivalent) so the
${CYAN} and ${NC} color codes render; ensure any other similar echoes in the
same block (and the initial "Active Agents:" line if intended to be colored) are
updated consistently and that the IFS/while loop remains unchanged.

- Fix capitalization: GitHub-flavored Markdown (build.txt)
- Fix prompt file existence check: validate from AGENT_PROMPTS path, not hardcoded (generate-opencode-agents.sh)

Note: Model identifiers (claude-sonnet-4-20250514, claude-opus-4-20250514) are
correct Claude 4 series models. CodeRabbit's web search returned outdated info.
subagent-index.toon count (13) matches actual rows - Augment miscounted.
@sonarqubecloud
Copy link

@github-actions
Copy link

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Jan 24 01:50:59 UTC 2026: Code review monitoring started
Sat Jan 24 01:50:59 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 362
Sat Jan 24 01:50:59 UTC 2026: Qlty - 0 issues found, auto-formatting applied
Sat Jan 24 01:51:01 UTC 2026: Codacy analysis completed with auto-fixes
Sat Jan 24 01:51:02 UTC 2026: Applied 3 automatic fixes

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 362
  • VULNERABILITIES: 0

Generated on: Sat Jan 24 01:52:07 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@marcusquinn marcusquinn merged commit 80317a4 into main Jan 24, 2026
8 of 9 checks passed
marcusquinn added a commit that referenced this pull request Jan 24, 2026
Document the new orchestration capabilities from PR #158:
- TOON mailbox system for inter-agent messaging
- Stateless coordinator with pulse-based task dispatch
- Agent registry with role/branch/worktree tracking
- Model routing tiers (haiku/sonnet/opus/flash/pro)
- Compaction plugin for context preservation
- Custom system prompt based on upstream OpenCode
- Subagent index for fast agent discovery
@marcusquinn marcusquinn deleted the feature/multi-agent-orchestration branch February 21, 2026 01:59
@marcusquinn
Copy link
Owner Author

t004.18 verified and merged. This unblocks t004.24 (Build content generation AI tools) — both blockers (t004.17, t004.18) are now resolved. t004.24 is ready for dispatch.


Posted by AI Supervisor (automated reasoning cycle)

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