fix(missions): agent teams do not form in print mode — say so where it is set
MEASURED, against the CLI in our own image (2.1.223): with
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 and an explicit request to "spawn two
teammates", `claude -p` did the work with two SUBAGENTS, wrote both files, and
created no ~/.claude/teams/ directory at all. The docs allow for it — "Claude may
sometimes use subagents instead of creating a team" — and headless appears to be
always: the whole feature is described around an interactive agent panel, which a
print-mode session does not have.
So Slice 3's switch, as written yesterday, set a flag with no mechanism behind it.
The first team mission caught it, because the probe was built to look for teammates
rather than to assume them.
Corrected rather than removed:
- `team_env` documents the measurement at the point the flag is set, so the next
reader does not have to rediscover it. The flag stays: harmless, and free if a
later version supports teams non-interactively.
- the prompt addendum now asks for parallel DELEGATION rather than naming
teammates, which is what print mode can actually deliver — and it keeps the two
anti-patterns worth stating (own different files; do not split one change into
stages).
- the "no teammates" warning was blaming the flag and the config path. It now
judges on the SUBAGENT count, which is the mechanism in play, and a zero
teammate count is documented as expected rather than as a fault.
What the switch buys today is real but smaller than the plan assumed: it changes
the prompt so the lead parallelises across files instead of working through them
alone. Whether that beats solo on our own missions is still unmeasured, and the
plan's prediction — that it will not be faster — stands untested.
482 tests pass, clippy clean.
UNEXPLAINED, filed as #54: that team run's `topology_runs` row is `failed` while
the log line that sits immediately before the UPDATE never printed — zero matches
for 'microvm phase' in the container's whole log. The prime suspect is
`topology_worker`'s stuck-run reaper, which fails runs that are `running` with no
step records and does not filter by tier; a microvm run has no step records by
design. If that is it, any sufficiently long VM phase is failed out from under
itself. The system failed safely here — the empty-delivery guard caught that
nothing was produced, and nothing false was reported — but the cause is not known
and it is not being written up as if it were.
This commit is contained in:
@@ -76,8 +76,21 @@ fn wants_claude_code_team(engine: Option<&str>) -> bool {
|
|||||||
|
|
||||||
/// Environment that turns agent teams on. Empty for a solo mission.
|
/// Environment that turns agent teams on. Empty for a solo mission.
|
||||||
///
|
///
|
||||||
/// Experimental and disabled by default in the CLI, so nothing changes for a
|
/// **MEASURED: agent teams do not form under `claude -p`.** With this flag set and
|
||||||
/// mission that did not ask. Kept separate from
|
/// an explicit request to "spawn two teammates", the CLI in our image (2.1.223)
|
||||||
|
/// did the work with two SUBAGENTS, wrote both files, and created no
|
||||||
|
/// `~/.claude/teams/` directory at all. The docs allow for it — "Claude may
|
||||||
|
/// sometimes use subagents instead of creating a team" — and headless appears to be
|
||||||
|
/// always. The whole agent-teams feature is described around an interactive agent
|
||||||
|
/// panel, which a print-mode session does not have.
|
||||||
|
///
|
||||||
|
/// The flag is still set, because it is harmless and costs nothing if a later
|
||||||
|
/// version does support teams non-interactively. What the switch actually buys
|
||||||
|
/// today is the prompt addendum, which does change behaviour: it gets the lead to
|
||||||
|
/// parallelise across files via subagents instead of working through them alone.
|
||||||
|
/// Read the outcome from the subagent count, not from a teammate count.
|
||||||
|
///
|
||||||
|
/// Kept separate from
|
||||||
/// [`crate::mission_runtime::microvm_provider_env`] so the credential path stays
|
/// [`crate::mission_runtime::microvm_provider_env`] so the credential path stays
|
||||||
/// exactly as narrow as it is — that function is subscription-only by
|
/// exactly as narrow as it is — that function is subscription-only by
|
||||||
/// construction, and widening it to carry feature flags is how an API key ends up
|
/// construction, and widening it to carry feature flags is how an API key ends up
|
||||||
@@ -133,8 +146,8 @@ fn vm_prompt(task: &str) -> String {
|
|||||||
fn team_prompt_addendum() -> String {
|
fn team_prompt_addendum() -> String {
|
||||||
format!(
|
format!(
|
||||||
"\nWORKING AS A TEAM\n\
|
"\nWORKING AS A TEAM\n\
|
||||||
You may spawn teammates — independent Claude Code sessions that share a \
|
Delegate the independent parts of this task and work them in parallel, \
|
||||||
task list with you and can message each other. Use at most \
|
rather than doing them one after another yourself. Use at most \
|
||||||
{MAX_TEAMMATES}, and fewer when fewer will do: teammates cost tokens \
|
{MAX_TEAMMATES}, and fewer when fewer will do: teammates cost tokens \
|
||||||
and coordination, and three focused ones beat five scattered ones.\n\
|
and coordination, and three focused ones beat five scattered ones.\n\
|
||||||
Split the work so each teammate owns DIFFERENT FILES. Two teammates \
|
Split the work so each teammate owns DIFFERENT FILES. Two teammates \
|
||||||
@@ -448,12 +461,17 @@ async fn run_inside(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if wants_claude_code_team(engine) && teammates.unwrap_or(0) == 0 {
|
// A team mission that fanned out to NOBODY is just a solo run that spent the
|
||||||
|
// same tokens, and nothing else in the output would say so. Judged on the
|
||||||
|
// subagent count, because that is the mechanism print mode actually uses —
|
||||||
|
// teammates never form here (see `team_env`), so a zero teammate count is
|
||||||
|
// expected and not itself a problem.
|
||||||
|
if wants_claude_code_team(engine) && subagents.unwrap_or(0) == 0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"microvm_executor: {} asked for an agent team and no teammates were found — \
|
"microvm_executor: {} asked for a team and delegated to nobody — it ran \
|
||||||
it ran SOLO. Agent teams are experimental; check that \
|
SOLO. Print mode satisfies a team request with subagents rather than \
|
||||||
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS reached the guest and that the \
|
teammates, so check that the Agent tool is in the allowlist and that \
|
||||||
team-config path is what this CLI version writes.",
|
the task is actually divisible.",
|
||||||
vm.vm_id()
|
vm.vm_id()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -686,6 +704,10 @@ mod tests {
|
|||||||
// into stages across teammates loses context at every handoff.
|
// into stages across teammates loses context at every handoff.
|
||||||
assert!(addendum.contains("DIFFERENT FILES"), "{addendum}");
|
assert!(addendum.contains("DIFFERENT FILES"), "{addendum}");
|
||||||
assert!(addendum.to_lowercase().contains("wait for your teammates"), "{addendum}");
|
assert!(addendum.to_lowercase().contains("wait for your teammates"), "{addendum}");
|
||||||
|
// Print mode delivers this as subagents, so the addendum must not depend on
|
||||||
|
// teammates existing — it asks for parallel delegation, whatever the
|
||||||
|
// mechanism turns out to be.
|
||||||
|
assert!(addendum.contains("Delegate"), "{addendum}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The teammate probe reads the team config, not the subagent transcripts —
|
/// The teammate probe reads the team config, not the subagent transcripts —
|
||||||
|
|||||||
Reference in New Issue
Block a user