sloplint 0.7.0 → 0.8.1

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.
data/docs/SPEC.md CHANGED
@@ -41,11 +41,15 @@ so the source never needs to enter the repository.
41
41
  - **`examples_ok` may quote real prose, public domain only**, with the source
42
42
  named in a comment. The Moby-Dick and Federalist No. 44 fixtures are the model.
43
43
  A common idiom or a title is not a quotation and needs no such treatment.
44
- - **`rationale:` states frequencies, not quotations.** "24 hits in 1.02M words"
45
- is the form.
44
+ - **`rationale:` says why the construct reads as AI-written, not how it was
45
+ tested.** Corpus sizes and hit counts belong in the commit message, not the
46
+ shipped text.
46
47
  - **A reference corpus of human prose must be public domain.** Copyrighted text
47
48
  can't be redistributed, so a corpus built from it can't live in the repo, and
48
49
  neither can the false-positive check that depends on it.
50
+ - **The probe corpus is fetched, not committed.** `script/probe-raid fetch` pulls
51
+ RAID (Dugan et al. 2024, MIT) into `.corpus/`, which is ignored. The script
52
+ and the numbers it prints may be quoted in a commit message; the texts may not.
49
53
 
50
54
  This repository is MIT-licensed, so anything committed is redistributable by
51
55
  anyone. Writing the fixtures ourselves also means nobody's prose gets held up as
@@ -107,7 +111,7 @@ sloplint/
107
111
  lib/sloplint/cli.rb # optparse, subcommands, exit codes
108
112
  lib/sloplint/rules.rb # RULES: array of Rule (Data) objects — the catalog
109
113
  lib/sloplint/engine.rb # Engine.scan(text, rules:, config:) -> [Note]
110
- lib/sloplint/output.rb # format_human / format_json / format_compact
114
+ lib/sloplint/output.rb # format_human / format_json
111
115
  docs/
112
116
  SPEC.md # this file
113
117
  spec/
@@ -180,17 +184,22 @@ path when multiple files are scanned).
180
184
  "line": 12,
181
185
  "column": 5,
182
186
  "severity": "warning",
187
+ "confidence": "high",
183
188
  "rule": "no-x-no-y",
184
- "category": "rhetorical-tic",
189
+ "category": "cadence",
185
190
  "message": "\"No X, no Y\" chain (3 items) reads as AI cadence.",
186
191
  "excerpt": "No fluff, no filler, no jargon",
187
192
  "context": "The report was blunt. [No fluff, no filler, no jargon]. Nothing held back at all.",
188
193
  "count": 3,
189
- "rationale": "Asyndetic negation chains are a signature model cadence, near-absent from human prose at any length -- 24 hits in 1.02M words across Austen, Melville, Madison, Thoreau, and Emerson combined. A careful writer occasionally stacks two (and, rarely, more), but a model reaches for the pattern constantly.",
194
+ "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.",
190
195
  "suggestion": "Cut the chain or make it one plain sentence."
191
196
  }
192
197
  ```
193
198
 
199
+ - `severity` is what the construct costs the prose (`error`, `warning`, `info`);
200
+ `confidence` is how likely the match is a false positive (`high`, `medium`,
201
+ `low`). Two axes, not one: a cheap tell we are sure about and an expensive
202
+ one we are guessing at no longer share a word.
194
203
  - `line`/`column` are 1-indexed, pointing at the start of the match.
195
204
  - `excerpt` is the bare match, nothing else. It is what `column` points at.
196
205
  - `context` is the match bracketed inside ~40 characters of surrounding prose,
@@ -203,7 +212,7 @@ path when multiple files are scanned).
203
212
  - `count` present when the rule counts items (the "badge" in the examples).
204
213
  - `rationale` is the same text `sloplint explain` prints under `Why:` — why the
205
214
  pattern reads as a tell. `check` carries it on every note so an agent acting
206
- on an `info` flag (or deciding whether to) doesn't have to shell out to
215
+ on a flag (or deciding whether to) doesn't have to shell out to
207
216
  `explain` first; that's the whole point of the field.
208
217
  - `suggestion` is a short fix hint; agents may use it, humans see it too.
209
218
 
@@ -214,18 +223,19 @@ built with `Data.define` (immutable value objects, Ruby 3.2+):
214
223
 
215
224
  ```ruby
216
225
  Rule = Data.define(
217
- :id, :category, :severity, :pattern, :message, :suggestion,
218
- :examples_bad, :examples_ok, :count_group, :skip
226
+ :id, :category, :severity, :confidence, :pattern, :message, :suggestion,
227
+ :examples_bad, :examples_ok, :count_group, :skip, :rationale
219
228
  ) do
220
229
  # sensible defaults for the optional fields
221
- def initialize(count_group: nil, skip: [], **rest) = super
230
+ def initialize(count_group: nil, skip: [], rationale: nil, **rest) = super
222
231
  end
223
232
 
