From a01dd36fb0c439da77d0cf173d94d31cf805ecb8 Mon Sep 17 00:00:00 2001 From: Yoilun Date: Mon, 25 May 2026 21:13:56 +0800 Subject: [PATCH] fix: localize toml parse errors --- internal/agents/store.go | 13 ++++++------- internal/agents/store_test.go | 1 + internal/agents/writeback_test.go | 12 ++++++++++++ progress.md | 9 +++++++++ task_plan.md | 1 + 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/internal/agents/store.go b/internal/agents/store.go index 860224e..560529d 100644 --- a/internal/agents/store.go +++ b/internal/agents/store.go @@ -2,7 +2,6 @@ package agents import ( "bufio" - "errors" "fmt" "os" "path/filepath" @@ -136,7 +135,7 @@ func parseSimpleTOML(input string) (map[string]string, error) { return values, fmt.Errorf("第 %d 行重复字段名 %q", lineNumber, key) } - value, err := parseTOMLString(raw, scanner) + value, err := parseTOMLString(raw, scanner, lineNumber) if err != nil { return values, err } @@ -167,27 +166,27 @@ func isValidBareKey(key string) bool { return true } -func parseTOMLString(raw string, scanner *bufio.Scanner) (string, error) { +func parseTOMLString(raw string, scanner *bufio.Scanner, lineNumber int) (string, error) { if strings.HasPrefix(raw, `"""`) { block := strings.TrimPrefix(raw, `"""`) for !strings.Contains(block, `"""`) && scanner.Scan() { block += "\n" + scanner.Text() } if !strings.Contains(block, `"""`) { - return "", errors.New("未闭合的多行字符串") + return "", fmt.Errorf("第 %d 行未闭合的多行字符串", lineNumber) } value, trailing, _ := strings.Cut(block, `"""`) if strings.TrimSpace(trailing) != "" { - return "", errors.New("多行字符串后存在不支持的内容") + return "", fmt.Errorf("第 %d 行多行字符串后存在不支持的内容", lineNumber) } return value, nil } if !strings.HasPrefix(raw, `"`) { - return "", errors.New("仅支持字符串字段") + return "", fmt.Errorf("第 %d 行仅支持字符串字段", lineNumber) } value, err := strconv.Unquote(raw) if err != nil { - return "", err + return "", fmt.Errorf("第 %d 行字符串字段语法无效", lineNumber) } return value, nil } diff --git a/internal/agents/store_test.go b/internal/agents/store_test.go index 7258b75..97cb638 100644 --- a/internal/agents/store_test.go +++ b/internal/agents/store_test.go @@ -70,6 +70,7 @@ func TestListAgentsReportsParseError(t *testing.T) { if got[0].ParseStatus != "invalid" || got[0].ParseError == "" { t.Fatalf("expected invalid parse status, got %#v", got[0]) } + assertChineseTOMLError(t, got[0].ParseError) } func TestListAgentsReportsDuplicateKeyAsParseError(t *testing.T) { diff --git a/internal/agents/writeback_test.go b/internal/agents/writeback_test.go index 88a3424..4f703ca 100644 --- a/internal/agents/writeback_test.go +++ b/internal/agents/writeback_test.go @@ -21,6 +21,7 @@ func TestValidateInvalidTOMLReturnsInvalidAndDoesNotWrite(t *testing.T) { if len(got.Errors) == 0 { t.Fatalf("expected validation errors: %#v", got) } + assertChineseTOMLError(t, got.Errors[0]) assertFileContent(t, target, `name = "旧名称"`+"\n") } @@ -56,6 +57,7 @@ func TestWriteInvalidTOMLRejectsAndLeavesOriginal(t *testing.T) { if err == nil { t.Fatal("expected invalid TOML write to be rejected") } + assertChineseTOMLError(t, err.Error()) assertFileContent(t, target, `name = "旧名称"`+"\n") } @@ -159,3 +161,13 @@ func assertFileContent(t *testing.T, path string, want string) { t.Fatalf("%s content = %q, want %q", path, string(got), want) } } + +func assertChineseTOMLError(t *testing.T, message string) { + t.Helper() + if strings.Contains(message, "invalid syntax") { + t.Fatalf("TOML error leaked English parser text: %q", message) + } + if !strings.Contains(message, "字符串字段语法无效") { + t.Fatalf("TOML error = %q, want Chinese string syntax error", message) + } +} diff --git a/progress.md b/progress.md index e363fdc..3497a35 100644 --- a/progress.md +++ b/progress.md @@ -28,6 +28,7 @@ | 2026-05-25 | 5 | spec review | 规格审查未通过:valid agent 状态不明确,工作流和 agent 只读文案仍含内部英文 | coding agent 按 blocking 范围修复 | | 2026-05-25 | 5 | quality review | 代码质量审查未通过:未知后端枚举值会直接进入 UI label | coding agent 按 blocking 范围修复 | | 2026-05-25 | 6 | coding agent | TDD 实现智能体草稿校验、diff、hash 冲突检测、备份和原子写回 | 完成;待最终全量验证 | +| 2026-05-25 | 6 | spec review | 规格审查未通过:TOML 字符串解析错误泄漏英文 `invalid syntax` | coding agent 按 blocking 范围修复 | ## Test Results @@ -153,6 +154,13 @@ | 2026-05-25 | `cd web && pnpm test` | PASS | Phase 6 前端单测验证通过;共 13 个单测 | | 2026-05-25 | `cd web && pnpm build` | PASS | Phase 6 前端生产构建通过 | | 2026-05-25 | `git diff --check` | PASS | Phase 6 whitespace 检查通过 | +| 2026-05-25 | `go test ./internal/agents` | FAIL | TDD 红灯:List/Validate/Write malformed string 均泄漏英文 `invalid syntax` | +| 2026-05-25 | `go test ./internal/agents ./internal/server` | PASS | Phase 6 TOML 错误中文化目标包测试通过 | +| 2026-05-25 | `go test ./internal/agents ./internal/server` | PASS | Phase 6 规格修复后指定后端目标包验证通过 | +| 2026-05-25 | `go test ./...` | PASS | Phase 6 规格修复后全量 Go 验证通过 | +| 2026-05-25 | `cd web && pnpm test` | PASS | Phase 6 规格修复后前端单测验证通过;共 13 个单测 | +| 2026-05-25 | `cd web && pnpm build` | PASS | Phase 6 规格修复后前端生产构建通过 | +| 2026-05-25 | `git diff --check` | PASS | Phase 6 规格修复 whitespace 检查通过 | ## Bug Loop @@ -181,3 +189,4 @@ | 5 | 未知 workflow phase status 被白名单过滤,真实阶段从 UI 消失 | phase 过滤改为只排除表头/伪行,未知 status 交给中文状态兜底显示“未知” | `pnpm test` PASS | | 6 | 写回可能覆盖校验后用户修改的文件 | validate 返回当前 sha256;write 重新读取并比较 expectedHash,不匹配返回冲突且不写回 | `go test ./internal/agents ./internal/server` PASS | | 6 | 无效 TOML 或 unsafe id/symlink 可能进入写回路径 | write 重新执行 TOML 校验,id 只允许安全 bare stem,拒绝 leaf symlink 和 symlinked agents 目录 | `go test ./internal/agents ./internal/server` PASS | +| 6 | TOML 未闭合字符串错误会把 `strconv.Unquote` 的英文 `invalid syntax` 返回给 UI/API | 在 parser 层将字符串字段语法错误包装为中文并带行号;List/Validate/Write 增加中文错误断言 | `go test ./internal/agents ./internal/server` PASS | diff --git a/task_plan.md b/task_plan.md index dfb98c8..79f5d23 100644 --- a/task_plan.md +++ b/task_plan.md @@ -39,3 +39,4 @@ | 2026-05-25 | 5 | 规格审查发现 valid agent 未明确显示 TOML 状态,且 UI 仍有 `handoffEdges`、`主 agent`、`developer_instructions` 可见文案 | TDD 补 valid agent 状态和 workflow 空交接文案测试后修复 normalizer 与 WorkflowView 文案 | 待复审 | | 2026-05-25 | 5 | 代码质量审查发现未知后端枚举值会直接暴露到 UI | TDD 补 unknown source/confidence/status/trust 测试后将未知 label 统一降级为中文兜底 | 待复审 | | 2026-05-25 | 5 | 质量复审发现未知 workflow phase status 会导致真实阶段被过滤消失 | TDD 补 `in_progress` phase 测试后只过滤表头/伪行,未知状态显示“未知” | 待复审 | +| 2026-05-25 | 6 | 规格审查发现 malformed TOML 会通过 `strconv.Unquote` 泄漏英文 `invalid syntax` | TDD 补 List/Validate/Write 中文错误断言后包装字符串解析错误 | 待最终验证 |