From 9fcd0dfd9a494b8c5614d7f8b8edbc090b06f1c9 Mon Sep 17 00:00:00 2001 From: Benno Weinzierl Date: Fri, 25 Sep 2026 14:37:22 +0200 Subject: [PATCH] test(llmops): reproduce checker JSON failure on typographic quotes refs #20 --- internal/llmops/json_response_test.go | 2 ++ internal/llmops/writingguide_eval_test.go | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/internal/llmops/json_response_test.go b/internal/llmops/json_response_test.go index c6002c0e..abc1e177 100644 --- a/internal/llmops/json_response_test.go +++ b/internal/llmops/json_response_test.go @@ -78,6 +78,8 @@ func TestCheckersJSONResponse(t *testing.T) { {name: "nested_code_literal", output: "Reviewed Options{Child: Child{Enabled: true}}.\n" + checker.payload, want: 1}, {name: "quoted_prose", output: "The \"review\" follows.\n" + checker.payload, want: 1}, {name: "escaped_strings", output: strings.Replace(checker.payload, "The", `Quoted \"{x}\" and C:\\tmp affect the`, 1), want: 1}, + {name: "typographic_quotes", output: strings.Replace(checker.payload, "The", "„Quoted“ and «cited» affect the", 1), want: 1}, + {name: "typographic_quote_normalized_unescaped", output: strings.Replace(checker.payload, "The", `„Quoted" affects the`, 1), wantErr: true}, {name: "two_results", output: checker.payload + "\n" + checker.payload, wantErr: true}, {name: "clean_result_after_findings", output: checker.payload + `{"findings":[]}`, wantErr: true}, {name: "empty_object_after_payload", output: checker.payload + "\n{}", wantErr: true}, diff --git a/internal/llmops/writingguide_eval_test.go b/internal/llmops/writingguide_eval_test.go index d6149238..e09ea031 100644 --- a/internal/llmops/writingguide_eval_test.go +++ b/internal/llmops/writingguide_eval_test.go @@ -150,3 +150,18 @@ func TestWritingGuideEval_ConflatedDraftYieldsConflation(t *testing.T) { return hasAxis(r, "conflation") }) } + +// TestWritingGuideEval_TypographicQuotesStayValidJSON pins the JSON contract +// for drafts quoted with typographic quotation marks: German „…“ close with +// U+201C, which the model tends to echo as an unescaped straight quote when it +// cites the draft, breaking the response. Any findings are fine — the case +// fails only on unparseable output. +func TestWritingGuideEval_TypographicQuotesStayValidJSON(t *testing.T) { + draft := &model.Entry{ + Type: model.TypeSignal, Kind: model.KindDone, Layer: model.LayerTactical, + Content: "Die nutzersichtbaren Bestie-Code-Texte sind umformuliert und über MR !106 nach `main` gemergt: Kein Button spricht mehr von „Code einlösen“ — die Wording-Direktive ist umgesetzt, und der Paywall-Einstieg verspricht keine Store-Code-Einlösung mehr.\n\n" + + "- Auf Bennos Wunsch wurden die begleitenden Fließtexte angeglichen: In `NoBestie.tsx` heißt es jetzt „… oder nutze einen Bestie-Code …“ statt „löse … ein“, und die Fehlermeldung spricht von „Verbindungslink“ statt „Einladungslink“.\n" + + "- Die Schreibweise ist auf „Bestie-Code“ mit Bindestrich vereinheitlicht.", + } + runGuideEvalPassRate(t, draft, blockingTier, func(*llmops.WritingGuideResult) error { return nil }) +}