AI Tells — Complete Blacklist (V3, 2026-09) skill

Scored the way readers read: by density per paragraph, not per word.

by sergebulaev·MIT license·★ 3,059 Stars on the repo·GitHub ↗

Use now

Files of AI Tells — Complete Blacklist (V3, 2026-09)

sergebulaev/main1 file
audit-ai-tells.md
Show the full text194 lines

AI Tells — Complete Blacklist (V3, 2026-09)

Scored the way readers read: by density per paragraph, not per word. One marker in a paragraph is English. Three is a signature. The exceptions that fail on a single hit are listed as such.

Contents

  • Punctuation (regex)
  • Vocabulary markers (density-scored)
  • Phrase blacklist (single hit)
  • Opening-line tells
  • Closing-line tells
  • Structural tells
  • 2026 dos-and-donts blockers (auto-fail)
  • Attention budget
  • Regex patterns (for audit implementation)

Punctuation (regex)

Pattern Why Fix
\u2014 (em dash —) above ~1 per 100 words (1-2 per post) Density tell, not a character tell. GPT-5.4 uses fewer than humans; 23% of top-creator LinkedIn posts contain one (author-relative ratio 1.09, not a tell). 3+ in a short post is the old GPT-4 glue habit Replace only the excess: , or : or ( ) or a rewrite. Never . (fragment stacking is worse)
\u2014 at zero across a 300+ word post that reads as if it wanted one Below the human baseline; reads as dash self-censoring Leave one in
\u2013 (en dash –) between clauses Same family Replace with ,; number ranges stay
-- Same family Replace with , or rewrite
\u201C\u201D (curly quotes) Copy-paste artifact Convert to "

Vocabulary markers (density-scored)

Count per paragraph. 3+ = rewrite the paragraph. 2 = replace the weakest. 1 = leave it. AI vocabulary is the one marker that is consistently reach-negative on LinkedIn in our own corpus (0.74-0.84 author-relative), so this pass stays even though the word list changed.

Durable 2026 set (common words, 2-5x human rate across GPT-5.5 / Claude 4.8 / Gemini 3.1): significant, crucial, notably, particularly, comprehensive, insights, robust, leverage, foster, landscape, nuanced, multifaceted, holistic, streamline, elevate, empower

Older corporate verbs (weaker but still cited by readers): utilize, facilitate, harness, unlock, navigate, seamless, ecosystem

Filler adverbs: fundamentally, essentially, ultimately, crucially, notably, particularly

Grammar markers: sentence-opening "-ing" clause ("Leveraging our data, we..."), nominalisation ("the implementation of"), stacked abstract nouns (alignment / transformation / optimization / synergy)

2026 LinkedIn layer: quietly, "X matters." as a sentence, compound(s), "a signal", "the work", "built different", load-bearing, "doing the heavy lifting", "let that sink in", "that's the real story"

Decaying 2023-24 set (count as one marker each, but do not chase in isolation): delve, tapestry, realm, intricate, journey, paradigm, cultivate

Phrase blacklist (single hit = fix)

Reveal bridges and negative parallelism are scrubbed on one hit because they are reach-negative on LinkedIn (vendor data, 2026):

  • "The result?" / "The catch?" / "The kicker?" (-4.8%)
  • "It's not just X, it's Y" and all 6 negative-parallelism forms (-4.9%)
  • "Stop X, start Y" (-6.7%)
  • "Here's what / Here's how / Here's the thing" (-4.3%)
  • "In today's fast-paced world"
  • "Game-changer"
  • "Deep dive"
  • "Needle-moving"
  • "Move the needle"
  • "At the end of the day"
  • "When it comes to"
  • "In the age of AI"
  • "Paradigm shift"
  • "The hard truth is" / "The uncomfortable reality is"
  • Sincerity announcements as opener or pivot: "let me be honest", "I'll be real", "honestly?", "to be direct", "the honest version is", "honest caveat", "real talk", "full transparency", "unpopular opinion:"

