package checks import ( "regexp" "strings" ) // output.go: shared hygiene over one model output, used by more than one check family. // thinkRE matches a leaked reasoning block, including its trailing whitespace. var thinkRE = regexp.MustCompile(`(?s).*?\s*`) // StripThink removes a leaked reasoning block from a model output. Both the // intrinsic classifier (which measures emptiness/echo on the delivered prose) and the coverage // gate (which measures how much of the source survived) MUST strip it before measuring: a // reasoning block left in inflates every length/sentence count and MASKS a real defect. It is a // pure string function — the same one the Python oracle applies (refusal_bench: re.sub then strip). func StripThink(s string) string { return thinkRE.ReplaceAllString(s, "") } // NarrativeStructure computes the claim-1 structural KPI over a FINAL Russian text: the count of // NARRATIVE paragraphs (non-empty lines that are NOT a dialogue turn opened with a dash «—»/«–»/«-») // and the total sentences within them (the oracle-parity splitSentences). Dialogue turns are excluded // (they are legitimately one short line). Deterministic and pure — the same signal the reflow lever // is supposed to move (exp14 mean-sentences-per-paragraph), reused here as in-loop observability. func NarrativeStructure(final string) (sentences, paragraphs int) { for _, line := range strings.Split(final, "\n") { t := strings.TrimSpace(line) if t == "" { continue } if rs := []rune(t); rs[0] == '—' || rs[0] == '–' || rs[0] == '-' { continue // a dialogue turn — not a narrative paragraph } paragraphs++ sentences += len(splitSentences(t)) } return sentences, paragraphs }