From c1902a69d108caa6c54f510b0c429f86c15bdad2 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 18 Feb 2026 12:16:59 -0700 Subject: [PATCH] feat: Created a CodeRabbit-style code-reviewer agent --- assets/agents/code-reviewer/config.yaml | 125 +++++++ assets/agents/code-reviewer/tools.sh | 478 ++++++++++++++++++++++++ assets/agents/file-reviewer/config.yaml | 111 ++++++ assets/agents/file-reviewer/tools.sh | 33 ++ src/function/supervisor.rs | 4 +- 5 files changed, 749 insertions(+), 2 deletions(-) create mode 100644 assets/agents/code-reviewer/config.yaml create mode 100755 assets/agents/code-reviewer/tools.sh create mode 100644 assets/agents/file-reviewer/config.yaml create mode 100755 assets/agents/file-reviewer/tools.sh diff --git a/assets/agents/code-reviewer/config.yaml b/assets/agents/code-reviewer/config.yaml new file mode 100644 index 0000000..8959237 --- /dev/null +++ b/assets/agents/code-reviewer/config.yaml @@ -0,0 +1,125 @@ +name: code-reviewer +description: CodeRabbit-style code reviewer - spawns per-file reviewers, synthesizes findings +version: 1.0.0 +temperature: 0.1 +top_p: 0.95 + +auto_continue: true +max_auto_continues: 20 +inject_todo_instructions: true + +can_spawn_agents: true +max_concurrent_agents: 10 +max_agent_depth: 2 + +variables: + - name: project_dir + description: Project directory to review + default: '.' + +global_tools: + - fs_read.sh + - fs_grep.sh + - fs_glob.sh + - execute_command.sh + +instructions: | + You are a code review orchestrator, similar to CodeRabbit. You coordinate per-file reviews and produce a unified report. + + ## Workflow + + 1. **Get the diff:** Run `get_diff` to get the git diff (defaults to staged changes, falls back to unstaged) + 2. **Parse changed files:** Extract the list of files from the diff + 3. **Create todos:** One todo per phase (get diff, spawn reviewers, collect results, synthesize report) + 4. **Spawn file-reviewers:** One `file-reviewer` agent per changed file, in parallel + 5. **Broadcast sibling roster:** Send each file-reviewer a message with all sibling IDs and their file assignments + 6. **Collect all results:** Wait for each file-reviewer to complete + 7. **Synthesize:** Combine all findings into a CodeRabbit-style report + + ## Spawning File Reviewers + + For each changed file, spawn a file-reviewer with a prompt containing: + - The file path + - The relevant diff hunk(s) for that file + - Instructions to review it + + ``` + agent__spawn --agent file-reviewer --prompt "Review the following diff for : + + + + Focus on bugs, security issues, logic errors, and style. Use the severity format (🔴🟡🟢💡). + End with REVIEW_COMPLETE." + ``` + + ## Sibling Roster Broadcast + + After spawning ALL file-reviewers (collecting their IDs), send each one a message with the roster: + + ``` + agent__send_message --to --message "SIBLING_ROSTER: + - : reviewing + - : reviewing + ... + Send cross-cutting alerts to relevant siblings if your changes affect their files." + ``` + + ## Diff Parsing + + Split the diff by file. Each file's diff starts with `diff --git a/ b/`. Extract: + - The file path (from the `+++ b/` line) + - All hunks for that file (from `@@` markers to the next `diff --git` or end) + + Skip binary files and files with only whitespace changes. + + ## Final Report Format + + After collecting all file-reviewer results, synthesize into: + + ``` + # Code Review Summary + + ## Walkthrough + <2-3 sentence overview of what the changes do as a whole> + + ## Changes + + | File | Changes | Findings | + |------|---------|----------| + | `path/to/file1.rs` | | 🔴 1 🟡 2 🟢 1 | + | `path/to/file2.rs` | | 🟢 2 💡 1 | + + ## Detailed Findings + + ### `path/to/file1.rs` + + + ### `path/to/file2.rs` + + + ## Cross-File Concerns + + + --- + *Reviewed N files, found X critical, Y warnings, Z suggestions, W nitpicks* + ``` + + ## Edge Cases + + - **Single file changed:** Still spawn one file-reviewer (for consistency), skip roster broadcast + - **Too many files (>10):** Group small files (< 20 lines changed) and review them together + - **No changes found:** Report "No changes to review" and exit + - **Binary files:** Skip with a note in the summary + + ## Rules + + 1. **Always use `get_diff` first:** Don't assume what changed + 2. **Spawn in parallel:** All file-reviewers should be spawned before collecting any + 3. **Don't review code yourself:** Delegate ALL review work to file-reviewers + 4. **Preserve severity tags:** Don't downgrade or remove severity from file-reviewer findings + 5. **Include ALL findings:** Don't summarize away specific issues + + ## Context + - Project: {{project_dir}} + - CWD: {{__cwd__}} + - Shell: {{__shell__}} diff --git a/assets/agents/code-reviewer/tools.sh b/assets/agents/code-reviewer/tools.sh new file mode 100755 index 0000000..8b656c5 --- /dev/null +++ b/assets/agents/code-reviewer/tools.sh @@ -0,0 +1,478 @@ +#!/usr/bin/env bash +set -eo pipefail + +# shellcheck disable=SC1090 +source "$LLM_PROMPT_UTILS_FILE" +source "$LLM_ROOT_DIR/agents/.shared/utils.sh" + +# @env LLM_OUTPUT=/dev/stdout +# @env LLM_AGENT_VAR_PROJECT_DIR=. +# @describe Code review orchestrator tools + +_project_dir() { + local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}" + (cd "${dir}" 2>/dev/null && pwd) || echo "${dir}" +} + +# @cmd Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean. +# @option --base Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA) +get_diff() { + local project_dir + project_dir=$(_project_dir) + # shellcheck disable=SC2154 + local base="${argc_base:-}" + + local diff_output="" + + if [[ -n "${base}" ]]; then + diff_output=$(cd "${project_dir}" && git diff "${base}" 2>&1) || true + else + diff_output=$(cd "${project_dir}" && git diff --cached 2>&1) || true + + if [[ -z "${diff_output}" ]]; then + diff_output=$(cd "${project_dir}" && git diff 2>&1) || true + fi + + if [[ -z "${diff_output}" ]]; then + diff_output=$(cd "${project_dir}" && git diff HEAD~1 2>&1) || true + fi + fi + + if [[ -z "${diff_output}" ]]; then + warn "No changes found to review" >> "$LLM_OUTPUT" + return 0 + fi + + local file_count + file_count=$(echo "${diff_output}" | grep -c '^diff --git' || true) + + { + info "Diff contains changes to ${file_count} file(s)" + echo "" + echo "${diff_output}" + } >> "$LLM_OUTPUT" +} + +# @cmd Get list of changed files with stats +# @option --base Optional base ref to diff against +get_changed_files() { + local project_dir + project_dir=$(_project_dir) + local base="${argc_base:-}" + + local stat_output="" + + if [[ -n "${base}" ]]; then + stat_output=$(cd "${project_dir}" && git diff --stat "${base}" 2>&1) || true + else + stat_output=$(cd "${project_dir}" && git diff --cached --stat 2>&1) || true + + if [[ -z "${stat_output}" ]]; then + stat_output=$(cd "${project_dir}" && git diff --stat 2>&1) || true + fi + + if [[ -z "${stat_output}" ]]; then + stat_output=$(cd "${project_dir}" && git diff --stat HEAD~1 2>&1) || true + fi + fi + + if [[ -z "${stat_output}" ]]; then + warn "No changes found" >> "$LLM_OUTPUT" + return 0 + fi + + { + info "Changed files:" + echo "" + echo "${stat_output}" + } >> "$LLM_OUTPUT" +} + +# @cmd Get project structure and type information +get_project_info() { + local project_dir + project_dir=$(_project_dir) + + local project_info + project_info=$(detect_project "${project_dir}") + + { + info "Project: ${project_dir}" + echo "Type: $(echo "${project_info}" | jq -r '.type')" + echo "" + get_tree "${project_dir}" 2 + } >> "$LLM_OUTPUT" +} + +# ARGC-BUILD { +# This block was generated by argc (https://github.com/sigoden/argc). +# Modifying it manually is not recommended + +_argc_run() { + if [[ "${1:-}" == "___internal___" ]]; then + _argc_die "error: unsupported ___internal___ command" + fi + if [[ "${OS:-}" == "Windows_NT" ]] && [[ -n "${MSYSTEM:-}" ]]; then + set -o igncr + fi + argc__args=("$(basename "$0" .sh)" "$@") + argc__positionals=() + _argc_index=1 + _argc_len="${#argc__args[@]}" + _argc_tools=() + _argc_parse + if [ -n "${argc__fn:-}" ]; then + $argc__fn "${argc__positionals[@]}" + fi +} + +_argc_usage() { + cat <<-'EOF' +Code review orchestrator tools + +USAGE: + +COMMANDS: + get_diff Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean. [aliases: get-diff] + get_changed_files Get list of changed files with stats [aliases: get-changed-files] + get_project_info Get project structure and type information [aliases: get-project-info] + +ENVIRONMENTS: + LLM_OUTPUT [default: /dev/stdout] + LLM_AGENT_VAR_PROJECT_DIR [default: .] +EOF + exit +} + +_argc_version() { + echo 0.0.0 + exit +} + +_argc_parse() { + local _argc_key _argc_action + local _argc_subcmds="get_diff, get-diff, get_changed_files, get-changed-files, get_project_info, get-project-info" + while [[ $_argc_index -lt $_argc_len ]]; do + _argc_item="${argc__args[_argc_index]}" + _argc_key="${_argc_item%%=*}" + case "$_argc_key" in + --help | -help | -h) + _argc_usage + ;; + --version | -version | -V) + _argc_version + ;; + --) + _argc_dash="${#argc__positionals[@]}" + argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}") + _argc_index=$_argc_len + break + ;; + get_diff | get-diff) + _argc_index=$((_argc_index + 1)) + _argc_action=_argc_parse_get_diff + break + ;; + get_changed_files | get-changed-files) + _argc_index=$((_argc_index + 1)) + _argc_action=_argc_parse_get_changed_files + break + ;; + get_project_info | get-project-info) + _argc_index=$((_argc_index + 1)) + _argc_action=_argc_parse_get_project_info + break + ;; + help) + local help_arg="${argc__args[$((_argc_index + 1))]:-}" + case "$help_arg" in + get_diff | get-diff) + _argc_usage_get_diff + ;; + get_changed_files | get-changed-files) + _argc_usage_get_changed_files + ;; + get_project_info | get-project-info) + _argc_usage_get_project_info + ;; + "") + _argc_usage + ;; + *) + _argc_die "error: invalid value \`$help_arg\` for \`\`"$'\n'" [possible values: $_argc_subcmds]" + ;; + esac + ;; + *) + _argc_die "error: \`\` requires a subcommand but one was not provided"$'\n'" [subcommands: $_argc_subcmds]" + ;; + esac + done + if [[ -n "${_argc_action:-}" ]]; then + $_argc_action + else + _argc_usage + fi +} + +_argc_usage_get_diff() { + cat <<-'EOF' +Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean. + +USAGE: get_diff [OPTIONS] + +OPTIONS: + --base Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA) + -h, --help Print help + +ENVIRONMENTS: + LLM_OUTPUT [default: /dev/stdout] + LLM_AGENT_VAR_PROJECT_DIR [default: .] +EOF + exit +} + +_argc_parse_get_diff() { + local _argc_key _argc_action + local _argc_subcmds="" + while [[ $_argc_index -lt $_argc_len ]]; do + _argc_item="${argc__args[_argc_index]}" + _argc_key="${_argc_item%%=*}" + case "$_argc_key" in + --help | -help | -h) + _argc_usage_get_diff + ;; + --) + _argc_dash="${#argc__positionals[@]}" + argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}") + _argc_index=$_argc_len + break + ;; + --base) + _argc_take_args "--base " 1 1 "-" "" + _argc_index=$((_argc_index + _argc_take_args_len + 1)) + if [[ -z "${argc_base:-}" ]]; then + argc_base="${_argc_take_args_values[0]:-}" + else + _argc_die "error: the argument \`--base\` cannot be used multiple times" + fi + ;; + *) + if _argc_maybe_flag_option "-" "$_argc_item"; then + _argc_die "error: unexpected argument \`$_argc_key\` found" + fi + argc__positionals+=("$_argc_item") + _argc_index=$((_argc_index + 1)) + ;; + esac + done + if [[ -n "${_argc_action:-}" ]]; then + $_argc_action + else + argc__fn=get_diff + if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then + _argc_usage_get_diff + fi + if [[ -z "${LLM_OUTPUT:-}" ]]; then + export LLM_OUTPUT=/dev/stdout + fi + if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then + export LLM_AGENT_VAR_PROJECT_DIR=. + fi + fi +} + +_argc_usage_get_changed_files() { + cat <<-'EOF' +Get list of changed files with stats + +USAGE: get_changed_files [OPTIONS] + +OPTIONS: + --base Optional base ref to diff against + -h, --help Print help + +ENVIRONMENTS: + LLM_OUTPUT [default: /dev/stdout] + LLM_AGENT_VAR_PROJECT_DIR [default: .] +EOF + exit +} + +_argc_parse_get_changed_files() { + local _argc_key _argc_action + local _argc_subcmds="" + while [[ $_argc_index -lt $_argc_len ]]; do + _argc_item="${argc__args[_argc_index]}" + _argc_key="${_argc_item%%=*}" + case "$_argc_key" in + --help | -help | -h) + _argc_usage_get_changed_files + ;; + --) + _argc_dash="${#argc__positionals[@]}" + argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}") + _argc_index=$_argc_len + break + ;; + --base) + _argc_take_args "--base " 1 1 "-" "" + _argc_index=$((_argc_index + _argc_take_args_len + 1)) + if [[ -z "${argc_base:-}" ]]; then + argc_base="${_argc_take_args_values[0]:-}" + else + _argc_die "error: the argument \`--base\` cannot be used multiple times" + fi + ;; + *) + if _argc_maybe_flag_option "-" "$_argc_item"; then + _argc_die "error: unexpected argument \`$_argc_key\` found" + fi + argc__positionals+=("$_argc_item") + _argc_index=$((_argc_index + 1)) + ;; + esac + done + if [[ -n "${_argc_action:-}" ]]; then + $_argc_action + else + argc__fn=get_changed_files + if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then + _argc_usage_get_changed_files + fi + if [[ -z "${LLM_OUTPUT:-}" ]]; then + export LLM_OUTPUT=/dev/stdout + fi + if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then + export LLM_AGENT_VAR_PROJECT_DIR=. + fi + fi +} + +_argc_usage_get_project_info() { + cat <<-'EOF' +Get project structure and type information + +USAGE: get_project_info + +ENVIRONMENTS: + LLM_OUTPUT [default: /dev/stdout] + LLM_AGENT_VAR_PROJECT_DIR [default: .] +EOF + exit +} + +_argc_parse_get_project_info() { + local _argc_key _argc_action + local _argc_subcmds="" + while [[ $_argc_index -lt $_argc_len ]]; do + _argc_item="${argc__args[_argc_index]}" + _argc_key="${_argc_item%%=*}" + case "$_argc_key" in + --help | -help | -h) + _argc_usage_get_project_info + ;; + --) + _argc_dash="${#argc__positionals[@]}" + argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}") + _argc_index=$_argc_len + break + ;; + *) + argc__positionals+=("$_argc_item") + _argc_index=$((_argc_index + 1)) + ;; + esac + done + if [[ -n "${_argc_action:-}" ]]; then + $_argc_action + else + argc__fn=get_project_info + if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then + _argc_usage_get_project_info + fi + if [[ -z "${LLM_OUTPUT:-}" ]]; then + export LLM_OUTPUT=/dev/stdout + fi + if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then + export LLM_AGENT_VAR_PROJECT_DIR=. + fi + fi +} + +_argc_take_args() { + _argc_take_args_values=() + _argc_take_args_len=0 + local param="$1" min="$2" max="$3" signs="$4" delimiter="$5" + if [[ "$min" -eq 0 ]] && [[ "$max" -eq 0 ]]; then + return + fi + local _argc_take_index=$((_argc_index + 1)) _argc_take_value + if [[ "$_argc_item" == *=* ]]; then + _argc_take_args_values=("${_argc_item##*=}") + else + while [[ $_argc_take_index -lt $_argc_len ]]; do + _argc_take_value="${argc__args[_argc_take_index]}" + if _argc_maybe_flag_option "$signs" "$_argc_take_value"; then + if [[ "${#_argc_take_value}" -gt 1 ]]; then + break + fi + fi + _argc_take_args_values+=("$_argc_take_value") + _argc_take_args_len=$((_argc_take_args_len + 1)) + if [[ "$_argc_take_args_len" -ge "$max" ]]; then + break + fi + _argc_take_index=$((_argc_take_index + 1)) + done + fi + if [[ "${#_argc_take_args_values[@]}" -lt "$min" ]]; then + _argc_die "error: incorrect number of values for \`$param\`" + fi + if [[ -n "$delimiter" ]] && [[ "${#_argc_take_args_values[@]}" -gt 0 ]]; then + local item values arr=() + for item in "${_argc_take_args_values[@]}"; do + IFS="$delimiter" read -r -a values <<<"$item" + arr+=("${values[@]}") + done + _argc_take_args_values=("${arr[@]}") + fi +} + +_argc_maybe_flag_option() { + local signs="$1" arg="$2" + if [[ -z "$signs" ]]; then + return 1 + fi + local cond=false + if [[ "$signs" == *"+"* ]]; then + if [[ "$arg" =~ ^\+[^+].* ]]; then + cond=true + fi + elif [[ "$arg" == -* ]]; then + if (( ${#arg} < 3 )) || [[ ! "$arg" =~ ^---.* ]]; then + cond=true + fi + fi + if [[ "$cond" == "false" ]]; then + return 1 + fi + local value="${arg%%=*}" + if [[ "$value" =~ [[:space:]] ]]; then + return 1 + fi + return 0 +} + +_argc_die() { + if [[ $# -eq 0 ]]; then + cat + else + echo "$*" >&2 + fi + exit 1 +} + +_argc_run "$@" + +# ARGC-BUILD } \ No newline at end of file diff --git a/assets/agents/file-reviewer/config.yaml b/assets/agents/file-reviewer/config.yaml new file mode 100644 index 0000000..93506da --- /dev/null +++ b/assets/agents/file-reviewer/config.yaml @@ -0,0 +1,111 @@ +name: file-reviewer +description: Reviews a single file's diff for bugs, style issues, and cross-cutting concerns +version: 1.0.0 +temperature: 0.1 +top_p: 0.95 + +variables: + - name: project_dir + description: Project directory for context + default: '.' + +global_tools: + - fs_read.sh + - fs_grep.sh + - fs_glob.sh + +instructions: | + You are a precise code reviewer. You review ONE file's diff and produce structured findings. + + ## Your Mission + + You receive a git diff for a single file. Your job: + 1. Analyze the diff for bugs, logic errors, security issues, and style problems + 2. Read surrounding code for context (use `fs_read` with targeted offsets) + 3. Check your inbox for cross-cutting alerts from sibling reviewers + 4. Send alerts to siblings if you spot cross-file issues + 5. Return structured findings + + ## Input + + You receive: + - The file path being reviewed + - The git diff for that file + - A sibling roster (other file-reviewers and which files they're reviewing) + + ## Cross-Cutting Alerts (Teammate Pattern) + + After analyzing your file, check if changes might affect sibling files: + - **Interface changes**: If a function signature changed, alert siblings reviewing callers + - **Type changes**: If a type/struct changed, alert siblings reviewing files that use it + - **Import changes**: If exports changed, alert siblings reviewing importers + - **Config changes**: Alert all siblings if config format changed + + To alert a sibling: + ``` + agent__send_message --to --message "ALERT: " + ``` + + Check your inbox periodically for alerts from siblings: + ``` + agent__check_inbox + ``` + + If you receive an alert, incorporate it into your findings under a "Cross-File Concerns" section. + + ## File Reading Strategy + + 1. **Read changed lines' context:** Use `fs_read --path "file" --offset --limit 50` to see surrounding code + 2. **Grep for usage:** `fs_grep --pattern "function_name" --include "*.rs"` to find callers + 3. **Never read entire large files:** Target the changed regions only + 4. **Max 5 file reads:** Be efficient + + ## Output Format + + Structure your response EXACTLY as: + + ``` + ## File: + + ### Summary + <1-2 sentence summary of the changes> + + ### Findings + + #### + - **Severity**: 🔴 CRITICAL | 🟡 WARNING | 🟢 SUGGESTION | 💡 NITPICK + - **Lines**: - + - **Description**: + - **Suggestion**: + + #### + ... + + ### Cross-File Concerns + + <"None" if no cross-file concerns> + + REVIEW_COMPLETE + ``` + + ## Severity Guide + + | Severity | When to use | + |----------|------------| + | 🔴 CRITICAL | Bugs, security vulnerabilities, data loss risks, crashes | + | 🟡 WARNING | Logic errors, performance issues, missing error handling, race conditions | + | 🟢 SUGGESTION | Better patterns, improved readability, missing docs for public APIs | + | 💡 NITPICK | Style preferences, minor naming issues, formatting | + + ## Rules + + 1. **Be specific:** Reference exact line numbers and code + 2. **Be actionable:** Every finding must have a suggestion + 3. **Don't nitpick formatting:** If a formatter/linter exists (check for .rustfmt.toml, .prettierrc, etc.) + 4. **Focus on the diff:** Don't review unchanged code unless it's directly affected + 5. **Never modify files:** You are read-only + 6. **Always end with REVIEW_COMPLETE** + + ## Context + - Project: {{project_dir}} + - CWD: {{__cwd__}} diff --git a/assets/agents/file-reviewer/tools.sh b/assets/agents/file-reviewer/tools.sh new file mode 100755 index 0000000..b6b42aa --- /dev/null +++ b/assets/agents/file-reviewer/tools.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -eo pipefail + +# shellcheck disable=SC1090 +source "$LLM_PROMPT_UTILS_FILE" +source "$LLM_ROOT_DIR/agents/.shared/utils.sh" + +# @env LLM_OUTPUT=/dev/stdout +# @env LLM_AGENT_VAR_PROJECT_DIR=. +# @describe File reviewer tools for single-file code review + +_project_dir() { + local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}" + (cd "${dir}" 2>/dev/null && pwd) || echo "${dir}" +} + +# @cmd Get project structure to understand codebase layout +get_structure() { + local project_dir + project_dir=$(_project_dir) + + info "Project structure:" >> "$LLM_OUTPUT" + echo "" >> "$LLM_OUTPUT" + + local project_info + project_info=$(detect_project "${project_dir}") + + { + echo "Type: $(echo "${project_info}" | jq -r '.type')" + echo "" + get_tree "${project_dir}" 2 + } >> "$LLM_OUTPUT" +} \ No newline at end of file diff --git a/src/function/supervisor.rs b/src/function/supervisor.rs index 4f208bb..d424b04 100644 --- a/src/function/supervisor.rs +++ b/src/function/supervisor.rs @@ -640,13 +640,13 @@ fn handle_send_message(config: &GlobalConfig, args: &Value) -> Result { .or_else(|| cfg.agent.as_ref().map(|a| a.name().to_string())) .unwrap_or_else(|| "parent".to_string()); - // Try local supervisor first (parent → child routing) + // Try local supervisor first (parent -> child routing) let inbox = cfg .supervisor .as_ref() .and_then(|sup| sup.read().inbox(id).cloned()); - // Fall back to parent_supervisor (sibling → sibling routing) + // Fall back to parent_supervisor (sibling -> sibling routing) let inbox = inbox.or_else(|| { cfg.parent_supervisor .as_ref()