{"slug": "conducting-agents-like-an-orchestra-how-agents-concerto-works", "title": "Conducting agents like an orchestra: how agents-concerto works", "summary": "A developer built agents-concerto, a multi-agent orchestrator on top of Claude Code that enforces behavior-driven development (BDD) and Tidy First discipline. The system uses four agents—orchestrator, classifier, implementer, and reviewer—to produce open pull requests ready for human review, with acceptance criteria automatically turned into tests that assert only observable behavior.", "body_md": "For months I had the same problem with coding agents: the session drops a pile of changes, and by review time I no longer know what it touched or why.\n\nThe agent writes, the agent validates, the agent congratulates itself. The human signs blind.\n\nAnd when the agent *did* write tests, it got worse — green tests that proved nothing. Mocks of the very code under test, assertions on private fields, snapshots of incidental structure.\n\nSuite green, confidence zero.\n\nSo I built [ agents-concerto](https://github.com/moruno21/agents-concerto): a multi-agent orchestrator on top of Claude Code. The name is literal: there's a\n\nThe repo isn't an application. It's an orchestration brain: one `CLAUDE.md`\n\n, four agents in Markdown, four Bash scripts. Its product is **open PRs ready for human review**.\n\n`config.md`\n\n. One repo or fifteen take the same code path.`backend-dev`\n\n/ `frontend-dev`\n\n.Rule 3 is the one you feel. When the author isn't the approver, the verdict means something.\n\nThis is the part that changed the system most: **a criterion isn't a note for humans, it's a test spec.**\n\nShaping (`/shape`\n\n, or inline in `/run`\n\n) refuses to emit a wishlist. Criteria come out in **Given-When-Then** — *given <context>, when <action>, then <observable result>* — inside a tiered contract where\n\n`## Task`\n\nand `## Acceptance criteria`\n\nare mandatory and `## Scope`\n\n/ `## Non-goals`\n\nare optional, included only when they add signal.A Given-When-Then criterion already names a context, a trigger, and an observable result. That's a test. And because the \"then\" must be observable, the criterion is hard to satisfy by asserting on internals.\n\nSo the criterion travels the pipeline: **written** at shaping, **turned into a test** at implementation, **verified as a criterion→test mapping** at review.\n\n`orchestrator`\n\n`classifier`\n\n`complex`\n\n→ opus for gnarly logic, concurrency, or sensitive paths; `trivial|standard`\n\n→ sonnet. When torn, pick higher.`implementer`\n\n`model:`\n\nin its frontmatter`reviewer`\n\n`Read, Grep, Glob, Bash`\n\n: `Write`\n\n, no `Edit`\n\nEvery criterion becomes at least one test asserting **observable behavior** — outputs, rendered UI, persisted state, HTTP responses, emitted events. Never internals: no private fields, no mocks of internal helpers, no asserting a function was called. Tests are driven from the public entry point, named from the user's view (`it(\"shows an error when the email already exists\")`\n\n), and only genuinely external dependencies get mocked — network, clock, third parties. Never the code under test.\n\nIt all compresses into one rule:\n\nIf a behavior-preserving refactor breaks the test, the test is wrong.\n\nThat's why Tidy First and BDD belong together. The structural commit, by definition, doesn't change behavior — so if a test breaks there, either the \"refactor\" wasn't one, or the test was coupled to internals. Every PR proves both at once.\n\nCorrectness against the criteria, tests green, Tidy First discipline (a mixed commit is `NEEDS_FIXES`\n\n, named), and the BDD gate: a criterion with no covering test, or a test asserting on internals, blocks the PR.\n\nThat last gate has a qualifier I think is the most important decision in it: **it applies only to tests added or modified in the diff.** Pre-existing tests the PR leaves alone aren't judged. Without that, a three-line change gets blocked demanding a suite rewrite — or the implementer tries to fix the old tests and scope creep eats the run. The standard applies going forward; old debt gets paid when you actually touch it.\n\nThe verdict is a consolidated comment citing `path:line`\n\nplus structured output. And `CLEAN`\n\nmeans **\"ready for a human to review\"**, not \"approved\". The reviewer never formally approves. It can't.\n\n```\n   Task  ──►  /run ──►  Step 0: shape it inline if it's vague\n                                          ▼\n                    Step 1  load config + open run log (RUN_ID)\n                    Step 2  read the task (per task_source)\n                    Step 3  scope: which repos? (filter over repos)\n                    Step 4  plan.md: sub-tasks + Blocked by\n                                          ▼\n                    Step 5  dependency waves (sub-tasks in parallel):\n\n                         5a classifier ──► tier → model (sonnet|opus)\n                         5b worktree-create.sh (branch off base_branch)\n                         5c implementer (tier's model) ─ TDD + Tidy First\n                              ▼  opens PR\n                         5d reviewer ──► CLEAN | NEEDS_FIXES (path:line)\n                         5e fix loop ≤ max_fix_cycles\n                              CLEAN ──────────────► ready for human review\n                              cap reached ────────► ready-for-human (escalate)\n                         5f worktree-cleanup.sh (branch and PR untouched)\n                                          ▼\n                    Step 6  run-log summary + notify + STOP\n\n           The human reviews the PRs and merges.\n```\n\nSub-tasks don't run in plan order — a **wave** is everything currently unblocked, and a wave runs in parallel, each sub-task in its own worktree.\n\nBecause nothing auto-merges, there's no conflict worker and none is needed: two branches touching the same file only collide at merge time, and the human picks the order.\n\nTwo config fields stay deliberately orthogonal here: `task_source`\n\nis where a task is **read** from (`none`\n\n, `github`\n\n, `gitlab`\n\n, `linear`\n\n, `jira`\n\n), while each repo's own `host`\n\nis where its PR gets **opened**. So a single run can read from Linear and open PRs on GitHub and GitLab at the same time.\n\nThe invariants don't live only in prompts. `.claude/settings.json`\n\ndenies them at the tool level:\n\n```\ngh pr merge      ← never merge\ngh pr review     ← it drags --approve along\ngit merge        ← never merge locally\ngit push --force ← never rewrite pushed history\n```\n\nA denied command is a deliberate boundary, not an obstacle to route around. An agent that hits one escalates the sub-task as `ready-for-human`\n\nand the run continues.\n\nWorth knowing: an agent team costs roughly **4–6× a single Claude Code session**. Every run writes a summary with that reminder plus PR, cycle, and escalation counts.\n\n```\nclaude plugin marketplace add moruno21/agents-concerto\nclaude plugin install agents-concerto@moruno-plugins\n/agents-concerto:setup\n/agents-concerto:run <task description>\n```\n\nMost of the value isn't in the agent that writes code — it's in **the boundaries around it**. The worktree. The reviewer with no write access. The cycle cap. The deny rules.\n\nAnd with tests, the boundary isn't a permission, it's an **entry point**. An agent told \"write tests\" writes tests coupled to what it just wrote, because that's what's in front of it.\n\nAn agent told \"exercise this through its public interface and assert what a user observes\" can't. The fix wasn't banning mocks — it was moving where you stand to look.\n\nThe agents play. The human holds the baton.", "url": "https://wpnews.pro/news/conducting-agents-like-an-orchestra-how-agents-concerto-works", "canonical_source": "https://dev.to/moruno21/conducting-agents-like-an-orchestra-how-agents-concerto-works-1b2c", "published_at": "2026-07-26 14:02:29+00:00", "updated_at": "2026-07-26 14:30:22.757050+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "developer-tools", "ai-products"], "entities": ["agents-concerto", "Claude Code", "GitHub", "moruno21"], "alternates": {"html": "https://wpnews.pro/news/conducting-agents-like-an-orchestra-how-agents-concerto-works", "markdown": "https://wpnews.pro/news/conducting-agents-like-an-orchestra-how-agents-concerto-works.md", "text": "https://wpnews.pro/news/conducting-agents-like-an-orchestra-how-agents-concerto-works.txt", "jsonld": "https://wpnews.pro/news/conducting-agents-like-an-orchestra-how-agents-concerto-works.jsonld"}}