Opening-line tells

  • Any sentence starting with "In today's..."
  • Rhetorical question hooks ("Have you ever wondered...?") — dead on LinkedIn
  • All-caps first line ("THIS CHANGED EVERYTHING.")
  • "Most people don't realize..."
  • "Here's a hard truth..."

Closing-line tells

  • "What do you think?"
  • "Thoughts?"
  • "Agree or disagree?"
  • "Let me know in the comments!"
  • "Tag someone who needs this."

Structural tells

  • Every sentence the same length, machine-flat (expert readers cite structure 36% of the time). Fix only where it reads flat; on LinkedIn sentence-length variance is not a reach lever in either direction (our corpus, within-creator: null to slightly negative), so never manufacture it
  • Staccato stacks: "Short. Punchy. Done.", "Simple. Effective. Easy.", "No X. No Y. Just Z.", "All the X. None of the Y."
  • One-word paragraphs ("Still." "Mostly." "Exactly.")
  • More than 2 standalone fragments (<4 words) in the post
  • Long/short/long/short seesaw across the whole post (mechanical alternation is a humanizer fingerprint)
  • Pseudo-Socratic Q&A ("Why? Because...")
  • Every paragraph 3 lines
  • Perfect parallel structure across a list
  • Stacked or perfectly parallel triads, or 3+ triads in one post ("faster, cheaper, better"). One natural triad is fine
  • Hedging stacks: "perhaps", "might", "could potentially", "it seems" (performed hesitancy runs 2x human rate)
  • Framed confession: a sincerity sentence wrapped around a fact ("I'll be honest, this hurt: we lost the client"). The fact alone is fine
  • Passive voice >10% of clauses
  • Uniformly flat tone with no reaction, no opinion, no concrete detail (the over-scrubbed fingerprint)

2026 dos-and-donts blockers (auto-fail)

Pattern Why Fix
External link in post body -40 to -60% reach penalty; LinkedIn suppresses off-platform traffic Move link to first comment, or summarize the insight inline
"Comment YES if you agree" / "Drop a 🙌" / manufactured CTA Algorithm explicitly detects and demotes engagement bait Ask a specific open question tied to the post's thesis
Press-release / corporate-polished tone Underperforms personal voice 3x; suppresses authenticity signals Rewrite in first person with a concrete moment
Humble-brag opener ("honored to announce…") Failures outperform humble brags 8.5x Lead with what broke or what you learned
Significant edits within first hour of posting Resets the algorithm's initial distribution test Fix typos only in first 60 min; hold structural edits
Posts >3x/week from one author Diminishing returns; cannibalizes own reach Cap at 2-3x/week, same time/days
Company-page-only distribution Employee posts get 6-8x more reach than company pages Publish from personal profile, let company reshare
Pure vanity-metric chasing (likes only) Likes are weakest signal; saves > comments > shares > likes Design for saves: frameworks, templates, data
Announcement openers ("I'm excited to share") Reads as PR; kills voice Replace with the concrete moment that prompted the post

Attention budget

Average user screen attention is 47 seconds (down from 150 seconds in 2004). Post dwell-time target: 31-60 seconds.

Flag any draft that demands >60s of continuous reading without a visual break, list, or fragment sentence — it'll lose the skim layer.

Regex patterns (for audit implementation)

import re

# Verb stems that should match every inflection (-s, -ing, -ed, -es).
# Use a non-capturing inflection suffix so "harnessed", "fostering", "unlocks" all match.
_VERB_STEMS = (
    "leverag", "utiliz", "facilitat", "streamlin", "delv", "navigat",
    "unlock", "harness", "foster", "cultivat", "elevat", "empower",
)
_VERB_GROUP = "|".join(_VERB_STEMS)

