S0-R1-C5.5-R1 Study-Causal Semantic Frame Closure

#84
Files changed (1) hide show
  1. core/quantitative_engine.py +52 -0
core/quantitative_engine.py CHANGED
@@ -630,6 +630,56 @@ Keywords: {keywords}"""
630
  r"\b(?:has|have|had|exerts?|exerted)\s+(?:an?\s+|the\s+)?causal\s+effect\b",
631
  )
632
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
633
  def direct_match_is_disclaimed(match):
634
  prefix = value[max(0, match.start() - 220):match.start()]
635
  if re.search(
@@ -670,6 +720,8 @@ Keywords: {keywords}"""
670
  if direct_match_is_disclaimed(match):
671
  saw_disclaimed_direct = True
672
  continue
 
 
673
  return "AFFIRMATIVE_CAUSAL"
674
 
675
  affirmative_noun_patterns = (
 
630
  r"\b(?:has|have|had|exerts?|exerted)\s+(?:an?\s+|the\s+)?causal\s+effect\b",
631
  )
632
 
633
+ def direct_match_is_statistical_reporting(match):
634
+ """Return True only for a statistical-operation -> statistical-artifact frame.
635
+
636
+ C5.5-R1: causal-looking verbs such as ``produced``/``resulted in``
637
+ are ambiguous in quantitative prose. A bootstrap procedure producing
638
+ a confidence interval is statistical reporting, not a claim that one
639
+ study construct causes another. The exemption therefore requires
640
+ *both* a statistical-operation source immediately before the verb and
641
+ a statistical-artifact target immediately after it. Merely seeing
642
+ ``analysis`` or ``model`` is not enough; e.g. ``the analysis produced
643
+ higher engagement`` remains fail-closed.
644
+ """
645
+ prefix = value[max(0, match.start() - 180):match.start()]
646
+ suffix = value[match.end():min(len(value), match.end() + 180)]
647
+
648
+ # Keep the source local to the current grammatical frame rather than
649
+ # letting an earlier sentence mention of an analysis whitelist a later
650
+ # construct-level causal assertion.
651
+ source = re.split(r"[.;!?]", prefix)[-1]
652
+ source_pattern = (
653
+ r"\b(?:bootstrap(?:\s+resampling|\s+procedure|\s+computation)?|"
654
+ r"resampling(?:\s+procedure|\s+algorithm)?|"
655
+ r"regression\s+(?:analysis|model)|"
656
+ r"association\s+(?:analysis|model)|"
657
+ r"mediation\s+(?:analysis|model)|"
658
+ r"estimation(?:\s+procedure|\s+method)?|estimator|"
659
+ r"statistical\s+(?:analysis|model|procedure|method)|"
660
+ r"analysis|analyses|model|procedure|method|algorithm|"
661
+ r"computation|calculation)\b"
662
+ )
663
+ if not re.search(source_pattern, source, flags=re.I):
664
+ return False
665
+
666
+ # The target must be a recognisable statistical artefact. Do not use
667
+ # generic outcomes such as ``result`` or ``change`` because those can
668
+ # also be substantive dependent variables.
669
+ target_pattern = (
670
+ r"^\s*(?:(?:an?|the|this|that|these|those|following|reported|"
671
+ r"standardized|standardised|numerical|estimated|95\s*percent|95%)\s+){0,6}"
672
+ r"(?:confidence\s+intervals?|interval\s+estimates?|"
673
+ r"coefficient(?:\s+estimates?)?s?|standard\s+errors?|"
674
+ r"parameter\s+estimates?|point\s+estimates?|indirect\s+estimates?|"
675
+ r"direct\s+estimates?|effect-size\s+estimates?|"
676
+ r"correlations?|covariances?|variances?|loadings?|"
677
+ r"test\s+statistics?|p[- ]?values?|vif(?:s|\s+values)?|"
678
+ r"cronbach(?:'s)?\s+alpha|composite\s+reliability|"
679
+ r"average\s+variance\s+extracted|ave|numerical\s+estimates?|estimates?)\b"
680
+ )
681
+ return bool(re.search(target_pattern, suffix, flags=re.I))
682
+
683
  def direct_match_is_disclaimed(match):
684
  prefix = value[max(0, match.start() - 220):match.start()]
685
  if re.search(
 
720
  if direct_match_is_disclaimed(match):
721
  saw_disclaimed_direct = True
722
  continue
723
+ if direct_match_is_statistical_reporting(match):
724
+ continue
725
  return "AFFIRMATIVE_CAUSAL"
726
 
727
  affirmative_noun_patterns = (