@heyramzi/lint 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/bin/heyramzi-comment-length.js +4 -3
  2. package/bin/heyramzi-file-tokens.js +2 -2
  3. package/bin/heyramzi-slop.js +3 -1
  4. package/config/oxlintrc.json +1 -1
  5. package/data/slop/artifacts.js +87 -0
  6. package/data/slop/pattern-parts.js +56 -0
  7. package/data/slop/patterns/appositives-and-sayings.js +81 -0
  8. package/data/slop/patterns/comma-clauses.js +106 -0
  9. package/data/slop/patterns/diagram-and-script-tells.js +71 -0
  10. package/data/slop/patterns/dropped-elements.js +149 -0
  11. package/data/slop/patterns/duration-and-openers.js +102 -0
  12. package/data/slop/patterns/headings-and-laws.js +116 -0
  13. package/data/slop/patterns/numbers-and-runs.js +94 -0
  14. package/data/slop/patterns/openers-and-tails.js +90 -0
  15. package/data/slop/patterns/parallel-shapes.js +100 -0
  16. package/data/slop/patterns/reveals-and-tallies.js +101 -0
  17. package/data/slop/patterns/setups-and-closers.js +83 -0
  18. package/data/slop/patterns/subject-voice.js +67 -0
  19. package/data/slop/patterns/vague-and-flourish.js +102 -0
  20. package/data/slop/patterns/wikipedia-signs.js +103 -0
  21. package/data/slop/patterns.js +62 -0
  22. package/data/slop/phrases-en-closers.js +66 -0
  23. package/data/slop/phrases-en-core.js +185 -0
  24. package/data/slop/phrases-en-wikipedia.js +136 -0
  25. package/data/slop/phrases-fr.js +152 -0
  26. package/data/slop/shapes-density.js +81 -0
  27. package/data/slop/shapes-framing.js +101 -0
  28. package/data/slop/shapes-reveals.js +88 -0
  29. package/data/slop/title-tells.js +8 -0
  30. package/data/slop/words-en.js +178 -0
  31. package/data/slop/words-fr.js +101 -0
  32. package/data/slop-words.js +18 -2576
  33. package/data/vendor/heading-words.js +6 -0
  34. package/inline-path.js +2 -0
  35. package/package.json +1 -1
  36. package/slop.js +4 -2
  37. package/ui-rules.js +4 -2
