merge: candidate fix-quality-heading-eleven into local_dev
This commit is contained in:
@@ -702,23 +702,56 @@ func extractHeadings(md string) []string {
|
||||
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
|
||||
}
|
||||
func isChineseNumeralRune(r rune) bool {
|
||||
switch r {
|
||||
case '零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '百', '千', '两':
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
// 去掉数字序号如 "1. ", "1、"
|
||||
}
|
||||
|
||||
// stripChineseOrdinalPrefix 去掉「一、」「十一、」「二十三、」等中文序号前缀。
|
||||
// 旧实现只枚举到「十、」,长文档 LLM 常用「十一、签署」会导致与模板「签署」对不上。
|
||||
func stripChineseOrdinalPrefix(t string) string {
|
||||
runes := []rune(t)
|
||||
if len(runes) == 0 || !isChineseNumeralRune(runes[0]) {
|
||||
return t
|
||||
}
|
||||
i := 0
|
||||
for i < len(runes) && isChineseNumeralRune(runes[i]) {
|
||||
i++
|
||||
}
|
||||
if i >= len(runes) {
|
||||
return t
|
||||
}
|
||||
switch runes[i] {
|
||||
case '、', '.', '.':
|
||||
i++
|
||||
default:
|
||||
return t
|
||||
}
|
||||
for i < len(runes) && (runes[i] == ' ' || runes[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
return string(runes[i:])
|
||||
}
|
||||
|
||||
func stripArabicOrdinalPrefix(t string) string {
|
||||
runes := []rune(t)
|
||||
// 去掉数字序号如 "1. ", "1、", "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)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func cleanHeadingTitle(title string) string {
|
||||
// 去除前缀序号如 "一、", "十一、", "1.", "1.1" 等
|
||||
t := strings.TrimSpace(title)
|
||||
t = stripChineseOrdinalPrefix(t)
|
||||
t = stripArabicOrdinalPrefix(t)
|
||||
// 去除可能括号后缀
|
||||
if idx := strings.Index(t, "("); idx > 0 {
|
||||
t = strings.TrimSpace(t[:idx])
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package squad
|
||||
|
||||
// CleanHeadingTitleForTest 暴露 cleanHeadingTitle,供同模块白盒用例验证序号清洗。
|
||||
var CleanHeadingTitleForTest = cleanHeadingTitle
|
||||
@@ -480,3 +480,55 @@ func TestQualityGate_ActionPlanStatus_DoesNotTriggerRepair(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 现场 nightly TestManager:LLM 写「## 十一、签署 / ## 十二、说明」,旧清洗只剥到「十、」会误报缺章。
|
||||
func TestCleanHeadingTitle_MultiDigitChineseOrdinal(t *testing.T) {
|
||||
cases := []struct {
|
||||
in, want string
|
||||
}{
|
||||
{"一、客户与项目画像", "客户与项目画像"},
|
||||
{"十、附录", "附录"},
|
||||
{"十一、签署", "签署"},
|
||||
{"十二、说明", "说明"},
|
||||
{"二十三、风险评估", "风险评估"},
|
||||
{"1. 结论", "结论"},
|
||||
{"签署", "签署"},
|
||||
{"十一、签署(终稿)", "签署"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := squad.CleanHeadingTitleForTest(c.in)
|
||||
if got != c.want {
|
||||
t.Errorf("cleanHeadingTitle(%q) = %q, want %q", c.in, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 现场 nightly TestManager:LLM 写「## 十一、签署 / ## 十二、说明」,旧清洗只剥到「十、」会误报缺章。
|
||||
func TestQualityGate_Validate_ChineseOrdinalElevenTwelve(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
engine := squad.NewWorkspaceEngine()
|
||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||
WorkDir: filepath.Join(tmpDir, "ws_tm"),
|
||||
TaskKey: "test_manager",
|
||||
StageTitle: "系统验证方案及计划",
|
||||
ProjectName: "医疗监护仪",
|
||||
TemplateContent: "# 系统验证方案及计划\n\n" +
|
||||
"## 结论\n\n## 用例与验证规程\n\n## 签署\n\n## 说明\n",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("PrepareWorkspace: %v", err)
|
||||
}
|
||||
gate := squad.NewQualityGate()
|
||||
body := "# 系统验证方案及计划\n\n" +
|
||||
"## 一、结论\n本方案覆盖核心验证路径,结论可交付。\n\n" +
|
||||
"## 二、用例与验证规程\n| TC-01 | 验证心率测量精度 | 追溯 REQ-01 | 判定准则误差 <= ±2bpm | 通过 |\n\n" +
|
||||
"## 十一、签署\n产品负责人签字确认。\n\n" +
|
||||
"## 十二、说明\n本说明补充验证边界与假设。\n"
|
||||
rep, err := gate.Validate(ws.WorkDir, "test_manager", body)
|
||||
if err != nil {
|
||||
t.Fatalf("Validate: %v", err)
|
||||
}
|
||||
if !rep.Passed {
|
||||
t.Fatalf("expected eleven/twelve ordinal headings to match template, violations=%v", rep.Violations)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user