diff --git a/internal/llm/checkpoint.go b/internal/llm/checkpoint.go index f1a622c..8b9e3fc 100644 --- a/internal/llm/checkpoint.go +++ b/internal/llm/checkpoint.go @@ -89,6 +89,14 @@ func ParseCheckpoint(raw string) ([]RawSuggestion, error) { if s.Original == "" { continue } + // Drop no-ops: the model sometimes "flags" a correct sentence and echoes + // it verbatim as the replacement, producing a card whose before/after are + // identical and whose explanation says it's already fine — a suggestion + // that suggests nothing. Awareness-only families (voice) carry an empty + // replacement, so this only ever fires on the edit families. + if s.Replacement != "" && s.Original == s.Replacement { + continue + } out = append(out, s) } return out, nil diff --git a/internal/llm/checkpoint_test.go b/internal/llm/checkpoint_test.go index b42c7ba..bddc6db 100644 --- a/internal/llm/checkpoint_test.go +++ b/internal/llm/checkpoint_test.go @@ -37,6 +37,25 @@ func TestParseCheckpoint(t *testing.T) { raw: "I could not find any issues!", wantErr: true, }, + { + // A correct sentence echoed verbatim as its own replacement is a card + // that suggests nothing — dropped. The second item is a real edit. + name: "drops no-op where replacement equals original", + raw: `{"suggestions":[{"original":"It wasn't the most graceful morning.","replacement":"It wasn't the most graceful morning.","explanation":"used correctly here","type":"idiom"},{"original":"teh","replacement":"the","explanation":"typo","type":"grammar"}]}`, + want: 1, + }, + { + // Whitespace-only difference is still a no-op once both are trimmed. + name: "drops no-op after whitespace trim", + raw: `{"suggestions":[{"original":"the cat sat","replacement":" the cat sat ","explanation":"already fine","type":"grammar"}]}`, + want: 0, + }, + { + // Awareness-only voice flags carry an empty replacement and must survive. + name: "keeps awareness-only flag with empty replacement", + raw: `{"suggestions":[{"original":"a long run-on passage","replacement":"","explanation":"this drifts from your voice","type":"voice"}]}`, + want: 1, + }, { name: "braces inside string values", raw: `{"suggestions":[{"original":"use {x}","replacement":"use x","explanation":"drop the braces","type":"clarity"}]}`,