fix: resolve 17 P2 defects and 5 P3 defects from pre-launch audit
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Batch fix covering multiple modules:
- P2-01: HandRegistry Semaphore-based max_concurrent enforcement
- P2-03: Populate toolCount/metricCount from Hand trait methods
- P2-06: heartbeat_update_config minimum interval validation
- P2-07: ReflectionResult used_fallback marker for rule-based fallback
- P2-08/09: identity_propose_change parameter naming consistency
- P2-10: ClassroomMetadata is_placeholder flag for LLM failure
- P2-11: classroomStore userDidCloseDuringGeneration intent tracking
- P2-12: workflowStore pipeline_create sends actionType
- P2-13/14: PipelineInfo step_count + PipelineStepInfo for proper step mapping
- P2-15: Pipe transform support in context.resolve (8 transforms)
- P2-16: Mustache {{...}} → \${...} auto-normalization
- P2-17: SaaSLogin password placeholder 6→8
- P2-19: serialize_skill_md + update_skill preserve tools field
- P2-22: ToolOutputGuard sensitive patterns from warn→block
- P2-23: Mutex::unwrap() → unwrap_or_else in relay/service.rs
- P3-01/03/07/08/09: Various P3 fixes
- DEFECT_LIST.md: comprehensive status sync (43/51 fixed, 8 remaining)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -40,10 +40,18 @@ pub struct UpdatePipelineRequest {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WorkflowStepInput {
|
||||
/// Action type discriminator (P2-12: enables non-Hand action types)
|
||||
pub action_type: Option<String>,
|
||||
pub hand_name: String,
|
||||
pub name: Option<String>,
|
||||
pub params: Option<HashMap<String, Value>>,
|
||||
pub condition: Option<String>,
|
||||
/// LLM generation template (for action_type = "llm_generate")
|
||||
pub template: Option<String>,
|
||||
/// Parallel collection path (for action_type = "parallel")
|
||||
pub each: Option<String>,
|
||||
/// Condition branches (for action_type = "condition")
|
||||
pub branches: Option<HashMap<String, Value>>,
|
||||
}
|
||||
|
||||
/// Create a new pipeline as a YAML file
|
||||
@@ -74,18 +82,57 @@ pub async fn pipeline_create(
|
||||
return Err(format!("Pipeline file already exists: {}", file_path.display()));
|
||||
}
|
||||
|
||||
// Build Pipeline struct
|
||||
// P2-12: Build PipelineSteps with proper action type from WorkflowStepInput
|
||||
let steps: Vec<PipelineStep> = request.steps.into_iter().enumerate().map(|(i, s)| {
|
||||
let step_id = s.name.clone().unwrap_or_else(|| format!("step-{}", i + 1));
|
||||
PipelineStep {
|
||||
id: step_id,
|
||||
action: Action::Hand {
|
||||
let params_map: HashMap<String, String> = s.params
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.to_string()))
|
||||
.collect();
|
||||
|
||||
let action = match s.action_type.as_deref().unwrap_or("hand") {
|
||||
"llm_generate" => Action::LlmGenerate {
|
||||
template: s.template.unwrap_or_default(),
|
||||
input: params_map,
|
||||
model: None,
|
||||
temperature: None,
|
||||
max_tokens: None,
|
||||
json_mode: false,
|
||||
},
|
||||
"parallel" => Action::Parallel {
|
||||
each: s.each.unwrap_or_else(|| "item".to_string()),
|
||||
step: Box::new(PipelineStep {
|
||||
id: format!("{}-body", step_id),
|
||||
action: Action::Hand {
|
||||
hand_id: s.hand_name.clone(),
|
||||
hand_action: "execute".to_string(),
|
||||
params: params_map,
|
||||
},
|
||||
description: None,
|
||||
when: None,
|
||||
retry: None,
|
||||
timeout_secs: None,
|
||||
}),
|
||||
max_workers: None,
|
||||
},
|
||||
"condition" => Action::Condition {
|
||||
condition: s.condition.unwrap_or_default(),
|
||||
branches: vec![],
|
||||
default: None,
|
||||
},
|
||||
_ => Action::Hand {
|
||||
hand_id: s.hand_name.clone(),
|
||||
hand_action: "execute".to_string(),
|
||||
params: s.params.unwrap_or_default().into_iter().map(|(k, v)| (k, v.to_string())).collect(),
|
||||
params: params_map,
|
||||
},
|
||||
};
|
||||
|
||||
PipelineStep {
|
||||
id: step_id,
|
||||
action,
|
||||
description: s.name,
|
||||
when: s.condition,
|
||||
when: None,
|
||||
retry: None,
|
||||
timeout_secs: None,
|
||||
}
|
||||
@@ -156,18 +203,58 @@ pub async fn pipeline_update(
|
||||
..existing.metadata.clone()
|
||||
};
|
||||
|
||||
// P2-12: Build PipelineSteps with proper action type (mirrors pipeline_create logic)
|
||||
let updated_steps = match request.steps {
|
||||
Some(steps) => steps.into_iter().enumerate().map(|(i, s)| {
|
||||
let step_id = s.name.clone().unwrap_or_else(|| format!("step-{}", i + 1));
|
||||
PipelineStep {
|
||||
id: step_id,
|
||||
action: Action::Hand {
|
||||
let params_map: HashMap<String, String> = s.params
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.to_string()))
|
||||
.collect();
|
||||
|
||||
let action = match s.action_type.as_deref().unwrap_or("hand") {
|
||||
"llm_generate" => Action::LlmGenerate {
|
||||
template: s.template.unwrap_or_default(),
|
||||
input: params_map,
|
||||
model: None,
|
||||
temperature: None,
|
||||
max_tokens: None,
|
||||
json_mode: false,
|
||||
},
|
||||
"parallel" => Action::Parallel {
|
||||
each: s.each.unwrap_or_else(|| "item".to_string()),
|
||||
step: Box::new(PipelineStep {
|
||||
id: format!("{}-body", step_id),
|
||||
action: Action::Hand {
|
||||
hand_id: s.hand_name.clone(),
|
||||
hand_action: "execute".to_string(),
|
||||
params: params_map,
|
||||
},
|
||||
description: None,
|
||||
when: None,
|
||||
retry: None,
|
||||
timeout_secs: None,
|
||||
}),
|
||||
max_workers: None,
|
||||
},
|
||||
"condition" => Action::Condition {
|
||||
condition: s.condition.unwrap_or_default(),
|
||||
branches: vec![],
|
||||
default: None,
|
||||
},
|
||||
_ => Action::Hand {
|
||||
hand_id: s.hand_name.clone(),
|
||||
hand_action: "execute".to_string(),
|
||||
params: s.params.unwrap_or_default().into_iter().map(|(k, v)| (k, v.to_string())).collect(),
|
||||
params: params_map,
|
||||
},
|
||||
};
|
||||
|
||||
PipelineStep {
|
||||
id: step_id,
|
||||
action,
|
||||
description: s.name,
|
||||
when: s.condition,
|
||||
when: None,
|
||||
retry: None,
|
||||
timeout_secs: None,
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use tauri::{AppHandle, Emitter, State};
|
||||
use zclaw_pipeline::{
|
||||
RunStatus,
|
||||
parse_pipeline_yaml,
|
||||
parse_pipeline_v2_yaml,
|
||||
PipelineExecutor,
|
||||
ActionRegistry,
|
||||
LlmActionDriver,
|
||||
@@ -16,7 +15,7 @@ use zclaw_pipeline::{
|
||||
|
||||
use super::{PipelineState, PipelineInfo, PipelineRunResponse, RunPipelineResponse, RunPipelineRequest};
|
||||
use super::adapters::{RuntimeLlmAdapter, PipelineSkillDriver, PipelineHandDriver};
|
||||
use super::helpers::{get_pipelines_directory, scan_pipelines_with_paths, scan_pipelines_full_sync, pipeline_to_info, pipeline_v2_to_info};
|
||||
use super::helpers::{get_pipelines_directory, scan_pipelines_with_paths, scan_pipelines_full_sync, pipeline_to_info};
|
||||
|
||||
use crate::kernel_commands::KernelState;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use zclaw_pipeline::{
|
||||
PipelineV2,
|
||||
};
|
||||
|
||||
use super::types::{PipelineInfo, PipelineInputInfo};
|
||||
use super::types::{PipelineInfo, PipelineInputInfo, PipelineStepInfo};
|
||||
|
||||
pub(crate) fn get_pipelines_directory() -> Result<PathBuf, String> {
|
||||
// Try to find pipelines directory
|
||||
@@ -169,6 +169,32 @@ pub(crate) fn pipeline_to_info(pipeline: &Pipeline) -> PipelineInfo {
|
||||
icon: pipeline.metadata.icon.clone().unwrap_or_else(|| "📦".to_string()),
|
||||
version: pipeline.metadata.version.clone(),
|
||||
author: pipeline.metadata.author.clone().unwrap_or_default(),
|
||||
// P2-13: Expose step count from actual pipeline spec
|
||||
step_count: pipeline.spec.steps.len(),
|
||||
// P2-14: Expose actual pipeline steps
|
||||
steps: pipeline.spec.steps.iter().map(|step| {
|
||||
use zclaw_pipeline::Action;
|
||||
let (action_type, hand_name) = match &step.action {
|
||||
Action::LlmGenerate { .. } => ("llm_generate".to_string(), None),
|
||||
Action::Parallel { .. } => ("parallel".to_string(), None),
|
||||
Action::Condition { .. } => ("condition".to_string(), None),
|
||||
Action::Hand { hand_id, .. } => ("hand".to_string(), Some(hand_id.clone())),
|
||||
Action::Skill { skill_id, .. } => ("skill".to_string(), Some(skill_id.clone())),
|
||||
Action::ClassroomRender { .. } => ("classroom_render".to_string(), None),
|
||||
Action::Sequential { .. } => ("sequential".to_string(), None),
|
||||
Action::FileExport { .. } => ("file_export".to_string(), None),
|
||||
Action::HttpRequest { .. } => ("http_request".to_string(), None),
|
||||
Action::SetVar { .. } => ("set_var".to_string(), None),
|
||||
Action::Delay { .. } => ("delay".to_string(), None),
|
||||
Action::SkillOrchestration { .. } => ("skill_orchestration".to_string(), None),
|
||||
};
|
||||
PipelineStepInfo {
|
||||
name: step.id.clone(),
|
||||
action_type,
|
||||
hand_name,
|
||||
condition: step.when.clone(),
|
||||
}
|
||||
}).collect(),
|
||||
inputs: pipeline.spec.inputs.iter().map(|input| {
|
||||
PipelineInputInfo {
|
||||
name: input.name.clone(),
|
||||
@@ -225,5 +251,8 @@ pub(crate) fn pipeline_v2_to_info(v2: &PipelineV2) -> PipelineInfo {
|
||||
options: param.options.clone(),
|
||||
}
|
||||
}).collect(),
|
||||
// V2 pipelines don't have steps in the same format
|
||||
step_count: 0,
|
||||
steps: vec![],
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,22 @@ pub struct PipelineInfo {
|
||||
pub author: String,
|
||||
/// Input parameters
|
||||
pub inputs: Vec<PipelineInputInfo>,
|
||||
/// P2-13: Step count (was missing, causing frontend to show 0)
|
||||
#[serde(default)]
|
||||
pub step_count: usize,
|
||||
/// P2-14: Actual pipeline steps (populated in pipeline_get detail view)
|
||||
#[serde(default)]
|
||||
pub steps: Vec<PipelineStepInfo>,
|
||||
}
|
||||
|
||||
/// P2-14: Pipeline step info for detail view
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PipelineStepInfo {
|
||||
pub name: String,
|
||||
pub action_type: String,
|
||||
pub hand_name: Option<String>,
|
||||
pub condition: Option<String>,
|
||||
}
|
||||
|
||||
/// Pipeline input parameter info
|
||||
|
||||
Reference in New Issue
Block a user