# DENSITY-SCORED markers: count hits per paragraph. 3+ = rewrite paragraph, 2 = replace weakest, 1 = leave.
DENSITY_PATTERNS = {
    "vocab_verbs": rf"\b(?:{_VERB_GROUP})(?:e|es|ed|ing|s)?\b",
    "vocab_2026": r"(?i)\b(significant(ly)?|crucial(ly)?|notably|particularly|comprehensive|insights?|robust|landscape|nuanced|multifaceted|holistic|seamless|ecosystem)\b",
    "adverb_filler": r"(?i)\b(fundamentally|essentially|ultimately|arguably|certainly|definitely|undoubtedly)\b",
    "ing_opener": r"(?m)^[\s>*\-]*[A-Z][a-z]+ing\b[^.\n]{0,60},",
    "nominalisation": r"(?i)\bthe \w+(?:tion|sion|ment|ance|ence|ization|isation) of\b",
    "linkedin_2026": r"(?i)\b(quietly|compound(s|ing)?|(a|the) signal|the work|built different|load-bearing|doing the heavy lifting)\b",
    "linkedin_2026_matters": r"(?im)^\w+ matters\.$",
    "decaying_2024": r"(?i)\b(delve|delving|tapestry|realm|intricate|journey|paradigm)\b",
}

# SINGLE-HIT patterns: one match = fix.
AI_PATTERNS = {
    "en_dash": r"\u2013",
    "double_dash": r"--",
    # Reveal bridges (reach-negative on LinkedIn).
    "reveal_bridge": r"(?im)^(the (result|outcome|answer|lesson|catch|kicker|truth)\?|here'?s (what|how|why|the thing)\b|stop \w+[^.\n]{0,40}[.,] ?start \b|plot twist:)",
    "inflated_symbolism": r"(?i)not just \w+, it'?s \w+",
    "neg_parallel": r"(?i)\b(isn'?t|not) (about )?[^,.\n]{1,40}, it'?s (about )?\b",
    # Staccato / forced rhythm.
    "staccato_stack": r"(?m)^(\w+\. ){2,}\w+\.$",
    "one_word_paragraph": r"(?m)^\w+\.$",
    "no_no_just": r"(?i)\bno \w+\. no \w+\. (just|only) \w+",
    "all_none": r"(?i)\ball (of )?the \w+\. none of the \w+",
    "pseudo_socratic": r"(?i)\b(why|how)\? (because|simple)\b",
    # Sincerity announcements as opener or pivot.
    "sincerity_marker": r"(?im)^[\s>*\-]*(let me be (honest|real|direct|clear)|i'?ll be (honest|real|direct)|honestly\?|honest (caveat|version|answer)|the honest (version|answer|truth) is|to be (direct|honest|transparent)|real talk|full transparency|can i be (honest|vulnerable)|not gonna lie|ngl|unpopular opinion)\b",
    # Case-insensitive opener match; allow leading whitespace, bullets, or quote marks.
    "opener_filler": r"(?im)^[\s>*\-]*[\"'\u201c]?(In today's|Have you ever|Most people don't realize|Here's a hard truth)",
    # Generic closing-question CTA: matches "What do you think?" / "What are your thoughts?" / "Thoughts?" / "Your take?" etc.
    "closer_filler": r"(?i)(what (do|are) you (think|your? thought)|what(?:'s| is) your (take|thoughts?)|thoughts\?|agree or disagree\?|let me know in the comments|tag someone|let that sink in|that'?s the real story)",
}

def em_dash_excess(text: str) -> int:
    """Em dashes above the cap (~1 per 100 words, floor 1, ceiling 2 per post). 0 = fine."""
    words = len(text.split())
    cap = max(1, min(2, round(words / 100)))
    return max(0, text.count("\u2014") - cap)

def fragment_count(text: str) -> int:
    """Standalone sentences under 4 words. More than 2 per post = forced rhythm."""
    return sum(1 for s in re.split(r"(?<=[.!?])\s+", text) if 0 < len(s.split()) < 4)