@@ -0,0 +1,83 @@
1
+ /** Patterns for the setup line before a point and the closer after it. */
2
+ export const patternsSetupsAndClosers = [
3
+ {
4
+ id: "stop-start",
5
+ name: "Stop this, start that",
6
+ // `stop`/`don't` and its answering `start`, across a stop, a comma, an `and`, or a line break. Bounded so it cannot reach across a paragraph and pair two unrelated sentences.
7
+
8
+ // The past tense and the `and` joiner were added 24 Aug 2026, after "So I stopped putting lessons in the room and started putting playbooks in it" shipped into a
9
+
10
+ // scheduled campaign and read clean here: `\bstop\b` does not match `stopped`, and the original required sentence punctuation between the halves. The narrative form is the same slogan wearing a story.
11
+ test: /\b(?:stop(?:ped)?|quit|don'?t|do not)\b[^.!?\n]{0,80}(?:[.!?,;\n]+|\s+and\s+)\s*(?:instead[, ]+)?(?:start(?:ed)?|begin|began)\b/i,
12
+ rule: "A negative imperative answered by its mirror, in either the imperative or the past tense. It is the negation pivot wearing a verb: the first half exists only to set up the swap, and the reader gets a slogan where an instruction or a fact was due. Write the instruction. The thing that stopped is implied by the thing that happens now.",
13
+ example:
14
+ '"stop adding tools. start taking them away." / "don\'t chase people, start building systems." / "I stopped shipping lessons and started shipping playbooks."',
15
+ },
16
+ {
17
+ id: "imperative-then-watch",
18
+ name: "Do this, watch that",
19
+ // Two imperatives back to back, the second one a spectator verb.
20
+
21
+ // Bounded to one sentence each side so it cannot reach across a paragraph, and the second verb list stays short: "See the reference" is documentation.
22
+ test: /(?:^|[.!?]["'’”)\]]?\s+|\n)(?:#{1,6}[ \t]+)?(?:Ask|Try|Take|Give|Tell|Show|Send|Pull|Type|Paste|Write|Read|Open|Run|Click|Drop|Post|Push|Put|Buy|Hire)\b[^.!?\n]{2,70}[.!?]\s+(?:Now\s+|Then\s+)?(?:[Ww]atch|[Nn]otice|[Oo]bserve)\b[^.!?\n]{2,70}[.!?]/,
23
+ rule: "An order to the reader followed by an order to admire the result. The second sentence states no fact: it tells the reader what to feel about the first, and the pair reads as a trailer for the thing instead of the thing. Write what happens, in one sentence, with a subject.",
24
+ example:
25
+ '"Ask a model for one line. Watch what it pays out instead." / "Run the import. Watch the duplicates disappear."',
26
+ },
27
+ {
28
+ id: "numeric-anaphora",
29
+ name: "Number, number, number",
30
+ // Two or more number-led noun phrases in a row, comma separated.
31
+
32
+ // Bounded to three words a phrase so it cannot reach across a clause and pair two unrelated counts, and to 3 digits a number,
33
+
34
+ // because "15 rue des Halles, 75001 Paris" is a postal address and failed 3 legal pages on the CBAM site, 15 Sep 2026.
35
+ test: /\b(?:one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|\d{1,3})\s+(?:[a-z][a-z-]{2,}\s+){0,2}[a-z][a-z-]{2,},\s+(?:and\s+)?(?:one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|\d{1,3})\s+(?:[a-z][a-z-]{2,}\s+){0,2}[a-z][a-z-]{2,}\b/i,
36
+ rule: "A run of counted noun phrases standing in for a sentence. The numbers make it feel measured while the line states nothing, and the cadence is doing the work a verb should. Write the sentence, or set the counts as a list where the reader can actually scan them.",
37
+ example:
38
+ '"one form, five fields, and the routing behind it." / "three jobs, four questions, twelve answers."',
39
+ },
40
+ {
41
+ id: "faux-insight-setup",
42
+ name: "What nobody tells you",
43
+ // A setup noun ("what", "the part", "the math") handed to an ignorant crowd and a verb of telling.
44
+
45
+ // One-directional: an ordinary "nobody tells me what to do" has no setup noun in front of it and does not match.
46
+
47
+ // `skips` is deliberately absent, because "the pass everyone skips" is a real course term rather than a flourish.
48
+ test: /\b(?:what|the\s+[a-zà-ÿ]+)\s+(?:nobody|no one|hardly anyone|most people|everyone|everybody)\s+(?:tells|warns|shows|mentions|misses|runs|talks about|leaves out|gets wrong)\b/i,
49
+ rule: "A setup clause that casts the writer as the one person who sees it, then delivers an ordinary claim. It needs an implied crowd to be wrong, and it opens on the writer's vantage rather than on the reader's problem. Cut the setup and let the claim carry itself; if the claim cannot stand without the crowd, it was not worth the sentence.",
50
+ example:
51
+ '"Here is what nobody tells you before you upgrade." / "Here is the math nobody runs." / "The part everyone misses: distribution is the moat."',
52
+ },
53
+ {
54
+ id: "removal-closer",
55
+ name: "That is the part we took out",
56
+ // The decidable half of the `deadpan-closer` shape: a demonstrative, a setup noun and a past-tense removal verb.
57
+
58
+ // Ramzi, 11 Sep 2026, on this exact line shipping to the Sitesmiths page: "that is the part we is AI slop".
59
+ test: /\bthat(?:'s|\s+is)\s+the\s+(?:part|bit|piece|step|thing)\s+(?:we|i|they)\s+(?:took|cut|removed|dropped|deleted|killed|skipped)\b/i,
60
+ rule: 'A flat demonstrative parked after a list, pointing back at it instead of stating the new thing. The reader has to hold the list in their head to decode `that`, and the sentence adds no fact. Name what you do: "So we build yours before we ask you for anything."',
61
+ example:
62
+ '"That is the part we took out." / "That\'s the bit we cut." / "That is the step we removed."',
63
+ },
64
+ {
65
+ id: "where-it-came-from",
66
+ name: "Where the X came from",
67
+ // Heading and topic-sentence shape. `Where` plus a subject plus `came from`
68
+ // or `comes from`, inside one clause.
69
+ test: /\bwhere\s+[^.!?\n]{2,60}\b(?:came|comes|come)\s+from\b/i,
70
+ rule: 'A heading that promises an origin story and then delivers the fact plainly anyway. It buys a beat of suspense the reader did not ask for. Name the fact: "the 104 hours" rather than "where the 104 hours came from".',
71
+ example: '"Where the 104 hours came from" / "Where the gap comes from"',
72
+ },
73
+ {
74
+ id: "hollow-actually",
75
+ name: "That actually",
76
+ // `actually` inside a relative clause, or standing in a heading. Both are pure intensifier: the word is doing the work the noun should.
77
+
78
+ // Bare `actually` elsewhere can be load-bearing (a measured result against a predicted one), so it is deliberately not matched.
79
+ test: /\b(?:that|who|which|people|nobody|somebody)\s+actually\b|^#{1,6}\s+[^\n]*\bactually\b/im,
80
+ rule: "`actually` in a relative clause is the writer insisting rather than the noun earning it. Traffic that actually sells is just traffic that sells, and if the contrast matters, name the thing it beats. In a heading it is always filler.",
81
+ example: '"get traffic that actually sells" / "a thumbnail people actually click"',
82
+ },
83
+ ];
@@ -0,0 +1,67 @@
1
+ /** Patterns where the wrong thing is the subject: the seller, a page, a tool or a place doing a person's job. */
2
+ export const patternsSubjectVoice = [
3
+ {
4
+ id: "seller-as-proof",
5
+ name: "The same agents we run our agency on",
6
+ test: /\bthe\s+same\s+(?:[\w-]+\s+){1,2}(?:that\s+)?(?:we|I)\s+(?:run|use|built|build)\b|\b(?:we|I)\s+run\s+(?:our|my|[A-Z][\w-]+)\s+(?:own\s+)?(?:[\w-]+\s+)?on\b|\b(?:our|my)\s+own\s+[\w-]+\s+(?:runs?|relies|is\s+built)\s+on\b|\bbuilt\s+(?:them|it)\s+for\s+(?:ourselves|us|[A-Z][\w-]+)\s+first\b|\bqui\s+font\s+tourner\s+notre\b|\bles\s+mêmes\s+(?:[\w'’-]+\s+){1,2}qui\s+font\s+tourner\b|\bconstruits?\s+d'abord\s+pour\b/i,
7
+ rule: 'Copy about the seller where the reader wanted their own result: "the agents we run our agency on" makes us the subject and the proof. Lead with what the client gets, in their numbers, and keep "we use it too" for a proof section, if anywhere. Ramzi, 30 Sep 2026, on his own agency\'s hero: "do not be focused about us, be focused on the client."',
8
+ example:
9
+ '"The same agents we run Acme on." / "The AI agents we run our agency on." / "the AI agents our own agency runs on" / "We built them for Acme first" / "Les mêmes agents qui font tourner Acme"',
10
+ },
11
+ {
12
+ id: "unasked-reassurance",
13
+ name: "The studio's own interface never changes",
14
+ test: /\b(?:its|our|the\s+[\w-]+'s|the\s+(?:tool|app|studio|page|site))\s+own\s+(?:interface|ui|chrome|look|design|styles?|colou?rs?)\s+(?:never|doesn't|does\s+not|won't|will\s+not|stays?|remains?)\b|\b(?:never|doesn't|does\s+not|won't|will\s+not)\s+(?:changes?|moves?|restyles?|touch(?:es)?|affects?)\s+(?:with|along\s+with)\s+(?:it|them|this|that|yours?)\b/i,
15
+ rule: 'A line spent on what does NOT happen, answering a worry the reader never had. It costs a sentence and plants the doubt it denies. Cut it, or say what the thing does. Ramzi, 28 Sep 2026, on the Editor OS brand page: "This is slop."',
16
+ example:
17
+ '"The palette your videos are drawn in. The studio\'s own interface never changes with it." / "It never changes with your brand"',
18
+ },
19
+ {
20
+ id: "the-one-x-everyone-y",
21
+ name: "The one map every agent reads",
22
+ test: /\bone\s+(?:[a-z./-]+\s+){1,2}(?:that\s+)?(?:every|each)\s+[a-z-]+\s+(?:reads?|uses?|needs?|looks?(?:\s+(?:at|up))?|checks?|knows?|shares?|sees?|opens?|follows?|runs?|works?\s+from|starts?\s+from)\b/i,
23
+ rule: 'The superlative-plus-universal line: "the one X every Y reads". It sounds like a definition and says nothing a reader can check, and it is the shape a diagram label takes when nobody stopped to say what the thing is. Say what it holds or what it does: "the file that lists your lists and statuses". Ramzi, 29 Sep 2026, on the ClickUp Agent Kit board: "the one xxx every xxx is AI slop."',
24
+ example:
25
+ '"the one map every agent reads" / "one file every tool reads" / "the one thing every founder needs"',
26
+ },
27
+ // The next 2 are Ramzi's, 23 Sep 2026, on course copy that passed this gate.
28
+ {
29
+ id: "object-as-agent",
30
+ name: "The page carries a fact",
31
+ test: /\b(?:page|row|file|task|field|lesson|card|list|slide|number|course|post|step|folder|doc|document|template|space|view|bucket|section)s?\s+(?:all\s+)?(?:carries|carry|holds|hold|owns|own|earns|earn|hands|gives|(?:lives|live|sits|sit)\s+(?:in|on|under|inside))\b|\bgive\s+(?:each|every|the|a)\s+(?:page|row|file|task|card|slide|post)\s+(?:one|a|1)\s+(?:fact|number|thing|claim|proof)\b/i,
32
+ rule: 'A thing doing what only a person does: a page carries, a number lives, a task sits, a row owns. It is a metaphor passed off as a statement, and it hides who acts. Say who does what, as a bare imperative: "Put one thing on each page that only you know", "Put every task in a BATCH bucket".',
33
+ example:
34
+ '"Every page carries one fact only you can supply" / "All the numbers live in 1 file" / "Open tasks all sit in a BATCH bucket" / "Give each page one fact only you can supply"',
35
+ },
36
+ {
37
+ id: "abstract-as-agent",
38
+ name: "This course gets ClickUp set up",
39
+ test: /\b(?:the|this|that|these|your|our|each|every|one|1)\s+(?:[a-z-]+\s+)?(?:course|lesson|module|prompt|guide|video|workshop|skill|playbook|framework|method|system|workspace|week|first|second|latter|former)s?\s+(?:(?:that|which)\s+)?(?:shows|teaches|walks|builds|reads|writes|plans|picks|finds|sets up|hands|helps|lets|runs|makes|turns|keeps|tells|does|handles|delivers|gets (?:you|your|it|them)|takes (?:you|them|your))\b/i,
40
+ rule: 'An abstract thing doing the reader\'s job: the course sets it up, the week runs, the prompt builds. The reader does the work, so make them the subject, as an order: "Set up your ClickUp with this course", "Use The Project Manager skill to plan your week in it". Ramzi, 23 Sep 2026.',
41
+ example:
42
+ '"This course gets your ClickUp set up" / "the week runs inside it" / "This lesson hands you 1 prompt"',
43
+ },
44
+ {
45
+ id: "place-as-person",
46
+ name: "See what the room is working on",
47
+ test: /\b(?:the|this|that|your|our|my)\s+(?:room|community|group|classroom|channel|space|school|newsletter|forum|list)\s+(?:is\s+|are\s+|was\s+|has\s+been\s+)?(?:working on|thinking|building|doing|talking about|discussing|saying|asking|wants|needs|shipping|learning|feels|believes|knows|decides)\b/i,
48
+ rule: 'A place doing a person\'s job: a room does not work, a community does not think. Name the people and what they did: "Read what members posted this week". Ramzi, 23 Sep 2026: "A room does not work. It\'s not a person."',
49
+ example: '"See what the room is working on" / "what the community is building"',
50
+ },
51
+ {
52
+ id: "thing-as-person",
53
+ name: "Automations that save you the afternoon",
54
+ // After "of a", the noun belongs to the one before it: "a review of an app complains" has the review complaining.
55
+ test: /(?<!\bof\s+(?:an?|the|this|that|its|their|your|our)\s+)\b(?:automations?|tools?|apps?|software|agents?|AI|bots?|dashboards?|systems?|process(?:es)?|templates?|workflows?|integrations?|boards?|scripts?|platforms?|CRMs?|inbox(?:es)?|calendars?|method|structure|views?)\s+(?:that\s+|which\s+|will\s+|can\s+|should\s+|finally\s+)?(?:saves?|gives?\s+(?:you|your|them|back)|frees\b|free\s+(?:you|up)|buys?\s+(?:you|back)|remembers?|notices?|decides?|thinks?|knows?|cares?|wants?|worr(?:y|ies)|forgets?|looks?\s+after|takes?\s+care\s+of|understands?|chases?|nags?|complains?|whines?|screams?|yells?|shouts?|begs?|sulks?|panics?|fears?|hates?|loves?)\b/i,
56
+ rule: 'A tool doing a person\'s job: an automation saves an afternoon, a dashboard knows, a system cares. It is a slogan, not a fact. Say what the tool does mechanically, or make the reader the subject: "Each one removes a decision somebody made by hand", "Get the afternoon back". Ramzi, 23 Sep 2026: "automations cannot save an afternoon. They are not a person."',
57
+ example: '"Automations that save you the afternoon" / "the dashboard knows who is late"',
58
+ },
59
+ {
60
+ id: "best-is-the-one",
61
+ name: "The best automation is the one that complains",
62
+ test: /\b[Tt]he\s+(?:best|worst|only|safest|cheapest|fastest|strongest|smartest|(?:most|least)\s+[a-z]+)\s+[a-z][\w'’-]*(?:\s+[\w'’-]+){0,5}?\s+(?:is|are|was)\s+the\s+ones?\s+(?:that|who|which|you|nobody|no\s+one)\b/,
63
+ rule: 'A superlative that crowns "the one that": the best automation is the one that complains, the best client is the one who leaves. It\'s a fortune cookie. The reader nods, then learns nothing they can check, and it often hides a tool acting like a person. Say the fact under it: "Set the automation to email you when a run fails, so a silent stop shows up the same day." Ramzi, 6 Oct 2026, on a LinkedIn draft graded A+: "using an automation as a human... it\'s an anti-pattern."',
64
+ example:
65
+ '"The best automation you own is the one that complains" / "The best client is the one who says no"',
66
+ },
67
+ ];
@@ -0,0 +1,102 @@
1
+ /** Patterns for vague stand-ins, named feelings and flourish endings. */
2
+ export const patternsVagueAndFlourish = [
3
+ {
4
+ id: "indefinite-stand-in",
5
+ name: "Indefinite stand-in",
6
+ // Bounded to the temporal and existential frames, where the indefinite is standing in for a named thing.
7
+
8
+ // An ordinary "anything else you need" is a real quantifier over a real set and passes.
9
+ test: /\b(?:before|until|when|while)\s+(?:anything|nothing|anyone|no\s?one|nobody|something|someone)\s+(?:exists|existed|is\s+(?:built|written|made|there|real)|happens|happened|has\s+happened)\b/i,
10
+ rule: "An indefinite pronoun standing where the thing belongs. It sounds absolute and costs no research, and the reader cannot picture it. Name what does not exist yet: the site, the quote, the first invoice.",
11
+ example:
12
+ '"A quote to sign before anything exists." / "You commit before anything is built." / "They bill you when nothing has happened."',
13
+ },
14
+ {
15
+ id: "recap-opener",
16
+ name: "Recap opener",
17
+ // Bounded to recap verbs, because "Now that you have a big enough audience" is a real condition Ramzi says out loud.
18
+
19
+ // From Vercel's writing guidelines, 13 Oct 2026; its two hits in our corpus were both other people's YouTube scripts.
20
+ test: /(?:^|[.!?]["”]?\s+)(?:Now that (?:we|you)(?:'ve|’ve| have)? (?:covered|seen|explored|learned|understand|know|set up|looked at|gone through|walked through|built)|With (?:this|that|these|everything|all (?:of )?(?:this|that))\b[^.,]{0,30}\b(?:in place|complete|done|set up|out of the way|covered|sorted|settled)|Having (?:covered|set up|explored|looked at|seen|established|walked through)|(?:So far|Up to now|By now),? (?:we|you)(?:'ve|’ve| have))\b/i,
21
+ rule: "A sentence that opens by summing up what the reader just read. They were there. Start on the next point.",
22
+ example:
23
+ '"Now that you understand the mechanics, the next question is which platform." / "With this setup complete, deploy the worker." / "Having covered the basics, we move to pricing." / "So far we\'ve built the form."',
24
+ },
25
+ {
26
+ id: "named-feeling",
27
+ name: "Named feeling",
28
+ // The label form only, where the copula does all the work. A feeling verb acting on something ("it frustrates the client every Friday") carries a fact and passes.
29
+
30
+ // Zero hits across his 60,000 words, counted 10 Sep 2026.
31
+ test: /\b(?:i\s+(?:was|am|felt|feel)\s+|it\s+(?:was|is)\s+(?:a\s+|an\s+)?)(?:truly\s+|really\s+|genuinely\s+)?(?:frustrated|frustrating|exciting|excited|thrilled|thrilling|humbling|humbled|proud|amazed|amazing|grateful|overwhelmed|overwhelming|inspiring|inspired|rewarding|incredible|surreal)\b/i,
32
+ rule: "The emotion named where its cause belongs. Delete the label and write the thing that caused it, and the reader supplies the word themselves. Never invent a cause to fill the gap: go and get the real one, or cut the sentence.",
33
+ example: '"I was frustrated." / "It was exciting." / "It was a humbling experience."',
34
+ },
35
+ {
36
+ id: "clean-resolution",
37
+ name: "Clean resolution",
38
+ // The closing formulas only. The diagnosis behind them, a piece with no
39
+ // loose ends left, is `clean-resolution` in `shapes` and needs a read.
40
+ test: /\b(?:that(?:(?:'|’)?s| is| was) when it (?:all )?clicked|looking back,? i (?:would ?n(?:'|’)?t|would not) change|the lesson here is|that(?:(?:'|’)?s| is) the (?:real )?lesson|i\s+could ?n(?:'|’)?t believe what i was)\b/i,
41
+ rule: "A closing formula that ties every thread off and states the takeaway. It is the sound of a piece built backwards from its conclusion. End on what is still open, or on the fact, and let the reader draw the lesson.",
42
+ example:
43
+ '"And that is when it all clicked." / "Looking back, I would not change a thing." / "The lesson here is simple."',
44
+ },
45
+ {
46
+ id: "vague-magnitude",
47
+ name: "Vague magnitude",
48
+ // A magnitude word standing in for a count. Zero hits across his 60,000 words, counted 10 Sep 2026, so the shape is nobody's ordinary English here.
49
+
50
+ // `hundreds of thousands of X` is excluded: it modifies a real scale.
51
+ test: /\bcountless\s+[a-z]|(?<!\bof\s)\b(?:hundreds|thousands|millions|dozens|scores)\s+of\s+(?!thousands\b|millions\b)[a-z]/i,
52
+ rule: "A magnitude word where a count belongs. It sounds measured and proves nothing, and it costs the writer no research. Write the figure, ugly as it is: 213 hours, 47 clients. If there is no figure, drop the claim.",
53
+ example:
54
+ '"hundreds of hours saved" / "thousands of dollars a month" / "countless integrations"',
55
+ },
56
+ {
57
+ id: "comma-not-pivot",
58
+ name: "Comma pivot",
59
+ // The `negation-pivot` shape was review-only because "is not" is ordinary English.
60
+
61
+ // The comma form is not: a clause, a comma, then `not` plus a determiner is the swap every time, so it is decidable and it fails.
62
+ test: /,\s+(?:and\s+|but\s+)?not\s+(?:a|an|the|just|only|your|our|my|its|his|her|their|this|that)(?![\p{L}\p{N}_])/iu,
63
+ rule: "A line that lands on what the thing is not. The half after the comma exists to make the half before it sound earned, and the reader is handed a contrast where an instruction was due. Write the instruction alone: what it is not is implied by what it is.",
64
+ example:
65
+ '"Open on the state they want, not the app." / "Sell the change, not the tool." / "Write the mechanism, not the slogan."',
66
+ },
67
+ {
68
+ id: "comma-not-pivot-fr",
69
+ name: "Comma pivot (FR)",
70
+ test: /,\s+(?:et\s+)?pas\s+(?:(?:un|une|le|la|les|de|votre|vos|ton|ta|tes|son|sa|ses)(?![\p{L}\p{N}_])|[ld]['’])/iu,
71
+ rule: "La version française du pivot: une virgule, puis ce que la chose n'est pas. Écrire l'instruction seule.",
72
+ example: '"Vendez le changement, pas l\'outil."',
73
+ },
74
+ {
75
+ id: "singular-count-flourish",
76
+ name: "The one X in this Y",
77
+ // `the one <noun>`, a preposition, then a deictic `this` or `that`, which is always the writer pointing at their own artefact.
78
+
79
+ // Bounded to one noun on each side so it cannot reach across a clause.
80
+
81
+ // `the` is deliberately not accepted in the second slot: "the one file in the folder" is a fact about the world and stays legal,
82
+
83
+ // while "in this" and "in that" only ever grade the thing the writer just made.
84
+ test: /\bthe one\s+[a-zà-ÿ][a-zà-ÿ-]+\s+(?:in|of|on|inside|behind|about)\s+(?:this|that)\s+(?:whole\s+|entire\s+|single\s+)?[a-zà-ÿ][a-zà-ÿ-]+/i,
85
+ rule: "A count used as a promotion. It tells the reader that what follows is the only one of its kind, which is the writer grading their own work, and the count is never checkable. It fits any noun, so it spreads: 6 courses closed on the same heading and 5 more files carried the same figure. Name the thing plainly and let it be the only one.",
86
+ example:
87
+ '"The one ask in this Classroom" / "the one rule in this whole system" / "the one thing on this panel that must keep its height"',
88
+ },
89
+ {
90
+ id: "echoed-word",
91
+ name: "Echoed word",
92
+ // The last word of one sentence opening the next, article allowed.
93
+
94
+ // The echo is the whole tell, so no negation is required: `nobody drops the ball. the ball has nowhere to sit.` is the same figure as `you are the glue.
95
+
96
+ // glue does not scale.`
97
+ test: /\b([a-zà-ÿ]{3,})\s*[.!?]["'’”)\]]?[\s\n]+(?:the |a |an |it |that )?\1\b/i,
98
+ rule: "The last word of a sentence handed straight to the next one as its subject. It reads as a written figure rather than as speech, and the second sentence almost always spends itself restating the first. Say the second thing without picking the word back up, or cut one of the two.",
99
+ example:
100
+ '"you are the glue. glue does not scale." / "nobody drops the ball. the ball has nowhere to sit."',
101
+ },
102
+ ];
@@ -0,0 +1,103 @@
1
+ import { HERITAGE_HEADING_WORD } from "#lint/data/vendor/heading-words.js";
2
+
3
+ /** Patterns from Wikipedia's signs of AI writing whose signature is literal, English and French. */
4
+ export const patternsWikipediaSigns = [
5
+ // --- Wikipedia:Signs of AI writing, read 2026-09-05 --- The signs from that page whose signature is literal enough to lint.
6
+
7
+ // The ones that are only literal across a whole document (title case, heading levels, boldface density, thematic breaks, mixed apostrophes) are the structural checks in `slop.js`;
8
+
9
+ // the machine residue is `artifacts` in `data/slop/artifacts.js`. Both languages, because the French page (fr:Aide:Identifier l'usage d'une IA générative) names the same signs and a draft can be in either.
10
+ {
11
+ id: "superficial-ing-clause",
12
+ name: "Superficial -ing analysis",
13
+ // A comma, then one of a closed set of participles, then a determiner.
14
+
15
+ // The determiner is what keeps it one-directional: "..., reflecting on it" and "..., ensuring we ship" have no determiner and read clean,
16
+
17
+ // while "..., highlighting the importance of X" is the shape every time.
18
+ test: /,\s+(?:highlighting|underscoring|emphasi[sz]ing|showcasing|reflecting|symboli[sz]ing|cementing|solidifying|illustrating|demonstrating|reinforcing|signal(?:l)?ing|marking|contributing to|ensuring)\s+(?:the|its|his|her|their|a|an|how|why|that)\b/i,
19
+ rule: "A trailing participial clause that restates the sentence as significance instead of adding a fact. The lint half of the `superficial-ing-analysis` shape. Cut the clause, or replace it with the fact it was standing in for.",
20
+ example:
21
+ '"..., highlighting the importance of clear processes." / "..., underscoring its role in the region."',
22
+ },
23
+ {
24
+ id: "superficial-ing-clause-fr",
25
+ name: "Clause participiale creuse",
26
+ // The French form of the same shape. The participle set is closed and each
27
+ // one is rare outside this construction, so the determiner is not needed to
28
+ // keep it one-directional.
29
+ test: /,\s+(?:soulignant|mettant en (?:lumière|avant|évidence)|illustrant|témoignant|reflétant|renforçant|marquant|démontrant|confirmant|consacrant|contribuant|permettant ainsi)\b/i,
30
+ rule: "La même forme en français : une virgule puis un participe présent qui commente la phrase au lieu d'ajouter un fait. Coupez la clause ou remplacez-la par le fait qu'elle remplaçait.",
31
+ example:
32
+ '"..., soulignant l\'importance de la démarche." / "..., témoignant d\'un savoir-faire reconnu."',
33
+ },
34
+ {
35
+ id: "copula-avoidance",
36
+ name: "Copula avoidance",
37
+ // WP:AINOCOPULA. A closed set of verbs standing in for "is".
38
+
39
+ // Each one takes a determiner straight after, which is what an ordinary "he stands as the last witness" does not do often enough to matter,
40
+
41
+ // and "the server operates as a proxy" is the one real use the set deliberately excludes by requiring the abstract nouns after `represents` and `marks`.
42
+ test: /\b(?:serves|stands|functions|acts)\s+as\s+(?:a|an|the)\b|\brepresents\s+a\s+(?:shift|significant|major|departure|milestone|turning)\b|\bmarks\s+(?:a|the)\s+(?:beginning|start|first|shift|turning|new)\b/i,
43
+ rule: 'A verb hired to avoid writing "is". Wikipedia measured a 10% drop in "is" and "are" in academic writing the year the chatbots landed, and this is where it went. Write "is", or write the verb that says what the thing actually does.',
44
+ example:
45
+ '"Gallery 825 serves as the exhibition arm." / "The launch marks a turning point." / "It stands as a vibrant town."',
46
+ },
47
+ {
48
+ id: "copula-avoidance-fr",
49
+ name: "Évitement de la copule",
50
+ test: /\b(?:s'impose|se pose|s'affirme|se présente|se veut)\s+comme\s+(?:un|une|le|la|l')\b|\bconstitue\s+(?:un|une)\s+(?:tournant|jalon|étape|pilier|témoignage|référence)\b/i,
51
+ rule: 'Un verbe employé pour ne pas écrire "est". Écrivez "est", ou le verbe qui dit ce que la chose fait vraiment.',
52
+ example:
53
+ '"La plateforme s\'impose comme une référence." / "Ce lancement constitue un tournant."',
54
+ },
55
+ {
56
+ id: "notability-claim",
57
+ name: "Canned notability claim",
58
+ // WP:OVERATTRIBUTION. A coverage verb, then a quantifier or a media-class
59
+ // word rather than a named outlet. A named source ("featured in Le Monde")
60
+ // has neither and reads clean.
61
+ test: /\b(?:featured|profiled|covered|cited|mentioned|recognized|recognised)\s+in\s+(?:multiple|numerous|several|various|major|leading|top|national|regional|international|independent|respected|widely)\b|\b(?:maintains?|has)\s+an?\s+active\s+(?:social media|online)\s+presence\b/i,
62
+ rule: "Notability asserted by describing the sources rather than saying what they reported. Name the outlet and what it said, or drop the sentence.",
63
+ example:
64
+ '"Profiled in multiple independent outlets." / "He maintains an active social media presence."',
65
+ },
66
+ {
67
+ id: "notability-claim-fr",
68
+ name: "Notoriété affirmée",
69
+ test: /\b(?:cité|mentionné|présenté|couvert|repris)e?s?\s+(?:dans|par)\s+(?:de nombreux|plusieurs|divers|différents|les principaux|de multiples)\b|\bprésence active sur les réseaux\b/i,
70
+ rule: "La notoriété affirmée en décrivant les sources au lieu de dire ce qu'elles ont rapporté. Nommez le média et ce qu'il a écrit, ou supprimez la phrase.",
71
+ example: '"Cité dans de nombreux médias nationaux."',
72
+ },
73
+ {
74
+ id: "challenges-outlook",
75
+ name: "Despite these challenges",
76
+ // WP:FACESCHALLENGES. The formula, not the word. "the challenges are real"
77
+ // has no `despite` in front of it and does not match.
78
+ test: /\bdespite\s+(?:its|these|those|the|such|ongoing)\s+(?:[a-z]+\s+){0,2}(?:challenges|limitations|drawbacks|setbacks|obstacles|hurdles|criticisms?)\b|\bfaces?\s+(?:several|a number of|numerous|various|many|its own)\s+(?:challenges|hurdles|obstacles)\b/i,
79
+ rule: "The closing paragraph that concedes a difficulty in order to end on an upswing. It states nothing checkable in either half. Write the difficulty and what happens because of it, or write neither.",
80
+ example:
81
+ '"Despite its rapid growth, the sector faces several challenges." / "Despite these challenges, it continues to thrive."',
82
+ },
83
+ {
84
+ id: "challenges-outlook-fr",
85
+ name: "Malgré ces défis",
86
+ test: /\bmalgré\s+(?:ces|ses|les|un|de)\s+(?:[a-zà-ÿ]+\s+){0,2}(?:défis|limites|obstacles|difficultés|critiques|freins)\b|\bfait face à\s+(?:plusieurs|de nombreux|divers|certains)\s+(?:défis|obstacles|difficultés)\b/i,
87
+ rule: "Le paragraphe de clôture qui concède une difficulté pour finir sur une note positive. Écrivez la difficulté et sa conséquence, ou aucune des deux.",
88
+ example: '"Malgré ces défis, le secteur continue de croître."',
89
+ },
90
+ {
91
+ id: "formulaic-heading",
92
+ name: "Formulaic section heading",
93
+ // The Wikipedia section on undue emphasis and the fr page's "Conclusion / Héritage" sign. A whole markdown heading, anchored at both ends, so a sentence that happens to contain "conclusion" does not match.
94
+
95
+ // The bare-noun heading is included.
96
+ test: new RegExp(
97
+ `^#{1,6}\\s*(?:awards? and recognition|recognition and awards?|challenges and (?:${HERITAGE_HEADING_WORD}|opportunities|future prospects)|future (?:outlook|prospects|directions)|${HERITAGE_HEADING_WORD} and impact|impact and ${HERITAGE_HEADING_WORD}|significance and ${HERITAGE_HEADING_WORD}|conclusion|${HERITAGE_HEADING_WORD}|overview|key takeaways?|final thoughts?|héritage|conclusion et perspectives|perspectives|perspectives d'avenir|prix et distinctions|défis et perspectives|points clés|en résumé)\\s*$`,
98
+ "im",
99
+ ),
100
+ rule: "A section heading from the outline a model reaches for when it has run out of subject. The section under it is always summary rather than fact. Delete the section, or retitle it after the one thing it actually says.",
101
+ example: '"## Awards and recognition" / "## Future Outlook" / "## Conclusion" / "## Héritage"',
102
+ },
103
+ ];
@@ -0,0 +1,62 @@
1
+ import { patternsSubjectVoice } from "#lint/data/slop/patterns/subject-voice.js";
2
+ import { patternsRevealsAndTallies } from "#lint/data/slop/patterns/reveals-and-tallies.js";
3
+ import { patternsOpenersAndTails } from "#lint/data/slop/patterns/openers-and-tails.js";
4
+ import { patternsCommaClauses } from "#lint/data/slop/patterns/comma-clauses.js";
5
+ import { patternsSetupsAndClosers } from "#lint/data/slop/patterns/setups-and-closers.js";
6
+ import { patternsNumbersAndRuns } from "#lint/data/slop/patterns/numbers-and-runs.js";
7
+ import { patternsVagueAndFlourish } from "#lint/data/slop/patterns/vague-and-flourish.js";
8
+ import { patternsAppositivesAndSayings } from "#lint/data/slop/patterns/appositives-and-sayings.js";
9
+ import { patternsWikipediaSigns } from "#lint/data/slop/patterns/wikipedia-signs.js";
10
+ import { patternsHeadingsAndLaws } from "#lint/data/slop/patterns/headings-and-laws.js";
11
+ import { patternsParallelShapes } from "#lint/data/slop/patterns/parallel-shapes.js";
12
+ import { patternsDroppedElements } from "#lint/data/slop/patterns/dropped-elements.js";
13
+ import { patternsDurationAndOpeners } from "#lint/data/slop/patterns/duration-and-openers.js";
14
+ import { patternsDiagramAndScriptTells } from "#lint/data/slop/patterns/diagram-and-script-tells.js";
15
+
16
+ /**
17
+ * The sentence shapes that CAN be matched exactly, so they are linted rather than left to a human read.
18
+ */
19
+
20
+ /*
21
+ * WHY this list is separate from `shapes`: `shapes` is deliberately unlintable, because a regex for "negation pivot" would fire on every ordinary "is not".
22
+ */
23
+
24
+ /*
25
+ * These are the sub-cases where the shape has a literal signature, and a signature is the only thing that turns a rule into a gate.
26
+ */
27
+
28
+ /*
29
+ * The first two were caught in shipped slides on 24 Aug 2026, in a batch that had passed the word and phrase lists clean,
30
+ */
31
+
32
+ /*
33
+ * which is what a rule with nothing behind it is worth.
34
+ */
35
+
36
+ /*
37
+ * A pattern only belongs here if it can be wrong in one direction only. Anything that needs judgment stays in `shapes` and is enforced by the humanizer read.
38
+ */
39
+
40
+ /*
41
+ * `id`, `name`, `test`, `rule` and `example` are required, and `example` is asserted against `test` by the suite, so a documented case its own regex cannot see fails the build.
42
+ */
43
+
44
+ /*
45
+ * `view` is optional and names which reading of the draft the regex judges: the whole draft by default, or `faqless` for the one check an FAQ is allowed to fail.
46
+ */
47
+ export const patterns = [
48
+ ...patternsSubjectVoice,
49
+ ...patternsRevealsAndTallies,
50
+ ...patternsOpenersAndTails,
51
+ ...patternsCommaClauses,
52
+ ...patternsSetupsAndClosers,
53
+ ...patternsNumbersAndRuns,
54
+ ...patternsVagueAndFlourish,
55
+ ...patternsAppositivesAndSayings,
56
+ ...patternsWikipediaSigns,
57
+ ...patternsHeadingsAndLaws,
58
+ ...patternsParallelShapes,
59
+ ...patternsDroppedElements,
60
+ ...patternsDurationAndOpeners,
61
+ ...patternsDiagramAndScriptTells,
62
+ ];
@@ -0,0 +1,66 @@
1
+ /** The English banned phrases added later: the humanizer catch-up, pause-for-effect closers and first-person softeners. */
2
+ export const phrasesEnClosers = [
3
+ // --- blader/humanizer 3.0.0 catch-up, 21 Sep 2026. Only the distinctive multi-word forms; upstream's bare "linked to", "tied to" and "of course" are ordinary English and stay out.
4
+
5
+ // --- Pause-for-effect closers and sayings that sound deep.
6
+ "read that again",
7
+ "that's the real win",
8
+ "that is the real win",
9
+ "the real question is",
10
+ "what really matters",
11
+ "the deeper issue",
12
+ "the heart of the matter",
13
+ // Staged run-ups before the point.
14
+ "let's explore",
15
+ "let's break this down",
16
+ "here's what you need to know",
17
+ "now let's look at",
18
+ "without further ado",
19
+ "let's be honest",
20
+ "real talk",
21
+ // Arguing with no one, and the fake alternative.
22
+ "i'm not saying",
23
+ "to be clear",
24
+ "don't get me wrong",
25
+ "this is not to say",
26
+ "a tempting approach",
27
+ "one might be tempted",
28
+ "it would be easy to just",
29
+ // Stacked qualifiers.
30
+ "could potentially",
31
+ "might arguably",
32
+ "this is an inference",
33
+ // Send-off inflation and sales language.
34
+ "the future looks bright",
35
+ "exciting times ahead",
36
+ "a step in the right direction",
37
+ "must-visit",
38
+ "active social media presence",
39
+ "several publications",
40
+ // Chatbot residue and guesses passed off as fact.
41
+ "should i continue",
42
+ "it is believed that",
43
+
44
+ // --- First-person softeners.
45
+
46
+ // Ramzi, 21 Sep 2026: "we need to speak with a little bit more confidence and assertiveness, like removing I think and please." He dictates them to Claude 1.8 times per
47
+
48
+ // 1,000 words; in copy they are hedges. Bare "please" stays out: error and legal microcopy needs it. ---
49
+ "i think",
50
+ "i believe",
51
+ "in my opinion",
52
+ "i guess",
53
+ "i suppose",
54
+ "i feel like",
55
+ "i'd say",
56
+ "i would say",
57
+ "i'm not sure",
58
+ "i was wondering",
59
+ "just wanted to",
60
+ "if that makes sense",
61
+ "hopefully",
62
+ "please feel free",
63
+ "please don't hesitate",
64
+ "please note that",
65
+ "please be aware",
66
+ ];