Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions scripts/check_paradigm_shift.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@
"antithesis-period-split",
"high",
),
(
# Restated subject across a period: "The goal is not a prettier policy.
# The goal is ..." / 'the transformation is not "X was added." The
# transformation is ...'. The rejected part may be a quotation that
# contains its own period. Requires the SAME noun after the/this/that
# in both sentences (backreference), which keeps ordinary follow-ups
# ("The key is not rotated. The vault is sealed.") out.
re.compile(
rf"(?i)\b(?:the|this|that)\s+(\w+)\s+(?:is|are|was|were)"
rf"(?:\s+not|n{_AP}?t)\s+"
rf"(?:\"[^\"\n]{{1,80}}\"|“[^”\n]{{1,80}}”|[^.;:\n\"“]{{1,80}})"
rf"\.?[\"”]?\s+(?:the|this|that)\s+\1\s+(?:is|are|was|were)\b"
),
"antithesis-restated-subject",
"high",
),
(
re.compile(
r"(?i)\bnot\s+[a-z]\w+(?:ing|ed|s|ly)?[,.\s]+but\s+[a-z]\w+"
Expand Down
46 changes: 46 additions & 0 deletions tests/test_check_paradigm_shift.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,53 @@ def test_subject_isnt_just_about_x_its_y(self):
self.assertIn("antithesis-subj-not-itis", labels_for(text))


class TestRestatedSubject(unittest.TestCase):
"""'The X is not A. The X is B.' - the subject is repeated instead of 'It is'.
Missed by every other pattern until 2026-10-23 (found in a full-sentence review)."""

def test_quoted_rejected_part(self):
# telemetry-turns-predatory L67 (2026-10-23): the quote has its own period
text = (
'From a threat-modeling perspective, the transformation is not '
'"surveillance was added." The transformation is that the response '
'stage was rewired.'
)
self.assertIn("antithesis-restated-subject", labels_for(text))

def test_unquoted(self):
# iam-blast-radius-architecture-problem L109
text = "The goal is not a prettier IAM policy. The goal is a smaller blast radius."
self.assertIn("antithesis-restated-subject", labels_for(text))

def test_adjective_and_case(self):
# telemetry-turns-predatory L57, with odd spacing and capitals
text = "THE difference is not architectural. The Difference is who acts."
self.assertIn("antithesis-restated-subject", labels_for(text))

def test_curly_quotes(self):
text = "The fix was not “just a patch.” The fix was a redesign."
self.assertIn("antithesis-restated-subject", labels_for(text))

def test_negative_different_subject(self):
text = "The key is not rotated. The vault is sealed."
self.assertNotIn("antithesis-restated-subject", labels_for(text))

def test_negative_no_negation(self):
text = "The goal is a smaller policy. The goal is also cheaper to review."
self.assertNotIn("antithesis-restated-subject", labels_for(text))


class TestExistingPatternsStillWork(unittest.TestCase):
def test_pronoun_period_split_iam_blast(self):
# iam-blast-radius-github-action L16 (2026-10-23): flagged here, but the
# Ralph review dropped this label (fixed in rivassec/research-feed).
text = (
"The problem is not that engineers cannot read JSON. It is that "
"reviewing individual permissions is not the same as reasoning "
"about their consequences."
)
self.assertIn("antithesis-period-split", labels_for(text))

def test_classic_form_a(self):
text = "It is not a bug, it is a feature."
self.assertIn("antithesis-it-not-it-is", labels_for(text))
Expand Down
Loading