From 11e40050e3fd83a7a2df709b62708b8e1dfb8646 Mon Sep 17 00:00:00 2001 From: PratikDhanave Date: Tue, 22 Sep 2026 08:48:10 +0530 Subject: [PATCH] Truncate example output on rune boundaries in verifyexamples truncate sliced by byte index, which can split a multi-byte UTF-8 rune (examples print degree signs, emoji, etc.) and emit invalid UTF-8 into logs, CSV, and the AI-verifier prompt. Back off to a rune boundary before cutting. --- cmd/verifyexamples/verifier.go | 9 ++++++++- cmd/verifyexamples/verifier_test.go | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/cmd/verifyexamples/verifier.go b/cmd/verifyexamples/verifier.go index 4e207c26..88ad7ec6 100644 --- a/cmd/verifyexamples/verifier.go +++ b/cmd/verifyexamples/verifier.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "strings" + "unicode/utf8" "github.com/microsoft/agent-framework-go/agent" ) @@ -139,5 +140,11 @@ func truncate(text string, maxLength int) string { if len(text) <= maxLength { return text } - return text[:maxLength] + "... (truncated)" + // Back off to a rune boundary so truncation never splits a multi-byte rune + // (examples print °C, emoji, etc.) and emits invalid UTF-8 into logs/CSV. + end := maxLength + for end > 0 && !utf8.RuneStart(text[end]) { + end-- + } + return text[:end] + "... (truncated)" } diff --git a/cmd/verifyexamples/verifier_test.go b/cmd/verifyexamples/verifier_test.go index e1b84fa6..ae8e84c7 100644 --- a/cmd/verifyexamples/verifier_test.go +++ b/cmd/verifyexamples/verifier_test.go @@ -4,7 +4,9 @@ package main import ( "context" + "strings" "testing" + "unicode/utf8" ) func TestVerifyDeterministicOutput(t *testing.T) { @@ -33,3 +35,18 @@ func TestVerifyRequiresAIAgentForSemanticChecks(t *testing.T) { t.Fatalf("Failures = %#v, want one", result.Failures) } } + +func TestTruncateDoesNotSplitRunes(t *testing.T) { + // "°" is two bytes (0xC2 0xB0); truncating at byte 1 must not split it. + got := truncate("a°cdef", 2) + if !utf8.ValidString(got) { + t.Fatalf("truncate produced invalid UTF-8: %q", got) + } + if !strings.HasPrefix(got, "a") || strings.ContainsRune(got, '�') { + t.Fatalf("truncate = %q, want a clean rune-boundary cut", got) + } + // Short input is returned unchanged. + if truncate("ok", 10) != "ok" { + t.Fatalf("short input was modified") + } +}