Share the D&D NPC registry and prompt grounding

This commit is contained in:
2026-07-21 04:34:56 +00:00
parent d3e171aa82
commit 92acb45775
23 changed files with 628 additions and 436 deletions

View File

@@ -0,0 +1,53 @@
// Package diagnostics provides bounded, safe text for deterministic D&D
// decisions and warnings.
package diagnostics
import (
"fmt"
"strconv"
"strings"
)
const (
MaxIssues = 20
MaxDisplayedRunes = 128
MaxMessageBytes = 4096
)
func Truncate(value string) string {
runes := []rune(value)
if len(runes) <= MaxDisplayedRunes {
return value
}
return string(runes[:MaxDisplayedRunes-1]) + "…"
}
func Quote(value string) string { return strconv.Quote(Truncate(value)) }
func Aggregate(prefix string, issues []string) string {
displayed := make([]string, 0, min(len(issues), MaxIssues))
for len(displayed) < len(issues) && len(displayed) < MaxIssues {
issue := Truncate(issues[len(displayed)])
candidate := aggregateMessage(prefix, append(displayed, issue), len(issues)-len(displayed)-1)
if len([]byte(candidate)) > MaxMessageBytes {
break
}
displayed = append(displayed, issue)
}
return aggregateMessage(prefix, displayed, len(issues)-len(displayed))
}
func aggregateMessage(prefix string, issues []string, omitted int) string {
message := prefix + ": " + strings.Join(issues, ", ")
if omitted > 0 {
message += fmt.Sprintf("; %d additional issue(s) omitted", omitted)
}
return message
}
func min(left, right int) int {
if left < right {
return left
}
return right
}

View File

@@ -0,0 +1,28 @@
package diagnostics
import (
"fmt"
"strings"
"testing"
"unicode/utf8"
)
func TestAggregateEnforcesByteBudgetAndReportsOmissions(t *testing.T) {
issues := make([]string, MaxIssues)
for index := range issues {
issues[index] = fmt.Sprintf("issue-%d-%s", index, strings.Repeat("火", MaxDisplayedRunes))
}
message := Aggregate("invalid D&D data", issues)
if !utf8.ValidString(message) || len([]byte(message)) > MaxMessageBytes {
t.Fatalf("Aggregate() returned invalid or oversized message: bytes=%d message=%q", len([]byte(message)), message)
}
displayed := strings.Count(message, "issue-")
if displayed == 0 || displayed >= len(issues) {
t.Fatalf("Aggregate() displayed %d issues, want byte-budget omission", displayed)
}
wantOmitted := fmt.Sprintf("%d additional issue(s) omitted", len(issues)-displayed)
if !strings.Contains(message, wantOmitted) {
t.Fatalf("Aggregate() = %q, want %q", message, wantOmitted)
}
}