Repository navigation
feat(spawn): auto-create team-of-one for globally registered teamless agents - #1174
Conversation
When a detached spawn hits resolveTeamAndResume with no team resolution (no --team flag, no GENIE_TEAM, no parent session, no on-disk team already listing the agent), fall back to auto-creating a team named after the agent itself — *provided* the agent is registered in the global agent directory. The team config is materialized downstream by resolveNativeTeam → ensureNativeTeam; no new plumbing required. Motivation: TUI Enter on an offline, teamless, globally registered agent (e.g. `khal-os` after cleanup) now spawns it under a clean `khal-os` team instead of failing silently with "--team is required", which the detached spawn swallowed into a log file the user never saw. Unknown agents still error out — the directory gate prevents typos from silently materializing bogus teams. Coverage: - Added findTeamsContainingAgent tests (empty dir, no-match, single match by name, multiple matches) to guard the fallback scan that precedes the new branch.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces unit tests for the findTeamsContainingAgent function and implements logic to automatically create a 'team-of-one' for globally registered agents when no team is specified. A review comment suggests simplifying the team resolution logic in src/term-commands/agents.ts by removing a redundant directory lookup and ensuring compatibility with custom roles.
| if (!team) { | ||
| const directoryEntry = await directory.get(effectiveRole); | ||
| if (directoryEntry) { | ||
| team = nativeTeams.sanitizeTeamName(effectiveRole); | ||
| } | ||
| } |
There was a problem hiding this comment.
The directory lookup here is redundant because handleWorkerSpawn already resolves the agent via resolveAgentForSpawn before calling this function. Furthermore, using effectiveRole for the lookup means that auto-creation will fail if the user provides a custom role via --role (as the alias won't be in the directory), even if the underlying agent is registered. Since handleWorkerSpawn only proceeds if the agent is found in the directory or built-ins, we can safely assume the agent is registered and simplify this block.
if (!team) {
team = nativeTeams.sanitizeTeamName(effectiveRole);
}There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a03794d34e
ℹ️ 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".
| const directoryEntry = await directory.get(effectiveRole); | ||
| if (directoryEntry) { | ||
| team = nativeTeams.sanitizeTeamName(effectiveRole); |
There was a problem hiding this comment.
Restrict team-of-one fallback to non-builtin agents
This fallback uses directory.get(effectiveRole), which returns built-in agents as well as globally registered ones, so detached spawns with no team context now auto-assign teams for built-ins too. In practice, genie spawn <builtin> can silently create or reuse a same-named team even when no explicit team/session context exists, which is broader than the intended “global directory agent” gate and can route work into an unintended existing team with that name. Consider resolving with builtin metadata and only allowing this path for non-builtin entries.
Useful? React with 👍 / 👎.
Follow-up to #1174 (auto-team-of-one). The isolatedSessionSpawn heuristic in #1173 used `sessionOverride !== team` as a proxy for "TUI per-agent spawn". That proxy breaks the moment resolveTeamAndResume auto-creates a team-of-one named after the agent — now session equals team, isolatedSessionSpawn goes false, and resolveSpawnTeamWindow runs again, producing the same 3-window (bash + team-named + claude) cruft topology Felipe hit before. The fix: when the caller explicitly passes both --new-window and --session, always skip team-window creation. The agent runs in its own window in its own session — team windows exist to group multi-member teams that share a session, and the TUI spawn path doesn't want that coordination surface.
…mplification) PR #1175 (merge f43db8d) simplified the isolatedSessionSpawn guard in launchTmuxSpawn — removed the `sessionOverride !== team` redundancy that was a stale proxy for TUI per-agent spawn. Auto-team-of-one (PR #1174) now makes session and team equal for globally-registered teamless agents, and the explicit `--session` flag should be honored unconditionally. Accepted theirs verbatim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
genie spawncan't resolve a team (no--team, noGENIE_TEAM, no parent session, no on-disk team already listing the agent), fall back to auto-creating a team-of-one named after the agent — only if that agent is registered in the global agent directory.khal-osafter ghost-team cleanup) was failing silently with"--team is required".Trace that motivated this
From a read-only investigation:
Nav.tsx:707spawns detached with--session <name> --new-windowand stripsGENIE_TEAMfrom env.resolveTeamAndResume(agents.ts:1647-1652) used to exit 1 with--team is requiredwhendiscoverTeamName()andfindTeamsContainingAgent()both came back empty.~/.genie/logs/tui-spawn/<session>-<ts>.log(PR fix(spawn): fall back to on-disk team config when GENIE_TEAM unresolved #1170), but the TUI pane doesn't surface that to the user, so the failure looked like "TUI refuses to start the agent".With this change: if the agent exists in the directory, spawn creates a team-of-one named after it.
resolveNativeTeam→ensureNativeTeammaterializes the config downstream exactly like any other team, so no new plumbing is introduced.Changes
src/term-commands/agents.ts: +11 lines inresolveTeamAndResume— directory-gated auto-team-of-one branch between the on-disk scan and the final--team is requirederror.src/lib/claude-native-teams.test.ts: +37 lines — regression tests forfindTeamsContainingAgent(empty dir, no-match, single match, multiple ghost teams). Guards the scan the new branch sits on top of.Test plan
bun test src/lib/claude-native-teams.test.ts— 37 pass, 0 fail (4 new)bun run typecheck— cleanbun run lint— no new warnings (1 pre-existing complexity warning atagents.ts:1096untouched)bun run build— 4.6 MB bundle producedkhal-osstarts it under teamkhal-os(planned as G-C of wishteam-of-one-auto-create)