launch: accept config.phase_teams as a valid team source
Missions created via the new multi-team wizard have neither team_id
nor team_template_id set — they carry config.phase_teams. Both the
frontend Launch button gate and the backend set_status precondition
were checking only the old two fields, disabling launch for every
new wizard-created mission with a "No team" tooltip.
- MissionCanvas: hasTeam now also returns true when
mission.config.phase_teams has at least one non-empty list.
- routes::missions::set_status: same check on the server so a
direct API caller with only config.phase_teams also gets past
the gate.
Directly unblocks the "we just finished the wizard, Launch is greyed
out" report. Agents materialize AFTER Launch — the button is the
trigger, not a post-condition of creation.
This commit is contained in:
@@ -483,8 +483,26 @@ pub async fn set_status(
|
|||||||
// BEFORE flipping status so a materialization failure keeps the
|
// BEFORE flipping status so a materialization failure keeps the
|
||||||
// mission in draft (no orphaned "running" mission with no agents).
|
// mission in draft (no orphaned "running" mission with no agents).
|
||||||
if prior.status == "draft" && body.status == "running" {
|
if prior.status == "draft" && body.status == "running" {
|
||||||
if prior.team_id.is_none() && prior.team_template_id.is_none() {
|
// Materializable when we have any of:
|
||||||
eprintln!("mission {id}: launch rejected — no team_id and no team_template_id");
|
// - team_id (already exists)
|
||||||
|
// - team_template_id (legacy single-team path)
|
||||||
|
// - config.phase_teams with at least one non-empty list (new multi-team)
|
||||||
|
let has_phase_teams = prior
|
||||||
|
.config
|
||||||
|
.get("phase_teams")
|
||||||
|
.and_then(|v| v.as_object())
|
||||||
|
.map(|obj| {
|
||||||
|
obj.values()
|
||||||
|
.any(|v| v.as_array().map(|a| !a.is_empty()).unwrap_or(false))
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
if prior.team_id.is_none()
|
||||||
|
&& prior.team_template_id.is_none()
|
||||||
|
&& !has_phase_teams
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"mission {id}: launch rejected — no team_id, no team_template_id, no config.phase_teams"
|
||||||
|
);
|
||||||
return Err(ApiError::BadRequest);
|
return Err(ApiError::BadRequest);
|
||||||
}
|
}
|
||||||
if let Err(e) = crate::mission_orchestrator::on_launch(
|
if let Err(e) = crate::mission_orchestrator::on_launch(
|
||||||
|
|||||||
@@ -411,8 +411,21 @@ export function MissionCanvas({
|
|||||||
<RefreshCw size={13} />
|
<RefreshCw size={13} />
|
||||||
</button>
|
</button>
|
||||||
{mission.status === "draft" && (() => {
|
{mission.status === "draft" && (() => {
|
||||||
|
// Launch is enabled when we have SOMETHING that can
|
||||||
|
// materialize agents:
|
||||||
|
// - team_id set (already materialized)
|
||||||
|
// - team_template_id set (legacy single-team path)
|
||||||
|
// - config.phase_teams has entries (new multi-team wizard path)
|
||||||
|
const phaseTeams =
|
||||||
|
(mission.config as { phase_teams?: Record<string, string[]> })
|
||||||
|
?.phase_teams;
|
||||||
|
const hasPhaseTeams =
|
||||||
|
phaseTeams &&
|
||||||
|
Object.values(phaseTeams).some((arr) => arr.length > 0);
|
||||||
const hasTeam =
|
const hasTeam =
|
||||||
mission.team_id !== null || mission.team_template_id !== null;
|
mission.team_id !== null ||
|
||||||
|
mission.team_template_id !== null ||
|
||||||
|
Boolean(hasPhaseTeams);
|
||||||
const disabled = launching || !hasTeam;
|
const disabled = launching || !hasTeam;
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user