diff --git a/backend/internal/checks/coverage_test.go b/backend/internal/checks/coverage_test.go index e044e8cd..7b978686 100644 --- a/backend/internal/checks/coverage_test.go +++ b/backend/internal/checks/coverage_test.go @@ -49,7 +49,7 @@ var oracleSplit = []struct { {"Mr. Smith went. He left.", []string{"Mr.", "Smith went.", "He left."}}, // naive over-split (known) // U+001F (unit separator) is whitespace to Python (\s / str.isspace) but NOT to // Go's unicode.IsSpace — isOracleSpace bridges the gap for oracle parity. - {"A.B.", []string{"A.", "B."}}, + {"A.\x1fB.", []string{"A.", "B."}}, } func TestSplitSentencesOracleParity(t *testing.T) { @@ -100,7 +100,7 @@ func boevoyGate(minChars int) config.CoverageGate { // information separators U+001C–U+001F, which Python treats as whitespace (\s / // str.isspace) but Go's unicode.IsSpace does not — a real bit-parity gap (self-review). func TestOracleSpaceControlChars(t *testing.T) { - us := "" // U+001F unit separator (survives EPUB/PDF extraction) + us := "\x1f" // U+001F unit separator (survives EPUB/PDF extraction) if got := splitSentences("A." + us + "B."); len(got) != 2 || got[0] != "A." || got[1] != "B." { t.Fatalf("U+001F after a terminator must split like the oracle, got %#v", got) } diff --git a/backend/internal/checks/labelcandidates_test.go b/backend/internal/checks/labelcandidates_test.go index 5b4ca8d5..6afe5ba5 100644 --- a/backend/internal/checks/labelcandidates_test.go +++ b/backend/internal/checks/labelcandidates_test.go @@ -33,7 +33,7 @@ func TestCheckerLabelsCandidates(t *testing.T) { if l.Bucket != "chevron" { continue } - line := strings.TrimLeft(lineForLabel(l, units), " \t  ") + line := strings.TrimLeft(lineForLabel(l, units), " \t ") rs := []rune(line) base := chevronSpeechShapeCommaOnly(rs) && c.isSpokenChevronLine(line) cand := chevronSpeechShape(rs) && c.isSpokenChevronLine(line) @@ -76,7 +76,7 @@ func TestCheckerLabelsCandidates(t *testing.T) { if l.Bucket != "em-dash" { continue } - line := strings.TrimLeft(lineForLabel(l, units), " \t  ") + line := strings.TrimLeft(lineForLabel(l, units), " \t ") rs := []rune(line) isDash := len(rs) > 0 && (rs[0] == '—' || rs[0] == '–' || rs[0] == '-') attr := speechAttribution(line) @@ -95,9 +95,10 @@ func TestCheckerLabelsCandidates(t *testing.T) { for _, l := range loadJSONL(t, filepath.Join(labelsDir, "labels", "k2.jsonl")) { tok := []rune(l.Token) if len(tok) >= 3 && isAllLatinLetters(tok) && hasUpper(tok) { - if l.Label == "defect" { + switch l.Label { + case "defect": capsDefect = append(capsDefect, l.Token) - } else if l.Label == "ok" { + case "ok": capsOK = append(capsOK, l.Token) } } diff --git a/backend/internal/checks/labelharness_test.go b/backend/internal/checks/labelharness_test.go index 9e83ec52..e874ac2e 100644 --- a/backend/internal/checks/labelharness_test.go +++ b/backend/internal/checks/labelharness_test.go @@ -350,14 +350,14 @@ func measureK4(labels []labelRow, units map[string]corpusUnit, c *Checkers) (met for _, l := range labels { switch l.Bucket { case "chevron": - line := strings.TrimLeft(lineForLabel(l, units), " \t  ") + line := strings.TrimLeft(lineForLabel(l, units), " \t ") flagged := chevronSpeechShape([]rune(line)) && c.isSpokenChevronLine(line) joinK4(&k4b, l.Speech, "spoken", flagged) case "em-dash": // k4_inverse (D39.39): a dash line whose ATTRIBUTION carries an inner-speech marker is a thought // typeset as spoken — scoped to the attribution (matches production speechAttribution), not the // whole line, so a spoken reply that merely mentions «про себя» is not flagged. - line := strings.TrimLeft(lineForLabel(l, units), " \t  ") + line := strings.TrimLeft(lineForLabel(l, units), " \t ") rs := []rune(line) isDash := len(rs) > 0 && (rs[0] == '—' || rs[0] == '–' || rs[0] == '-') attr := speechAttribution(line) @@ -375,7 +375,7 @@ func measureK4(labels []labelRow, units map[string]corpusUnit, c *Checkers) (met func measureK4a(labels []labelRow, units map[string]corpusUnit) metric { var m metric for _, l := range labels { - rs := []rune(strings.TrimLeft(lineForLabel(l, units), " \t  ")) + rs := []rune(strings.TrimLeft(lineForLabel(l, units), " \t ")) defect := false if len(rs) > 0 { switch rs[0] { diff --git a/backend/internal/miner/miner_alias.go b/backend/internal/miner/miner_alias.go index dcf62dba..70c4a043 100644 --- a/backend/internal/miner/miner_alias.go +++ b/backend/internal/miner/miner_alias.go @@ -158,8 +158,7 @@ func clusterAlias(ident []aliasEdge, allSurfaces []string) [][]string { for _, s := range allSurfaces { parent[s] = s } - var find func(string) string - find = func(x string) string { + find := func(x string) string { for parent[x] != x { parent[x] = parent[parent[x]] x = parent[x] diff --git a/backend/internal/pipeline/ratelimit_test.go b/backend/internal/pipeline/ratelimit_test.go index 4bbfb235..0b8afec8 100644 --- a/backend/internal/pipeline/ratelimit_test.go +++ b/backend/internal/pipeline/ratelimit_test.go @@ -2,6 +2,7 @@ package pipeline import ( "context" + "errors" "sync" "sync/atomic" "testing" @@ -84,7 +85,7 @@ func TestRateGuardHonoursContextCancel(t *testing.T) { cancel() select { case err := <-done: - if err != context.Canceled { + if !errors.Is(err, context.Canceled) { t.Fatalf("blocked acquire on cancel = %v, want context.Canceled", err) } case <-time.After(time.Second): diff --git a/backend/internal/pipeline/repair.go b/backend/internal/pipeline/repair.go index 2c2682ca..189860b6 100644 --- a/backend/internal/pipeline/repair.go +++ b/backend/internal/pipeline/repair.go @@ -500,7 +500,7 @@ func classInvariantRestored(cls checks.RepairClass, c *checks.Checkers, original case checks.RepairDC1Fractional: // The halved duration must be gone AND the replacement must still state a duration: a reply that // simply drops the time expression is a deletion, not a conversion. - return checks.FractionalUnitPresent(c, reply) == false && checks.MentionsHourWord(c, reply) + return !checks.FractionalUnitPresent(c, reply) && checks.MentionsHourWord(c, reply) case checks.RepairDC1TimeUnits: // The counted-hours class: assert the hours count POSITIVELY equals twice the source count. On the // pair data as it stands this also rejects a correct repair phrased in a case form the hours table diff --git a/backend/internal/pipeline/runner_memory_test.go b/backend/internal/pipeline/runner_memory_test.go index dd430ad8..c48b7df7 100644 --- a/backend/internal/pipeline/runner_memory_test.go +++ b/backend/internal/pipeline/runner_memory_test.go @@ -284,7 +284,7 @@ func TestRunnerMemoryResnapshotOnApprovedChange(t *testing.T) { // snapshot changes → a resume without --resnapshot must fail loud (F1: no silent // divergent re-pay). seedPath := filepath.Join(filepath.Dir(bookPath), "glossary-seed.yaml") - if err := os.WriteFile(seedPath, []byte(strings.Replace(suzukiSeed, "Судзуки", "Сузуки", -1)), 0o644); err != nil { + if err := os.WriteFile(seedPath, []byte(strings.ReplaceAll(suzukiSeed, "Судзуки", "Сузуки")), 0o644); err != nil { t.Fatal(err) } r2 := newRunner(t, bookPath)