Files
Agent-System/internal/workflowdef/validator_v2.go
T
weichenandCursor 37b1aaa3ae feat(rdev): add medical design-control workflow (13 stages / 60 docs)
Seed rdev.v2.json, rd_ templates, catalog policy (order 40), squad
kind orchestration, and hard precheck requiring completed main (CDO).
Includes StageKey dynamicization from P0 and docs for P2–P4 skeleton.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 09:46:26 +00:00

105 lines
3.7 KiB
Go

package workflowdef
import (
"fmt"
"chat-agent-system/internal/workflow"
)
var officialDocCounts = map[string]int{
OfficialKeyMain: 7,
OfficialKeyProductManager: 1,
OfficialKeyTestManager: 1,
OfficialKeyRDev: 60,
}
// ValidateDefinitionV2 schema v2 发布校验。
func ValidateDefinitionV2(def VersionDefinition) ValidationResult {
issues := make([]ValidationIssue, 0)
if def.SchemaVersion < 2 {
issues = append(issues, ValidationIssue{Code: "SCHEMA_VERSION", Message: "schema_version must be >= 2"})
}
if def.RequiredUpstreamWorkflow != "" {
issues = append(issues, ValidationIssue{
Code: "UPSTREAM_NOT_ALLOWED", Message: "正式工作流不得声明 required_upstream_workflow",
})
}
seenPos := map[int]bool{}
seenTask := map[string]bool{}
docCount := 0
for _, st := range def.Stages {
if st.Key == "" {
issues = append(issues, ValidationIssue{Code: "STAGE_KEY_REQUIRED", Message: "stage key required"})
continue
}
if st.Position <= 0 {
issues = append(issues, ValidationIssue{Code: "STAGE_POSITION", Field: st.Key, Message: "position must be > 0"})
}
if seenPos[st.Position] {
issues = append(issues, ValidationIssue{Code: "DUPLICATE_POSITION", Field: st.Key, Message: "duplicate stage position"})
}
seenPos[st.Position] = true
if len(st.DocTasks) == 0 {
issues = append(issues, ValidationIssue{Code: "STAGE_NO_DOC_TASKS", Field: st.Key, Message: "each stage must have at least one doc task"})
}
for _, dt := range st.DocTasks {
docCount++
if dt.Key == "" {
issues = append(issues, ValidationIssue{Code: "DOC_TASK_KEY_REQUIRED", Message: "doc task key required"})
continue
}
if seenTask[dt.Key] {
issues = append(issues, ValidationIssue{Code: "DUPLICATE_DOC_TASK", Field: dt.Key, Message: "duplicate doc task key"})
}
seenTask[dt.Key] = true
if dt.ExecutorKey != "" {
if err := workflow.ValidateExecutorKey(dt.ExecutorKey); err != nil {
issues = append(issues, ValidationIssue{Code: "UNKNOWN_EXECUTOR", Field: dt.Key, Message: err.Error()})
}
}
if dt.QualityGateKey != "" {
if err := workflow.ValidateQualityGateKey(dt.QualityGateKey); err != nil {
issues = append(issues, ValidationIssue{Code: "UNKNOWN_QUALITY_GATE", Field: dt.Key, Message: err.Error()})
}
}
if dt.OutputDocType == "" {
issues = append(issues, ValidationIssue{Code: "OUTPUT_DOC_TYPE", Field: dt.Key, Message: "output_doc_type required"})
}
}
}
if len(def.Stages) == 0 {
issues = append(issues, ValidationIssue{Code: "NO_STAGES", Message: "at least one stage required"})
}
// 固定文档数约束医疗正式工作流;软件工作流按自身 manifest 完整性校验。
if def.WorkflowKey != "" && IsMedicalWorkflowKey(def.WorkflowKey) {
if want, ok := officialDocCounts[def.WorkflowKey]; ok && docCount != want {
issues = append(issues, ValidationIssue{
Code: "OFFICIAL_DOC_COUNT", Field: def.WorkflowKey,
Message: fmt.Sprintf("official workflow %s requires exactly %d doc tasks, got %d", def.WorkflowKey, want, docCount),
})
}
}
return ValidationResult{Valid: len(issues) == 0, Issues: issues}
}
// ValidateVersionBySchema 按 schema_version 路由校验器。
func ValidateVersionBySchema(def VersionDefinition) ValidationResult {
if def.SchemaVersion >= 2 {
return ValidateDefinitionV2(def)
}
return validateDefinitionV1(def)
}
func validateDefinitionV1(def VersionDefinition) ValidationResult {
issues := make([]ValidationIssue, 0)
for i, st := range def.Stages {
if st.Position != i {
issues = append(issues, ValidationIssue{
Code: "STAGE_POSITION", Field: st.Key,
Message: fmt.Sprintf("position must equal index %d", i),
})
}
}
return ValidationResult{Valid: len(issues) == 0, Issues: issues}
}