Files
zesdex/crates/zesdex-backend/src/app/workflow/engine/execution.rs
T
asepharyana 9a67137954 refactor: massive codebase restructuring — naming, splitting, DRY
Crate renames:
  - zesdex-entities::seaorm → domain (misleading name, no SeaORM used)
  - zesdex-dto → merged into zesdex-entities (100% re-exports)
  - zesdex-libs → zesdex-infra (vague name)

Module renames:
  - app/harness → guard (misleading: safety gatekeeper, not test harness)
  - runtime/commands → action_dispatch (name clashed with controller/command)
  - resources → prompts (embedded prompt text, not general resources)
  - tool/seqthink → sequential_think (unreadable abbreviation)
  - msglog/query → insert (module only inserts, never queries)

Dead code removal:
  - app/mode/help.rs (orphaned — not declared in mod.rs)
  - app/mode/loading.rs (orphaned — not declared in mod.rs)

File splitting (71 new files, avg ~115 lines/file):
  - app/runtime/actions/: 1→8 files (was 2030 lines)
  - view/overlays/: 1→16 files (was 1167 lines)
  - tool/lsp/: 1→8 per-tool files (was 909 lines)
  - main.rs: 1→5 files (session, daemon, attach, event_loop)
  - workflow/engine + hive_mind: 2→10 files
  - subagent/engine + auto: 2→9 files
  - lsp/provisioner: 1→5 files
  - review/: 1→6 files
  - guard/: 1→2 files (extracted patterns)
  - state/misc: 1→3 files (input, scroll)
  - mcp/: 1→3 files (transport, adapter)
  - stream/json_repair extracted from turn.rs

DRY:
  - Pattern constants (STUB_PATTERNS etc) in guard/patterns shared with subagent
  - 3 near-identical background spawners → 1 generic + thin wrappers
  - Shared spawn_subagent_with_drain() extracted
  - Shared create_session() in main
  - write_osc52 deduplicated

Bug fixes:
  - archive_message(): sess.db → db (wrong variable name)
  - execute_one_tool(): wrong parameter name
  - check_credential_read() function was missing (restored from test expectations)
2026-07-17 09:08:41 +07:00

89 lines
2.9 KiB
Rust

//! Top-level workflow execution functions.
//!
//! `run_workflow` and `run_workflow_tracked` are the public entry-points
//! for running a complete `WorkflowScript`. They create an isolated findings
//! scope and delegate to `execute_primitive`, then format the results into a
//! human-readable summary string.
use crate::app::workflow::script::WorkflowScript;
use std::collections::HashMap;
use std::sync::{
atomic::AtomicBool,
Arc, Mutex,
};
use super::primitives::{execute_primitive, PrimitiveCtx};
use super::LiveStateFn;
/// Run a `WorkflowScript` with the given template arguments and produce a
/// summary string. Uses no live-state callback.
///
/// Return: a human-readable summary string.
pub fn run_workflow(
script: &WorkflowScript,
args: &HashMap<String, String>,
session_dir: &std::path::Path,
workspaces: &[std::path::PathBuf],
) -> anyhow::Result<String> {
run_workflow_tracked(script, args, &None, None, session_dir, workspaces)
}
/// Run a `WorkflowScript` with real-time live-state callbacks so the TUI
/// panel updates as each agent transitions between Idle/Running/Done/Failed.
///
/// Flow: create an empty findings Arc (scoped to this invocation) → cap
/// concurrency to 8 → call `execute_primitive` with the live callback and
/// findings → format results.
///
/// Why: findings are scoped to an `Arc<Mutex<Vec<String>>>` rather than a
/// global static, so concurrent `run_workflow_tracked` calls from different
/// `spawn_agents` invocations remain fully isolated.
///
/// Return: a human-readable summary string.
pub fn run_workflow_tracked(
script: &WorkflowScript,
args: &HashMap<String, String>,
abort_flag: &Option<Arc<AtomicBool>>,
live: Option<&LiveStateFn>,
session_dir: &std::path::Path,
workspaces: &[std::path::PathBuf],
) -> anyhow::Result<String> {
let concurrency_cap = if script.options.max_concurrency > 0 {
script.options.max_concurrency.min(10) // allow up to 10 parallel agents
} else {
10
};
let findings = Arc::new(Mutex::new(Vec::new()));
let results = execute_primitive(PrimitiveCtx {
primitive: &script.script,
args,
concurrency_cap,
continue_on_error: script.options.continue_on_error,
abort_flag,
live,
session_dir,
workspaces,
findings: &findings,
timeout_ms: script.options.timeout_ms,
})?;
let summary = if results.is_empty() {
"workflow completed with no output".to_string()
} else {
format!(
"workflow '{}' completed. {} agent result(s):\n{}",
script.name,
results.len(),
results
.iter()
.enumerate()
.map(|(i, r)| format!("[{}] {}", i + 1, r.lines().next().unwrap_or(r)))
.collect::<Vec<_>>()
.join("\n")
)
};
Ok(summary)
}