From 4737316d26428a8a7b137ee67a2d34c7909faa1c Mon Sep 17 00:00:00 2001 From: WeiCHEN Date: Mon, 7 Sep 2026 23:16:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(squad):=20=E4=BC=98=E5=8C=96=E8=B4=A8?= =?UTF-8?q?=E9=87=8F=E9=97=A8=E7=A6=81=E7=AB=A0=E8=8A=82=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E5=8C=96=E5=8C=B9=E9=85=8D=E4=B8=8E=E5=AE=A1=E6=A0=B8=E4=B8=93?= =?UTF-8?q?=E5=91=98=E9=A2=84=E7=BD=AE=E8=8D=89=E7=A8=BF=E6=B5=81=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- internal/squad/quality_gate.go | 43 ++++++++++++++++++++++++++++++++-- internal/squad/workflow.go | 11 +++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/internal/squad/quality_gate.go b/internal/squad/quality_gate.go index d3249fa7..7d75f6cc 100644 --- a/internal/squad/quality_gate.go +++ b/internal/squad/quality_gate.go @@ -54,8 +54,16 @@ func (q *QualityGate) Validate(wsDir string, taskKey string, content string) (*Q tmplData, err := os.ReadFile(tmplPath) if err == nil { expectedHeadings := extractHeadings(string(tmplData)) + actualHeadings := extractHeadings(content) for _, h := range expectedHeadings { - if !strings.Contains(content, h) { + found := false + for _, act := range actualHeadings { + if strings.Contains(act, h) || strings.Contains(h, act) { + found = true + break + } + } + if !found && !strings.Contains(content, h) { report.MissingSections = append(report.MissingSections, h) report.Violations = append(report.Violations, fmt.Sprintf("缺少必填章节: %s", h)) report.Score -= 15.0 @@ -269,12 +277,43 @@ func extractHeadings(md string) []string { for _, line := range lines { trimmed := strings.TrimSpace(line) if strings.HasPrefix(trimmed, "## ") { - headings = append(headings, trimmed) + raw := strings.TrimPrefix(trimmed, "## ") + clean := cleanHeadingTitle(raw) + if clean != "" { + headings = append(headings, clean) + } } } return headings } +func cleanHeadingTitle(title string) string { + // 去除前缀序号如 "一、", "二、", "1.", "1.1", "(一)" 等 + t := strings.TrimSpace(title) + runes := []rune(t) + // 去掉中文序号如 "一、", "二、", "三、", "四、", "五、", "六、", "七、", "八、", "九、", "十、" + chineseNums := []string{"一、", "二、", "三、", "四、", "五、", "六、", "七、", "八、", "九、", "十、"} + for _, cn := range chineseNums { + if strings.HasPrefix(t, cn) { + t = strings.TrimPrefix(t, cn) + break + } + } + // 去掉数字序号如 "1. ", "1、" + for len(runes) > 0 && ((runes[0] >= '0' && runes[0] <= '9') || runes[0] == '.' || runes[0] == '、' || runes[0] == ' ') { + t = strings.TrimSpace(string(runes[1:])) + runes = []rune(t) + } + // 去除可能括号后缀 + if idx := strings.Index(t, "("); idx > 0 { + t = strings.TrimSpace(t[:idx]) + } + if idx := strings.Index(t, "("); idx > 0 { + t = strings.TrimSpace(t[:idx]) + } + return strings.TrimSpace(t) +} + func computeSHA256(filePath string) (string, error) { f, err := os.Open(filePath) if err != nil { diff --git a/internal/squad/workflow.go b/internal/squad/workflow.go index bc876ac9..2c91e22b 100644 --- a/internal/squad/workflow.go +++ b/internal/squad/workflow.go @@ -348,6 +348,17 @@ func (d *WorkflowDriver) runOneStep( } stepStart := time.Now() + // 若当前步骤为 leader 审核定稿,且依赖前序专员稿件,先预置 baseline 草稿,供审查与润色 + if step.Role == "leader" && deliverableName == "output.md" { + targetFile := filepath.Join(subDir, deliverableName) + if _, err := os.Stat(targetFile); os.IsNotExist(err) && len(step.Needs) > 0 { + srcFile := filepath.Join(wsDir, filepath.FromSlash(step.Needs[0])) + if srcData, err := os.ReadFile(srcFile); err == nil && len(srcData) > 0 { + _ = os.WriteFile(targetFile, srcData, 0o644) + } + } + } + // 1. 读取 TASK.md 与 AGENTS.md taskData, _ := os.ReadFile(filepath.Join(subDir, "TASK.md")) agentsData, _ := os.ReadFile(filepath.Join(subDir, "AGENTS.md"))