Spaces:
Running
Running
S1-R4 Method Authority Guard + Balanced Final Abstract
#125
by GuXiaopeng - opened
- core/generation_runner.py +24 -3
- core/pipeline_agent.py +111 -0
- core/quantitative_engine.py +95 -0
core/generation_runner.py
CHANGED
|
@@ -1242,11 +1242,32 @@ class GenerationRunner:
|
|
| 1242 |
sentence = relation_sentence(moderation[0])
|
| 1243 |
if sentence:
|
| 1244 |
out.append(sentence)
|
| 1245 |
-
if "final_abstract_missing_empirical_finding" in wanted
|
| 1246 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1247 |
sentence = relation_sentence(candidate or {})
|
| 1248 |
if sentence:
|
| 1249 |
-
|
|
|
|
| 1250 |
|
| 1251 |
if "final_abstract_missing_indirect_finding" in wanted:
|
| 1252 |
indirect_rows = [dict(x) for x in (ledger.get("indirect_analyses") or []) if isinstance(x, dict)]
|
|
|
|
| 1242 |
sentence = relation_sentence(moderation[0])
|
| 1243 |
if sentence:
|
| 1244 |
out.append(sentence)
|
| 1245 |
+
if "final_abstract_missing_empirical_finding" in wanted:
|
| 1246 |
+
# Preserve representativeness even when the abstract already
|
| 1247 |
+
# contains null/unsupported findings. Prefer one supported
|
| 1248 |
+
# non-moderation relation so the fallback communicates a
|
| 1249 |
+
# central positive/negative empirical result rather than
|
| 1250 |
+
# becoming a list of only non-significant findings.
|
| 1251 |
+
supported = [
|
| 1252 |
+
item for item in items
|
| 1253 |
+
if str(item.get("relation") or "").strip().lower() != "moderation"
|
| 1254 |
+
and cls._final_abstract_ci_status(
|
| 1255 |
+
dict(relation_results.get(str(item.get("relation_id") or "")) or {})
|
| 1256 |
+
) == "supported"
|
| 1257 |
+
]
|
| 1258 |
+
candidate = next(
|
| 1259 |
+
(item for item in supported if str(item.get("outcome") or "").strip()),
|
| 1260 |
+
supported[0] if supported else None,
|
| 1261 |
+
)
|
| 1262 |
+
if candidate is None and not out:
|
| 1263 |
+
candidate = next(
|
| 1264 |
+
(item for item in items if relation_results.get(str(item.get("relation_id") or ""))),
|
| 1265 |
+
None,
|
| 1266 |
+
)
|
| 1267 |
sentence = relation_sentence(candidate or {})
|
| 1268 |
if sentence:
|
| 1269 |
+
if sentence not in out:
|
| 1270 |
+
out.append(sentence)
|
| 1271 |
|
| 1272 |
if "final_abstract_missing_indirect_finding" in wanted:
|
| 1273 |
indirect_rows = [dict(x) for x in (ledger.get("indirect_analyses") or []) if isinstance(x, dict)]
|
core/pipeline_agent.py
CHANGED
|
@@ -5088,6 +5088,84 @@ INPUT:
|
|
| 5088 |
return True
|
| 5089 |
return False
|
| 5090 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5091 |
@classmethod
|
| 5092 |
def _is_study_owned_qualitative_claim(cls, sentence, section_name=""):
|
| 5093 |
"""Conservatively identify only this manuscript's empirical/procedural claims.
|
|
@@ -5102,6 +5180,11 @@ INPUT:
|
|
| 5102 |
return False
|
| 5103 |
if re.match(r'^["“‘\']', stripped) or re.search(r"\bQUOTE-\d+\b", value, flags=re.I):
|
| 5104 |
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5105 |
if cls._has_external_literature_marker(value):
|
| 5106 |
return False
|
| 5107 |
if re.search(r"(?i)\b(?:prior|previous|earlier|existing)\s+(?:research|stud(?:y|ies)|literature)\b", value):
|
|
@@ -5155,6 +5238,9 @@ INPUT:
|
|
| 5155 |
"ethics": [str(x or "").strip().casefold() for x in (facts.get("ethics_procedures") or []) if str(x or "").strip()],
|
| 5156 |
"reflexivity": [str(x or "").strip().casefold() for x in (facts.get("reflexivity_procedures") or []) if str(x or "").strip()],
|
| 5157 |
}
|
|
|
|
|
|
|
|
|
|
| 5158 |
procedure_patterns = {
|
| 5159 |
"trustworthiness": r"\bmember\s+check(?:ing|s)?\b|\bpeer\s+debrief(?:ing|ed)?\b|\baudit\s+trail\b|\bexternal\s+audit\b",
|
| 5160 |
"ethics": r"\binformed\s+consent\b|\bethics?\s+approval\b|\binstitutional\s+review\s+board\b|\bIRB\b|\bapproval\s+(?:number|code)\b",
|
|
@@ -5191,6 +5277,18 @@ INPUT:
|
|
| 5191 |
if not core.strip() or not cls._is_study_owned_qualitative_claim(core, section_name):
|
| 5192 |
continue
|
| 5193 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5194 |
# Check every explicit procedure mention; do not stop after the first.
|
| 5195 |
for category, pattern in procedure_patterns.items():
|
| 5196 |
for match in re.finditer(pattern, core, flags=re.I):
|
|
@@ -5253,6 +5351,9 @@ INPUT:
|
|
| 5253 |
"ethics": [str(x or "").strip() for x in (facts.get("ethics_procedures") or []) if str(x or "").strip()],
|
| 5254 |
"reflexivity": [str(x or "").strip() for x in (facts.get("reflexivity_procedures") or []) if str(x or "").strip()],
|
| 5255 |
}
|
|
|
|
|
|
|
|
|
|
| 5256 |
allowed_by_actor = {}
|
| 5257 |
canonical_relations = []
|
| 5258 |
canonical_by_actor = {}
|
|
@@ -5287,6 +5388,16 @@ INPUT:
|
|
| 5287 |
lower = core.casefold()
|
| 5288 |
if not core.strip() or not cls._is_study_owned_qualitative_claim(core, section_name):
|
| 5289 |
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5290 |
removed = False
|
| 5291 |
for category, pattern in procedure_patterns.items():
|
| 5292 |
procedure_match = re.search(pattern, core, flags=re.I)
|
|
|
|
| 5088 |
return True
|
| 5089 |
return False
|
| 5090 |
|
| 5091 |
+
@staticmethod
|
| 5092 |
+
def _has_explicit_current_qualitative_study_anchor(sentence):
|
| 5093 |
+
"""Identify an explicit current-study owner even in a cited sentence.
|
| 5094 |
+
|
| 5095 |
+
A method citation may justify a procedure, but it must not make this
|
| 5096 |
+
manuscript's own empirical/procedural clause externally owned. Keep
|
| 5097 |
+
this narrower than the general qualitative ownership heuristic so a
|
| 5098 |
+
cited phrase such as ``the study conducted interviews`` is not
|
| 5099 |
+
accidentally reclassified as the current manuscript.
|
| 5100 |
+
"""
|
| 5101 |
+
value = str(sentence or "")
|
| 5102 |
+
return bool(re.search(
|
| 5103 |
+
r"(?i)\b(?:we|our\s+study|this\s+study|the\s+present\s+study|"
|
| 5104 |
+
r"research\s+team|our\s+data|our\s+analysis|our\s+interviews?|"
|
| 5105 |
+
r"our\s+focus\s+groups?|our\s+observations?|in\s+this\s+study|"
|
| 5106 |
+
r"in\s+our\s+study)\b",
|
| 5107 |
+
value,
|
| 5108 |
+
))
|
| 5109 |
+
|
| 5110 |
+
@staticmethod
|
| 5111 |
+
def _normalize_qualitative_analytic_design_identity(value):
|
| 5112 |
+
folded = re.sub(r"\s+", " ", str(value or "")).strip().casefold()
|
| 5113 |
+
if not folded:
|
| 5114 |
+
return ""
|
| 5115 |
+
if "gioia" in folded:
|
| 5116 |
+
return "gioia"
|
| 5117 |
+
if "grounded theory" in folded:
|
| 5118 |
+
return "grounded_theory"
|
| 5119 |
+
if "comparative case" in folded:
|
| 5120 |
+
return "comparative_case"
|
| 5121 |
+
if "longitudinal" in folded and "qualitative" in folded:
|
| 5122 |
+
return "longitudinal_qualitative"
|
| 5123 |
+
if "thematic" in folded:
|
| 5124 |
+
return "thematic_process"
|
| 5125 |
+
if "process analysis" in folded:
|
| 5126 |
+
return "process_analysis"
|
| 5127 |
+
return folded
|
| 5128 |
+
|
| 5129 |
+
@classmethod
|
| 5130 |
+
def _performed_qualitative_analytic_design_identity(cls, sentence):
|
| 5131 |
+
"""Return a performed current-study qualitative design, if explicit.
|
| 5132 |
+
|
| 5133 |
+
This is a prose-level evidence guard, not a new method router. It
|
| 5134 |
+
prevents a cited method name from silently replacing the analytic design
|
| 5135 |
+
frozen in the qualitative evidence packet.
|
| 5136 |
+
"""
|
| 5137 |
+
value = re.sub(r"\s+", " ", str(sentence or "")).strip()
|
| 5138 |
+
if not value or not cls._has_explicit_current_qualitative_study_anchor(value):
|
| 5139 |
+
return ""
|
| 5140 |
+
candidates = (
|
| 5141 |
+
("gioia", r"\bGioia(?:-style)?(?:\s+inductive\s+coding|\s+methodology|\s+method|\s+approach)?\b"),
|
| 5142 |
+
("grounded_theory", r"\bgrounded\s+theory\b"),
|
| 5143 |
+
("comparative_case", r"\bcomparative\s+case\s+analysis\b"),
|
| 5144 |
+
("longitudinal_qualitative", r"\blongitudinal\s+qualitative\s+analysis\b"),
|
| 5145 |
+
("process_analysis", r"\bprocess\s+analysis\b"),
|
| 5146 |
+
("thematic_process", r"\b(?:iterative\s+)?thematic(?:/process)?\s+analysis\b|\bthematic\s+analysis\b"),
|
| 5147 |
+
)
|
| 5148 |
+
for identity, pattern in candidates:
|
| 5149 |
+
match = re.search(pattern, value, flags=re.I)
|
| 5150 |
+
if not match:
|
| 5151 |
+
continue
|
| 5152 |
+
before = value[max(0, match.start() - 120):match.start()]
|
| 5153 |
+
if re.search(
|
| 5154 |
+
r"(?i)\b(?:did\s+not|does\s+not|do\s+not|was\s+not|were\s+not|"
|
| 5155 |
+
r"not|without|never)\b[^.;:]{0,45}$",
|
| 5156 |
+
before,
|
| 5157 |
+
):
|
| 5158 |
+
continue
|
| 5159 |
+
full_before = value[:match.start()]
|
| 5160 |
+
if re.search(
|
| 5161 |
+
r"(?i)\b(?:use|uses|used|employ|employs|employed|apply|applies|applied|"
|
| 5162 |
+
r"adopt|adopts|adopted|follow|follows|followed|conduct|conducts|conducted|"
|
| 5163 |
+
r"analy[sz]e|analy[sz]es|analy[sz]ed|code|codes|coded)\b[^.;:]{0,180}$",
|
| 5164 |
+
full_before,
|
| 5165 |
+
):
|
| 5166 |
+
return identity
|
| 5167 |
+
return ""
|
| 5168 |
+
|
| 5169 |
@classmethod
|
| 5170 |
def _is_study_owned_qualitative_claim(cls, sentence, section_name=""):
|
| 5171 |
"""Conservatively identify only this manuscript's empirical/procedural claims.
|
|
|
|
| 5180 |
return False
|
| 5181 |
if re.match(r'^["“‘\']', stripped) or re.search(r"\bQUOTE-\d+\b", value, flags=re.I):
|
| 5182 |
return False
|
| 5183 |
+
# Explicit current-study ownership dominates an appended method or
|
| 5184 |
+
# theory citation. The cited source may justify the procedure, but it
|
| 5185 |
+
# cannot own the statement that *this* study performed it.
|
| 5186 |
+
if cls._has_explicit_current_qualitative_study_anchor(value):
|
| 5187 |
+
return True
|
| 5188 |
if cls._has_external_literature_marker(value):
|
| 5189 |
return False
|
| 5190 |
if re.search(r"(?i)\b(?:prior|previous|earlier|existing)\s+(?:research|stud(?:y|ies)|literature)\b", value):
|
|
|
|
| 5238 |
"ethics": [str(x or "").strip().casefold() for x in (facts.get("ethics_procedures") or []) if str(x or "").strip()],
|
| 5239 |
"reflexivity": [str(x or "").strip().casefold() for x in (facts.get("reflexivity_procedures") or []) if str(x or "").strip()],
|
| 5240 |
}
|
| 5241 |
+
authorized_design = cls._normalize_qualitative_analytic_design_identity(
|
| 5242 |
+
facts.get("analytic_design") or facts.get("analysis_method")
|
| 5243 |
+
)
|
| 5244 |
procedure_patterns = {
|
| 5245 |
"trustworthiness": r"\bmember\s+check(?:ing|s)?\b|\bpeer\s+debrief(?:ing|ed)?\b|\baudit\s+trail\b|\bexternal\s+audit\b",
|
| 5246 |
"ethics": r"\binformed\s+consent\b|\bethics?\s+approval\b|\binstitutional\s+review\s+board\b|\bIRB\b|\bapproval\s+(?:number|code)\b",
|
|
|
|
| 5277 |
if not core.strip() or not cls._is_study_owned_qualitative_claim(core, section_name):
|
| 5278 |
continue
|
| 5279 |
|
| 5280 |
+
observed_design = cls._performed_qualitative_analytic_design_identity(core)
|
| 5281 |
+
if observed_design and authorized_design and observed_design != authorized_design:
|
| 5282 |
+
sig = ("analytic_design", observed_design)
|
| 5283 |
+
if sig not in seen:
|
| 5284 |
+
seen.add(sig)
|
| 5285 |
+
violations.append({
|
| 5286 |
+
"type":"unsupported_qualitative_analytic_design",
|
| 5287 |
+
"observed":observed_design,
|
| 5288 |
+
"expected":authorized_design,
|
| 5289 |
+
"preview":core.strip()[:180],
|
| 5290 |
+
})
|
| 5291 |
+
|
| 5292 |
# Check every explicit procedure mention; do not stop after the first.
|
| 5293 |
for category, pattern in procedure_patterns.items():
|
| 5294 |
for match in re.finditer(pattern, core, flags=re.I):
|
|
|
|
| 5351 |
"ethics": [str(x or "").strip() for x in (facts.get("ethics_procedures") or []) if str(x or "").strip()],
|
| 5352 |
"reflexivity": [str(x or "").strip() for x in (facts.get("reflexivity_procedures") or []) if str(x or "").strip()],
|
| 5353 |
}
|
| 5354 |
+
authorized_design = cls._normalize_qualitative_analytic_design_identity(
|
| 5355 |
+
facts.get("analytic_design") or facts.get("analysis_method")
|
| 5356 |
+
)
|
| 5357 |
allowed_by_actor = {}
|
| 5358 |
canonical_relations = []
|
| 5359 |
canonical_by_actor = {}
|
|
|
|
| 5388 |
lower = core.casefold()
|
| 5389 |
if not core.strip() or not cls._is_study_owned_qualitative_claim(core, section_name):
|
| 5390 |
continue
|
| 5391 |
+
observed_design = cls._performed_qualitative_analytic_design_identity(core)
|
| 5392 |
+
if observed_design and authorized_design and observed_design != authorized_design:
|
| 5393 |
+
replacements.append((start, end, ""))
|
| 5394 |
+
diagnostics.append({
|
| 5395 |
+
"type":"unsupported_qualitative_analytic_design_removed",
|
| 5396 |
+
"observed":observed_design,
|
| 5397 |
+
"expected":authorized_design,
|
| 5398 |
+
"preview":core.strip()[:180],
|
| 5399 |
+
})
|
| 5400 |
+
continue
|
| 5401 |
removed = False
|
| 5402 |
for category, pattern in procedure_patterns.items():
|
| 5403 |
procedure_match = re.search(pattern, core, flags=re.I)
|
core/quantitative_engine.py
CHANGED
|
@@ -1024,6 +1024,24 @@ Keywords: {keywords}"""
|
|
| 1024 |
return "study"
|
| 1025 |
return "unknown"
|
| 1026 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1027 |
@staticmethod
|
| 1028 |
def _unique_numeric(pattern, evidence, *, integer=False):
|
| 1029 |
values = []
|
|
@@ -1182,7 +1200,41 @@ Keywords: {keywords}"""
|
|
| 1182 |
if explicit_nonuse:
|
| 1183 |
return {"term": label, "category": category, "state": "NOT_PERFORMED_OR_LIMITATION"}
|
| 1184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1185 |
performed = bool(
|
|
|
|
|
|
|
| 1186 |
re.search(
|
| 1187 |
r"\b(?:we|the\s+study|this\s+study|the\s+analysis|our\s+analysis|the\s+model|the\s+authors?)\b[^.;:]{0,70}\b(?:use|uses|used|employ|employs|employed|conduct|conducts|conducted|perform|performs|performed|estimate|estimates|estimated|test|tests|tested|assess|assesses|assessed|evaluate|evaluates|evaluated|implement|implements|implemented|apply|applies|applied|ran|run|runs|fit|fits|fitted|model|models|modeled|modelled|control|controls|controlled|correct|corrects|corrected|address|addresses|addressed)\b[^.;:]{0,70}$",
|
| 1188 |
before,
|
|
@@ -1917,11 +1969,34 @@ Keywords: {keywords}"""
|
|
| 1917 |
|
| 1918 |
for _start, _end, sentence in cls._sentence_spans(value):
|
| 1919 |
ownership = cls._quantitative_sentence_ownership(sentence, section_name, known_constructs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1920 |
method_scope_unknown = (
|
| 1921 |
strict and ownership == "unknown"
|
| 1922 |
and bool(re.search(r"(?i)method|measure", str(section_name or "")))
|
| 1923 |
and cls._first_performed_unowned_quantitative_term(sentence, forbidden) is not None
|
| 1924 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1925 |
if ownership not in {"study", "result"} and not method_scope_unknown:
|
| 1926 |
continue
|
| 1927 |
if sample_size:
|
|
@@ -2065,11 +2140,31 @@ Keywords: {keywords}"""
|
|
| 2065 |
replacements = []
|
| 2066 |
for start, end, sentence in cls._sentence_spans(value):
|
| 2067 |
ownership = cls._quantitative_sentence_ownership(sentence, section_name, known_constructs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2068 |
method_scope_unknown = (
|
| 2069 |
strict and ownership == "unknown"
|
| 2070 |
and bool(re.search(r"(?i)method|measure", str(section_name or "")))
|
| 2071 |
and cls._first_performed_unowned_quantitative_term(sentence, forbidden) is not None
|
| 2072 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2073 |
if ownership not in {"study", "result"} and not method_scope_unknown:
|
| 2074 |
continue
|
| 2075 |
revised = sentence
|
|
|
|
| 1024 |
return "study"
|
| 1025 |
return "unknown"
|
| 1026 |
|
| 1027 |
+
@staticmethod
|
| 1028 |
+
def _has_explicit_current_study_anchor(sentence):
|
| 1029 |
+
"""Return True only for an explicit *current manuscript* ownership anchor.
|
| 1030 |
+
|
| 1031 |
+
A literature citation does not transfer ownership of a clause such as
|
| 1032 |
+
``We tested ... (Author, year)`` to the cited source. This helper is
|
| 1033 |
+
intentionally narrower than the general sentence-ownership heuristic:
|
| 1034 |
+
generic phrases such as ``the study`` are excluded because they may
|
| 1035 |
+
refer to a cited study.
|
| 1036 |
+
"""
|
| 1037 |
+
value = str(sentence or "")
|
| 1038 |
+
return bool(re.search(
|
| 1039 |
+
r"(?i)\b(?:we|our\s+study|this\s+study|the\s+present\s+study|"
|
| 1040 |
+
r"our\s+analysis|our\s+model|our\s+sample|our\s+results|"
|
| 1041 |
+
r"in\s+this\s+study|in\s+our\s+study)\b",
|
| 1042 |
+
value,
|
| 1043 |
+
))
|
| 1044 |
+
|
| 1045 |
@staticmethod
|
| 1046 |
def _unique_numeric(pattern, evidence, *, integer=False):
|
| 1047 |
values = []
|
|
|
|
| 1200 |
if explicit_nonuse:
|
| 1201 |
return {"term": label, "category": category, "state": "NOT_PERFORMED_OR_LIMITATION"}
|
| 1202 |
|
| 1203 |
+
# The original local-window classifier can miss a long current-study
|
| 1204 |
+
# frame such as ``We tested the hypotheses ... using partial least
|
| 1205 |
+
# squares structural equation modeling (PLS-SEM)`` because the action
|
| 1206 |
+
# verb may sit more than 70 characters before the forbidden term. A
|
| 1207 |
+
# citation appended to the same sentence must not make that performed
|
| 1208 |
+
# method claim externally owned. Detect the explicit current-study
|
| 1209 |
+
# action across the whole pre-term clause before applying the narrower
|
| 1210 |
+
# legacy patterns below.
|
| 1211 |
+
full_before = folded[:match.start()]
|
| 1212 |
+
if re.search(
|
| 1213 |
+
r"\b(?:did\s+not|does\s+not|do\s+not|was\s+not|were\s+not|never)\s+"
|
| 1214 |
+
r"(?!only\b)(?:use|uses|used|employ|employs|employed|conduct|conducts|conducted|"
|
| 1215 |
+
r"perform|performs|performed|estimate|estimates|estimated|test|tests|tested|"
|
| 1216 |
+
r"assess|assesses|assessed|evaluate|evaluates|evaluated|implement|implements|"
|
| 1217 |
+
r"implemented|apply|applies|applied|fit|fits|fitted|model|models|modeled|modelled)\b"
|
| 1218 |
+
r"[^.;:]{0,240}$",
|
| 1219 |
+
full_before,
|
| 1220 |
+
):
|
| 1221 |
+
return {"term": label, "category": category, "state": "NOT_PERFORMED_OR_LIMITATION"}
|
| 1222 |
+
current_study_performed = bool(
|
| 1223 |
+
cls._has_explicit_current_study_anchor(text)
|
| 1224 |
+
and re.search(
|
| 1225 |
+
r"\b(?:use|uses|used|employ|employs|employed|conduct|conducts|conducted|"
|
| 1226 |
+
r"perform|performs|performed|estimate|estimates|estimated|test|tests|tested|"
|
| 1227 |
+
r"assess|assesses|assessed|evaluate|evaluates|evaluated|implement|implements|"
|
| 1228 |
+
r"implemented|apply|applies|applied|ran|run|runs|fit|fits|fitted|model|models|"
|
| 1229 |
+
r"modeled|modelled|control|controls|controlled|correct|corrects|corrected|"
|
| 1230 |
+
r"address|addresses|addressed)\b[^.;:]{0,240}$",
|
| 1231 |
+
full_before,
|
| 1232 |
+
)
|
| 1233 |
+
)
|
| 1234 |
+
|
| 1235 |
performed = bool(
|
| 1236 |
+
current_study_performed
|
| 1237 |
+
or
|
| 1238 |
re.search(
|
| 1239 |
r"\b(?:we|the\s+study|this\s+study|the\s+analysis|our\s+analysis|the\s+model|the\s+authors?)\b[^.;:]{0,70}\b(?:use|uses|used|employ|employs|employed|conduct|conducts|conducted|perform|performs|performed|estimate|estimates|estimated|test|tests|tested|assess|assesses|assessed|evaluate|evaluates|evaluated|implement|implements|implemented|apply|applies|applied|ran|run|runs|fit|fits|fitted|model|models|modeled|modelled|control|controls|controlled|correct|corrects|corrected|address|addresses|addressed)\b[^.;:]{0,70}$",
|
| 1240 |
before,
|
|
|
|
| 1969 |
|
| 1970 |
for _start, _end, sentence in cls._sentence_spans(value):
|
| 1971 |
ownership = cls._quantitative_sentence_ownership(sentence, section_name, known_constructs)
|
| 1972 |
+
current_study_method_frame = (
|
| 1973 |
+
cls._first_performed_unowned_quantitative_term(sentence, forbidden)
|
| 1974 |
+
if strict and cls._has_explicit_current_study_anchor(sentence)
|
| 1975 |
+
else None
|
| 1976 |
+
)
|
| 1977 |
+
method_scope_hybrid = bool(
|
| 1978 |
+
strict
|
| 1979 |
+
and ownership == "external"
|
| 1980 |
+
and current_study_method_frame is not None
|
| 1981 |
+
)
|
| 1982 |
method_scope_unknown = (
|
| 1983 |
strict and ownership == "unknown"
|
| 1984 |
and bool(re.search(r"(?i)method|measure", str(section_name or "")))
|
| 1985 |
and cls._first_performed_unowned_quantitative_term(sentence, forbidden) is not None
|
| 1986 |
)
|
| 1987 |
+
if method_scope_hybrid:
|
| 1988 |
+
violations.append({
|
| 1989 |
+
"type":"unsupported_quantitative_method",
|
| 1990 |
+
"method":current_study_method_frame.get("term"),
|
| 1991 |
+
"claim_state":current_study_method_frame.get("state"),
|
| 1992 |
+
"method_category":current_study_method_frame.get("category"),
|
| 1993 |
+
"preview":sentence.strip()[:180],
|
| 1994 |
+
})
|
| 1995 |
+
# The literature-cited part remains externally owned. Do not
|
| 1996 |
+
# run unrelated metric/role rewrites across this hybrid
|
| 1997 |
+
# sentence; the method violation alone is sufficient to force
|
| 1998 |
+
# bounded regeneration/fail-closed handling.
|
| 1999 |
+
continue
|
| 2000 |
if ownership not in {"study", "result"} and not method_scope_unknown:
|
| 2001 |
continue
|
| 2002 |
if sample_size:
|
|
|
|
| 2140 |
replacements = []
|
| 2141 |
for start, end, sentence in cls._sentence_spans(value):
|
| 2142 |
ownership = cls._quantitative_sentence_ownership(sentence, section_name, known_constructs)
|
| 2143 |
+
current_study_method_frame = (
|
| 2144 |
+
cls._first_performed_unowned_quantitative_term(sentence, forbidden)
|
| 2145 |
+
if strict and cls._has_explicit_current_study_anchor(sentence)
|
| 2146 |
+
else None
|
| 2147 |
+
)
|
| 2148 |
+
method_scope_hybrid = bool(
|
| 2149 |
+
strict
|
| 2150 |
+
and ownership == "external"
|
| 2151 |
+
and current_study_method_frame is not None
|
| 2152 |
+
)
|
| 2153 |
method_scope_unknown = (
|
| 2154 |
strict and ownership == "unknown"
|
| 2155 |
and bool(re.search(r"(?i)method|measure", str(section_name or "")))
|
| 2156 |
and cls._first_performed_unowned_quantitative_term(sentence, forbidden) is not None
|
| 2157 |
)
|
| 2158 |
+
if method_scope_hybrid:
|
| 2159 |
+
diagnostics.append({
|
| 2160 |
+
"type":"unsupported_quantitative_method_removed",
|
| 2161 |
+
"method":current_study_method_frame.get("term"),
|
| 2162 |
+
"claim_state":current_study_method_frame.get("state"),
|
| 2163 |
+
"method_category":current_study_method_frame.get("category"),
|
| 2164 |
+
"preview":sentence.strip()[:180],
|
| 2165 |
+
})
|
| 2166 |
+
replacements.append((start, end, ""))
|
| 2167 |
+
continue
|
| 2168 |
if ownership not in {"study", "result"} and not method_scope_unknown:
|
| 2169 |
continue
|
| 2170 |
revised = sentence
|