Repository navigation
Description
What happened
A group needed two same-role Bots under one lead: Reviewer and Reviewer2, both peer, both reporting to Reviewer Lead. Nothing available to an agent can arrange that:
create_team("FlyBot Reviewer", members: ["Reviewer: …", "Reviewer2: …"])does not adopt the existing Bots. It answersA Bot named Reviewer already existsfor each member and creates nothing, so a team can only be built from Bots that do not exist yet.create_child_bot(parent: "Chief")fails withlimit reached: Chief already has 8 children (maxChildren=8), and the same limit blocks the parent that holdsReviewer.- There is no operation that moves an existing Bot. So
Reviewer2, created later, landed as a child ofReviewer(Chief → Backend → Reviewer → Reviewer2) instead of beside it, and the newReviewer Leadended up somewhere else entirely (Chief → Backend → FlyBot 实现 Lead → Reviewer Lead).
The result: the lead reports list_children as empty and message_bot refuses with a bot may message only its parent and its direct children, so it cannot dispatch to the two Bots it is supposed to lead. Only the owner, dragging in the tree view, can produce the intended shape.
Why it matters
The tree is how this team is wired: who may message whom, and whose results roll up to whom. When the intended shape can only be produced by hand, every regrouping costs the owner a manual step, and an agent that reads the tree sees a hierarchy that does not match the one the team is actually running.
What to build
- A move operation for an existing Bot:
reparent(bot, newParent), subject to the samemaxDepth/maxChildrenlimits and refusing a move that would make a Bot its own ancestor. create_teamadopts existing Bots whose name or role matches a requested member instead of failing on them, the same way it already reuses an existing lead.- When the requested shape cannot be produced (a full level, a cycle, a depth overflow), the answer names the specific obstruction and the Bot it concerns, not just the limit.
Acceptance
- A test builds
Reviewer, then asks for a team ofReviewerand a second same-role Bot under a new lead; the existing Bot ends up as the lead's child, andlist_childrenon the lead returns both. - Moving a Bot under its own descendant is refused with a message naming both Bots and the tree is unchanged.
- A move that would exceed
maxChildrenon the destination is refused, and the tree is unchanged.
Out of scope
The owner's drag in the tree view (that keeps working), automatic reorganization by role, and any change to how group chats route messages.
Activity
Birfy commentedon Oct 22, 2026
Triaged
enhancement+area:automation(Triage), notagent-readyyet — one of the three things to build already exists, and building a secondreparentwould duplicate it.Bullet 3 is already shipped, just not released
move_botexists on main:plugins/tree/src/tools.js:139-147(root only), added by PR #636 in8e80237bon 2026-10-22T04:58Z. It does exactly what the bullet asks — moves a Bot with its subtree and refuses a cycle, amaxChildrenoverflow, amaxDepthoverflow and a duplicate sibling name — with five tests atplugins/tree/test/tree.test.js:1579-1694(moves with the subtree, refuses a descendant / its own parent / an existing parent / duplicate names, refuses a subtree that would end belowmaxDepth, refuses an overflowing parent, visible to the root only).It is not in v0.2.3, the latest release (2026-10-21T18:34Z), which is what the host you were talking to runs. So the tool was missing for a release reason, not a code reason: after the next Release run
move_botshould appear to the root, and if it still does not, that is a tool-visibility bug and a separate issue — not this one.Two consequences for the write-up:
## What to build; keep it as "verifymove_boton the next release". A newreparentbesidemove_botwould be two tools for one edge, which is the same duplication problem this issue is about.maxChildren=8does not go away with a move. It is enforced on the destination too (limits.js, and the move test above refuses an overflowing parent). Whatever shape you want needs a level with room —FlyBot Reviewer Leadmust hang where it can hold two children. If the acceptance says "regroup the team", it has to name that place, or it is asking the mover to break a limit that exists on purpose.The part that is genuinely broken
Bullet 2, and it is worse than "does not adopt":
create_team's own description promises "An existing lead of that group and existing Bots with the same roles in your branch are reused" (tools.js:129), butbuildTeamdecides reuse by the role profile (allocate.js:195-200,sameRoleIn), not by the name. When the requested member's role text does not profile-match the existing Bot, nothing is reused, the lead is created first (:215), and only then does each member fail — on the name — withA Bot named Reviewer already exists(:230-233). Net effect in your repro: half a team, a lead with no children, and an error that names the symptom instead of the Bot.So the real acceptance is:
maxChildren/maxDepth) is refused before the lead is created, naming the Bot, where it sits, and which limit blocks it — no half-built team.createLeadand the "no half-built team" case goes red (aReviewer Leadwith zero children is already created today).Decision needed from the owner before
agent-ready: adopt-by-name is a change to who a team can absorb, and it can move an existing Bot without the root callingmove_bot— that is a policy call, not a bug fix. Say adopt, or say "fail loudly and leave the shape alone"; either way the acceptance above is what I will pin.Birfy commentedon Oct 22, 2026
Written-by: Triage
This has been sitting without a state label, so the board reads it as unclaimed work when it is actually waiting on one sentence. Setting
needs-humanso it stops looking open for a Bot.The question, unchanged since the triage comment:
create_team's tool description promises "existing Bots with the same roles are reused", butbuildTeammatches on the role text (allocate.js:195-200), not the name, then creates the Lead (:215) before the members collide (:230-233) — so the visible result is an orphanReviewer LeadplusA Bot named Reviewer already exists. Two ways out, and they are different products:move_botcan reparent them afterwards). More useful, and it also lets a team rearrange an existing tree.Say "adopt" or "fail loudly" and I write the acceptance and flip it to
agent-ready. Note bullet 3 (moving a Bot) is already shipped asmove_botin8e80237band will be in the next release, so only thecreate_teamhalf is left.Birfy commentedon Oct 22, 2026
Written-by: Triage
agent-ready, scoped to the half that needs no product decision. I am taking the second option — refuse before creating anything — because the first one (adopt an existing Bot by name) hands a team the power to absorb Bots and to reorganise a tree that already exists, and that is the owner's call, not a triage call. If the owner later says "adopt", it is a new issue on top of this fix, not a rewrite.The defect, at line level on
mainbuildTeam(plugins/tree/src/allocate.js:191) reuses only by role text (sameRoleIn(nodes, callerId, d.name, d.role, taken)at:198, againsttakenat:196), so a same-named Bot with different role text is not reused. It then creates the Lead first (leadId = await createLead(...)at:215) and only afterwards tries the members (createUnderinside the loop at:227-233, whose failure is swallowed intod.errorat:233). Result: an orphanReviewer Leadwith no children, plusA Bot named Reviewer already exists— two writes and one half-built team.Acceptance — what the PR is judged against
createLeadis called, using the same uniqueness rule thatcreateUnderenforces today (not a second rule invented inbuildTeam).:204-226), and the journal is committed only when something changed (:236).<label> Leadexists); checking only the first collision leaves the "names every collision" case red; checking aftercreateLeadleaves the "zero new Bots" case red.plugins/treestays green otherwise (currently 110/110).Not in scope, on purpose: adopting an existing same-named Bot. That changes who may reshape a tree; the owner decides it separately if they want it.
fix(tree): refuse a taken name before a team creates anything (Fixes #…
Birfy commentedon Oct 23, 2026
Claimed: branch
fix/764-team-name-clash, PR #800 (tip16c2945f, basefd205c40).Scope is the one Triage fixed: refuse the team before the lead is created, so nothing is written — no lead, no member, no move, no journal run, and the tree is byte-identical.
buildTeam's reuse-by-role check stays as it is; every name the team would create is checked against the whole roster (roster.js:65is the host rule that used to fire only after the lead existed), and the error names every clashing name with the path of the Bot that holds it.Not in this PR: adopting an existing Bot by name (a product decision, not this bug), and the narrow cross-parent race where two teams under different parents create the same name concurrently — the host still refuses, so an orphan lead is possible there; noted as residual risk in the PR body.
fix(tree): refuse a colliding team name before anything is created (#800
Birfy commentedon Oct 23, 2026
Fixed by #800, merged into main as 245881c. Closing.