224
233
  RULES = [
225
234
  Rule.new(
226
235
  id: "no-x-no-y",
227
- category: "rhetorical-tic",
228
- severity: "warning",
236
+ category: "cadence",
237
+ severity: "warning", # error, warning, info -- cost to the prose
238
+ confidence: "high", # high, medium, low -- false-positive risk
229
239
  pattern: /.../i, # regex literal; add /m if multiline
230
240
  message: "...", # may reference %{count}
231
241
  suggestion: "...",
@@ -259,114 +269,159 @@ rules to a file (non-devs editing them, third-party rule packs) isn't real yet.
259
269
  If it becomes real, the migration is cheap precisely because the rules are
260
270
  already pure data: write one loader, point it at a JSON dir, done.
261
271
 
262
- Categories (for `--select`/`--ignore` by group):
272
+ Categories (for `--select`/`--ignore` by group). Every category names the
273
+ rhetorical move the construct makes, so a new rule goes where its move goes:
274
+
275
+ - `self-rating` — the writer grades their own prose or claim
276
+ - `closer` — closes by restating or announcing the point
277
+ - `cadence` — rhythm: repetition, parallelism, and the long-then-short kicker
278
+ - `puffery` — inflates the subject
279
+ - `false-correction` — corrects a reading nobody offered
280
+ - `false-concession` — performs balance or candour and gives nothing up
281
+ - `reader-address` — instructs or flatters the reader
282
+ - `borrowed-metaphor` — an engineering term applied to an argument
283
+ - `punctuation` — the mark itself
284
+
285
+ Two ratings, answering two questions. **Severity** is what the construct
286
+ costs the prose: `error` when the sentence is worse for it in any register
287
+ (the puffery family, the tautology closers, the self-ranking superlatives),
288
+ `warning` when it dates the draft as model output but the sentence still
289
+ says something, `info` when it is mostly harmless and worth knowing (the em
290
+ dash). **Confidence** is how likely a match is a false positive: `high` when
291
+ almost every hit is the real tell, `medium` when ordinary prose makes the
292
+ same shape often enough that an agent should read the rationale first, `low`
293
+ when the pattern cannot separate the tell from the ordinary use at all. The
294
+ five `low` rules are the ones that stay out of the default run; `--strict`,
295
+ or naming one by its own id, turns them on. The catalog below tags each
296
+ rule's confidence, not its severity.
263
297
 
264
- - `rhetorical-tic` — the cadence patterns (the user's list below)
265
- - `puffery` — Wikipedia "words to watch" (boasts, vibrant, nestled, tapestry…)
266
- - `structure` — rule-of-three, "not just X but Y", "the question isn't X, it's Y", "less about X more about Y", the trailing significance participle, the trailing restatement, em dash, em-dash overuse
267
- - `hedging` — vague attribution ("some critics argue", "it is widely regarded")
298
+ ## Rule catalog (v1)
268
299
 
269
- Severities: `warning` for strong tells, `info` for weak/contextual ones. No
270
- rule ships at `error` yet -- reserved for a pattern with essentially zero
271
- false-positive risk, which none has demonstrated.
300
+ ### self-rating
301
+
302
+ The writer grades their own prose or claim.
303
+
304
+ - `clean-x` — "a clean abstraction/distinction/framing", "clean line between"; `medium` confidence
305
+ - `clean-count` — "two/three clean parts/buckets/categories…"; needs a partition noun
306
+ - `cleanest-x` — "the cleanest framing/formulation", "cleanest way to put it"; noun list only
307
+ - `cleanly` — "cleanly" into/onto/in two/in half — the partition frame; the preposition is the narrowing; the engineering idiom ("applies cleanly", "separated cleanly", "cleanly compiled") is a checkable fact and stays out, and the clause-final form is left out. `medium` confidence: the frame is not the sense, and separating "splits cleanly into two parts" from "retracted cleanly into the well" needs the subject
308
+ - `honest-x` — "an honest comparison", "the honest framing"; short noun list; "answer", "assessment", "account" excluded as ordinary; superlative yielded to the rule below
309
+ - `most-honest-x` — "the most honest framing", "the most honest way to put it"; wider noun list than `honest-x`; no human nouns, so "the most honest person" stays out
310
+ - `honestly` — a word, then "honestly", then a full stop or comma; terminal position required; discourse-marker slot guarded ("and/quite/but honestly,"); no animacy test
311
+ - `worth-naming` — "worth naming/flagging/separating/spelling out"; skip "naming names"; yields to the rule below when a manner adverb follows; `medium` confidence
312
+ - `worth-saying-plainly` — "it's worth saying plainly / better put bluntly…", plus the bare "Put plainly," / "Said bluntly,"; sentence-initial; the bare branch drops "simply"/"clearly" so "put simply" and "simply put" stay clean
313
+ - `earns-its-place` — "earns its place/keep" (any possessive); possessive required; `high` confidence
314
+ - `does-a-lot-of-work` — "does a lot of work here/in that sentence", "a lot of heavy lifting"; plain "the heavy lifting" excluded
315
+ - `exact-exactly` — the reflexive intensifier: "that's exactly", "exactly right", "exactly the point", "the exact problem", "know exactly why"; matches the tell as a closed set of frames rather than matching the word and subtracting an allow-list; a measured quantity ("the exact diameter", "the exact CPU time") is silent by construction. `medium` confidence
316
+ - `genuinely` — any "genuinely"; `low` confidence, off by default; no narrowing holds
317
+ - `the-punchline-is` — "the punchline is/:/?", "the honest answer/version is"; "short version" left out; ordinary writing
318
+ - `announced-takeaway` — colon-led label: "The pattern/lesson/takeaway…:"; sentence-initial
319
+ - `cataphoric-teaser` — the forward-pointing tease, sentence-initial, in three closed frames: an optional forward demonstrative ("here's", "here is", "this is", with one lead-in word allowed: "So here's"), a noun slot (what / the part / the thing / the bit / the secret), a scarcity subject (nobody, no one, most X, few people, hardly anyone, almost nobody, people), up to two adverbs, and a verb of telling, getting wrong, talking about, admitting or noticing: "Here's what nobody tells you about hiring.", "The part most people get wrong is the rollback." Without the demonstrative the sentence must reach a copula or a colon ("What nobody tells you is…"), since "What nobody tells you gets forgotten." is a report, and "they" is allowed only after a demonstrative, since a bare "What they don't realize is" refers to the people in the story. "That's what nobody understands" points backward and is left alone.
320
+
321
+ ### closer
322
+
323
+ Closes by restating or announcing the point.
324
+
325
+ - `thats-the-whole` — "that/this is the whole point/game/thing…"; also value and fix, the closers agents write in technical prose, when they end the sentence or the paragraph or run into a closer's tail word; either apostrophe; `is-the-whole-x` yields the same list
326
+ - `is-the-whole-x` — any subject + "is the whole/real/actual/entire N" (tell, point, test, work, …); opens on the subject word; yields only the exact sentences `thats-the-whole` and `is-the-entire` own; interrogative subjects out; "only", "deal", "thing", "cost" left out; `medium` confidence
327
+ - `is-the-entire` — "X is the entire point/game/business model"
328
+ - `the-entire-is` — "the entire point/game/… is" (flip of above)
329
+ - `thats-how-x` — sentence-initial "that's how…"
330
+ - `thats-the-tension` — sentence-initial "that's the tension/bet" as a closer; noun must end the clause; "tradeoff"/"catch" excluded
331
+ - `right-up-until` — "right up until it doesn't/isn't/stops/breaks"; bare "until it doesn't" excluded
332
+ - `and-nothing-else` — trailing ", and nothing else/more/further", ", and no more"; tail must close the sentence; comma required; bare "no more" needs "and"; "?" excluded
333
+ - `nothing-else-frag` — the same exclusion as a fragment: "Nothing else."; sentence-initial capital; semicolon excluded; must be the whole sentence; "No more." left out
334
+ - `bare-equative` — sentence-initial "The N (here) is (not) the …" with an abstract head noun; the-x-is-the-x's head list, so concrete heads are out; the copula (is, is not, isn't) must be followed by "the" and a lowercase word, so predicate adjectives, indefinites, pointing complements ("the same/one/first/…") and proper nouns are out; a list marker may open it; `medium` confidence
335
+ - `trailing-restatement` — comma plus "which means", "which is to say", or "meaning" opening on one of a closed set of determiners and pronouns; or one of four participle frames with a pronoun object and a closing word ("making it easier", "allowing us to", "giving them more", "leaving you with"). The closer on "making" is a comparative that ends the clause or leads into "to", "for" or "than". Nothing before the comma is inspected, so a gloss ("_ma_, which means hand") matches; the bare participles ("leaving the door open") never do. Off by default: the pattern sees the connective, not whether the tail restates the head. `low` confidence.
336
+ - `and-what-it-should` — ", and what it should." — a second "what" clause closing on a bare modal or a negated auxiliary; comma, conjunction and full stop required; the affirmative copula and do-verb ("what he does.") are complete clauses and out; a question is out
337
+
338
+ ### cadence
339
+
340
+ Rhythm: repetition, parallelism, and the long-then-short kicker.
341
+
342
+ - `no-x-no-y` — 2+ comma-separated "no …" items in a row; counts items
343
+ - `no-x-no-y-frag` — the same cadence as sentence fragments ("No fluff. No filler."); counts items; `medium` confidence
344
+ - `did-not-x-did-not-y` — 2+ "did not …"/"didn't …" in a row; counts items
345
+ - `one-x-one-y` — 3+ comma-separated "one X" items standing on their own; counts items; must open a sentence or follow a colon, so a chain after a verb is counting; items letter-led; the distributive "one for …" skipped; a pair never flags
346
+ - `from-x-to-y-chain` — 2+ comma-separated "from X to Y" spans; counts spans; operands open with a letter, so ranges are out; skips the relay ("to B, from B") and the reduplication ("from X to X")
347
+ - `same-determiner-chain` — 3+ comma-separated items opening on the same determiner or quantifier (every, each, your, more, …); backreference; counts items; "one" and "no" left to their own rules; the narrative possessives (my, his, her, their, its) left out; items lowercase-led, so proper nouns are not a chain; `medium` confidence
348
+ - `real-x-real-y` — the same "real" used twice attributively in one sentence, in front of two different nouns: "real API calls … real credentials"; needs no noun list -- narrowed by requiring two attributive uses of the intensifier naming two different things; a hyphen on either side of "real" (real-time, non-real) takes it out of the running; a closed list drops the fixed senses (real time, real-world, real numbers/roots, real user (monitoring), real estate, real money) and the function words that continue a predicative "is real"
349
+ - `epistrophe` — two clauses ending on the same two-word phrase, the second closing the sentence; two backreferences, so the phrase may be hard-wrapped; no article-led phrase, second word 4+ letters, second clause 5–60 chars with no internal punctuation and capped whitespace; off by default; `low` confidence
350
+ - `phrase-echo` — the same three words again within about 400 words; backreference in a lookahead, so the match is the first occurrence; each word 4+ characters and lowercase-led, one of them 6+ letters with nothing but letters, so function-word runs, contractions, proper nouns and Title Case headings are out; a repeat that opens on a quote mark, backtick, emphasis marker or hyphen is out; the gap crosses paragraph breaks but not a list item, a table row or 80+ non-word characters; off by default; `low` confidence
351
+ - `is-is` — doubled copula: "what it is is …", "the thing is, is that …"; comma optional
352
+ - `the-x-is-the-x` — "the X … is the X …": the same abstract head noun on both sides of the copula; backreference; closed list of heads that cannot name an object (key, cost, unit are out); the clause between is capped at 50 chars and may not hold a comma, semicolon or colon; the second head must be followed by a preposition, determiner, quantifier, pronoun, plural noun or punctuation, so compounds are out; "isn't" counts
353
+ - `rule-of-three` — three single-word comma items ending a sentence (heuristic; `low` confidence, off by default; runs under `--select` or `--strict` since it false-positives).
354
+ - `everyone-nobody` — the comma-spliced antithesis on quantifier subjects: one clause opens on everyone/everybody, the other on nobody/no one/none or "one N", joined by a bare comma, the second closing the sentence ("Everyone wants the dashboard, nobody maintains it."). A conjunction, a period, or the same subject twice is not a hinge; "one of/by/per", a measure, a proper noun, "none of which" and "none louder than" are not second subjects; clauses are capped at eighty characters.
355
+ - `short-run` — three consecutive sentences of thirty characters or fewer, each letter-led and closing on a full stop, no quotation marks or digits, no lone-letter labels (either case, though a possessive is not one) or abbreviations, starting at a real sentence boundary (never on a wrap continuation), crossing a hard wrap but not a paragraph break; `medium` confidence. A list marker may open a run but never sit inside one, so consecutive bullets are a list — the marker set covers glyphs, the literal "o" plain-text documents use as a bullet, and numbered and lettered items. Two whole-run exclusions keep document furniture out, each a property of the run rather than of any sentence in it: three single-word sentences in a row is a citation line ("Natl. Inst. Stand. Technol."), and three lowercase-led sentences in a row is transcribed speech ("we all get gas. we go to divert to Albany."). One single-word sentence is the archetypal kicker and stays, and a run that reaches a capital anywhere is prose, so identifier-initial writing ("npm was slow. git blame helped. We moved on.") is untouched. One is a question; a draft that repeats it is the tell.
356
+ - `mic-drop-closer` — a sentence of 60+ characters, then a paragraph-final closer of two to eight words opening on a **quantifier** (Nothing, Most, None, Everything, Everyone, Nobody, Then, Neither, Both): "Nothing here needs a new login."; `medium` confidence. The bare demonstratives (That, This, It) were in the list and are out: procedural writing ends a step with one as a matter of course ("This completes the roughing operations."). A blank line or the end of the text must follow the closer; both sentences may be hard-wrapped; whitespace runs in the long sentence are capped so blanked Markdown cannot make it, and the long-sentence prefix is an atomic group so an unpunctuated stretch cannot send it into catastrophic backtracking. The note points at the closer. One means nothing; a draft where it repeats is the tell.
357
+ - `bare-auxiliary-closer` — the same long-sentence-then-short-closer shape as `mic-drop-closer`, but the tell sits in the verb rather than the subject: the closer's verb phrase is elided down to a bare auxiliary with no object, "The agent did."; `medium` confidence. No subject list is needed — a one-to-three word subject runs straight into a bare `did/does/do/was/were/is/are/had/has/can/could/would/will/should/might/must` (contracted forms included) and a period, and the closer must be the last thing in the paragraph, same as `mic-drop-closer`, so a closing quotation mark after the period excludes quoted dialogue the same way `short-run` excludes it. A negative lookahead drops a closer that still holds "what", "that", "which", "who", "why" or "how", since those introduce a subordinate clause supplying its own complement rather than an elided one ("Nobody knew who did." asks who did it). Reuses `mic-drop-closer`'s long-sentence prefix rather than a second copy of it. One means nothing; a draft where it repeats is the tell.
358
+ - `punch-sentence` — the same long-then-short shape in the middle of a paragraph, where the closer rules refuse it: after the shared sixty-character prefix, a verbless beat of one to three words closing on a full stop, then one or two spaces and a sentence of forty or more characters on the same paragraph ("…three nights running that week. Not anymore. The job now checks…"); `info`, `medium` confidence. The beat is one of two closed shapes: a negator (Not, No, Never, Nothing, None) followed by up to two lowercase words, or a single capitalised word of five letters or more ("Simple."). The stop before the beat may not close a capitalised word of one to four letters or a lone lowercase letter, so a title, an initial or a case citation ("Mrs. Cadaver.", "John F. Kennedy.", "Swift v. Tyson.") never yields a one-word sentence. Short clauses with a subject ("He agrees.", "Dawes refuses.") are how plot summaries and news briefs move and are not matched; a one-word imperative in procedural prose ("Drain.") is, at about one per 25,000 words of recipes. A run of short sentences is `short-run`'s, and a paragraph-final beat is `mic-drop-closer`'s or `bare-auxiliary-closer`'s.- `np-fragment-and` — a whole sentence made of two noun phrases and an "and", opening on A/An/One at a sentence start or after a list marker ("A named owner and a quarterly review."). One to three words a side, no auxiliary or modal anywhere (contractions included); a lexical verb is invisible, so "A car and a truck collided." flags, which is why it ships at `medium` confidence.
272
359
 
273
- ## Rule catalog (v1)
360
+ ### puffery
274
361
 
275
- ### rhetorical-tic (from the request)
276
-
277
- | id | catches | notes |
278
- |----|---------|-------|
279
- | `no-x-no-y` | 2+ comma-separated "no …" items in a row | counts items |
280
- | `no-x-no-y-frag` | the same cadence as sentence fragments ("No fluff. No filler.") | counts items; `info` |
281
- | `thats-the-whole` | "that/this is the whole point/game/thing…" | also value and fix, the closers agents write in technical prose, when they end the sentence or the paragraph or run into a closer's tail word; either apostrophe; `is-the-whole-x` yields the same list |
282
- | `is-the-whole-x` | any subject + "is the whole/real/actual/entire N" (tell, point, test, work, …) | opens on the subject word; yields only the exact sentences `thats-the-whole` and `is-the-entire` own; interrogative subjects out; "only", "deal", "thing", "cost" left out; `info` |
283
- | `did-not-x-did-not-y` | 2+ "did not …"/"didn't …" in a row | counts items |
284
- | `from-x-to-y-chain` | 2+ comma-separated "from X to Y" spans | counts spans; operands open with a letter, so ranges are out; skips the relay ("to B, from B") and the reduplication ("from X to X") |
285
- | `one-x-one-y` | 3+ comma-separated "one X" items standing on their own | counts items; must open a sentence or follow a colon, so a chain after a verb is counting; items letter-led; the distributive "one for …" skipped; a pair never flags |
286
- | `and-what-it-should` | ", and what it should." — a second "what" clause closing on a bare modal or a negated auxiliary | comma, conjunction and full stop required; the affirmative copula and do-verb ("what he does.") are complete clauses and out; a question is out |
287
- | `abstract-lives-in` | an abstract noun that "lives/sits in/between/inside/…" | closed subject list; capitalised subjects skipped; "with" (responsibility), "at" (quantity), "lies in" and "resides in" left out; `info` |
288
- | `the-x-is-the-x` | "the X … is the X …": the same abstract head noun on both sides of the copula | backreference; closed list of heads that cannot name an object (key, cost, unit are out); the clause between is capped at 50 chars and may not hold a comma, semicolon or colon; the second head must be followed by a preposition, determiner, quantifier, pronoun, plural noun or punctuation, so compounds are out; "isn't" counts |
289
- | `same-determiner-chain` | 3+ comma-separated items opening on the same determiner or quantifier (every, each, your, more, …) | backreference; counts items; "one" and "no" left to their own rules; the narrative possessives (my, his, her, their, its) left out; items lowercase-led, so proper nouns are not a chain; `info` |
290
- | `bare-equative` | sentence-initial "The N (here) is (not) the …" with an abstract head noun | the-x-is-the-x's head list, so concrete heads are out; the copula (is, is not, isn't) must be followed by "the" and a lowercase word, so predicate adjectives, indefinites, pointing complements ("the same/one/first/…") and proper nouns are out; a list marker may open it; `info` |
291
- | `dont-verb-it` | "Don't call it X. Call it Y." (negated verb+it, same verb+it) | |
292
- | `sit-with-that` | "sit with that/this/it", "sit with the discomfort" | |
293
- | `hold-onto-that` | sentence-initial "hold onto/on to that/this" | imperative only |
294
- | `cleanly` | "cleanly" into/onto/in two/in half — the partition frame | the preposition is the narrowing; the engineering idiom ("applies cleanly", "separated cleanly", "cleanly compiled") is a checkable fact and stays out, and the clause-final form is left out. `info`: the frame is not the sense, and separating "splits cleanly into two parts" from "retracted cleanly into the well" needs the subject |
295
- | `clean-count` | "two/three clean parts/buckets/categories…" | needs a partition noun |
296
- | `cleanest-x` | "the cleanest framing/formulation", "cleanest way to put it" | noun list only |
297
- | `clean-x` | "a clean abstraction/distinction/framing", "clean line between" | `info` |
298
- | `you-already-know` | "you already know" (+ the answer / standalone) | |
299
- | `is-the-entire` | "X is the entire point/game/business model" | |
300
- | `the-entire-is` | "the entire point/game/… is" (flip of above) | |
301
- | `is-real-and-not` | "the X is real, and/not…", "is the real … and it" | skip "real estate/time"; `info` |
302
- | `real-x-real-y` | the same "real" used twice attributively in one sentence, in front of two different nouns: "real API calls … real credentials" | needs no noun list -- narrowed by requiring two attributive uses of the intensifier naming two different things; a hyphen on either side of "real" (real-time, non-real) takes it out of the running; a closed list drops the fixed senses (real time, real-world, real numbers/roots, real user (monitoring), real estate, real money) and the function words that continue a predicative "is real" |
303
- | `the-punchline-is` | "the punchline is/:/?", "the honest answer/version is" | "short version" left out; ordinary writing |
304
- | `worth-naming` | "worth naming/flagging/separating/spelling out" | skip "naming names"; yields to the rule below when a manner adverb follows; `info` |
305
- | `worth-saying-plainly` | "it's worth saying plainly / better put bluntly…", plus the bare "Put plainly," / "Said bluntly," | sentence-initial; the bare branch drops "simply"/"clearly" so "put simply" and "simply put" stay clean |
306
- | `not-nothing` | copula + "not nothing" litotes, any subject | skip personal/there subjects |
307
- | `exact-exactly` | the reflexive intensifier: "that's exactly", "exactly right", "exactly the point", "the exact problem", "know exactly why" | matches the tell as a closed set of frames rather than matching the word and subtracting an allow-list; a measured quantity ("the exact diameter", "the exact CPU time") is silent by construction. `info` |
308
- | `load-bearing` | "load-bearing" outside its construction sense | skip building nouns either side |
309
- | `intersection-of` | "the intersection of X and Y" as positioning | skip street corners, geometry, set arithmetic, airfield surfaces (runway, taxiway, apron), matrix rows and columns, and operands shaped like a US route designator ("US-27A") or a quadrant plus house number ("NE 140th Court") |
310
- | `impact-verb` | "impact" used as a verb ("the outage impacted 4,000 accounts") | needs an auxiliary or subject pronoun for the base form; "to impact" requires a following object, so the preposition ("prior to impact") stays out; skip the medical and soil sense of "impacted", the struck object of a real collision ("impacted terrain"), the fixed compounds, and hyphenated forms |
311
- | `impact-noun-bare` | "the impact of X", "measure the impact" — `info` | needs a measuring verb in front or "of" behind; skip the collision sense and the fixed compounds |
312
- | `thats-how-x` | sentence-initial "that's how…" | |
313
- | `announced-takeaway` | colon-led label: "The pattern/lesson/takeaway…:" | sentence-initial |
314
- | `earns-its-place` | "earns its place/keep" (any possessive) | possessive required; `warning` |
315
- | `does-a-lot-of-work` | "does a lot of work here/in that sentence", "a lot of heavy lifting" | plain "the heavy lifting" excluded |
316
- | `failure-mode-here` | "the failure mode here is" | deictic required; bare "the failure mode is" excluded |
317
- | `thats-the-tension` | sentence-initial "that's the tension/bet" as a closer | noun must end the clause; "tradeoff"/"catch" excluded |
318
- | `right-up-until` | "right up until it doesn't/isn't/stops/breaks" | bare "until it doesn't" excluded |
319
- | `two-things-true` | "two/both things can be/are true" | count fixed at two |
320
- | `notice-what-there` | "notice what X did there", "read that again" | sentence-initial |
321
- | `notice-what` | bare sentence-initial "Notice what…" | yields the "there" frame to the rule above; "how" excluded; `info` |
322
- | `none-of-this-is-to-say` | "none of this/that/the above is to say" | every other "not to say" phrasing excluded |
323
- | `if-im-being-honest` | "if I'm/we're (being) honest", "honestly, the answer/truth" | plain "to be honest" and "I'll be honest" excluded |
324
- | `honestly` | a word, then "honestly", then a full stop or comma | terminal position required; discourse-marker slot guarded ("and/quite/but honestly,"); no animacy test |
325
- | `honest-x` | "an honest comparison", "the honest framing" | short noun list; "answer", "assessment", "account" excluded as ordinary; superlative yielded to the rule below |
326
- | `most-honest-x` | "the most honest framing", "the most honest way to put it" | wider noun list than `honest-x`; no human nouns, so "the most honest person" stays out |
327
- | `genuinely` | any "genuinely" | off by default; no narrowing holds |
328
- | `actually-not-x` | "actually" and a trailing "…, not X" in one clause | the comma before "not" must be the first comma of the clause and must follow a word, so a fronted setup ("Despite the name, …") and a parenthetical both drop it; never crosses a line break; bare "actually" and "not actually" both left out |
329
- | `epistrophe` | two clauses ending on the same two-word phrase, the second closing the sentence | two backreferences, so the phrase may be hard-wrapped; no article-led phrase, second word 4+ letters, second clause 5–60 chars with no internal punctuation and capped whitespace; off by default; `info` |
330
- | `phrase-echo` | the same three words again within about 400 words | backreference in a lookahead, so the match is the first occurrence; each word 4+ characters and lowercase-led, one of them 6+ letters with nothing but letters, so function-word runs, contractions, proper nouns and Title Case headings are out; a repeat that opens on a quote mark, backtick, emphasis marker or hyphen is out; the gap crosses paragraph breaks but not a list item, a table row or 80+ non-word characters; off by default; `info` |
331
- | `and-thats-fine` | "And that's fine/okay." as a whole sentence | "and" required; must open and close the sentence |
332
- | `and-nothing-else` | trailing ", and nothing else/more/further", ", and no more" | tail must close the sentence; comma required; bare "no more" needs "and"; "?" excluded |
333
- | `nothing-else-frag` | the same exclusion as a fragment: "Nothing else." | sentence-initial capital; semicolon excluded; must be the whole sentence; "No more." left out |
334
- | `is-is` | doubled copula: "what it is is …", "the thing is, is that …" | comma optional |
335
-
336
- ### puffery (Wikipedia: Signs of AI writing)
337
-
338
- Single flat rule per word-cluster, matched as whole words:
362
+ Inflates the subject.
339
363
 
340
364
  - `puffery-words` — boasts a, vibrant, rich (history/cultural/tapestry), nestled (gated to a following in/among/between, so the literal verb — a head nestled against a shoulder — doesn't count), in the heart of (gated to a place object), groundbreaking, renowned, diverse array, breathtaking, natural beauty, stands as a testament, indelible mark, deeply rooted.
341
- - `stands-serves-as` — "stands as / serves as", "is a testament/reminder to".
365
+ - `rich-tapestry` — "rich tapestry", "tapestry of".
342
366
  - `vital-role` — "plays a (vital/crucial/pivotal/significant/key) role".
367
+ - `stands-serves-as` — "stands as / serves as", "is a testament/reminder to".
343
368
  - `underscores-highlights` — "underscore(s)" + determiner and "underscored/underscoring" anywhere (the emphasis verb); "highlights/emphasizes its (importance/significance)" stays narrow.
344
- - `rich-tapestry` — "rich tapestry", "tapestry of".
345
369
  - `impact-noun-vague` — an intensity adjective plus "impact" (significant, real, meaningful, lasting, massive, huge, profound, big and the rest), or "make/have an impact". "positive" and "negative" stay out: they name a direction, which is more than the intensity words do. "statistically significant impact" is skipped — that one is a finding.
370
+ - `trailing-significance-participle` — comma plus a participle from a closed verb list (highlighting, showcasing, reinforcing, shaping, enhancing, cementing, solidifying, embodying, fostering, facilitating, signalling), the clause a model hangs off a sentence to say what a fact means. Guards drop gerund lists and "signalling to". `driving`, `representing`, `reflecting`, `marking`, `contributing`, `illustrating`, `demonstrating`, `emphasising`, `echoing` and `affirming` stay out: humans write them in the same position, usually with a person as the subject, and the pattern cannot see the subject. `underscoring` is left to `underscores-highlights`.
371
+ - `abstract-lives-in` — an abstract noun that "lives/sits in/between/inside/…"; closed subject list; capitalised subjects skipped; "with" (responsibility), "at" (quantity), "lies in" and "resides in" left out; `medium` confidence
346
372
 
347
- ### structure
373
+ ### false-correction
374
+
375
+ Corrects a reading nobody offered.
348
376
 
349
377
  - `not-just-x-but-y` — copula + "not just/only/merely/simply/solely X … but (also) Y", plus "not because X, but because Y". Requires the escalation word.
350
- - `not-x-but-y` — the bare corrective "is not X but Y" with no escalation word; `info`, because the corrective/concession distinction is syntactic and the pattern can only approximate it.
351
- - `isnt-x-its-y` — the corrective with no conjunction: a negated copula, then a second clause that supplies the replacement ("It isn't the tool. It's the habit."), joined by a period, semicolon, comma, or dash; `info`. Neither complement may open with a pronoun, possessive, preposition ("about" excepted), degree word, or predicate adjective, which keeps out ordinary two-part contrast; some hits are still the same shape written by a person.
352
- - `not-by-x-but-by-y` — the corrective anchored on a repeated preposition rather than a copula: "not by A, but by B", "not from A but from B" (by, for, from, with, about, in, on, at, to, of, through, because of, out of); `info`. The B side must repeat the A preposition, A is capped at six words, and B may not open with a pronoun. Every corpus hit is the real shape written by a person, which is why it sits at info rather than being narrowed further.
353
- - `everyone-nobody` — the comma-spliced antithesis on quantifier subjects: one clause opens on everyone/everybody, the other on nobody/no one/none or "one N", joined by a bare comma, the second closing the sentence ("Everyone wants the dashboard, nobody maintains it."). A conjunction, a period, or the same subject twice is not a hinge; "one of/by/per", a measure, a proper noun, "none of which" and "none louder than" are not second subjects; clauses are capped at eighty characters.
354
- - `np-fragment-and` — a whole sentence made of two noun phrases and an "and", opening on A/An/One at a sentence start or after a list marker ("A named owner and a quarterly review."). One to three words a side, no auxiliary or modal anywhere (contractions included); a lexical verb is invisible, so "A car and a truck collided." flags, which is why it ships at `info`.
355
- - `quip-question` — the verbless opening question ("No invite?", "New to the tool?", "Still stuck?"): sentence-initial, one of a short list of opening words, one to four more words, no auxiliary or contraction, closing on the question mark; "Need" and "Want" are left out as elided verbs; `info`, because people ask the same shape in conversation.
356
- - `mic-drop-closer` — a sentence of 60+ characters, then a paragraph-final closer of two to eight words opening on a **quantifier** (Nothing, Most, None, Everything, Everyone, Nobody, Then, Neither, Both): "Nothing here needs a new login."; `info`. The bare demonstratives (That, This, It) were in the list and are out: procedural writing ends a step with one as a matter of course ("This completes the roughing operations."). A blank line or the end of the text must follow the closer; both sentences may be hard-wrapped; whitespace runs in the long sentence are capped so blanked Markdown cannot make it, and the long-sentence prefix is an atomic group so an unpunctuated stretch cannot send it into catastrophic backtracking. The note points at the closer. One means nothing; a draft where it repeats is the tell.
357
- - `ellipsis-closer` — the same long-sentence-then-short-closer shape as `mic-drop-closer`, but the tell sits in the verb rather than the subject: the closer's verb phrase is elided down to a bare auxiliary with no object, "The agent did."; `info`. No subject list is needed — a one-to-three word subject runs straight into a bare `did/does/do/was/were/is/are/had/has/can/could/would/will/should/might/must` (contracted forms included) and a period, and the closer must be the last thing in the paragraph, same as `mic-drop-closer`, so a closing quotation mark after the period excludes quoted dialogue the same way `short-run` excludes it. A negative lookahead drops a closer that still holds "what", "that", "which", "who", "why" or "how", since those introduce a subordinate clause supplying its own complement rather than an elided one ("Nobody knew who did." asks who did it). Reuses `mic-drop-closer`'s long-sentence prefix rather than a second copy of it. One means nothing; a draft where it repeats is the tell.
358
- - `short-run` — three consecutive sentences of thirty characters or fewer, each letter-led and closing on a full stop, no quotation marks or digits, no lone-letter labels (either case, though a possessive is not one) or abbreviations, starting at a real sentence boundary (never on a wrap continuation), crossing a hard wrap but not a paragraph break; `info`. A list marker may open a run but never sit inside one, so consecutive bullets are a list — the marker set covers glyphs, the literal "o" plain-text documents use as a bullet, and numbered and lettered items. Two whole-run exclusions keep document furniture out, each a property of the run rather than of any sentence in it: three single-word sentences in a row is a citation line ("Natl. Inst. Stand. Technol."), and three lowercase-led sentences in a row is transcribed speech ("we all get gas. we go to divert to Albany."). One single-word sentence is the archetypal kicker and stays, and a run that reaches a capital anywhere is prose, so identifier-initial writing ("npm was slow. git blame helped. We moved on.") is untouched. One is a question; a draft that repeats it is the tell.
359
- - `question-isnt` — "the question isn't/is not (whether|if|how|what|why|who) X, it's/but Y"; `info`. The resolving clause is required, so a plain rhetorical question never matches; "the real question is" is excluded.
360
- - `less-about-more-about` — "it's/this is/that's less about X (and) more about Y", also "… than about Y"; `info`. Both halves of the frame are required, and the subject slot is limited to the pronouns.
361
- - `trailing-significance-participle` — comma plus a participle from a closed verb list (highlighting, showcasing, reinforcing, shaping, enhancing, cementing, solidifying, embodying, fostering, facilitating, signalling), the clause a model hangs off a sentence to say what a fact means. Guards drop gerund lists and "signalling to". `driving`, `representing`, `reflecting`, `marking`, `contributing`, `illustrating`, `demonstrating`, `emphasising`, `echoing` and `affirming` stay out: humans write them in the same position, usually with a person as the subject, and the pattern cannot see the subject. `underscoring` is left to `underscores-highlights`.
362
- - `trailing-restatement` — comma plus "which means", "which is to say", or "meaning" opening on one of a closed set of determiners and pronouns; or one of four participle frames with a pronoun object and a closing word ("making it easier", "allowing us to", "giving them more", "leaving you with"). The closer on "making" is a comparative that ends the clause or leads into "to", "for" or "than". Nothing before the comma is inspected, so a gloss ("_ma_, which means hand") matches; the bare participles ("leaving the door open") never do. Off by default: the pattern sees the connective, not whether the tail restates the head. `info`.
363
- - `rule-of-three` — three single-word comma items ending a sentence (heuristic; `info` severity, off by default; runs under `--select` or `--strict` since it false-positives).
364
- - `em-dash` — any em dash; `info`.
365
- - `em-dash-overuse` — 3+ em dashes in one paragraph; `warning`.
378
+ - `not-x-but-y` — the bare corrective "is not X but Y" with no escalation word; `medium` confidence, because the corrective/concession distinction is syntactic and the pattern can only approximate it.
379
+ - `not-by-x-but-by-y` — the corrective anchored on a repeated preposition rather than a copula: "not by A, but by B", "not from A but from B" (by, for, from, with, about, in, on, at, to, of, through, because of, out of); `medium` confidence. The B side must repeat the A preposition, A is capped at six words, and B may not open with a pronoun. Every corpus hit is the real shape written by a person, which is why it sits at `medium` confidence rather than being narrowed further.
380
+ - `isnt-x-its-y` — the corrective with no conjunction: a negated copula, then a second clause that supplies the replacement ("It isn't the tool. It's the habit."), joined by a period, semicolon, comma, or dash; `medium` confidence. Neither complement may open with a pronoun, possessive, preposition ("about" excepted), degree word, or predicate adjective, which keeps out ordinary two-part contrast; some hits are still the same shape written by a person.
381
+ - `question-isnt` — "the question isn't/is not (whether|if|how|what|why|who) X, it's/but Y"; `medium` confidence. The resolving clause is required, so a plain rhetorical question never matches; "the real question is" is excluded.
382
+ - `less-about-more-about` — "it's/this is/that's less about X (and) more about Y", also "… than about Y"; `medium` confidence. Both halves of the frame are required, and the subject slot is limited to the pronouns.
383
+ - `actually-not-x` — "actually" and a trailing "…, not X" in one clause; the comma before "not" must be the first comma of the clause and must follow a word, so a fronted setup ("Despite the name, …") and a parenthetical both drop it; never crosses a line break; bare "actually" and "not actually" both left out
384
+ - `dont-verb-it` — "Don't call it X. Call it Y." (negated verb+it, same verb+it)
385
+
386
+ ### false-concession
387
+
388
+ Performs balance or candour and gives nothing up.
389
+
390
+ - `two-things-true` — "two/both things can be/are true"; count fixed at two
391
+ - `none-of-this-is-to-say` — "none of this/that/the above is to say"; every other "not to say" phrasing excluded
392
+ - `is-real-and-not` — "the X is real, and/not…", "is the real … and it"; skip "real estate/time"; `medium` confidence
393
+ - `not-nothing` — copula + "not nothing" litotes, any subject; skip personal/there subjects
394
+ - `vague-attribution` — "some (critics/experts/observers) (argue/say/believe)", "it is widely (regarded/considered/seen)", "many would argue".
395
+ - `if-im-being-honest` — "if I'm/we're (being) honest", "honestly, the answer/truth"; plain "to be honest" and "I'll be honest" excluded
396
+ - `and-thats-fine` — "And that's fine/okay." as a whole sentence; "and" required; must open and close the sentence
366
397
 
367
- ### hedging
398
+ ### reader-address
368
399
 
369
- - `vague-attribution` — "some (critics/experts/observers) (argue/say/believe)", "it is widely (regarded/considered/seen)", "many would argue".
400
+ Instructs or flatters the reader.
401
+
402
+ - `you-already-know` — "you already know" (+ the answer / standalone)
403
+ - `sit-with-that` — "sit with that/this/it", "sit with the discomfort"
404
+ - `hold-onto-that` — sentence-initial "hold onto/on to that/this"; imperative only
405
+ - `notice-what` — bare sentence-initial "Notice what…"; yields the "there" frame to the rule above; "how" excluded; `medium` confidence
406
+ - `notice-what-there` — "notice what X did there", "read that again"; sentence-initial
407
+ - `quip-question` — the verbless opening question ("No invite?", "New to the tool?", "Still stuck?"): sentence-initial, one of a short list of opening words, one to four more words, no auxiliary or contraction, closing on the question mark; "Need" and "Want" are left out as elided verbs; `medium` confidence, because people ask the same shape in conversation.
408
+
409
+ ### borrowed-metaphor
410
+
411
+ An engineering term applied to an argument.
412
+
413
+ - `load-bearing` — "load-bearing" outside its construction sense; skip building nouns either side
414
+ - `failure-mode-here` — "the failure mode here is"; deictic required; bare "the failure mode is" excluded
415
+ - `intersection-of` — "the intersection of X and Y" as positioning; skip street corners, geometry, set arithmetic, airfield surfaces (runway, taxiway, apron), matrix rows and columns, and operands shaped like a US route designator ("US-27A") or a quadrant plus house number ("NE 140th Court")
416
+ - `impact-verb` — "impact" used as a verb ("the outage impacted 4,000 accounts"); needs an auxiliary or subject pronoun for the base form; "to impact" requires a following object, so the preposition ("prior to impact") stays out; skip the medical and soil sense of "impacted", the struck object of a real collision ("impacted terrain"), the fixed compounds, and hyphenated forms
417
+ - `impact-noun-bare` — "the impact of X", "measure the impact" — `medium` confidence; needs a measuring verb in front or "of" behind; skip the collision sense and the fixed compounds
418
+
419
+ ### punctuation
420
+
421
+ The mark itself.
422
+
423
+ - `em-dash` — any em dash; `medium` confidence.
424
+ - `em-dash-overuse` — 3+ em dashes in one paragraph; `high` confidence.
370
425
 
371
426
  ## Markdown handling
372
427
 
@@ -385,12 +440,18 @@ This is a first-class requirement, not an afterthought.
385
440
  # Recommended for agents:
386
441
  cat FILE | sloplint check --markdown -o json -
387
442
  # exit 0 = clean, 1 = notes found, >1 = error
388
- # each note: {path,line,column,severity,rule,category,message,excerpt,context,rationale,suggestion}
443
+ # each note: {path,line,column,severity,confidence,rule,category,message,excerpt,context,rationale,suggestion,count}
444
+ # (count is present only for the rules that tally items)
389
445
  ```
390
446
 
391
447
  - Every option has a full-sentence help string (no telegraphic fragments).
392
- - `sloplint rules` prints the catalog: id, category, severity, one-line
393
- description — and with `--json`, the machine version an agent can enumerate.
448
+ - `sloplint rules` prints the catalog: id, category, severity, confidence,
449
+ one-line description — and with `--json`, the machine version an agent can
450
+ enumerate, including each rule's `severity`, `confidence` and `rationale`.
451
+ - Naming a category in `check --select` runs only that category's non-low
452
+ rules; naming a rule's own id runs it regardless. `--strict` alone runs the
453
+ whole catalog, low-confidence rules included; with `--select` it only widens
454
+ the named categories to include their low-confidence members.
394
455
  - `sloplint explain no-x-no-y` prints the rule's message, rationale, a bad
395
456
  example and an ok (non-matching) example. Agents call this to decide whether a
396
457
  flag is worth acting on.
data/lib/sloplint/cli.rb CHANGED
@@ -139,14 +139,14 @@ module Sloplint
139
139
 
140
140
  if as_json
141
141
  payload = RULES.map do |r|
142
- { id: r.id, category: r.category, severity: r.severity,
143
- message: r.message, suggestion: r.suggestion, default_on: r.default_on }
142
+ { id: r.id, category: r.category, severity: r.severity, confidence: r.confidence,
143
+ message: r.message, rationale: r.rationale, suggestion: r.suggestion }
144
144
  end
145
145
  out.puts(JSON.pretty_generate(payload))
146
146
  else
147
147
  RULES.each do |r|
148
- off = r.default_on ? "" : " [off by default]"
149
- out.puts("#{r.id.ljust(24)} #{r.category.ljust(14)} #{r.severity.ljust(8)} #{r.message}#{off}")
148
+ off = r.confidence == "low" ? " [off by default]" : ""
149
+ out.puts("#{r.id.ljust(24)} #{r.category.ljust(18)} #{r.severity.ljust(8)} #{r.confidence.ljust(7)} #{r.message}#{off}")
150
150
  end
151
151
  end
152
152
  0
@@ -166,7 +166,7 @@ module Sloplint
166
166
  return 2
167
167
  end
168
168
  out.puts(<<~TXT)
169
- #{rule.id} (#{rule.category}, #{rule.severity}#{rule.default_on ? "" : ", off by default"})
169
+ #{rule.id} (#{rule.category}, #{rule.severity}, #{rule.confidence} confidence#{rule.confidence == "low" ? ", off by default" : ""})
170
170
 
171
171
  #{rule.message}
172
172
 
@@ -197,15 +197,19 @@ module Sloplint
197
197
  refs - known
198
198
  end
199
199
 
200
- # --select/--ignore accept rule ids or category names. Default set excludes
201
- # default_on:false rules unless they are explicitly selected.
200
+ # --select/--ignore accept rule ids or category names. The default set
201
+ # excludes low-confidence rules unless they are explicitly selected. A
202
+ # category ref selects only that category's non-low rules unless --strict
203
+ # is set; naming a rule by its own id still selects it whatever its
204
+ # confidence.
202
205
  def select_rules(select, ignore, strict = false)
206
+ runs_by_default = ->(r) { r.confidence != "low" }
203
207
  rules = if select
204
- RULES.select { |r| select.include?(r.id) || select.include?(r.category) }
208
+ RULES.select { |r| select.include?(r.id) || (select.include?(r.category) && (runs_by_default.call(r) || strict)) }
205
209
  elsif strict
206
210
  RULES
207
211
  else
208
- RULES.select(&:default_on)
212
+ RULES.select(&runs_by_default)
209
213
  end
210
214
  if ignore
211
215
  rules = rules.reject { |r| ignore.include?(r.id) || ignore.include?(r.category) }
@@ -221,7 +225,8 @@ module Sloplint
221
225
  # Recommended for agents:
222
226
  cat FILE | sloplint check --markdown -o json -
223
227
  # exit 0 = clean, 1 = notes found, >1 = error (empty input is an error)
224
- # each note: {path,line,column,severity,rule,category,message,excerpt,context,rationale,suggestion}
228
+ # each note: {path,line,column,severity,confidence,rule,category,message,excerpt,context,rationale,suggestion,count}
229
+ # (count is present only for the rules that tally items)
225
230
 
226
231
  usage: sloplint [-o full|json] [command] [args]
227
232
 
@@ -5,7 +5,7 @@ require_relative "rules"
5
5
  module Sloplint
6
6
  # One match = one Note. See docs/SPEC.md "Note".
7
7
  Note = Data.define(
8
- :path, :line, :column, :severity, :rule, :category,
8
+ :path, :line, :column, :severity, :confidence, :rule, :category,
9
9
  :message, :excerpt, :context, :count, :rationale, :suggestion
10
10
  )
11
11
 
@@ -32,7 +32,8 @@ module Sloplint
32
32
  message = count ? rule.message % { count: count } : rule.message
33
33
  notes << Note.new(
34
34
  path: path, line: line, column: column,
35
- severity: rule.severity, rule: rule.id, category: rule.category,
35
+ severity: rule.severity, confidence: rule.confidence,
36
+ rule: rule.id, category: rule.category,
36
37
  message: message, excerpt: matched.gsub(/\s+/, " ").strip,
37
38
  context: context_for(source, m),
38
39
  count: count, rationale: rule.rationale, suggestion: rule.suggestion