Skip to content

feat: agent groups — @groupname addressing and group management#2

Open
javimosch wants to merge 6 commits into
mainfrom
feat-group
Open

feat: agent groups — @groupname addressing and group management#2
javimosch wants to merge 6 commits into
mainfrom
feat-group

Conversation

@javimosch
Copy link
Copy Markdown
Owner

@javimosch javimosch commented May 28, 2026

Introduce named agent groups for addressing multiple agents at once.

CLI (a2a.py):

  • a2a group create <name> — create/validate a group name
  • a2a group add <name> <members...> — add registered agents to a group
  • a2a group remove <name> <member> — remove a specific member
  • a2a group delete <name> — delete an entire group
  • a2a group list — list all groups with member counts
  • a2a group show <name> — show members of a group

Python client (a2a_client.py):

  • create_group(), add_to_group(), remove_from_group()
  • delete_group(), list_groups(), group_members()

Messaging fan-out:

  • a2a send @groupname ... sends to every member of the group
  • A2AClient.send(@groupname) same via the client library
  • Respects agent registration check on group add

Schema: new agent_groups table (name, member_id, created_at) with indexes on name and member_id. Group names limited to 64 chars, alphanumeric/dash/underscore only.

Summary by CodeRabbit

  • New Features

    • Agent groups: create, add/remove members, delete, list/show; group names normalized/validated.
    • Send to groups via @group fan-out to each member.
    • Client APIs for programmatic group management and member queries.
  • Chores

    • CI lint step expanded to check additional shell scripts.
  • Tests

    • New unit and CLI smoke tests covering group workflows; smoke test skips if CLI absent.
    • Updated full test transcript included.

Review Change Stack

Introduce named agent groups for addressing multiple agents at once:

CLI (a2a.py):
  - a2a group create <name> — create/validate a group name
  - a2a group add <name> <members...> — add registered agents to a group
  - a2a group remove <name> <member> — remove a specific member
  - a2a group delete <name> — delete an entire group
  - a2a group list — list all groups with member counts
  - a2a group show <name> — show members of a group

Python client (a2a_client.py):
  - create_group(), add_to_group(), remove_from_group()
  - delete_group(), list_groups(), group_members()

Messaging fan-out:
  - a2a send @groupName ... sends to every member of the group
  - A2AClient.send(@groupName) same via the client library
  - Respects agent registration check on group add

Schema: new agent_groups table (name, member_id, created_at) with
indexes on name and member_id. Group names limited to 64 chars,
alphanumeric/dash/underscore only.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

Adds named agent groups stored in a new SQLite agent_groups table, validation helpers, CLI group subcommands, group-aware send behaviour in the CLI and client (one message per member), and client APIs to manage and query groups.

Changes

Agent Groups Feature

