sloplint 0.6.0 → 0.8.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +131 -0
- data/README.md +31 -24
- data/docs/SPEC.md +170 -107
- data/lib/sloplint/cli.rb +30 -14
- data/lib/sloplint/engine.rb +10 -10
- data/lib/sloplint/rules.rb +699 -165
- data/lib/sloplint/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0aabc7314411211ed5adcbb5e09bb81a4291b2ffb7748e8d07d15f0150e6c43c
|
|
4
|
+
data.tar.gz: f8c9af283d490a6acb2d4cd6260ccc776e868542be3f1b10066ec6232e856ea0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 508f10329c7698ab4cce5b1dd09902984975ed2baeaa5646b7c288d33ee913f1a8f19e2aba36ab860a77a7b2ea2acde07748ce11db39367958c6ec86af3b4b1f
|
|
7
|
+
data.tar.gz: b75def69bddf045ddab9f99798985cf210ae7b3997266f9199600cf77638b5db8759738879b51949f31945b2ad183706b34654736cfdd7420f292c5442d2445d
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,137 @@
|
|
|
3
3
|
All notable changes to this project are documented here. Format loosely
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.8.0] - 2026-09-15
|
|
9
|
+
|
|
10
|
+
### Changed (breaking)
|
|
11
|
+
|
|
12
|
+
- The four old categories are gone: `rhetorical-tic`, `structure`, `puffery`
|
|
13
|
+
and `hedging`. Nine new categories replace them, each named for the move
|
|
14
|
+
the construct makes: `self-rating` (the writer grades their own prose or
|
|
15
|
+
claim), `closer` (closes by restating or announcing the point), `cadence`
|
|
16
|
+
(rhythm: repetition, parallelism, the long-then-short kicker), `puffery`
|
|
17
|
+
(inflates the subject), `false-correction` (corrects a reading nobody
|
|
18
|
+
offered), `false-concession` (performs balance or candor and gives nothing
|
|
19
|
+
up), `reader-address` (instructs or flatters the reader),
|
|
20
|
+
`borrowed-metaphor` (an engineering term applied to an argument), and
|
|
21
|
+
`punctuation` (the mark itself). Every note, `--select`, and `--ignore` now
|
|
22
|
+
use these nine names; the four old names are gone, not aliased, so a
|
|
23
|
+
script still passing one gets a usage error instead of a silent no-op.
|
|
24
|
+
- The rule `ellipsis-closer` is renamed `bare-auxiliary-closer`. Nothing
|
|
25
|
+
about its pattern, message, or fixtures changed, only the id.
|
|
26
|
+
- `severity` now rates only what the construct costs the prose (`error`,
|
|
27
|
+
`warning`, `info`). A new field, `confidence`, rates how likely a match is
|
|
28
|
+
a false positive (`high`, `medium`, `low`), and a `low` rule replaces what
|
|
29
|
+
used to be `default_on: false` — it stays out of a normal run and needs
|
|
30
|
+
`--strict` or its own id named directly to turn on.
|
|
31
|
+
- `sloplint rules --json` drops the `default_on` field and adds `confidence`
|
|
32
|
+
and `rationale` for every rule. Each note in `check` output also gains a
|
|
33
|
+
`confidence` field alongside `severity`.
|
|
34
|
+
- `--select <category>` no longer turns on that category's low-confidence
|
|
35
|
+
rules. Naming a category now selects only its default-on rules; to run a
|
|
36
|
+
low-confidence rule, pass `--strict` or name the rule's own id.
|
|
37
|
+
- Every rule's severity was re-rated for what the construct costs the prose,
|
|
38
|
+
now that confidence carries the false-positive question on its own. The
|
|
39
|
+
catalog reads 11 `error`, 56 `warning`, 13 `info`. Nothing shipped at
|
|
40
|
+
`error` before: the eleven that do now are the ones that hurt a sentence in
|
|
41
|
+
any register, the puffery family (`puffery-words`, `rich-tapestry`,
|
|
42
|
+
`vital-role`, `impact-noun-vague`,
|
|
43
|
+
`trailing-significance-participle`), the tautology and self-ranking moves
|
|
44
|
+
(`the-x-is-the-x`, `cleanest-x`, `most-honest-x`), the fake balance
|
|
45
|
+
(`two-things-true`, `none-of-this-is-to-say`) and `vague-attribution`.
|
|
46
|
+
Twenty rules moved from `info` to `warning`, among them `em-dash-overuse`:
|
|
47
|
+
one em dash is a style choice, three in a paragraph is the tell, so the
|
|
48
|
+
dense form now costs more than `em-dash`. Any tool that keys off severity
|
|
49
|
+
sees different values for the same text.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- The test suite failed with no locale set, because `spec/plugin_spec.rb`
|
|
54
|
+
read files without forcing UTF-8. Fixed to read as UTF-8 regardless of the
|
|
55
|
+
environment's locale.
|
|
56
|
+
- The README and SPEC quoted an outdated rationale for `no-x-no-y` and an
|
|
57
|
+
outdated sample of `sloplint check` output. Both are corrected and now
|
|
58
|
+
pinned by tests, so a future drift between the docs and the code fails the
|
|
59
|
+
build instead of sitting unnoticed.
|
|
60
|
+
- SPEC's description of the `Rule` model and of the package's file layout
|
|
61
|
+
had fallen out of step with the code. Both are corrected to match.
|
|
62
|
+
- `--help` was missing `count` from its list of note keys, even though a
|
|
63
|
+
note already includes it for rules that tally items. Added.
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
|
|
67
|
+
- A test asserting that no rule id ever matches a category name, since
|
|
68
|
+
`--select` and `--ignore` resolve a name against both and a collision
|
|
69
|
+
would make one shadow the other.
|
|
70
|
+
|
|
71
|
+
## [0.7.0] - 2026-09-15
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
|
|
75
|
+
- `--strict` runs the full catalog, including the five rules that ship off
|
|
76
|
+
by default. Before this, the only way to turn one on was to name it in
|
|
77
|
+
`--select`, which also turned off every other rule unless you listed
|
|
78
|
+
those too. `--ignore` still subtracts on top of `--strict`, and
|
|
79
|
+
`--select` is unchanged.
|
|
80
|
+
- `ellipsis-closer` (structure, info) flags the long-sentence-then-short-closer
|
|
81
|
+
kicker `mic-drop-closer` catches, but built on the opposite verb shape: the
|
|
82
|
+
closer's verb phrase is elided down to a bare auxiliary with no object,
|
|
83
|
+
"The agent did." rather than a quantifier plus a full verb. No subject list
|
|
84
|
+
is needed — the tell is the missing verb, not the subject — so "That",
|
|
85
|
+
"This" and "It" are not excluded the way `mic-drop-closer` excludes them.
|
|
86
|
+
A negative lookahead drops a closer that still holds a wh-word, since that
|
|
87
|
+
marks a subordinate clause with its own complement rather than an elided
|
|
88
|
+
one. Reuses `mic-drop-closer`'s long-sentence prefix.
|
|
89
|
+
- `actually-not-x`: the adverb and a trailing "…, not X" in one clause, as in
|
|
90
|
+
"The disclosure actually covered two incidents, not one". Either marker
|
|
91
|
+
alone is ordinary technical English and neither is matched on its own; the
|
|
92
|
+
tell is the two of them doubled up, correcting an alternative the reader was
|
|
93
|
+
never offered. The narrowing is structural, not a word list: the comma in
|
|
94
|
+
front of "not" has to be the first comma of its clause and has to follow a
|
|
95
|
+
word, so a fronted setup ("Despite the name, …") and a parenthetical both
|
|
96
|
+
drop it. Nothing crosses a line break, so two rows of a table cannot weld
|
|
97
|
+
into one correction.
|
|
98
|
+
- `real-x-real-y` (rhetorical-tic, info) flags the same "real" used twice
|
|
99
|
+
attributively in one sentence, in front of two different nouns: "it can
|
|
100
|
+
trigger real API calls or hold real credentials". Bare "real" is an
|
|
101
|
+
ordinary word, so the narrowing is the repetition itself, not a noun
|
|
102
|
+
list. A hyphen on either side takes it out ("real-time", "non-real"), a
|
|
103
|
+
backreference requires the second noun to differ from the first, and a
|
|
104
|
+
short closed list drops the fixed senses of this register (real time,
|
|
105
|
+
real-world, real numbers, real user monitoring, real estate, real money).
|
|
106
|
+
- `phrase-echo` (rhetorical-tic, info, off by default) flags a three-word
|
|
107
|
+
phrase that comes back within a few hundred words, the model reusing a
|
|
108
|
+
phrase it has just minted. Each word must be four characters or more and
|
|
109
|
+
lowercase-led, and one of them six letters with nothing but letters, so "in
|
|
110
|
+
order to", "couldn't have been" and "New York City" never match. The window
|
|
111
|
+
does not cross into a list item or a table row. Off by default: a term of
|
|
112
|
+
art repeats because it must, and in reference prose the pattern fires on
|
|
113
|
+
names by the thousand per million words.
|
|
114
|
+
- `trailing-restatement` (structure, info, off by default) flags the tail
|
|
115
|
+
that says the sentence again in other words: ", which means …", ", which
|
|
116
|
+
is to say …", ", meaning …", and four participles that hang a result off
|
|
117
|
+
the sentence (", making it easier", ", allowing us to", ", giving them
|
|
118
|
+
more", ", leaving you with"). The connective is all a pattern can see, and the
|
|
119
|
+
same connective states a real consequence in careful prose, so the rule
|
|
120
|
+
runs only when selected.
|
|
121
|
+
|
|
122
|
+
### Fixed
|
|
123
|
+
|
|
124
|
+
- `check`'s options now work without the command word: `sloplint --markdown -`
|
|
125
|
+
and `sloplint --strict -` scan, as the README has said `sloplint -` does.
|
|
126
|
+
Before, the global parser rejected any option it did not own with
|
|
127
|
+
`invalid option`, so `check` had to be spelled out whenever an option
|
|
128
|
+
came first. An option no command knows still exits 2.
|
|
129
|
+
- `--markdown` now blanks `<!-- -->` comment blocks along with fenced code,
|
|
130
|
+
inline code and URLs. Planning notes kept in comments never render, so
|
|
131
|
+
every rule that fired inside one was a permanent false positive.
|
|
132
|
+
- `rule-of-three`'s message, rationale, README and SPEC entries said it
|
|
133
|
+
flagged three parallel comma items. The pattern requires single words in
|
|
134
|
+
the closing two slots, so a triad of phrases never matched; the text now
|
|
135
|
+
says that.
|
|
136
|
+
|
|
6
137
|
## [0.6.0] - 2026-09-08
|
|
7
138
|
|
|
8
139
|
The catalog grows from 50 rules to 75, and sloplint ships as a Claude Code
|
data/README.md
CHANGED
|
@@ -60,24 +60,24 @@ The recipe sloplint is built around, and the one an agent should use:
|
|
|
60
60
|
cat draft.md | sloplint check --markdown -o json -
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
`--markdown` blanks out code and URLs first, `-o json` emits the machine-readable form, and `-` reads stdin. Exit 0 means clean, 1 means notes found, anything higher is an error. `check` is the default command, so `sloplint draft.md`, `sloplint -`, and a bare `sloplint` with piped stdin all scan.
|
|
63
|
+
`--markdown` blanks out code, HTML comments, and URLs first, `-o json` emits the machine-readable form, and `-` reads stdin. Exit 0 means clean, 1 means notes found, anything higher is an error. `check` is the default command, so `sloplint draft.md`, `sloplint -`, and a bare `sloplint` with piped stdin all scan.
|
|
64
64
|
|
|
65
65
|
The human-readable form drops `-o json`:
|
|
66
66
|
|
|
67
67
|
```
|
|
68
68
|
$ printf 'The report is a rich tapestry of vibrant detail.\nThat is exactly the point I keep making about it.\n' | sloplint check -
|
|
69
|
-
-:1:17:
|
|
69
|
+
-:1:17: error rich-tapestry "rich tapestry"/"tapestry of" is a signature AI cliché.
|
|
70
70
|
excerpt: The report is a [rich tapestry] of vibrant detail. That is exactly the…
|
|
71
71
|
why: 'tapestry of' is one of the most reliable single-phrase model tells.
|
|
72
72
|
fix: Cut the metaphor; name the actual things.
|
|
73
73
|
|
|
74
|
-
-:1:34:
|
|
74
|
+
-:1:34: error puffery-words Wikipedia-style puffery word/phrase — a common AI tell.
|
|
75
75
|
excerpt: The report is a rich tapestry of [vibrant] detail. That is exactly the point I…
|
|
76
76
|
why: Travel-brochure adjectives and phrases that models reach for and careful writers avoid.
|
|
77
77
|
fix: Replace with a concrete, specific detail or cut it.
|
|
78
78
|
|
|
79
|
-
-:2:
|
|
80
|
-
excerpt: …tapestry of vibrant detail. That is
|
|
79
|
+
-:2:1: info exact-exactly "exact/exactly" is reflexive emphasis unless it names something checkable.
|
|
80
|
+
excerpt: …is a rich tapestry of vibrant detail. [That is exactly] the point I keep making about it.
|
|
81
81
|
why: Models reach for 'exact/exactly' as filler emphasis on a claim with nothing to check; it earns its place only next to a number, a name, or a stated identity.
|
|
82
82
|
fix: Cut it, or replace with the number, name, or match it's supposed to be precise about.
|
|
83
83
|
```
|
|
@@ -98,19 +98,20 @@ version print the sloplint version
|
|
|
98
98
|
|
|
99
99
|
`check` takes files as arguments, or `-` (or nothing) to read stdin, and these options:
|
|
100
100
|
|
|
101
|
-
- `--markdown` skips fenced code, inline code, and URLs before scanning. Off by default so it never silently eats prose.
|
|
101
|
+
- `--markdown` skips fenced code, inline code, HTML comments, and URLs before scanning. Off by default so it never silently eats prose.
|
|
102
102
|
- `--select IDS` runs only these rules. Accepts comma-separated rule ids or category names.
|
|
103
103
|
- `--ignore IDS` skips these rules. Same id-or-category form.
|
|
104
|
+
- `--strict` runs every rule, including the five that are off by default. `--ignore` still applies on top.
|
|
104
105
|
|
|
105
106
|
`explain` is the command an agent calls to decide whether a flag is worth acting on:
|
|
106
107
|
|
|
107
108
|
```
|
|
108
109
|
$ sloplint explain no-x-no-y
|
|
109
|
-
no-x-no-y (
|
|
110
|
+
no-x-no-y (cadence, warning, high confidence)
|
|
110
111
|
|
|
111
112
|
"No X, no Y" chain (%{count} items) reads as AI cadence.
|
|
112
113
|
|
|
113
|
-
Why: Asyndetic negation chains are a signature model cadence, near-absent from human prose at any length
|
|
114
|
+
Why: Asyndetic negation chains are a signature model cadence, near-absent from human prose at any length. A careful writer occasionally stacks two (and, rarely, more), but a model reaches for the pattern constantly.
|
|
114
115
|
Fix: Cut the chain or make it one plain sentence.
|
|
115
116
|
|
|
116
117
|
Flags: No fluff, no filler, no jargon.
|
|
@@ -127,18 +128,19 @@ One match is one note. JSON output is an array of these, or an object keyed by p
|
|
|
127
128
|
"line": 12,
|
|
128
129
|
"column": 5,
|
|
129
130
|
"severity": "warning",
|
|
131
|
+
"confidence": "high",
|
|
130
132
|
"rule": "no-x-no-y",
|
|
131
|
-
"category": "
|
|
133
|
+
"category": "cadence",
|
|
132
134
|
"message": "\"No X, no Y\" chain (3 items) reads as AI cadence.",
|
|
133
135
|
"excerpt": "No fluff, no filler, no jargon",
|
|
134
136
|
"context": "The report was blunt. [No fluff, no filler, no jargon]. Nothing held back at all.",
|
|
135
137
|
"count": 3,
|
|
136
|
-
"rationale": "Asyndetic negation chains are a signature model cadence, near-absent from human prose at any length
|
|
138
|
+
"rationale": "Asyndetic negation chains are a signature model cadence, near-absent from human prose at any length. A careful writer occasionally stacks two (and, rarely, more), but a model reaches for the pattern constantly.",
|
|
137
139
|
"suggestion": "Cut the chain or make it one plain sentence."
|
|
138
140
|
}
|
|
139
141
|
```
|
|
140
142
|
|
|
141
|
-
`line` and `column` are 1-indexed and point at the start of the match. `excerpt` is the bare match; `context` is the same match bracketed inside about 40 characters of surrounding prose, which is what you want when the match is a single word or a lone em dash. A match already 40 characters long carries its own context, so `context` returns it alone rather than padding it further. `count` appears only when the rule tallies items (a "no X, no Y" chain, a "did not, did not" chain). `rationale` is why the pattern is a tell — the same text `sloplint explain` prints — so an agent deciding whether
|
|
143
|
+
`severity` is what the construct costs the prose; `confidence` is how likely the match is a false positive. The two are separate, so a cheap tell we are sure about and an expensive one we are guessing at no longer collapse into the same word. `line` and `column` are 1-indexed and point at the start of the match. `excerpt` is the bare match; `context` is the same match bracketed inside about 40 characters of surrounding prose, which is what you want when the match is a single word or a lone em dash. A match already 40 characters long carries its own context, so `context` returns it alone rather than padding it further. `count` appears only when the rule tallies items (a "no X, no Y" chain, a "did not, did not" chain). `rationale` is why the pattern is a tell — the same text `sloplint explain` prints — so an agent deciding whether a flag is worth acting on doesn't have to run `explain` separately to find out. `suggestion` is a short fix hint.
|
|
142
144
|
|
|
143
145
|
## Exit codes
|
|
144
146
|
|
|
@@ -154,24 +156,29 @@ An unknown id or category in `--select`/`--ignore` is a usage error (exit 2, nam
|
|
|
154
156
|
|
|
155
157
|
## The rule catalog
|
|
156
158
|
|
|
157
|
-
|
|
159
|
+
80 rules across nine categories, each named for the rhetorical move the construct makes. `sloplint rules` prints them; `sloplint rules --json` gives an agent the enumerable form.
|
|
158
160
|
|
|
159
|
-
- **
|
|
160
|
-
- **
|
|
161
|
-
- **
|
|
162
|
-
- **
|
|
161
|
+
- **self-rating** (15) the writer grades their own prose or claim: `clean-x`, `clean-count`, `cleanest-x`, `cleanly`, `honest-x`, `most-honest-x`, `honestly` (the honesty family, built the same way as the four `clean` rules), `worth-naming`, `worth-saying-plainly`, `earns-its-place`, `does-a-lot-of-work`, `exact-exactly`, `genuinely` (off by default), `the-punchline-is`, `announced-takeaway`.
|
|
162
|
+
- **closer** (12) closes by restating or announcing the point: `thats-the-whole`, `is-the-whole-x` (the same closer on any subject: "Consistency is the real test.", at `medium` confidence), `is-the-entire`, `the-entire-is`, `thats-how-x`, `thats-the-tension`, `right-up-until`, `and-nothing-else` (the trailing "…, and nothing else"), `nothing-else-frag`, `bare-equative` ("The lesson is the handoff.", at `medium` confidence), `trailing-restatement` (the "…, which means …" tail that says the sentence again, off by default), `and-what-it-should` (the elliptical tail: "…, and what it should.").
|
|
163
|
+
- **cadence** (17) rhythm: repetition, parallelism, and the long-then-short kicker: `no-x-no-y`, `no-x-no-y-frag`, `did-not-x-did-not-y`, `one-x-one-y` ("one reviewer, one queue, one deadline"), `from-x-to-y-chain` ("from guessing to measuring, from hoping to knowing"), `same-determiner-chain` (any other repeated determiner, at `medium` confidence), `real-x-real-y`, `epistrophe` (off by default), `phrase-echo` (off by default), `is-is` (doubled copula), `the-x-is-the-x` ("the problem with A is the problem with B"), `rule-of-three` (off by default), `everyone-nobody` (the comma-spliced antithesis: "Everyone wants the dashboard, nobody maintains it."), `short-run` (three sentences of thirty characters or fewer in a row, at `medium` confidence), `mic-drop-closer` (the short quantifier-led sentence that ends a paragraph after a long one, at `medium` confidence), `bare-auxiliary-closer` (the same shape, but the closer's verb is elided down to a bare auxiliary: "The agent did.", at `medium` confidence), `np-fragment-and` (the verbless "A named owner and a quarterly review.", at `medium` confidence).
|
|
164
|
+
- **puffery** (8) inflates the subject: `puffery-words` (vibrant, nestled, groundbreaking, in the heart of), `rich-tapestry`, `vital-role`, `stands-serves-as`, `underscores-highlights`, `impact-noun-vague` ("a significant impact", "make an impact"), `trailing-significance-participle` (the "…, showcasing its importance" clause), `abstract-lives-in` ("the value sits in the follow-up", at `medium` confidence).
|
|
165
|
+
- **false-correction** (8) corrects a reading nobody offered: `not-just-x-but-y`, `not-x-but-y` (the bare corrective), `not-by-x-but-by-y` ("not by luck, but by design"), `isnt-x-its-y` (the same corrective split across two clauses: "It isn't the tool. It's the habit."), `question-isnt` (the corrective frame in interrogative dress), `less-about-more-about`, `actually-not-x`, `dont-verb-it`.
|
|
166
|
+
- **false-concession** (7) performs balance or candour and gives nothing up: `two-things-true`, `none-of-this-is-to-say`, `is-real-and-not`, `not-nothing`, `vague-attribution` ("some critics argue", "it is widely regarded"), `if-im-being-honest` (the candor preamble, from slopwash.com's "false intimacy"), `and-thats-fine`.
|
|
167
|
+
- **reader-address** (6) instructs or flatters the reader: `you-already-know`, `sit-with-that`, `hold-onto-that`, `notice-what`, `notice-what-there`, `quip-question` (the verbless "No invite?", at `medium` confidence).
|
|
168
|
+
- **borrowed-metaphor** (5) an engineering term applied to an argument: `load-bearing`, `failure-mode-here`, `intersection-of`, `impact-verb` ("the outage impacted four thousand accounts"), `impact-noun-bare` ("the impact of X", at `medium` confidence because research prose uses it straight).
|
|
169
|
+
- **punctuation** (2) the mark itself: `em-dash` (any em dash), `em-dash-overuse` (three or more in one paragraph).
|
|
163
170
|
|
|
164
|
-
Severity is `
|
|
171
|
+
Every rule carries two ratings, and they answer different questions. **Severity** is what the construct costs the prose: `error` when the sentence is worse for it in any register (`rich-tapestry`, `puffery-words`, `vague-attribution`), `warning` when it dates the draft as model output but the sentence still says something, `info` when it is mostly harmless and worth knowing (`em-dash`). **Confidence** is how likely the match is a false positive: `high` when almost every hit is the real tell, `medium` when ordinary prose produces the same shape often enough that an agent should read the rationale first, `low` when the pattern cannot tell the tell from the ordinary use at all.
|
|
165
172
|
|
|
166
|
-
|
|
173
|
+
The two used to be one word, so a cheap tell we were sure about and an expensive one we were guessing at both came out as `warning`. They no longer do. `error` is in use: the puffery family, the tautology closers, and the self-ranking superlatives all cost the sentence something wherever they appear, however sure or unsure we are of the match.
|
|
167
174
|
|
|
168
|
-
`and-nothing-else` and `nothing-else-frag` are a pair of the same shape
|
|
175
|
+
Some tells come in a confident form and an ambiguous one, and those ship as a pair rather than as one rule stretched over both. `no-x-no-y` wants the comma chain a writer clearly authored; `no-x-no-y-frag` takes the same cadence built from sentence fragments, which ordinary prose also produces, so it ships at `medium` confidence. Same with `not-just-x-but-y` and `not-x-but-y`, and with `notice-what-there` and `notice-what`. The quiet half is still worth flagging — an agent that reads the rationale can judge — but it should not carry the same weight as the half we're sure about. `and-nothing-else` and `nothing-else-frag` are a pair of the same shape and both sit at `high`, because the fragment half carries a capital letter and a whole-sentence requirement that the comma half has no equivalent of, so it is the narrower of the two rather than the quieter one.
|
|
169
176
|
|
|
170
|
-
|
|
177
|
+
The five `low` rules are the ones that run **off by default**. They run when you name them — `sloplint check --select rule-of-three -` — or when you pass `--strict`, which runs the whole catalog when you give it no `--select`. `rule-of-three` flags three single words in a comma series closing a sentence, which humans do all the time; the closing two items must be single words, so a triad of phrases does not match, because a regex cannot tell one from an ordinary list. `genuinely` flags every occurrence of the word; as an intensifier it rates the writer's sincerity, but it still does real work when it draws a contrast, and nothing in the sentence separates the two. `epistrophe` flags two clauses ending on the same phrase, a named figure that careful writers use on purpose and that, on Hacker News, is mostly plain phrase reuse. `trailing-restatement` flags the "…, which means …" tail and the participles that hang a result off the sentence ("…, making it easier"); the connective is visible and the restatement is not, so a real consequence flags the same way. `phrase-echo` flags a three-word phrase that comes back within a few hundred words; a term of art comes back because it must, and the pattern cannot tell one from a phrase the writer coined. Naming a category in `--select` only turns on that category's non-low rules; naming the rule's own id runs it regardless. `--strict` on its own runs the whole catalog, but alongside `--select` it only widens the named categories to include their low-confidence members. `sloplint rules --json` lists every rule's `severity`, `confidence` and `rationale`, so an agent can tell which rules are off by default without reading this file.
|
|
171
178
|
|
|
172
179
|
### Markdown handling
|
|
173
180
|
|
|
174
|
-
`--markdown` replaces fenced code, inline code, and URLs with same-length whitespace before scanning, so line and column stay correct. Without it, sloplint treats the whole file as prose and will flag text inside your code fences. Pass `--markdown` whenever the input is Markdown.
|
|
181
|
+
`--markdown` replaces fenced code, inline code, HTML comments, and URLs with same-length whitespace before scanning, so line and column stay correct. Without it, sloplint treats the whole file as prose and will flag text inside your code fences. Pass `--markdown` whenever the input is Markdown.
|
|
175
182
|
|
|
176
183
|
## Adding a rule
|
|
177
184
|
|
|
@@ -180,14 +187,14 @@ Rules are data, not code. Each is a `Data.define` object in `lib/sloplint/rules.
|
|
|
180
187
|
```ruby
|
|
181
188
|
Rule.new(
|
|
182
189
|
id: "rule-id",
|
|
183
|
-
category: "
|
|
184
|
-
severity: "warning", #
|
|
190
|
+
category: "cadence", # one of the nine in the catalog above
|
|
191
|
+
severity: "warning", # what it costs the prose: error, warning, info
|
|
192
|
+
confidence: "high", # false-positive risk: high, medium, low
|
|
185
193
|
pattern: /.../i,
|
|
186
194
|
message: "What the reader sees. %{count} interpolates the tally.",
|
|
187
195
|
suggestion: "One short fix hint.",
|
|
188
196
|
count_group: /.../i, # optional: a regex tallied over the match
|
|
189
197
|
skip: [/.../i], # optional: drop the note if these match
|
|
190
|
-
default_on: false, # optional: runs only when named in --select
|
|
191
198
|
examples_bad: ["A sentence the rule must flag."],
|
|
192
199
|
examples_ok: ["A sentence it must leave alone."],
|
|
193
200
|
rationale: "Why this is a tell, and what it costs when it's wrong."
|