Diff
1diff --git a/internal/llm/contract.go b/internal/llm/contract.go
2index 1abe5d36465a721dcea63503ae393b8ee1f0c36c..0d541e46091f4d0bcf4835a8205de1aa12a8669f 100644
3--- a/internal/llm/contract.go
4+++ b/internal/llm/contract.go
5@@ -104,15 +104,19 @@ type JudgeResult struct {
6 }
7
8 // ParseJudge parses the three-field judge contract. The score must be an integer
9-// in 0-100; anything else is a recoverable *ContractError.
10+// in 0-100; anything else is a recoverable *ContractError. Feedback must be
11+// non-empty below 100 and is discarded at 100.
12 func ParseJudge(raw string) (JudgeResult, error) {
13 vals, err := parseFields(raw, judgeFieldNames)
14 if err != nil {
15 return JudgeResult{}, err
16 }
17- if err := requireFields(vals, judgeFieldNames); err != nil {
18+ if err := requireFields(vals, []string{"SCORE", "ROMAJI"}); err != nil {
19 return JudgeResult{}, err
20 }
21+ if _, ok := vals["FEEDBACK"]; !ok {
22+ return JudgeResult{}, &ContractError{Kind: MissingField, Field: "FEEDBACK"}
23+ }
24 score, perr := strconv.Atoi(vals["SCORE"])
25 if perr != nil {
26 return JudgeResult{}, &ContractError{Kind: InvalidFormat, Field: "SCORE"}
27@@ -120,7 +124,13 @@ func ParseJudge(raw string) (JudgeResult, error) {
28 if score < 0 || score > 100 {
29 return JudgeResult{}, &ContractError{Kind: OutOfRange, Field: "SCORE"}
30 }
31- return JudgeResult{Score: score, Romaji: vals["ROMAJI"], Feedback: vals["FEEDBACK"]}, nil
32+ feedback := vals["FEEDBACK"]
33+ if score == 100 {
34+ feedback = ""
35+ } else if feedback == "" {
36+ return JudgeResult{}, &ContractError{Kind: EmptyValue, Field: "FEEDBACK"}
37+ }
38+ return JudgeResult{Score: score, Romaji: vals["ROMAJI"], Feedback: feedback}, nil
39 }
40
41 type GameReply struct {
42diff --git a/internal/llm/prompt.go b/internal/llm/prompt.go
43index de0781fc3b43183fc1c85a21c570ecbda9ecd410..abf3e57f8de9a2cc480b7c1a9ff56790079ca4eb 100644
44--- a/internal/llm/prompt.go
45+++ b/internal/llm/prompt.go
46@@ -17,7 +17,7 @@ Your reply must be exactly three lines of plain text, in this order. No extra li
47
48 SCORE|<integer from 0 to 100>
49 ROMAJI|<hepburn romaji transcription of the given Japanese line>
50-FEEDBACK|<one or two concise sentences in english>
51+FEEDBACK|<one or two concise sentences in english; leave empty when SCORE is 100>
52
53 Example reply:
54 SCORE|85