Insert `from __future__ import annotations` in the 17 files that use
PEP 604/585 annotations in module-level positions evaluated at import
time, so scripts/banned_phrase_scan.py and friends no longer raise
TypeError on Python 3.8/3.9. Add a 3.8 leg to the CI matrix so the
floor claim in README.md is actually gated, and correct the two
imprecise "439 deterministic cases" references to "440 deterministic
script cases (439 pass, 1 documented xfail)".
New scripts must carry the future-import until the floor is raised;
if the maintainer later chooses 3.10+, delete the CI 3.8 leg and
README claim together.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6CYksdLbXbTAxcAQjvHz5
Robustness:
- every script now exits cleanly (code 2) on a missing/unreadable input file
instead of raising a traceback (was only fixed for validate_preservation)
- runner adds a per-case timeout and survives a missing command/crash instead
of aborting the whole run
- gated two more context-sensitive single words behind their jargon
collocations: "harness" (a horse harness vs "harness the power of") and
"foster" (a foster family vs "foster a culture of")
CI:
- .github/workflows/evals.yml compiles the scripts and runs the regression
harness on every push and PR; only an undocumented FAIL breaks the build
New eval cases (script target, all passing):
- FP-07/08 literal harness/foster; FP-09 smart-quote exemption
- REC-01/02/03 recall guards proving the gating still flags real jargon and
stacked slop (so de-noising can't silently gut detection)
- PRES-05 comma-format equality, PRES-06 $2.4M == $2.4 million, PRES-07 dropped
quarter is a lost fact
- ROB-04..07 missing-file robustness for each script, ROB-08 empty stdin
Suite: 53 cases (35 script: 34 PASS / 1 XFAIL / 0 FAIL, 18 behavioral skill).
Flips 19 of the 20 deterministic xfail cases to PASS (FP-06, literal
"delve into a place", stays xfail as an accepted regex sense-ambiguity limit).
Final: 20 PASS, 1 XFAIL, 0 FAIL.
Fact preservation (validate_preservation.py, extract_constraints.py):
- currency compares absolute magnitude, so $47.3M no longer equals $47.3 billion
- percentages require an exact token match (12% no longer matches 120%)
- dates require the month, not just the year (March 3 2020 != December 2020)
- bare integers and whole phone numbers are now tracked as constraints
- faithful rewordings ($47.3M -> $47.3 million) still pass, no false missing
- missing input files exit cleanly instead of raising a traceback
Scanner (banned_phrase_scan.py):
- removed over-broad entries: "the real" (hit "the real estate"), bare
"period."/"full stop." (hit any sentence ending in "period")
- gated context-sensitive words (leverage, navigate, tapestry, boasts) behind
jargon collocations so literal/financial senses aren't flagged
- added missing tells: in conclusion, firstly/secondly, underscore the
importance, treasure trove, ever-evolving, rich mosaic, plus stop-slop's
false-agency family (numbers speak for themselves, data tells a story)
- quote masking: dropped the 500-char cap, allowed multi-line quotes, and
stopped masking single-quoted prose that was hiding real slop
Robustness (readability_metrics.py, diff_check.py):
- numeric-only text counts as words instead of reading as empty
- punctuation-only edits register as change instead of 0%
Kept references/taboo-phrases.md in sync with the new scanner patterns.