Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit 6fcc4d9

Browse files
z23ccclaude
andcommitted
feat(review): parallel execution warning on flowctl start
When starting a task while other tasks in the same epic are already in_progress, flowctl now emits: - stderr warning about worktree isolation requirement - JSON field "parallel_warning" with concurrent task count This catches the #1 Critical audit finding (3 consecutive rounds): workers running in the same directory without worktree isolation. The warning is informational (not blocking) because flowctl cannot control the Agent tool's isolation parameter. But the message appears in the agent's tool output, making it harder to ignore. Use --force to suppress the warning. 370 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 06f9ab4 commit 6fcc4d9

2 files changed

Lines changed: 44 additions & 3 deletions

File tree

bin/flowctl

0 Bytes
Binary file not shown.

flowctl/crates/flowctl-cli/src/commands/workflow/lifecycle.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,31 @@ pub fn cmd_start(json_mode: bool, id: String, force: bool, _note: Option<String>
1717
let flow_dir = ensure_flow_exists();
1818
let actor = resolve_actor();
1919

20+
// ── Parallel execution safety check ────────────────────────
21+
// If other tasks in the same epic are already in_progress, warn about
22+
// worktree isolation requirement. This catches the #1 audit failure:
23+
// workers running in the same directory without isolation.
24+
let epic_id = flowctl_core::id::epic_id_from_task(&id).unwrap_or_default();
25+
if !epic_id.is_empty() {
26+
if let Ok(tasks) = flowctl_core::json_store::task_list_by_epic(&flow_dir, &epic_id) {
27+
let in_progress: Vec<String> = tasks
28+
.iter()
29+
.filter(|t| t.status == flowctl_core::state_machine::Status::InProgress && t.id != id)
30+
.map(|t| t.id.clone())
31+
.collect();
32+
if !in_progress.is_empty() && !force {
33+
eprintln!(
34+
"⚠️ PARALLEL EXECUTION WARNING: {} other task(s) already in_progress: {}\n\
35+
Workers MUST use isolation: \"worktree\" to prevent race conditions.\n\
36+
If workers share the same directory, file edits will conflict.\n\
37+
Use --force to suppress this warning.",
38+
in_progress.len(),
39+
in_progress.join(", ")
40+
);
41+
}
42+
}
43+
}
44+
2045
let req = StartTaskRequest {
2146
task_id: id.clone(),
2247
force,
@@ -26,11 +51,27 @@ pub fn cmd_start(json_mode: bool, id: String, force: bool, _note: Option<String>
2651
match flowctl_core::lifecycle::start_task(&flow_dir, req) {
2752
Ok(resp) => {
2853
if json_mode {
29-
json_output(json!({
54+
let mut out = json!({
3055
"id": resp.task_id,
3156
"status": "in_progress",
32-
"message": format!("Task {} started", resp.task_id),
33-
}));
57+
});
58+
// Include parallel warning in JSON so agent sees it
59+
let epic_id = flowctl_core::id::epic_id_from_task(&resp.task_id).unwrap_or_default();
60+
if !epic_id.is_empty() {
61+
if let Ok(tasks) = flowctl_core::json_store::task_list_by_epic(&flow_dir, &epic_id) {
62+
let in_progress_count = tasks
63+
.iter()
64+
.filter(|t| t.status == flowctl_core::state_machine::Status::InProgress)
65+
.count();
66+
if in_progress_count > 1 {
67+
out["parallel_warning"] = json!(format!(
68+
"{} tasks now in_progress — use isolation:worktree for each worker",
69+
in_progress_count
70+
));
71+
}
72+
}
73+
}
74+
json_output(out);
3475
} else {
3576
println!("Task {} started", resp.task_id);
3677
}

0 commit comments

Comments
 (0)