def paragraph_density(paragraph: str) -> int:
    return sum(len(re.findall(p, paragraph)) for p in DENSITY_PATTERNS.values())

# Compile-time sanity: catches inflected and conjugated forms.
assert re.search(DENSITY_PATTERNS["vocab_verbs"], "We harnessed cross-functional synergy.")
assert re.search(DENSITY_PATTERNS["vocab_verbs"], "We fostered alignment.")
assert re.search(DENSITY_PATTERNS["vocab_verbs"], "We unlocked 47% gains.")
assert re.search(DENSITY_PATTERNS["ing_opener"], "Leveraging our data, we cut churn.")
assert re.search(DENSITY_PATTERNS["linkedin_2026"], "The work quietly compounds.")
assert re.search(DENSITY_PATTERNS["linkedin_2026_matters"], "Consistency matters.")
# Inline (?i)/(?m) flags are only legal at position 0 from Python 3.11 on; a mid-pattern
# flag raises re.error at import and takes the whole audit down before it scores anything.
for _name, _pat in {**DENSITY_PATTERNS, **AI_PATTERNS}.items():
    re.compile(_pat)
assert re.search(AI_PATTERNS["closer_filler"], "What are your thoughts?")
assert re.search(AI_PATTERNS["closer_filler"], "What's your take?")
assert re.search(AI_PATTERNS["reveal_bridge"], "The result? We doubled.")
assert re.search(AI_PATTERNS["no_no_just"], "No meetings. No decks. Just code.")
assert re.search(AI_PATTERNS["sincerity_marker"], "Let me be honest: this one hurt.")
assert em_dash_excess("a \u2014 b " * 3 + "word " * 90) == 2
assert em_dash_excess("one \u2014 dash in " + "word " * 120) == 0
1# AI Tells — Complete Blacklist (V3, 2026-09)
2 
3Scored the way readers read: by density per paragraph, not per word. One marker in a paragraph is English. Three is a signature. The exceptions that fail on a single hit are listed as such.
4 
5## Contents
6 
7- Punctuation (regex)
8- Vocabulary markers (density-scored)
9- Phrase blacklist (single hit)
10- Opening-line tells
11- Closing-line tells
12- Structural tells
13- 2026 dos-and-donts blockers (auto-fail)
14- Attention budget
15- Regex patterns (for audit implementation)
16 
17## Punctuation (regex)
18 
19| Pattern | Why | Fix |
20|---|---|---|
21| `\u2014` (em dash `—`) above ~1 per 100 words (1-2 per post) | Density tell, not a character tell. GPT-5.4 uses fewer than humans; 23% of top-creator LinkedIn posts contain one (author-relative ratio 1.09, not a tell). 3+ in a short post is the old GPT-4 glue habit | Replace only the excess: `,` or `:` or `( )` or a rewrite. Never `.` (fragment stacking is worse) |
22| `\u2014` at zero across a 300+ word post that reads as if it wanted one | Below the human baseline; reads as dash self-censoring | Leave one in |
23| `\u2013` (en dash `–`) between clauses | Same family | Replace with `,`; number ranges stay |
24| `--` | Same family | Replace with `,` or rewrite |
25| `\u201C\u201D` (curly quotes) | Copy-paste artifact | Convert to `"` |
26 
27## Vocabulary markers (density-scored)
28 
29Count per paragraph. **3+ = rewrite the paragraph. 2 = replace the weakest. 1 = leave it.** AI vocabulary is the one marker that is consistently reach-negative on LinkedIn in our own corpus (0.74-0.84 author-relative), so this pass stays even though the word list changed.
30 
31**Durable 2026 set (common words, 2-5x human rate across GPT-5.5 / Claude 4.8 / Gemini 3.1):** significant, crucial, notably, particularly, comprehensive, insights, robust, leverage, foster, landscape, nuanced, multifaceted, holistic, streamline, elevate, empower
32 
33**Older corporate verbs (weaker but still cited by readers):** utilize, facilitate, harness, unlock, navigate, seamless, ecosystem
34 
35**Filler adverbs:** fundamentally, essentially, ultimately, crucially, notably, particularly
36 
37**Grammar markers:** sentence-opening "-ing" clause ("Leveraging our data, we..."), nominalisation ("the implementation of"), stacked abstract nouns (alignment / transformation / optimization / synergy)
38 
39**2026 LinkedIn layer:** quietly, "X matters." as a sentence, compound(s), "a signal", "the work", "built different", load-bearing, "doing the heavy lifting", "let that sink in", "that's the real story"
40 
41**Decaying 2023-24 set (count as one marker each, but do not chase in isolation):** delve, tapestry, realm, intricate, journey, paradigm, cultivate
42 
43## Phrase blacklist (single hit = fix)
44 
45Reveal bridges and negative parallelism are scrubbed on one hit because they are reach-negative on LinkedIn (vendor data, 2026):
46 
47- "The result?" / "The catch?" / "The kicker?" (-4.8%)
48- "It's not just X, it's Y" and all 6 negative-parallelism forms (-4.9%)
49- "Stop X, start Y" (-6.7%)
50- "Here's what / Here's how / Here's the thing" (-4.3%)
51- "In today's fast-paced world"
52- "Game-changer"
53- "Deep dive"
54- "Needle-moving"
55- "Move the needle"
56- "At the end of the day"
57- "When it comes to"
58- "In the age of AI"
59- "Paradigm shift"
60- "The hard truth is" / "The uncomfortable reality is"
61- Sincerity announcements as opener or pivot: "let me be honest", "I'll be real", "honestly?", "to be direct", "the honest version is", "honest caveat", "real talk", "full transparency", "unpopular opinion:"
62 
63## Opening-line tells
64 
65- Any sentence starting with "In today's..."
66- Rhetorical question hooks ("Have you ever wondered...?") — dead on LinkedIn
67- All-caps first line ("THIS CHANGED EVERYTHING.")
68- "Most people don't realize..."
69- "Here's a hard truth..."
70 
71## Closing-line tells
72 
73- "What do you think?"
74- "Thoughts?"
75- "Agree or disagree?"
76- "Let me know in the comments!"
77- "Tag someone who needs this."
78 
79## Structural tells
80 
81- Every sentence the same length, machine-flat (expert readers cite structure 36% of the time). Fix only where it reads flat; on LinkedIn sentence-length variance is not a reach lever in either direction (our corpus, within-creator: null to slightly negative), so never manufacture it
82- Staccato stacks: "Short. Punchy. Done.", "Simple. Effective. Easy.", "No X. No Y. Just Z.", "All the X. None of the Y."
83- One-word paragraphs ("Still." "Mostly." "Exactly.")
84- More than 2 standalone fragments (<4 words) in the post
85- Long/short/long/short seesaw across the whole post (mechanical alternation is a humanizer fingerprint)
86- Pseudo-Socratic Q&A ("Why? Because...")
87- Every paragraph 3 lines
88- Perfect parallel structure across a list
89- Stacked or perfectly parallel triads, or 3+ triads in one post ("faster, cheaper, better"). One natural triad is fine
90- Hedging stacks: "perhaps", "might", "could potentially", "it seems" (performed hesitancy runs 2x human rate)
91- Framed confession: a sincerity sentence wrapped around a fact ("I'll be honest, this hurt: we lost the client"). The fact alone is fine
92- Passive voice >10% of clauses
93- Uniformly flat tone with no reaction, no opinion, no concrete detail (the over-scrubbed fingerprint)
94 
95## 2026 dos-and-donts blockers (auto-fail)
96 
97| Pattern | Why | Fix |
98|---|---|---|
99| External link in post body | -40 to -60% reach penalty; LinkedIn suppresses off-platform traffic | Move link to first comment, or summarize the insight inline |
100| "Comment YES if you agree" / "Drop a 🙌" / manufactured CTA | Algorithm explicitly detects and demotes engagement bait | Ask a specific open question tied to the post's thesis |
101| Press-release / corporate-polished tone | Underperforms personal voice 3x; suppresses authenticity signals | Rewrite in first person with a concrete moment |
102| Humble-brag opener ("honored to announce…") | Failures outperform humble brags **8.5x** | Lead with what broke or what you learned |
103| Significant edits within first hour of posting | Resets the algorithm's initial distribution test | Fix typos only in first 60 min; hold structural edits |
104| Posts >3x/week from one author | Diminishing returns; cannibalizes own reach | Cap at 2-3x/week, same time/days |
105| Company-page-only distribution | Employee posts get 6-8x more reach than company pages | Publish from personal profile, let company reshare |
106| Pure vanity-metric chasing (likes only) | Likes are weakest signal; saves > comments > shares > likes | Design for saves: frameworks, templates, data |
107| Announcement openers ("I'm excited to share") | Reads as PR; kills voice | Replace with the concrete moment that prompted the post |
108 
109## Attention budget
110 
111Average user screen attention is **47 seconds** (down from 150 seconds in 2004). Post dwell-time target: 31-60 seconds.
112 
113Flag any draft that demands >60s of continuous reading without a visual break, list, or fragment sentence — it'll lose the skim layer.
114 
115## Regex patterns (for audit implementation)
116 
117```python
118import re
119 
120# Verb stems that should match every inflection (-s, -ing, -ed, -es).
121# Use a non-capturing inflection suffix so "harnessed", "fostering", "unlocks" all match.
122_VERB_STEMS = (
123 "leverag", "utiliz", "facilitat", "streamlin", "delv", "navigat",
124 "unlock", "harness", "foster", "cultivat", "elevat", "empower",
125)
126_VERB_GROUP = "|".join(_VERB_STEMS)
127 
128# DENSITY-SCORED markers: count hits per paragraph. 3+ = rewrite paragraph, 2 = replace weakest, 1 = leave.
129DENSITY_PATTERNS = {
130 "vocab_verbs": rf"\b(?:{_VERB_GROUP})(?:e|es|ed|ing|s)?\b",
131 "vocab_2026": r"(?i)\b(significant(ly)?|crucial(ly)?|notably|particularly|comprehensive|insights?|robust|landscape|nuanced|multifaceted|holistic|seamless|ecosystem)\b",
132 "adverb_filler": r"(?i)\b(fundamentally|essentially|ultimately|arguably|certainly|definitely|undoubtedly)\b",
133 "ing_opener": r"(?m)^[\s>*\-]*[A-Z][a-z]+ing\b[^.\n]{0,60},",
134 "nominalisation": r"(?i)\bthe \w+(?:tion|sion|ment|ance|ence|ization|isation) of\b",
135 "linkedin_2026": r"(?i)\b(quietly|compound(s|ing)?|(a|the) signal|the work|built different|load-bearing|doing the heavy lifting)\b",
136 "linkedin_2026_matters": r"(?im)^\w+ matters\.$",
137 "decaying_2024": r"(?i)\b(delve|delving|tapestry|realm|intricate|journey|paradigm)\b",
138}
139 
140# SINGLE-HIT patterns: one match = fix.
141AI_PATTERNS = {
142 "en_dash": r"\u2013",
143 "double_dash": r"--",
144 # Reveal bridges (reach-negative on LinkedIn).
145 "reveal_bridge": r"(?im)^(the (result|outcome|answer|lesson|catch|kicker|truth)\?|here'?s (what|how|why|the thing)\b|stop \w+[^.\n]{0,40}[.,] ?start \b|plot twist:)",
146 "inflated_symbolism": r"(?i)not just \w+, it'?s \w+",
147 "neg_parallel": r"(?i)\b(isn'?t|not) (about )?[^,.\n]{1,40}, it'?s (about )?\b",
148 # Staccato / forced rhythm.
149 "staccato_stack": r"(?m)^(\w+\. ){2,}\w+\.$",
150 "one_word_paragraph": r"(?m)^\w+\.$",
151 "no_no_just": r"(?i)\bno \w+\. no \w+\. (just|only) \w+",
152 "all_none": r"(?i)\ball (of )?the \w+\. none of the \w+",
153 "pseudo_socratic": r"(?i)\b(why|how)\? (because|simple)\b",
154 # Sincerity announcements as opener or pivot.
155 "sincerity_marker": r"(?im)^[\s>*\-]*(let me be (honest|real|direct|clear)|i'?ll be (honest|real|direct)|honestly\?|honest (caveat|version|answer)|the honest (version|answer|truth) is|to be (direct|honest|transparent)|real talk|full transparency|can i be (honest|vulnerable)|not gonna lie|ngl|unpopular opinion)\b",
156 # Case-insensitive opener match; allow leading whitespace, bullets, or quote marks.
157 "opener_filler": r"(?im)^[\s>*\-]*[\"'\u201c]?(In today's|Have you ever|Most people don't realize|Here's a hard truth)",
158 # Generic closing-question CTA: matches "What do you think?" / "What are your thoughts?" / "Thoughts?" / "Your take?" etc.
159 "closer_filler": r"(?i)(what (do|are) you (think|your? thought)|what(?:'s| is) your (take|thoughts?)|thoughts\?|agree or disagree\?|let me know in the comments|tag someone|let that sink in|that'?s the real story)",
160}
161 
162def em_dash_excess(text: str) -> int:
163 """Em dashes above the cap (~1 per 100 words, floor 1, ceiling 2 per post). 0 = fine."""
164 words = len(text.split())
165 cap = max(1, min(2, round(words / 100)))
166 return max(0, text.count("\u2014") - cap)
167 
168def fragment_count(text: str) -> int:
169 """Standalone sentences under 4 words. More than 2 per post = forced rhythm."""
170 return sum(1 for s in re.split(r"(?<=[.!?])\s+", text) if 0 < len(s.split()) < 4)
171 
172def paragraph_density(paragraph: str) -> int:
173 return sum(len(re.findall(p, paragraph)) for p in DENSITY_PATTERNS.values())
174 
175# Compile-time sanity: catches inflected and conjugated forms.
176assert re.search(DENSITY_PATTERNS["vocab_verbs"], "We harnessed cross-functional synergy.")
177assert re.search(DENSITY_PATTERNS["vocab_verbs"], "We fostered alignment.")
178assert re.search(DENSITY_PATTERNS["vocab_verbs"], "We unlocked 47% gains.")
179assert re.search(DENSITY_PATTERNS["ing_opener"], "Leveraging our data, we cut churn.")
180assert re.search(DENSITY_PATTERNS["linkedin_2026"], "The work quietly compounds.")
181assert re.search(DENSITY_PATTERNS["linkedin_2026_matters"], "Consistency matters.")
182# Inline (?i)/(?m) flags are only legal at position 0 from Python 3.11 on; a mid-pattern
183# flag raises re.error at import and takes the whole audit down before it scores anything.
184for _name, _pat in {**DENSITY_PATTERNS, **AI_PATTERNS}.items():
185 re.compile(_pat)
186assert re.search(AI_PATTERNS["closer_filler"], "What are your thoughts?")
187assert re.search(AI_PATTERNS["closer_filler"], "What's your take?")
188assert re.search(AI_PATTERNS["reveal_bridge"], "The result? We doubled.")
189assert re.search(AI_PATTERNS["no_no_just"], "No meetings. No decks. Just code.")
190assert re.search(AI_PATTERNS["sincerity_marker"], "Let me be honest: this one hurt.")
191assert em_dash_excess("a \u2014 b " * 3 + "word " * 90) == 2
192assert em_dash_excess("one \u2014 dash in " + "word " * 120) == 0
193```
194 

Discussion