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
}