Skip to content

fix(examples): make coding_agents Docker stack spin up cleanly [INT-500]#447

Open
nir-singher-band wants to merge 2 commits into
mainfrom
int-500-validate-claude-codex-dockers-with-plannerreviewer
Open

fix(examples): make coding_agents Docker stack spin up cleanly [INT-500]#447
nir-singher-band wants to merge 2 commits into
mainfrom
int-500-validate-claude-codex-dockers-with-plannerreviewer

Conversation

@nir-singher-band

Copy link
Copy Markdown
Collaborator

Summary

Validated the examples/coding_agents two-agent Docker Compose stack (Claude SDK planner + Codex reviewer) end-to-end against app.band.ai and fixed everything that stopped it from spinning up. Both containers now boot, connect, load their role prompts, and coordinate in a room.

Root causes fixed

1. Phantom agent_config.yaml directory → config-load crash.
Compose bind-mounts ./agent_config.yaml; when the file is missing Docker auto-creates the source as an empty directory, so both runners crashed reading config. create_agents.py now writes the combined, keyed agent_config.yaml directly (single source of truth), replacing the old "copy the example, paste creds" step that produced the phantom dir. It refuses to overwrite an existing config without FORCE=1 (re-running registers new agents and would orphan the old ones).

2. Platform mismatch / unreachable host → httpx.ConnectError: [Errno -2] Name or service not known crash loop.
Agents registered on one Band platform can't be found by containers pointed at another, and a misspelled/unreachable BAND_REST_URL fails DNS before auth. create_agents.py now logs the platform it registered against and warns to match .env; .env.example and the README troubleshooting section document the exact symptom → fix.

Also in this change

  • Reviewer role prompt now loads — mount ./prompts into the reviewer service; it read /app/prompts/reviewer.md but nothing was mounted there, so it was booting without its Band collaboration prompt.
  • Repo block is optional throughout (example, README, script) — with no repo block the agents boot chat-only, needing zero GitHub/SSH. Add REPO_URLREPO_BRANCH/REPO_INDEX) to wire a shared repo.
  • test_communication.py reads the combined agent_config.yaml; dropped the stale "3 agents" docstring.
  • .gitignores the coding_agents per-run artifacts (.agent_ids.txt, legacy planner.yaml/reviewer.yaml) so credentials/IDs can't be committed.

Test plan

Verified live on app.band.ai:

  • python create_agents.py registers planner + reviewer and writes agent_config.yaml (no phantom dir).
  • docker compose up -d → both containers Up and stable (no restart loop).
  • Logs show both connecting over REST/WS, both loading role prompts (incl. reviewer at /app/prompts/reviewer.md), and the planner completing a room turn.
  • No secrets in the diff — .env, agent_config.yaml, .agent_ids.txt are gitignored and excluded.

The coding_agents two-agent Compose example (Claude SDK planner + Codex
reviewer) crashed on `docker compose up`. Two root causes, both fixed here,
plus guardrails so neither recurs.

1. Phantom config directory. Compose bind-mounts ./agent_config.yaml, and
   Docker auto-creates the source as an empty *directory* when the file is
   missing — so both runners crashed loading config. create_agents.py now
   writes the combined, keyed agent_config.yaml directly (single source of
   truth), replacing the old copy-the-example-then-paste-creds flow that left
   the phantom dir behind. It refuses to overwrite an existing config without
   FORCE=1 (re-running registers new agents and would orphan the old ones).

2. Platform mismatch / unreachable host. Agents registered against one Band
   platform can't be found by containers pointed at another; a misspelled or
   unreachable BAND_REST_URL crashes both with
   "httpx.ConnectError: [Errno -2] Name or service not known". create_agents.py
   now logs the platform it registered against and warns to match .env;
   .env.example and the README troubleshooting section document the symptom.

Also in this change:
- Mount ./prompts into the reviewer service — it read /app/prompts/reviewer.md
  but nothing was mounted there, so it booted without its role prompt.
- Treat the repo block as optional throughout (example, README, script): with
  no repo block the agents boot chat-only, needing no GitHub/SSH.
- test_communication.py reads the combined agent_config.yaml; drop the stale
  "3 agents" docstring.
- gitignore coding_agents per-run artifacts (.agent_ids.txt, legacy
  planner/reviewer.yaml) so credentials/IDs can't be committed.
@linear-code

linear-code Bot commented Jul 19, 2026

Copy link
Copy Markdown

INT-500

@nir-singher-band
nir-singher-band requested a review from a team July 19, 2026 09:00
if not api_key:
raise ValueError("BAND_API_KEY environment variable is required")

if CONFIG_PATH.is_file() and os.environ.get("FORCE", "").strip().lower() not in (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

is_file() misses the stale agent_config.yaml directory this PR is meant to recover from. The script will register both agents, then fail at write_text() with IsADirectoryError, without writing cleanup IDs. Check exists() or handle directories before registering agents.

)
logger.info("Registered against: %s", base_url)
logger.info(
"IMPORTANT: set BAND_REST_URL in .env to the SAME platform (%s), or the "

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The setup guide has users configure BAND_REST_URL in .env, but this script does not load .env. On a non-default platform it registers agents on app.band.ai, while Compose connects to the URL in .env. Please load the same configuration source or make the command pass BAND_REST_URL explicitly.

config[agent_def["key"]] = section
created.append({"name": agent.name, "id": agent.id, "role": agent_def["role"]})

body = yaml.dump(config, default_flow_style=False, sort_keys=False)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we move config and cleanup-file writing into small helpers? main() currently registers agents, builds config, writes files, and reports the result. Separating persistence would make the flow easier to follow and test.

CLEANUP_PATH.write_text("".join(f"{a['id']}\n" for a in created), encoding="utf-8")
logger.info("Agent IDs saved to %s for cleanup", CLEANUP_PATH.name)

logger.info("\n=== Summary ===")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we move this summary output into a log_summary() helper? It would keep main() focused on the registration flow and put the user-facing setup instructions in one place.

}
)
config[agent_def["key"]] = section
created.append({"name": agent.name, "id": agent.id, "role": agent_def["role"]})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If reviewer registration fails, the planner ID is never written to the cleanup file. FORCE=1 also overwrites the previous cleanup IDs. Both paths can leave agents orphaned. Could we persist each successful registration immediately or clean up created agents on failure?


def load_agent_config(filename: str) -> dict:
"""Load agent config from YAML file."""
def load_config_file(filename: str) -> dict:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we use band.config.load_agent_config() here? It already supports keyed config and validates missing files, empty values, and required credentials, so we do not need another YAML loader.

planner = config["planner"]
reviewer = config["reviewer"]

base_url = os.environ.get("BAND_REST_URL", "https://app.band.ai")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This script also ignores the .env file used by Compose, so the documented command targets app.band.ai on custom deployments. Could we load the same settings here as create_agents.py?


```bash
cp agent_config.yaml.example agent_config.yaml
BAND_API_KEY=band_u_... python create_agents.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Since this script is now the required setup path, can we add PEP 723 metadata and document uv run create_agents.py? Plain python currently depends on the SDK already being installed.

Base automatically changed from dev to main July 20, 2026 13:47
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.

2 participants