13e5170e9e
Nudge wording as data instead of generation. The wording lives in
internal/phraser/nudges_ru_v1.json (embedded), about 10 variants per rule:
water, meal, break, service_down, netdata_critical, routine:, morning:, plus
a contentless default. That JSON is long because it is data — the owner can
edit any line of Russian without touching Go.
The picker:
- random, but never the same variant twice in a row for the same rule
- deterministic when seeded (math/rand with an injectable source)
- fills {since} / {service} / {what} from the candidate, and skips any variant
whose value is missing, so no raw placeholder can reach the piper voice
- {since} is spelled out in words ("полтора часа", "семь часов"), because
"3 ч" is wrong in a Russian voice
Scores 15/15 on the existing nudge fixture, on every seed swept. Nothing is
wired yet — that is the next commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
59 lines
1.7 KiB
Go
59 lines
1.7 KiB
Go
package eval
|
|
|
|
import (
|
|
"context"
|
|
"math/rand"
|
|
"testing"
|
|
|
|
"github.com/kami/maven/internal/phraser"
|
|
)
|
|
|
|
// TestTemplateNudges scores the hand-written Russian templates on the same
|
|
// fixture the model is scored on. No model, no network — it runs in milliseconds.
|
|
//
|
|
// The bar is every case, not most of them: the templates are hand-written, so a
|
|
// failure is a bug in one line of Russian, not model variance.
|
|
func TestTemplateNudges(t *testing.T) {
|
|
f, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
// Fixed seed: the score must not depend on which variant came up.
|
|
nt, err := phraser.NewNudgeTemplates(rand.NewSource(20260731))
|
|
if err != nil {
|
|
t.Fatalf("NewNudgeTemplates: %v", err)
|
|
}
|
|
rep, err := Score(context.Background(), "ru templates", nt, f)
|
|
if err != nil {
|
|
t.Fatalf("Score: %v", err)
|
|
}
|
|
t.Log("\n" + rep.String())
|
|
t.Log("\n" + rep.Messages())
|
|
if rep.Passed != rep.Total {
|
|
t.Errorf("templates scored %d/%d, want every case:\n%s",
|
|
rep.Passed, rep.Total, rep.Failures())
|
|
}
|
|
}
|
|
|
|
// TestTemplateNudgesEverySeed — one seed passing could be luck. Every variant of
|
|
// every rule has to pass every check, so sweep seeds until each has been used.
|
|
func TestTemplateNudgesEverySeed(t *testing.T) {
|
|
f, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
for seed := int64(0); seed < 60; seed++ {
|
|
nt, err := phraser.NewNudgeTemplates(rand.NewSource(seed))
|
|
if err != nil {
|
|
t.Fatalf("NewNudgeTemplates: %v", err)
|
|
}
|
|
rep, err := Score(context.Background(), "ru templates", nt, f)
|
|
if err != nil {
|
|
t.Fatalf("Score: %v", err)
|
|
}
|
|
if rep.Passed != rep.Total {
|
|
t.Errorf("seed %d: %d/%d\n%s", seed, rep.Passed, rep.Total, rep.Failures())
|
|
}
|
|
}
|
|
}
|