Layer / File(s) Summary
Group name validation foundation
a2a_common.py, a2a.py, a2a_client.py
MAX_GROUP_NAME_LENGTH and _validate_group_name() normalize and validate group names (trim, strip leading @, non-empty, max length, allowed chars).
Database schema and CLI group management
a2a.py
Adds agent_groups table and indexes; implements a2a group subcommands (create,add,remove,delete,list,show) with sentinel rows and parser wiring.
Group-aware send in CLI
a2a.py
cmd_send treats recipients starting with @ as groups: resolves name, queries members, inserts one message per member (shared timestamp/thread), commits, prints the first message id, and returns early.
Client library group APIs and messaging
a2a_client.py
A2AClient.send() supports @group sending by validating name, fetching members, and inserting per-member messages; adds create_group, add_to_group, remove_from_group, delete_group, list_groups, and group_members.
CI and smoke tests
.github/workflows/test.yml, smoke_test.sh, smoke_test_group.sh
CI expands bash -n lint checks to multiple scripts; smoke test exits early with success if claude CLI is missing; adds an end-to-end smoke test for group behaviour.
Tests and recorded output
test_a2a.py, test_a2a_client.py, test_output.txt
Adds unit tests covering group validation, CRUD, membership mutations, send-to-group fan-out, client group APIs, and updates the test transcript file.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through rows and names so neat,
Trimmed the whitespace, checked each seat,
One send becomes many, shared thread and time,
Groups stitched in SQLite, tiny and prime,
Hooray — agents gather, and messages rhyme! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the primary changes: introduction of named agent groups with both @groupname addressing and group management functionality.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-group

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.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 318cb0bb5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread a2a_client.py
Comment on lines +622 to +624
conn.execute(
"INSERT OR IGNORE INTO agent_groups(name, member_id, created_at) VALUES (?,?,?)",
(name, member_id, ts),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Create the agent_groups table during client init

When a Python-client user follows the documented fresh-project path (A2AClient(...).init_project() and then registers agents), init_project() still creates only agents, messages, and reads, so this new insert into agent_groups raises sqlite3.OperationalError: no such table: agent_groups. Please add the new table/indexes to the client initializer (or shared schema) so the new group API works without a prior CLI a2a init.

Useful? React with 👍 / 👎.

Comment thread a2a.py
Comment on lines +736 to +740
_, conn = _open(args)
# upsert-safe: just ensures the name is valid; rows added via add
conn.close()
if getattr(args, 'json', False):
print(json.dumps({"group": name, "created": True}, indent=2))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Persist groups created by the CLI

In the CLI path, a2a group create team only validates the name and then reports success; because no row is stored for empty groups, a2a group list immediately shows no groups and a2a send @team ... still errors until group add implicitly creates membership rows. For a public create command, either persist group metadata or avoid reporting that the group was created.

Useful? React with 👍 / 👎.

Comment thread a2a_client.py
Comment on lines +602 to +606
def create_group(self, name: str) -> None:
"""Create a named group (validates name; members added separately)."""
_validate_group_name(name)

def add_to_group(self, name: str, *member_ids: str) -> int:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Expose group methods in the type stub

This adds public A2AClient group methods, but a2a_client.pyi was not updated, so typed consumers and IDEs using the shipped stub will still report create_group, add_to_group, group_members, etc. as missing even though they exist at runtime. Please keep the stub in sync with the new Python API.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@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

🧹 Nitpick comments (2)
a2a_common.py (1)

12-20: ⚡ Quick win

Consider returning the normalized name to prevent caller inconsistency.

The function strips name internally before validation but returns None. Callers that pass untrimmed input (e.g., " team ") will pass validation, but if they use the original value afterward, the database will store whitespace-padded names. From context, a2a_client.py:add_to_group calls _validate_group_name(name) then uses the original name in the INSERT.

Returning the normalized value would make misuse harder:

Proposed change
-def _validate_group_name(name: str) -> None:
+def _validate_group_name(name: str) -> str:
+    """Validate group name and return normalized value."""
     import re
     name = name.strip()
     if not name:
         raise ValueError("group name must not be empty")
     if len(name) > MAX_GROUP_NAME_LENGTH:
         raise ValueError(f"group name too long ({len(name)} chars, max {MAX_GROUP_NAME_LENGTH})")
     if not re.match(r'^[a-zA-Z0-9_-]+$', name):
         raise ValueError("group name must contain only alphanumeric characters, dashes, or underscores")
+    return name
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@a2a_common.py` around lines 12 - 20, _validate_group_name currently strips
and validates the input but returns None, which lets callers like
a2a_client.py:add_to_group continue using the original (possibly untrimmed)
name; change _validate_group_name to return the normalized (stripped) name and
update callers to use the returned value (e.g., assign name =
_validate_group_name(name)) so the stored/used group name is the validated,
trimmed version.
a2a.py (1)

72-72: ⚖️ Poor tradeoff

Optional: Consider importing MAX_GROUP_NAME_LENGTH from a2a_common.py.

The constant and validation logic are duplicated between a2a_common.py and a2a.py. While the function implementations differ (die vs raise), the constant could be shared to reduce drift risk. This mirrors the existing duplication pattern for MAX_ID_LENGTH, so deferring is reasonable.

Also applies to: 101-109

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@a2a.py` at line 72, Replace the duplicated MAX_GROUP_NAME_LENGTH constant in
a2a.py with a single import from a2a_common.py (remove the local definition of
MAX_GROUP_NAME_LENGTH) and update the validation logic in the functions that
reference it to use the imported symbol; ensure any existing behavior
differences (die vs raise) remain unchanged while referencing
a2a_common.MAX_GROUP_NAME_LENGTH for consistency with MAX_ID_LENGTH's reuse.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@a2a_client.py`:
- Around line 602-604: create_group currently only calls _validate_group_name
and does not persist anything, so callers like list_groups will not see the
group; update create_group to persist a sentinel row in the agent_groups table
(or alter schema to allow an empty-group row) so a group can exist without
members: call whatever DB-insert helper you use (the same layer used by
add_to_group) to insert a row for (name, sentinel_member) or (name, NULL)
consistent with the agent_groups PK and adjust list_groups to ignore sentinel
rows; alternatively, if you intend groups to be implicit, remove create_group or
rename it to validate_group_name to avoid misleading API (refer to create_group,
_validate_group_name, agent_groups, add_to_group, list_groups).

In `@a2a.py`:
- Around line 112-115: _resolve_group_name can return a name with a leading
space when input is like "@ team" because raw.strip().lstrip('@') leaves the
space after removing '@'; change the normalization to remove whitespace both
before and after removing the '@' by calling strip again after lstrip (e.g. name
= raw.strip().lstrip('@').strip()), then call _validate_group_name(name) and
return name so stored/queried group names are consistent; references: function
_resolve_group_name and _validate_group_name.

---

Nitpick comments:
In `@a2a_common.py`:
- Around line 12-20: _validate_group_name currently strips and validates the
input but returns None, which lets callers like a2a_client.py:add_to_group
continue using the original (possibly untrimmed) name; change
_validate_group_name to return the normalized (stripped) name and update callers
to use the returned value (e.g., assign name = _validate_group_name(name)) so
the stored/used group name is the validated, trimmed version.

In `@a2a.py`:
- Line 72: Replace the duplicated MAX_GROUP_NAME_LENGTH constant in a2a.py with
a single import from a2a_common.py (remove the local definition of
MAX_GROUP_NAME_LENGTH) and update the validation logic in the functions that
reference it to use the imported symbol; ensure any existing behavior
differences (die vs raise) remain unchanged while referencing
a2a_common.MAX_GROUP_NAME_LENGTH for consistency with MAX_ID_LENGTH's reuse.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f434d8c8-cbf6-48b7-88df-fa6508dede7f

📥 Commits

Reviewing files that changed from the base of the PR and between 99c91a8 and 318cb0b.

📒 Files selected for processing (3)
  • a2a.py
  • a2a_client.py
  • a2a_common.py

Comment thread a2a_client.py Outdated
Comment thread a2a.py
Copy link
Copy Markdown

@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

🧹 Nitpick comments (2)
a2a.py (2)

59-66: Operational notes on the new agent_groups schema.

  • idx_agent_groups_name is redundant: the PRIMARY KEY (name, member_id) already creates an index whose leading column is name, which serves WHERE name=? lookups. You can drop it. (idx_agent_groups_member is useful and should stay.)
  • There is no cleanup of agent_groups when an agent is unregistered (cmd_unregister), so stale memberships persist and group sends will fan out messages to deregistered recipients. Consider deleting memberships on unregister — idx_agent_groups_member makes DELETE FROM agent_groups WHERE member_id=? efficient.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@a2a.py` around lines 59 - 66, The agent_groups schema includes a redundant
index idx_agent_groups_name because the PRIMARY KEY (name, member_id) already
provides an index on name; remove the CREATE INDEX IF NOT EXISTS
idx_agent_groups_name statement and keep idx_agent_groups_member. Also update
the unregister flow in cmd_unregister to clean up memberships by running a
DELETE FROM agent_groups WHERE member_id=? (using the member id parameter) so
deregistered agents don't retain stale group entries; rely on
idx_agent_groups_member for efficient deletes.

102-117: ⚡ Quick win

Delegate _validate_group_name to a2a_common to avoid future drift

This duplication currently matches a2a_common._validate_group_name (same .strip(), MAX_GROUP_NAME_LENGTH = 64, and same charset regex), but it will need manual syncing if the shared validator changes. Converting its ValueError to die() keeps behavior consistent while eliminating drift risk.

♻️ Proposed refactor
-from a2a_common import MAX_GROUP_NAME_LENGTH
+from a2a_common import MAX_GROUP_NAME_LENGTH, _validate_group_name as _validate_group_name_common
 def _validate_group_name(name: str) -> str:
     """Validate and normalize a group name.

     Strips whitespace, checks length and character constraints.
     Exits via die() on failure.
     Returns the normalized (stripped) name on success.
     """
-    import re
-    name = name.strip()
-    if not name:
-        die("group name must not be empty")
-    if len(name) > MAX_GROUP_NAME_LENGTH:
-        die(f"group name too long ({len(name)} chars, max {MAX_GROUP_NAME_LENGTH})")
-    if not re.match(r'^[a-zA-Z0-9_-]+$', name):
-        die("group name must contain only alphanumeric characters, dashes, or underscores")
-    return name
+    try:
+        return _validate_group_name_common(name)
+    except ValueError as exc:
+        die(str(exc))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@a2a.py` around lines 102 - 117, Replace the local _validate_group_name
implementation with a call to the shared validator in a2a_common: import
a2a_common and call a2a_common._validate_group_name(name) and return its result;
catch a ValueError from a2a_common._validate_group_name and call die(...) with
the error message to preserve current behavior (die on invalid names). Remove
the duplicated regex/MAX_GROUP_NAME_LENGTH logic from this file so validation is
delegated to a2a_common._validate_group_name and ensure die and the function
name _validate_group_name remain used as the public API here.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@a2a.py`:
- Around line 752-759: Define a single sentinel constant GROUP_SENTINEL =
"__group__" near the other limits and replace the hard-coded '__group__' in
group creation (where _resolve_group_name and conn.execute insert into
agent_groups) with GROUP_SENTINEL; then update all reads to exclude this
sentinel: in cmd_send (the per-member fan-out loop that builds members and
reports len(members)), in cmd_group_list (the COUNT(*) query), and in
cmd_group_show (the members listing) add "WHERE member_id != GROUP_SENTINEL" or
equivalent exclusion to queries so the sentinel never appears as a real member
or inflates counts; finally mirror the same sentinel constant and exclusions in
a2a_client.py methods send, list_groups, and group_members.

---

Nitpick comments:
In `@a2a.py`:
- Around line 59-66: The agent_groups schema includes a redundant index
idx_agent_groups_name because the PRIMARY KEY (name, member_id) already provides
an index on name; remove the CREATE INDEX IF NOT EXISTS idx_agent_groups_name
statement and keep idx_agent_groups_member. Also update the unregister flow in
cmd_unregister to clean up memberships by running a DELETE FROM agent_groups
WHERE member_id=? (using the member id parameter) so deregistered agents don't
retain stale group entries; rely on idx_agent_groups_member for efficient
deletes.
- Around line 102-117: Replace the local _validate_group_name implementation
with a call to the shared validator in a2a_common: import a2a_common and call
a2a_common._validate_group_name(name) and return its result; catch a ValueError
from a2a_common._validate_group_name and call die(...) with the error message to
preserve current behavior (die on invalid names). Remove the duplicated
regex/MAX_GROUP_NAME_LENGTH logic from this file so validation is delegated to
a2a_common._validate_group_name and ensure die and the function name
_validate_group_name remain used as the public API here.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 92f2874e-e2ac-439f-a854-3cd85a7b4d48

📥 Commits

Reviewing files that changed from the base of the PR and between 318cb0b and 6feec13.

📒 Files selected for processing (5)
  • .github/workflows/test.yml
  • a2a.py
  • a2a_client.py
  • a2a_common.py
  • smoke_test.sh
💤 Files with no reviewable changes (1)
  • .github/workflows/test.yml
🚧 Files skipped from review as they are similar to previous changes (2)
  • a2a_common.py
  • a2a_client.py

Comment thread a2a.py
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@smoke_test_group.sh`:
- Around line 5-21: Enable strict failure handling and stop hiding errors:
replace the lone "set -u" with "set -euo pipefail" at the top and remove the "||
true" that masks failures from the "$A2A clear" invocation; ensure the script
still uses the A2A variable (A2A="${A2A_BIN:-...}/a2a.py") and call "$A2A" clear
without swallowing errors so setup failures surface in CI (if you need to
tolerate a missing resource, perform an explicit existence check before calling
"$A2A" clear rather than using "|| true").
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 71f8025f-a91d-4306-b438-c31d417d4719

📥 Commits

Reviewing files that changed from the base of the PR and between 6feec13 and 6ad6558.

📒 Files selected for processing (6)
  • a2a.py
  • a2a_client.py
  • smoke_test_group.sh
  • test_a2a.py
  • test_a2a_client.py
  • test_output.txt
✅ Files skipped from review due to trivial changes (1)
  • test_output.txt
🚧 Files skipped from review as they are similar to previous changes (2)
  • a2a.py
  • a2a_client.py

Comment thread smoke_test_group.sh
Comment on lines +5 to +21
set -u

A2A="${A2A_BIN:-$(dirname "$(readlink -f "$0")")/a2a.py}"
PROJECT="${1:-a2a-group-smoke-$$}"
LOG_DIR="${LOG_DIR:-/tmp/a2a-$PROJECT}"
mkdir -p "$LOG_DIR"

export A2A_PROJECT="$PROJECT"

echo "== a2a group smoke test =="
echo "project: $PROJECT"
echo "logs: $LOG_DIR"
echo

# ---- Fresh bus ----
"$A2A" clear --yes >/dev/null 2>&1 || true
"$A2A" init
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast and stop masking setup failures in the smoke test.

Using only set -u and swallowing clear errors with || true can hide real breakages (bad A2A_BIN, command/runtime failures), making CI smoke results less trustworthy.

Proposed fix
-set -u
+set -euo pipefail
@@
-"$A2A" clear --yes >/dev/null 2>&1 || true
+"$A2A" clear --yes >/dev/null 2>&1
📝 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
set -u
A2A="${A2A_BIN:-$(dirname "$(readlink -f "$0")")/a2a.py}"
PROJECT="${1:-a2a-group-smoke-$$}"
LOG_DIR="${LOG_DIR:-/tmp/a2a-$PROJECT}"
mkdir -p "$LOG_DIR"
export A2A_PROJECT="$PROJECT"
echo "== a2a group smoke test =="
echo "project: $PROJECT"
echo "logs: $LOG_DIR"
echo
# ---- Fresh bus ----
"$A2A" clear --yes >/dev/null 2>&1 || true
"$A2A" init
set -euo pipefail
A2A="${A2A_BIN:-$(dirname "$(readlink -f "$0")")/a2a.py}"
PROJECT="${1:-a2a-group-smoke-$$}"
LOG_DIR="${LOG_DIR:-/tmp/a2a-$PROJECT}"
mkdir -p "$LOG_DIR"
export A2A_PROJECT="$PROJECT"
echo "== a2a group smoke test =="
echo "project: $PROJECT"
echo "logs: $LOG_DIR"
echo
# ---- Fresh bus ----
"$A2A" clear --yes >/dev/null 2>&1
"$A2A" init
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@smoke_test_group.sh` around lines 5 - 21, Enable strict failure handling and
stop hiding errors: replace the lone "set -u" with "set -euo pipefail" at the
top and remove the "|| true" that masks failures from the "$A2A clear"
invocation; ensure the script still uses the A2A variable
(A2A="${A2A_BIN:-...}/a2a.py") and call "$A2A" clear without swallowing errors
so setup failures surface in CI (if you need to tolerate a missing resource,
perform an explicit existence check before calling "$A2A" clear rather than
using "|| true").

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