@heyramzi/lint 1.34.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.
- package/README.md +52 -5
- package/bin/heyramzi-comment-length.js +5 -6
- package/bin/heyramzi-file-tokens.js +2 -2
- package/bin/heyramzi-old-names.js +72 -0
- package/bin/heyramzi-push-diff.js +16 -5
- package/bin/heyramzi-slop.js +3 -1
- package/bin/heyramzi-ui-rules.js +18 -9
- package/comment-length.js +18 -12
- package/config/oxlintrc.json +3 -4
- package/data/jev-code-bench.js +5 -5
- package/data/slop/artifacts.js +87 -0
- package/data/slop/pattern-parts.js +56 -0
- package/data/slop/patterns/appositives-and-sayings.js +81 -0
- package/data/slop/patterns/comma-clauses.js +106 -0
- package/data/slop/patterns/diagram-and-script-tells.js +71 -0
- package/data/slop/patterns/dropped-elements.js +149 -0
- package/data/slop/patterns/duration-and-openers.js +102 -0
- package/data/slop/patterns/headings-and-laws.js +116 -0
- package/data/slop/patterns/numbers-and-runs.js +94 -0
- package/data/slop/patterns/openers-and-tails.js +90 -0
- package/data/slop/patterns/parallel-shapes.js +100 -0
- package/data/slop/patterns/reveals-and-tallies.js +101 -0
- package/data/slop/patterns/setups-and-closers.js +83 -0
- package/data/slop/patterns/subject-voice.js +67 -0
- package/data/slop/patterns/vague-and-flourish.js +102 -0
- package/data/slop/patterns/wikipedia-signs.js +103 -0
- package/data/slop/patterns.js +62 -0
- package/data/slop/phrases-en-closers.js +66 -0
- package/data/slop/phrases-en-core.js +185 -0
- package/data/slop/phrases-en-wikipedia.js +136 -0
- package/data/slop/phrases-fr.js +152 -0
- package/data/slop/shapes-density.js +81 -0
- package/data/slop/shapes-framing.js +101 -0
- package/data/slop/shapes-reveals.js +88 -0
- package/data/slop/title-tells.js +8 -0
- package/data/slop/words-en.js +178 -0
- package/data/slop/words-fr.js +101 -0
- package/data/slop-words.js +18 -2576
- package/data/vendor/heading-words.js +6 -0
- package/file-tokens.js +26 -13
- package/index.js +4 -6
- package/inline-path.js +2 -0
- package/jev-code.js +1 -1
- package/old-names-mask.js +206 -0
- package/old-names.js +145 -0
- package/package.json +8 -2
- package/push-diff.js +2 -2
- package/repo-rules/packages.js +5 -5
- package/repo-rules.js +2 -2
- package/rules/convex-index-name.js +3 -1
- package/rules/convex-internal-only.js +2 -1
- package/rules/{convex-no-deprecated-crons.js → convex-no-hourly-daily-crons.js} +8 -7
- package/rules/convex-runtime-imports.js +2 -1
- package/rules/{no-legacy-compat.js → no-underscore-reexport.js} +1 -1
- package/slop.js +4 -2
- package/ui-rules-css.js +221 -0
- package/ui-rules-markup.js +130 -0
- package/ui-rules.js +98 -25
- package/vendor/old-names-words.js +9 -0
- package/rules/no-relative-type-imports.js +0 -40
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** Patterns for appositive headings and clauses, sayings that sound deep and narration about a previous version. */
|
|
2
|
+
export const patternsAppositivesAndSayings = [
|
|
3
|
+
{
|
|
4
|
+
id: "appositive-heading",
|
|
5
|
+
name: "The X, participled",
|
|
6
|
+
// Anti-voice 13, the half that is decidable.
|
|
7
|
+
|
|
8
|
+
// The shape is a determiner-led noun phrase, a comma, and a participle or a relative pronoun, with the whole thing being a heading: no main verb ever arrives.
|
|
9
|
+
|
|
10
|
+
// The determiner is the entire tell, and it is what a first attempt on 6 Sep 2026 missed: bare `<noun phrase>, <participle>` matched 25 lines across the UpSys corpus and
|
|
11
|
+
|
|
12
|
+
// almost all were legitimate feature labels ("Your tools, connected", "Automations, switched on", "Price per user, per month, billed yearly").
|
|
13
|
+
|
|
14
|
+
// A label drops the article; a comment on a thing already named keeps it.
|
|
15
|
+
|
|
16
|
+
// Re-measured with the article required: 6 of 6 known instances of the shape, 0 false hits on 24,366 copy units.
|
|
17
|
+
|
|
18
|
+
// Noun phrase widened to 8 words, 8 Sep 2026: at 4 it missed the shipped slide "The same 5 containers for every client, nested 3 deep".
|
|
19
|
+
|
|
20
|
+
// Anchored to a quote, a backtick or a markdown heading on the left and to a quote or a line end on the right,
|
|
21
|
+
|
|
22
|
+
// so it only fires on a whole string.
|
|
23
|
+
|
|
24
|
+
// That is what keeps an ordinary mid-sentence appositive out ("The report, published in 2024, showed a 40% gap"): the sentence runs on past a full stop the character class refuses.
|
|
25
|
+
|
|
26
|
+
// The line end refuses a lowercase continuation, added 6 Sep 2026 after a hard-wrapped source string ("...that doc moves run\n\t\t\t\ton.") read as a heading.
|
|
27
|
+
|
|
28
|
+
// A wrapped sentence resumes in lowercase; a heading's body does not. English only.
|
|
29
|
+
|
|
30
|
+
// French uses the definite article for a generic plural, so "Les automatisations, activées" is the legitimate label "Automations, switched on" and the tell does not survive translation.
|
|
31
|
+
test: /(?:^|["'`]|#{1,6}[ \t])[ \t]*(?:the|a|an)\s+(?:[\w-]+\s+){0,7}[\w-]+,\s+(?:[a-z]+(?:ed|ing)|(?:re|un|over|pre)?(?:built|run|made|done|kept|held|sold|told|given|taken|written|drawn|shown|set|put|sent|spent|left|lost|found|chosen|driven|known|paid|read|met|won|cut|bought|brought|caught|taught|thought)|which|who|that|where|when)\b[^.!?\n"'`<>]{0,70}(?=[ \t]*(?:["'`]|$(?![\r\n][ \t]*[a-z])))/im,
|
|
32
|
+
rule: "A heading built as a thing, a comma and a comment on that thing, with no verb anywhere. It reads as a caption rather than as a claim, and the half after the comma grades the half before it instead of saying what happens. Write the sentence: name the subject and give it a verb.",
|
|
33
|
+
example:
|
|
34
|
+
'"The architecture, decided before it is built" / "The build, run against your real delivery" / "The adoption, which is the part that fails"',
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
id: "appositive-as-clause",
|
|
39
|
+
name: "The X, as we found it",
|
|
40
|
+
// `appositive-fragment`'s `as` half: noun phrase, comma, `as`, pronoun, verb, no main verb
|
|
41
|
+
// ("The cut, as it stands"). 17 Sep 2026: 11 hits in 30,898 files, 0 false.
|
|
42
|
+
test: /(?:^|["'`]|#{1,6}[ \t])[ \t]*(?:the|a|an|your|our|their|his|her|its|my)\s+(?:[\w-]+\s+){0,7}[\w-]+,\s+as\s+(?:we|you|they|it|i|he|she)\s+[\w-]+[^.!?\n"'`<>]{0,70}(?=[ \t]*(?:["'`]|$(?![\r\n][ \t]*[a-z])))/im,
|
|
43
|
+
rule: "A thing, a comma, and an `as` clause that only says when it was seen. No main verb arrives, so it reads as a museum caption rather than a claim, and on a page written to somebody it also drops them into the third person. Write the sentence: say who did what.",
|
|
44
|
+
example:
|
|
45
|
+
'"Your website, as we found it" / "The cut, as it stands" / "The entitlement, as it now stands"',
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// --- blader/humanizer 3.0.0 catch-up, 21 Sep 2026. Four shapes its rebuilt
|
|
49
|
+
// list names that no phrase can hold. ---
|
|
50
|
+
{
|
|
51
|
+
id: "deep-saying",
|
|
52
|
+
test: /\b(?:is|are)\s+the\s+(?:language|currency|architecture|grammar|oxygen)\s+of\b|\bbecomes\s+a\s+trap\b/i,
|
|
53
|
+
name: "Saying that sounds deep",
|
|
54
|
+
rule: "An ordinary point dressed as a hidden truth. Replace the aphorism with the specific claim it stands in for.",
|
|
55
|
+
example:
|
|
56
|
+
'"Symmetry is the language of trust." / "Efficiency becomes a trap when teams forget the human layer."',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "clipped-negative-tail",
|
|
60
|
+
test: /,\s+no\s+(?:guessing|guesswork|fluff|fuss|surprises|hassle|jargon|ceremony|bloat|nonsense|hype|gimmicks)\b/i,
|
|
61
|
+
name: "Clipped negative tail",
|
|
62
|
+
rule: "A comma and a `no X` that denies something nobody claimed, so the sentence sounds larger. State what the thing does.",
|
|
63
|
+
example:
|
|
64
|
+
'"The options come from the selected item, no guessing." / "Setup takes ten minutes, no fluff."',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "dotted-emphasis",
|
|
68
|
+
test: /\b(?:every|each)\.\s+(?:single|one)\b/i,
|
|
69
|
+
name: "Periods between the words",
|
|
70
|
+
rule: "Emphasis by full stop after every word. Write the sentence and let the fact carry the weight.",
|
|
71
|
+
example: '"Every. single. day." / "Each. one. matters."',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "previous-version-narration",
|
|
75
|
+
test: /\b(?:was|were)\s+(?:added|introduced|created)\s+to\s+replace\b|\breplaces?\s+the\s+(?:previous|old|former|earlier|original)\s+(?:approach|version|implementation|behaviou?r|way)\b/i,
|
|
76
|
+
name: "Writing about the previous version",
|
|
77
|
+
rule: "Docs and comments describe what the text replaced instead of the current behavior. Say what it does now; the history lives in the changelog and git.",
|
|
78
|
+
example:
|
|
79
|
+
'"This function was added to replace the previous approach of iterating all items." / "It replaces the old implementation."',
|
|
80
|
+
},
|
|
81
|
+
];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/** Patterns where a comma hangs a clause, participle or aside on the sentence. */
|
|
2
|
+
export const patternsCommaClauses = [
|
|
3
|
+
{
|
|
4
|
+
id: "comma-and-opener",
|
|
5
|
+
// Anchored to the start of the text, a sentence or a paragraph, and limited to ONE word before the comma, so a mid-sentence clause ("he said yes,
|
|
6
|
+
|
|
7
|
+
// and then left") and a real adverbial pair ("On Monday, and only then, we ship") cannot reach it.
|
|
8
|
+
|
|
9
|
+
// The word is letters only, so a date or a number opener does not match.
|
|
10
|
+
|
|
11
|
+
// Not per line: a hard-wrapped Oxford list breaks before "integrators, and autonomous agents" and failed the CBAM about page, 15 Sep 2026.
|
|
12
|
+
test: /(?:^|(?<=[.!?]\s)|(?<=\n\n)|(?<=\n\n[ \t]))[ \t]*[A-Za-z]{2,12},\s+(?:and|but)\b/,
|
|
13
|
+
name: "Comma-and opener",
|
|
14
|
+
rule: "A sentence that spends its first word on a verdict, then hinges on a comma plus and or but before it says anything. The opener carries nothing the next clause does not, and the hinge is where a model hedges before answering. Ramzi, 14 Sep 2026, on a shipped Reddit comment. Delete the opener and start on the answer.",
|
|
15
|
+
example:
|
|
16
|
+
'"Yes, and the honest answer is that a full reconcile is not available." / "No, but the reason matters more than the number." / "Honestly, and this is the part nobody says, it never worked."',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "comma-and-title",
|
|
20
|
+
name: "The rest of the site, and what each part is for",
|
|
21
|
+
// A LINE with no sentence punctuation in it, hinged on a comma plus `and` and a wh-word.
|
|
22
|
+
|
|
23
|
+
// The line shape is what makes it decidable: a heading, a card label or a copy-module value, never a comma mid-sentence in prose,
|
|
24
|
+
|
|
25
|
+
// which ends on a stop and cannot reach the line anchor. The trailing `["'`]?[,;]?` is the source view, where a value ends on `",`.
|
|
26
|
+
|
|
27
|
+
// The wh-word takes a space and a clause after it, so an Oxford list of bare wh-words ("What you do, where, and when") is a list and stays one.
|
|
28
|
+
test: /^[ \t]*(?:#{1,6}[ \t]+|[-*+][ \t]+|\*\*)?[A-Z][^.!?\n]{2,70},[ \t]+(?:and|or)[ \t]+(?:what|where|why|how|who|when|which|whether)[ \t][^.!?\n]{2,70}?["'`]?[,;]?[ \t]*$/m,
|
|
29
|
+
rule: 'A title that names a thing, then bolts a wh-clause on with a comma and `and`. The second half promises an explanation the page was going to give anyway, and the shape says the writer could not choose which half was the subject. Ramzi, 17 Sep 2026, on "The rest of the site, and what each part is for": the sloper should have caught it. Cut to the half that carries the page: "The rest of the site".',
|
|
30
|
+
example:
|
|
31
|
+
'"The rest of the site, and what each part is for" / "## Mail, and where every secret lives" / "The queue, and how a job gets claimed"',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "comma-hung-participle",
|
|
35
|
+
// The decidable half of `comma-hung-fragment`: a LINE that ends on a comma plus one participle.
|
|
36
|
+
|
|
37
|
+
// Any `-ed` word, not a closed list: the list missed "resolved" and shipped it to the CBAM footer, 15 Sep 2026.
|
|
38
|
+
|
|
39
|
+
// Per line, and the whole line is the fragment, so the shape is a headline, a card label or a slide band and never a comma mid-sentence in prose.
|
|
40
|
+
|
|
41
|
+
// `[ \t]` and not `\s`, so a hard-wrapped paragraph cannot hand the anchor the next line.
|
|
42
|
+
test: /^[ \t]*(?:#{1,6}[ \t]+)?[A-Z0-9][^.!?\n]{2,60},[ \t]*(?:[a-zà-ÿ]{3,}ed|back|done|gone|built)[ \t]*[.!?]?[ \t]*$/m,
|
|
43
|
+
name: "Comma-hung participle",
|
|
44
|
+
rule: "A noun, a comma, and a past participle standing in for a sentence. Write the sentence: say who does it and what happens.",
|
|
45
|
+
example:
|
|
46
|
+
'"Sales tax report, filed" / "The week, planned" / "4 to 5 hours a week, back" / "EU carbon border data, resolved."',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "comma-hung-phrase",
|
|
50
|
+
// Per LINE, like `comma-hung-participle`: a label or headline whose second
|
|
51
|
+
// half is a stock verbless tail, or a mirrored "one X, one Y".
|
|
52
|
+
test: /^[ \t]*(?:#{1,6}[ \t]+|[-*+][ \t]+|[\w$]+:[ \t]*["'`])?(?:[A-Za-z][^.!?\n,]{1,50},[ \t]+(?:end to end|start to finish|top to bottom|front to back|beginning to end|step by step|in one place|at a glance|all in one)|(?:[Oo]ne|[Nn]o|[Ee]very|[Ee]ach)[ \t]+[\w'’-]+,[ \t]+(?:one|no|every|each)[ \t]+[\w'’-]+)[ \t]*[.!]?["'`]?[,;]?[ \t]*$/m,
|
|
53
|
+
name: "Comma-hung phrase",
|
|
54
|
+
rule: 'A name, a comma, then a stock tail with no verb ("end to end", "in one place"), or a mirrored "one X, one Y". It reads as a slogan and says nothing a sentence would not. Ramzi, 28 Sep 2026, on "Editor OS, end to end". Write the sentence, or cut to the name.',
|
|
55
|
+
example: '"Editor OS, end to end" / "one camera, one go" / "No setup, no code"',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "guided-tour-promise",
|
|
59
|
+
// A relative clause restating a thing's obvious job as hand-holding.
|
|
60
|
+
|
|
61
|
+
// The verb set is closed and always takes "you" plus a direction word, so ordinary English does not reach it: "it takes you 3 minutes" has no direction word,
|
|
62
|
+
|
|
63
|
+
// and "the wiki that covers one page per space" has no second person.
|
|
64
|
+
test: /\b(?:that|which)\s+(?:walks?|takes?|guides?|shows?)\s+you\s+(?:through|step by step|exactly)\b/i,
|
|
65
|
+
name: "Guided-tour promise",
|
|
66
|
+
rule: "A thing named, then a relative clause that restates its obvious job as hand-holding, usually with a totality claim bolted on. Say what the thing covers and let the reader work out that reading it helps.",
|
|
67
|
+
example:
|
|
68
|
+
'"A wiki that walks you through every piece." / "A guide that takes you step by step through everything you need." / "A dashboard that shows you exactly where you stand."',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "vague-attribution",
|
|
72
|
+
// An unnamed authority as the subject of a reporting verb.
|
|
73
|
+
|
|
74
|
+
// A named source does not match, because the subject has to be the bare category word: "The Gartner report says 40%" and "Researchers and conservationists study the river" both read
|
|
75
|
+
|
|
76
|
+
// clean.
|
|
77
|
+
test: /\b(?:experts?|observers?|analysts?|critics?|researchers?|industry reports?|studies|sources)\s+(?:say|says|argue|argues|believe|believes|note|notes|suggest|suggests|have cited|show|shows)\b/i,
|
|
78
|
+
name: "Vague attribution",
|
|
79
|
+
rule: "An unnamed authority standing in for evidence. Name the source or drop the claim.",
|
|
80
|
+
example:
|
|
81
|
+
'"Experts believe it plays a crucial role." / "Industry reports say the market doubled."',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "wasted-time-confession",
|
|
85
|
+
// Simple past plus a duration. The present perfect that the rule explicitly
|
|
86
|
+
// allows, "I have spent 10 years inside project tools", cannot match,
|
|
87
|
+
// because `spent` there is preceded by `have`.
|
|
88
|
+
test: /\bi\s+(?:spent|wasted|burned|lost)\s+(?:a|an|\d+|one|two|three|six)\s+(?:year|years|month|months|week|weeks|day|days)\b/i,
|
|
89
|
+
name: "Wasted-time confession",
|
|
90
|
+
rule: 'Simple past plus a duration plus the writer\'s own past foolishness, used as a credential or a closer: "I spent a year ...". The reader is asked to trade attention for a stranger\'s regret. Time served that built competence is a different sentence and is fine ("I have spent 10 years inside project tools"): present perfect, and the years bought something.',
|
|
91
|
+
example: '"I spent a year following up on people who were still inside their terms."',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "advisory-aside",
|
|
95
|
+
// The announcement, not the advice. Closed set of openers, each one a fixed phrase nobody writes by accident.
|
|
96
|
+
|
|
97
|
+
// "one thing I'd/I'll/I would" added 14 Sep 2026, Ramzi: "this sentence is pure AI slop 'One thing I'd revisit'" — the same self-assigned-critique announcement as "one thing worth",
|
|
98
|
+
|
|
99
|
+
// one pronoun over.
|
|
100
|
+
test: /\b(?:one thing worth|a quick note before|worth flagging|one last thing that|a small tip before|one thing (?:i'd|i'll|i would|i will))\b/i,
|
|
101
|
+
name: "Advisory aside",
|
|
102
|
+
rule: "The writer awarding themselves a bonus tip and announcing it before giving it. The announcement is pure throat-clearing and it grades the advice on the reader's behalf. State the thing.",
|
|
103
|
+
example:
|
|
104
|
+
'"One thing worth checking once: your own payment terms." / "Worth flagging: the Edge key expires."',
|
|
105
|
+
},
|
|
106
|
+
];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** Patterns read off a diagram caption and a script about spotting slop. */
|
|
2
|
+
export const patternsDiagramAndScriptTells = [
|
|
3
|
+
// --- Next 4: Ramzi, 28 Sep 2026, on the "Editor OS" diagram: "This is AI slop." Every line he
|
|
4
|
+
// pointed at passed this gate with no hit. ---
|
|
5
|
+
{
|
|
6
|
+
id: "not-because-pivot",
|
|
7
|
+
name: "Not because X. Because Y.",
|
|
8
|
+
// `negation-pivot` with the reason swapped in for the thing, but decidable: a sentence opening
|
|
9
|
+
// on "Not because" never opens anything else. The comma form is the same swap.
|
|
10
|
+
test: /(?:^|[.!?]["'’”)\]]?\s+|\n)(?:#{1,6}[ \t]+)?(?:(?:it|that)(?:'s|’s|\s+is|\s+was)\s+)?not\s+because\b[^.!?\n]{2,120}(?:[.!?]\s+|,\s+|\n)(?:(?:it|that)(?:'s|’s|\s+is|\s+was)\s+)?because\b/i,
|
|
11
|
+
rule: "A wrong reason set up so the right one can knock it down. The reader never held the wrong reason, so the first sentence is there for the drama. Give the real reason as a plain sentence.",
|
|
12
|
+
example:
|
|
13
|
+
'"Not because the editing got faster. Because the editing stopped being a decision." / "It\'s not because the tool is slow, it\'s because nobody owns it."',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "comma-then-slogan",
|
|
17
|
+
name: "Do X, then Y",
|
|
18
|
+
// 2 bare verb phrases hung on ", then", no subject, alone on a line, 7 words max: on the UpSys
|
|
19
|
+
// corpus, 28 Sep 2026, every longer hit was UI copy.
|
|
20
|
+
view: "prose",
|
|
21
|
+
test: /^[ \t]*(?=(?:[^\s\n]+[ \t]+){1,6}[^\s\n]+[ \t]*$)[\p{L}][^.!?\n,]{0,40},\s+then\s+[^.!?\n,]{2,40}[.!]?[ \t]*$/imu,
|
|
22
|
+
rule: 'Two orders on one line with a comma and a `then` between them. It reads like a product promise on a box and says nothing about what happens. Say who does what: "You install the layouts once. Every video after that starts at the record button."',
|
|
23
|
+
example:
|
|
24
|
+
'"Set up once, then press record" / "Record, then hand it over" / "Brief it once, then walk away."',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "in-out-pair",
|
|
28
|
+
name: "X goes in. Y comes out.",
|
|
29
|
+
test: /\b(?:goes|go|went|comes|come|came)\s+in\b[^.!?\n]{0,20}[.!?]\s+[^.!?\n]{2,60}\b(?:comes|come|came|goes|go|went)\s+out\b/i,
|
|
30
|
+
rule: "A machine drawn as a pipe with two ends. It skips everything the reader wanted to know, which is what happens in the middle. Name the steps, or the one step that matters.",
|
|
31
|
+
example:
|
|
32
|
+
'"A raw take goes in. A cut, dressed video comes out." / "Your notes go in. A finished deck comes out."',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "zero-tail",
|
|
36
|
+
name: "X, zero Y",
|
|
37
|
+
// Verbless: noun phrase, comma, `zero` and a noun phrase, or "No X, no Y". A bare `, no` tail
|
|
38
|
+
// only hit status labels ("Parked, no slot yet"), 28 Sep.
|
|
39
|
+
view: "prose",
|
|
40
|
+
test: /^[ \t]*(?:[^.!?\n,]{3,40},\s+zero|no\s+[^.!?\n,]{2,40},\s+no)\s+[\p{L}][^.!?\n,]{1,30}[.!]?[ \t]*$/imu,
|
|
41
|
+
rule: "A thing and a missing thing, joined by a comma, with no verb. It's a tagline, and the tag is always a cost the reader never mentioned. Write the sentence: \"The edit took under an hour and I didn't choose a single layout.\"",
|
|
42
|
+
example:
|
|
43
|
+
'"An hour of editing, zero decisions" / "No brand, no named pieces" / "Two clicks, zero setup"',
|
|
44
|
+
},
|
|
45
|
+
// The next 3 are Ramzi's, 2 Oct 2026, on a CutKit script about spotting AI slop that scored 94 and passed this gate with zero findings.
|
|
46
|
+
{
|
|
47
|
+
id: "once-twice-law",
|
|
48
|
+
name: "Once is craft. Twice is a tell.",
|
|
49
|
+
test: /\b(?:once|one\s+time|a\s+single\s+time)\s+(?:is|=)\s+(?:an?\s+)?[\w-]+(?:\s+[\w-]+)?[.,;]\s*(?:twice|two\s+times|again)\s+(?:is|=)\s+(?:an?\s+)?[\w-]+|\b(?:appears?|happens?|shows?\s+up)\s+once\s+(?:is|are)\s+[^.!?]{2,25},?\s+(?:while|but|and)\s+[^.!?]{2,40}\b(?:repeats?|twice|again)\b[^.!?]{0,20}\b(?:is|are)\b/i,
|
|
50
|
+
rule: 'A two-beat law: "once is X, twice is Y". It sounds like a rule and is a slogan, because the reader can\'t check it and it stands in for the sentence that would say what to look for. Say the thing: what repeats, how often, and what you do about it.',
|
|
51
|
+
example:
|
|
52
|
+
'"Once is craft. Twice is a tell." / "A shape that appears once is craft, while a shape that repeats is a tell"',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "while-antithesis",
|
|
56
|
+
name: "Real writing jumps, while raw AI stays flat",
|
|
57
|
+
view: "prose",
|
|
58
|
+
test: /[^.!?\n]{8,80},\s+while\s+(?:a|an|the|raw|real|good|bad|plain|your)\s+[^.!?\n,]{2,60}\s+(?:is|are|stays?|runs?|ships?|sits?|reads?|hides?|repeats?)\b/i,
|
|
59
|
+
rule: 'A comma, "while", and the mirror clause. The sentence exists to set two things side by side, and the contrast does the work a fact should. One per script is a style. Several is the shape of a model. Split it into two sentences and give each its own evidence, or drop the half that only mirrors the other.',
|
|
60
|
+
example:
|
|
61
|
+
'"Real writing jumps from 3 words to 40, while raw AI stays flat." / "Accuse a good writer and you lose them, while the real slop ships."',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "youd-think-setup",
|
|
65
|
+
name: "You'd think finding one word is proof",
|
|
66
|
+
test: /\byou(?:'|’)?d\s+(?:think|assume|expect)\b|\byou\s+(?:might|may|would)\s+(?:think|assume|expect)\b/i,
|
|
67
|
+
rule: 'A strawman handed to the reader so the next clause can correct it: "you\'d think X, but Y". It sets a belief up only to knock it over and attributes it to the viewer. State what is true, then the evidence. When a real belief needs breaking, name who holds it ("most agencies check for...").',
|
|
68
|
+
example:
|
|
69
|
+
'"You\'d think finding one word is proof, but a shape that appears once is craft." / "You might think it needs a rewrite"',
|
|
70
|
+
},
|
|
71
|
+
];
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { FINITE_AUXILIARY, COUNT, SENTENCE_OPEN } from "#lint/data/slop/pattern-parts.js";
|
|
2
|
+
|
|
3
|
+
/** Patterns where the sentence drops the element a reader would check. */
|
|
4
|
+
export const patternsDroppedElements = [
|
|
5
|
+
{
|
|
6
|
+
id: "interrogative-heading",
|
|
7
|
+
name: "What the answer is built on",
|
|
8
|
+
// A whole line opening on an interrogative and ending on a verb, with no question mark: the heading asks the section's own question instead of naming what is in it.
|
|
9
|
+
|
|
10
|
+
// Distinct from `question-heading`, which needs the `?`.
|
|
11
|
+
|
|
12
|
+
// The trailing lookahead drops a hard-wrapped paragraph whose next line continues in lower case, and a line that ends in `.` is a sentence,
|
|
13
|
+
|
|
14
|
+
// not a heading, so it never reaches the anchor.
|
|
15
|
+
test: /^[ \t]*(?:#{1,6}[ \t]+)?(?:\d+ • )?(?:What|How|Where|Which|Why|When|Who)[ \t]+(?:[A-Za-zà-ÿ0-9][\w'’-]*[ \t]+){1,9}[a-zà-ÿ0-9][\w'’-]*[ \t]*$(?!\n[ \t]*[a-zà-ÿ])/m,
|
|
16
|
+
rule: 'A heading that restates the section as a question the reader did not ask, then leaves the answer to the body. Ramzi, 15 Sep 2026, on the CBAM calculator page, and 23 Sep 2026 on a Skool lesson card titled "What you take away from lesson 4". Name what the section holds.',
|
|
17
|
+
example: '"What the answer is built on" / "What Annex I covers" / "How the dataset is built"',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "positioned-noun-heading",
|
|
21
|
+
name: "The X beside the Y",
|
|
22
|
+
// A whole line made of two noun phrases and a position word, with no verb.
|
|
23
|
+
|
|
24
|
+
// Per line and anchored both ends, like `the-x-that-y-headline`, so the same words inside a sentence ("the rule beside the field is cited") are untouched.
|
|
25
|
+
test: /^[ \t]*(?:#{1,6}[ \t]+)?The[ \t]+[a-zà-ÿ-]+[ \t]+(?:beside|behind|beneath|below|above|under|over|inside|within|alongside|around|between|next[ \t]+to|across[ \t]+from|in[ \t]+front[ \t]+of|on[ \t]+top[ \t]+of)[ \t]+(?:the|its|your|each)[ \t]+[a-zà-ÿ-]+[ \t]*[.!?]?[ \t]*$/m,
|
|
26
|
+
rule: "A card title that places one noun next to another and stops, with no verb and nothing asserted. Ramzi, 15 Sep 2026, on the CBAM home cards. Say what the thing does where it sits.",
|
|
27
|
+
example: '"The rule beside the field" / "The source behind each number"',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "the-x-follows",
|
|
31
|
+
name: "When X changes, the Y follows",
|
|
32
|
+
// A "when" clause, a comma, then the product as the subject of a bare reaction verb that ends the sentence.
|
|
33
|
+
|
|
34
|
+
// The verb must close the sentence, so "the API follows the weekly price" or "and the API follows it" passes.
|
|
35
|
+
test: /\bwhen[ \t]+[^,.!?\n]{2,50},[ \t]*(?:the|our|your)[ \t]+[\w'’-]+[ \t]+(?:follows|adapts|keeps[ \t]+up|keeps[ \t]+pace|responds|moves[ \t]+with[ \t]+it)[ \t]*[.!]/i,
|
|
36
|
+
rule: 'A headline that casts a regulator as an actor and the product as its shadow, which reads as a slogan and never says what the product is. Ramzi, 16 Sep 2026, on the CBAM home headline: "too pompous". Name the category: what it is and what it is for.',
|
|
37
|
+
example:
|
|
38
|
+
'"When Brussels changes CBAM, the API follows." / "When the market moves, our data keeps up."',
|
|
39
|
+
},
|
|
40
|
+
// --- 17 Sep 2026. Three shapes named off one cold sales email, which opened "42 skills across seven folders" about a repo holding 26.
|
|
41
|
+
|
|
42
|
+
// One fault under all three: the sentence drops the element that would let a reader check it.
|
|
43
|
+
|
|
44
|
+
// The count has no verb, so nobody counted; the promise names the drawback and not the mechanism; the benefit names the reader's absence and not the work.
|
|
45
|
+
|
|
46
|
+
// Ramzi the same day, setting how they had to be written: "think about the heuristic of the grammar so that the rule is strong enough",
|
|
47
|
+
|
|
48
|
+
// "understand the first principles of the grammatical choice to fix the root cause".
|
|
49
|
+
{
|
|
50
|
+
id: "bare-count-clause",
|
|
51
|
+
name: "N things across M things",
|
|
52
|
+
/**
|
|
53
|
+
* Two counts, a distributive preposition, and no verb: a measurement parked where the predicate belongs.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* The preposition set is closed to the three that can ONLY distribute.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* `over` and `in` were measured and dropped: "159 comments and over 7,000 likes" and "28 emails over 10 days" are a comparative and a duration, and neither is this shape.
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/*
|
|
65
|
+
* The clause has to CLOSE on a comma, a stop or "and", which is what leaves a verb out: "627 transcripts across 13 channels put the winner ahead" has a
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/*
|
|
69
|
+
* predicate and passes.
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/*
|
|
73
|
+
* 7 hits across 190 posts and 1,458 files, every one of them the shape.
|
|
74
|
+
*/
|
|
75
|
+
test: new RegExp(
|
|
76
|
+
`(?:^|[.!?:;]["'’”)\\]]?\\**[ \\t]+|\\n)[ \\t]*\\**${COUNT}[ \\t]+(?:[a-zà-ÿ-]+[ \\t]+){0,2}[a-zà-ÿ]{3,}[ \\t]+(?:across|spanning|throughout)[ \\t]+${COUNT}[ \\t]+(?:[a-zà-ÿ-]+[ \\t]+){0,2}[a-zà-ÿ]{3,}\\**(?=[,.;!?]|[ \\t]+and[ \\t]|[ \\t]*\\n|[ \\t]*$)`,
|
|
77
|
+
),
|
|
78
|
+
rule: 'A count, a second count, and no verb between them. Breadth stands in for the claim, and because nobody is said to have counted anything the number is never checked: the email that named this wrote "42 skills across seven folders" about a repo that shipped 26 in 6. Give the count a doer and a verb, and the reader can test it: "I counted 270 frames on ten channels", "the pack holds 86 face plates in 26 expressions".',
|
|
79
|
+
example:
|
|
80
|
+
'"42 skills across seven folders." / "30 agents across nine business areas." / "270 frames across ten channels, each split into two bands."',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "promise-minus-drawback",
|
|
84
|
+
name: "X without the Y",
|
|
85
|
+
/**
|
|
86
|
+
* A noun phrase, a privative `without`, and still no verb. The hair-dye frame: "colour without the ammonia".
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* Three things keep it off ordinary English, and each was measured.
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* The head noun cannot end in `s`, `d` or `ing`, so a finite verb in that slot fails ("The build runs without the dev server").
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* No auxiliary may sit in the filler, so a copula fails ("The output is right without the correction").
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/*
|
|
102
|
+
* And the `without` phrase has to end the clause, so a predicate after it fails ("A feature tour without the claim line is a screen recording").
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* 0 hits across 190 posts and 1,458 files.
|
|
107
|
+
*/
|
|
108
|
+
test: new RegExp(
|
|
109
|
+
`${SENTENCE_OPEN}\\**(?:[Aa]ll[ \\t]+the|[Tt]he|[Aa]|[Aa]n|[Yy]our|[Oo]ur|[Ee]very)[ \\t]+(?:(?!without[ \\t])(?!${FINITE_AUXILIARY}[ \\t])[a-zà-ÿ-]+[ \\t]+){0,3}[a-zà-ÿ]{2,}(?<![sd])(?<!ing)[ \\t]+without[ \\t]+(?:the|a|an|any)[ \\t]+(?:[a-zà-ÿ-]+[ \\t]+){0,2}[a-zà-ÿ-]{3,}\\**[ \\t]*(?=[,.;!?]|[ \\t]*\\n|$)`,
|
|
110
|
+
),
|
|
111
|
+
rule: "A thing offered, then the drawback it does not have, and nothing about how. It is the negation pivot sold as a benefit: the reader learns what is absent, never what is present, and a competitor's weakness carries the sentence instead of the product. Say what it does. If the missing drawback is the point, name the mechanism that removes it.",
|
|
112
|
+
example:
|
|
113
|
+
'"A hair colour change without the ammonia." / "All the power without the complexity." / "The reach of an agency without the retainer."',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "absentee-benefit",
|
|
117
|
+
name: "While you sleep",
|
|
118
|
+
/**
|
|
119
|
+
* The work described as happening in the reader's absence, which is the value the sentence offers instead of a mechanism.
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
* Second person is required throughout, because the state words are ordinary on their own: Ramzi's own 2023 post, "90% is on autopilot until I start the client project",
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
/*
|
|
127
|
+
* is a bounded fact about a system and is spared.
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
* "so you do not have to" needs the clause to END there, for the same reason: the ellipsis IS the fault, and naming the work makes the sentence informative ("so
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* you do not have to open it").
|
|
136
|
+
*/
|
|
137
|
+
test: new RegExp(
|
|
138
|
+
`\\bwhile[ \\t]+you(?:[ \\t]+(?:are|'re|’re))?[ \\t]+(?:sleep\\w*|asleep|rest\\w*|relax\\w*|away|off|out|do[ \\t]+nothing|focus[ \\t]+on|get[ \\t]+on[ \\t]+with)\\b` +
|
|
139
|
+
`|\\bin[ \\t]+your[ \\t]+sleep\\b` +
|
|
140
|
+
`|\\b(?:you|your)\\b[^.!?\\n]{0,60}\\bon[ \\t]+autopilot\\b|\\bon[ \\t]+autopilot\\b[^.!?\\n]{0,60}\\b(?:you|your)\\b` +
|
|
141
|
+
`|\\bset[ \\t]+it[ \\t]+and[ \\t]+forget[ \\t]+it\\b` +
|
|
142
|
+
`|\\bso[ \\t]+you[ \\t]+(?:do[ \\t]*not|don'?t|never|no[ \\t]+longer)[ \\t]+have[ \\t]+to[ \\t]*(?=[.!?,;]|[ \\t]*$)`,
|
|
143
|
+
"i",
|
|
144
|
+
),
|
|
145
|
+
rule: 'The benefit stated as the reader\'s absence. It says when the work happens and never what does it, so the sentence cannot be wrong and cannot be checked, and it carries the passive-income register every ad in the category uses. Ramzi, 17 Sep 2026, on "A calendar takes them while you sleep": the subject is an abstraction and "while you" is slop. Name what runs and what it does: "the booking page writes the event to your calendar".',
|
|
146
|
+
example:
|
|
147
|
+
'"A calendar takes them while you sleep." / "It books the call while you focus on the work." / "Your pipeline runs on autopilot." / "The agent files them so you do not have to."',
|
|
148
|
+
},
|
|
149
|
+
];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { COUNT, DURATION, TAIL_WORD, SENTENCE_OPEN } from "#lint/data/slop/pattern-parts.js";
|
|
2
|
+
|
|
3
|
+
/** Patterns for a duration with no verb and for stop, unlock and cleft openers. */
|
|
4
|
+
export const patternsDurationAndOpeners = [
|
|
5
|
+
// --- 18 Sep 2026.
|
|
6
|
+
|
|
7
|
+
// Named off a demo-page caption, "2 minutes, sound on.", that passed the gate clean: the fault is `comma-hung-fragment`, and that shape is in `shapes` because an imperative and a noun
|
|
8
|
+
|
|
9
|
+
// are the same token to a regex ("sound on" against "turn the sound on").
|
|
10
|
+
|
|
11
|
+
// A duration is the sub-case where the token cannot be a verb, so the missing predicate is decidable.
|
|
12
|
+
{
|
|
13
|
+
id: "bare-duration-clause",
|
|
14
|
+
name: "N minutes, no verb",
|
|
15
|
+
/**
|
|
16
|
+
* A count, a time unit, a comma, and no verb on either side of it.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* A duration noun is never an imperative, so the head has no predicate by construction.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* The comma is what makes it decidable: a real adverbial opener carries its preposition ("In 2 minutes, the site is live") and fails on the first token,
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
* and a real second clause carries a subject and a finite verb, both of which `TAIL_WORD` forbids.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* Three exclusions, each measured over 20,860 files. A tail may not open on a conjunction, because "2 minutes, and you are done" is a coordination; nor on a second count,
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* because "3 years, 2 months" and "Four days, five" are lists; and the clause has to close on a stop rather than a newline,
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
* because markdown prose here is hard-wrapped and a line ending in "four weeks, the" is the middle of a sentence. 12 hits left, every one the shape.
|
|
41
|
+
*/
|
|
42
|
+
test: new RegExp(
|
|
43
|
+
`${SENTENCE_OPEN}\\**${COUNT}[ \\t]+${DURATION}\\**[ \\t]*,[ \\t]+` +
|
|
44
|
+
`(?!(?:and|or|but|so|then|which|that|who|because|if|while|until)[ \\t])` +
|
|
45
|
+
`(?!(?:\\d|${COUNT})(?![\\w'’-]))` +
|
|
46
|
+
`(?:${TAIL_WORD}[ \\t]+){0,2}${TAIL_WORD}\\**[ \\t]*(?=[.!?]|[ \\t]*$)`,
|
|
47
|
+
"i",
|
|
48
|
+
),
|
|
49
|
+
rule: 'A length of time parked where the predicate belongs. The sentence measures something and never says what, so there is nothing in it a reader can agree or disagree with, and the duration reads as a label on a button rather than a fact about the thing. Give the duration a subject and a verb, or fold it into the sentence next to it: "It runs 2 minutes, so turn the sound on", "The call takes 20 minutes".',
|
|
50
|
+
example: '"2 minutes, sound on." / "Three days, tops." / "20 minutes, no slides."',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "stop-opener",
|
|
54
|
+
// Sentence-initial "Stop", bare, no "start"/"begin" pairing (that shape is
|
|
55
|
+
// `stop-start`). Case-sensitive so mid-sentence "stop" ("when you stop
|
|
56
|
+
// guessing") never matches.
|
|
57
|
+
test: /(?:^|[.!?]["'’”)\]]?\s+|\n|["'`])\s*(?:#{1,6}[ \t]+)?Stop\s/,
|
|
58
|
+
name: "Stop opener",
|
|
59
|
+
rule: 'A sentence that opens on the bare command "Stop". It is a listicle hook borrowed wholesale, and it tells the reader what to feel about the advice before giving it. Ramzi, 22 Sep 2026, on a queued Reddit draft: "Stop at the beginning of a sentence is AI slop." Open on the thing itself: "One report per platform is the wrong shape."',
|
|
60
|
+
example:
|
|
61
|
+
'"Stop trying to build one report per platform." / "Stop guessing which query converts."',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "thing-does-it",
|
|
65
|
+
// Sentence-initial "An automation / A script / The checklist" plus a doing verb is narrow on purpose, because product copy says "The app sends" all the time.
|
|
66
|
+
test: /(?:^|[.!?]["'’”)\]]?\s+|\n)(?:#{1,6}[ \t]+)?(?:An?|The)\s+(?:automation|script|workflow|bot|checklist|scenario|macro)\s+(?:creates|makes|sends|assigns|posts|handles|catches|flags|routes|fires|picks|runs|only)\b/,
|
|
67
|
+
name: "Thing as the doer",
|
|
68
|
+
rule: 'A sentence that leads with a tool a person built and gives it the verb, so it reads like a description of a machine with nobody behind it. Ramzi, 30 Sep 2026, on "An automation creates it when a task moves to review and assigns it to someone who isn\'t a founder": Jev scored it 0.73 under `law` and let it through. Put the person first ("We have an automation create it") or make it an instruction ("Have an automation create it").',
|
|
69
|
+
example:
|
|
70
|
+
'"An automation creates it when a task moves to review." / "The script only posts what is approved." / "A checklist catches the rest."',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "unlock-promise",
|
|
74
|
+
name: "Unlock as a promise",
|
|
75
|
+
// A banned word until 15 Oct 2026, when it failed a diagram editor's "Lock or unlock". A lock control takes no object; the promise always has one.
|
|
76
|
+
test: /\bunlock(?:ing)?\s+(?!(?:it|them)\b)[\p{L}\p{N}$€]/iu,
|
|
77
|
+
rule: 'Unlock as the verb of a promise: the reader gets a feeling of access instead of the thing. Say what they can do now: "You see every client\'s margin on one page."',
|
|
78
|
+
example: '"Unlock your team\'s potential" / "unlocking growth"',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "things-that-headline",
|
|
82
|
+
name: "Things that do X",
|
|
83
|
+
// Count-led "N things that X" at a sentence start, or an unpunctuated line opening on a plural noun ("Repos that X"), so "Translate the pages that work" passes.
|
|
84
|
+
|
|
85
|
+
// After a person noun, French `qui` is "who", so a field label ("Personnes qui doivent se connecter") passes.
|
|
86
|
+
test: /(?:(?:^|[.!?]["'’”)\]]?\s+|\n|:\s+|#+\s+)\d{1,3}\s+(?:[\p{L}\w'’-]+\s+){1,4}(?:that|qui)\s+\p{L}|(?:^|\n|:\s+|#+\s+)(?!(?:People|Users|Members|Personnes|Gens|Utilisateurs|Membres)\s)\p{Lu}[\p{L}'’-]*s\s+(?:[\p{L}'’-]+\s+)?(?:that|qui)\s+[^.!?\n]{3,60}(?=\n|$))/u,
|
|
87
|
+
rule: 'A headline built as "N things that do X": the stock listicle frame, with a vague loss or gain hung on "that". Ramzi, 15 Oct 2026, on "5 agent habits that cost you a review": "XXX that XXX should have been slopped." Name what the reader stops suffering: "5 free repos that fix your coding agent" became "Clean up less after your coding agent".',
|
|
88
|
+
example:
|
|
89
|
+
'"5 agent habits that cost you a review" / "7 ClickUp mistakes that break client work" / "Repos that fix your coding agent"',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "what-is-opener",
|
|
93
|
+
// Sentence-initial "What" heading a cleft ("What X is Y", up to 4 words in X) that closes on a stop rather than a question mark within the same clause,
|
|
94
|
+
|
|
95
|
+
// so a real question ("What changed?") never matches. Bounded to 100 chars so it cannot reach across a paragraph break and pair an unrelated later "?".
|
|
96
|
+
test: /(?:^|[.!?]["'’”)\]]?\s+|\n|["'`])\s*(?:#{1,6}[ \t]+)?[Ww]hat(?:'s|\s+(?:[a-z][\w'’-]*\s+){0,4}?(?:is|was|are|were|works|worked|matters|counts|helped|changed|fixed|killed|caught|survives))\b[^?\n]{0,100}[.!:]/,
|
|
97
|
+
name: "What-is cleft opener",
|
|
98
|
+
rule: 'A sentence that opens "What X is/was Y" instead of just stating Y. The cleft exists to sound considered, and it delays the answer by a clause for nothing. Ramzi, 22 Sep 2026, on the same draft: "\'What\' at the beginning of a sentence that is not a question" is also slop. Say the thing: "A single Programmes list works better," not "What works better is a single Programmes list."',
|
|
99
|
+
example:
|
|
100
|
+
'"What works better is a single Programmes list." / "What\'s actually caught stuff for me is checking the output."',
|
|
101
|
+
},
|
|
102
|
+
];
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { SENTENCE_OPEN } from "#lint/data/slop/pattern-parts.js";
|
|
2
|
+
|
|
3
|
+
/** Patterns for question and placeholder headings, definitional leads and hypothetical laws. */
|
|
4
|
+
export const patternsHeadingsAndLaws = [
|
|
5
|
+
{
|
|
6
|
+
id: "question-heading",
|
|
7
|
+
name: "Question as a heading",
|
|
8
|
+
// The fr page's "titres de sections sous forme de question".
|
|
9
|
+
|
|
10
|
+
// A heading is a label, so the question mark is the whole signature and no exception is needed: an FAQ entry is a question in the body,
|
|
11
|
+
|
|
12
|
+
// not in the outline.
|
|
13
|
+
view: "faqless",
|
|
14
|
+
test: /^#{1,6}\s+[^\n?]{5,90}\?\s*$/m,
|
|
15
|
+
rule: "A section heading written as a question the writer then answers. It buys a beat of suspense the reader did not ask for and it makes the outline unscannable. Name the answer in the heading.",
|
|
16
|
+
example: '"## Why does this matter?" / "## Qu\'est-ce que le pilotage ?"',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "inline-header-list",
|
|
20
|
+
name: "Inline-header vertical list",
|
|
21
|
+
// WP:AILIST. A bullet or a number, a bold run, then a colon, on either side of the closing stars because a chatbot writes it both ways.
|
|
22
|
+
|
|
23
|
+
// The colon is required: a list of bold link labels with no colon is ordinary markdown.
|
|
24
|
+
test: /^[ \t]*(?:[-*+•–—]|\d+[.)])\s+\*\*[^*\n]{2,60}(?:[::]\*\*|\*\*\s*[::])/m,
|
|
25
|
+
rule: "The bullet, bold label, colon, sentence shape a chatbot renders by default. It turns prose into a specification nobody asked for and the bold adds no emphasis because every line has it. Write the paragraph, or drop the bold and the colon.",
|
|
26
|
+
example:
|
|
27
|
+
'"- **Route Details**: Starts at Medak and ends at Bhainsa." / "1. **Recommended:** use the shorter form."',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "placeholder-text",
|
|
31
|
+
name: "Unfilled placeholder",
|
|
32
|
+
// WP:AIPLACEHOLDER. A bracketed fill-in-the-blank, a parenthesised instruction to the writer, or a date field the model could not resolve.
|
|
33
|
+
|
|
34
|
+
// `x` and `y` are deliberately not accepted as bracket words, because `- [x]` is a task list.
|
|
35
|
+
|
|
36
|
+
// The bracket cannot follow an identifier or a `)` either, because TypeScript indexes a type that way: `(typeof locales)[number]` read as an unfilled `[number]`, 15 Sep 2026.
|
|
37
|
+
test: /(?<![\w)\]])\[(?:your|insert|inserez|insérez|add|ajouter|name|nom|company|société|client|product|produit|brand|marque|topic|sujet|entertainer|artist|city|ville|date|number|tbd|todo|placeholder)\b[^\]\n]{0,40}\]|\((?:add|insert|ajoutez|insérez)\b[^)\n]{0,40}(?:here|ici|if available|si disponible)\)|\b20\d\d-(?:XX|xx)-(?:XX|xx)\b/i,
|
|
38
|
+
rule: "A template blank the writer never filled in. It is the one sign on this list that is proof rather than evidence: nobody types a bracketed field name into finished copy.",
|
|
39
|
+
example: '"[Entertainer\'s Name]" / "(Add your channel URL here)" / "access-date=2025-xx-xx"',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "definitional-lead",
|
|
43
|
+
name: "Title as a proper noun",
|
|
44
|
+
// A lead sentence that defines its own headline instead of the thing.
|
|
45
|
+
|
|
46
|
+
// The anchor is a line start plus a capital, so an ordinary mid-paragraph "the term refers to a shorthand" does not match.
|
|
47
|
+
|
|
48
|
+
// The second branch is the list article that reads its own title as a noun, which "Paris is a city" is not,
|
|
49
|
+
|
|
50
|
+
// so the branch starts at "List of" and nowhere else.
|
|
51
|
+
test: /^["'“]?[A-Z][^.\n]{2,60}["'”]?\s+(?:refers to|is the term for|désigne)\s+(?:the|a|an|le|la|un|une|l')\b|^(?:List of|Liste des?) [^.\n]{3,60}\s+(?:is|est)\s+(?:a|an|un|une)\b/m,
|
|
52
|
+
rule: 'A lead that treats its own title as a thing in the world and defines the words rather than the subject. Write about the subject: "The catchment area of a hospital is ..." rather than "Catchment area (health) refers to ...".',
|
|
53
|
+
example:
|
|
54
|
+
'"Catchment area (health) refers to the geographic area from which ..." / "List of songs about Mexico is a curated compilation ..."',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "sentence-not-pivot",
|
|
58
|
+
name: "Sentence pivot",
|
|
59
|
+
// The full-stop form of `negation-pivot`, which `comma-not-pivot` cannot see.
|
|
60
|
+
|
|
61
|
+
// A sentence opening on a denial, then a sentence asserting what the thing is, is the same swap with a period doing the comma's work.
|
|
62
|
+
|
|
63
|
+
// The follow-on has to be definitional (`It is`, `That is`) and must not itself be a denial, so "No client asked. It is not urgent either" does not match.
|
|
64
|
+
test: /(?:^|[.!?]\s+)(?:No|Nothing|Nobody)\s+[^.!?\n]{2,70}[.!?]\s+(?:It|That|This)(?:'s|’s|\s+is)\s+(?!not\b)/u,
|
|
65
|
+
rule: "A denial standing in for a statement, split across two sentences. State what the thing is and let the contrast be implied.",
|
|
66
|
+
example:
|
|
67
|
+
'"No video generator made this. It is code." / "Nothing was automated here. That is the point." / "Nobody wrote the copy. It is a template."',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "elliptical-flip",
|
|
71
|
+
name: "This one doesn't",
|
|
72
|
+
// Ramzi, 23 Sep 2026. 3 hits across all first-party copy, 0 in his transcripts.
|
|
73
|
+
test: new RegExp(
|
|
74
|
+
`${SENTENCE_OPEN}(?:(?:This|That|These|Those)(?:\\s+(?:one|ones|part|bit|lesson|course|step|time|video|post|page|room|piece|tool|kind|way|version|week|email))?|Mine|Yours|Ours|Theirs)\\s+(?:(?:does|do|did|is|are|was|were|will|would|can|could|should|has|have|had|isn|aren|wasn|weren|doesn|don|didn|hasn|haven|hadn|wouldn|couldn|shouldn)(?:n['’]t|\\s+not)?|cannot|won['’]t|can['’]t)[.!]`,
|
|
75
|
+
"u",
|
|
76
|
+
),
|
|
77
|
+
rule: 'A broad claim, then a 3-word sentence that flips it with a pointer and a bare verb: "Most of it sits behind an install. This part doesn\'t." It\'s the negation pivot with the second half cut off, and it reads as a reveal. Say what the thing is: "You can do this part without installing anything."',
|
|
78
|
+
example:
|
|
79
|
+
'"Most of what I teach sits behind an install. This part doesn\'t." / "Most tools guess. This one doesn\'t." / "Should an agent write to a client record? Mine does."',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "watching-metadiscourse",
|
|
83
|
+
name: "Telling the reader what they are doing",
|
|
84
|
+
// Interpretive metadiscourse aimed at the artefact in front of the reader.
|
|
85
|
+
|
|
86
|
+
// Scoped to a determiner so "you are watching closely" and "you are seeing fewer replies" do not match: the tell is pointing at the thing on screen and narrating the
|
|
87
|
+
|
|
88
|
+
// act of looking at it.
|
|
89
|
+
test: /\byou(?:'re|’re|\s+are)\s+(?:watching|looking\s+at|seeing|reading)\s+(?:the|a|an|this|that|my|our)\b/i,
|
|
90
|
+
rule: "A line that narrates the reader's own act of looking instead of saying the thing. Cut the frame and keep the fact.",
|
|
91
|
+
example:
|
|
92
|
+
'"You are watching the second attempt." / "You\'re looking at the rebuild." / "You are reading the third draft."',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "once-you-law",
|
|
96
|
+
name: "Hypothetical law",
|
|
97
|
+
// The advisory aside dressed as a rule of nature: a condition the writer invented, then a consequence the reader is told they will feel.
|
|
98
|
+
|
|
99
|
+
// Anchored on "Once you" plus a second-person consequence verb, so an ordinary "Once you have the invoice, send it" does not match.
|
|
100
|
+
test: /\bOnce\s+you(?:'ve|’ve)?\s+(?:have\s+|had\s+)?\w+[^.!?\n]{0,60}?,?\s+you\s+(?:never|no longer|stop|start|realise|realize|understand|see)\b/i,
|
|
101
|
+
rule: "A law about what the reader will feel, invented one clause earlier. Say the fact and let the reader draw the consequence.",
|
|
102
|
+
example:
|
|
103
|
+
'"Once you have seen the restaurant you stop asking what it is." / "Once you understand the model, you never go back." / "Once you\'ve run it, you realise the rest is noise."',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "the-x-that-y-headline",
|
|
107
|
+
name: "The X that Y",
|
|
108
|
+
// A whole line that is nothing but "The [noun phrase] that [clause]", anchored per-line so an ordinary mid-paragraph relative clause ("the file that changed is small") does not match:
|
|
109
|
+
|
|
110
|
+
// a title or a card label sits alone on its line, and this only fires when that entire line is the construction.
|
|
111
|
+
test: /^\s*(?:#{1,6}\s+)?The\s+(?:[A-Za-z][\w'-]*\s+){1,6}that\s+(?:[A-Za-z][\w'-]*\s+){1,8}[A-Za-z][\w'-]*[.!?]?\s*$/im,
|
|
112
|
+
rule: 'A headline or card title built as "The [noun] that [clause]": the noun is named, then a relative clause delivers the payoff. It is a title formula, not a sentence anyone would say, and it repeats itself the moment two of them sit near each other. Ramzi, 14 Sep 2026, on "The fifth ask that cost me nothing": "This is slop. The XX that Is an anti pattern." Say the payoff as the title and drop the frame: "Zero-cost on the fifth ask."',
|
|
113
|
+
example:
|
|
114
|
+
'"The fifth ask that cost me nothing" / "The favor that stopped eating my week" / "The handover that leaves no loose thread"',
|
|
115
|
+
},
|
|
116
|
+
];
|