Commit Graph

67 Commits

Author SHA1 Message Date
Codex bbc8bb23c5 fix: scan punctuation prefixes in linear time 2026-09-12 10:56:22 -06:00
Conor Bronsdon 1f299b5e37 fix: remove quadratic scans from analyzeText on whitespace-heavy input
Four regexes rescanned a long whitespace or blank-line run from every
position, so a document ending in blank lines or carrying a large masked
comment block took seconds instead of milliseconds:

- the sentence splitter behind highlight regions, replaced with a
  single-pass scan that yields the same spans as the former regex
- its boundary-whitespace trim in rendered-Markdown mode, now trimStart
  and trimEnd arithmetic instead of a `\s*$` match
- the Markdown table delimiter test, now run on the trimmed line
- the line-anchored `Interesting part:` opener, whose leading `\s*`
  crossed newlines under /m and is now `[ \t]*`

The #190 fixture still scaled quadratically after #196 because masked
comments become whitespace, which the sentence splitter then choked on.
64000 masked comments now analyze in under a second.

Regression tests compare sentence boundaries against the former regex on
every boundary shape and assert linear growth by ratio rather than by a
wall-clock budget. Bundled detector copies regenerated.

Closes #235

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmCsufeKSGpesBP7w4Xf4j
2026-09-12 16:51:58 +00:00
Conor Bronsdon 3202951c0a fix: make rendered comment masking single-pass 2026-09-12 10:05:04 -06:00
Conor Bronsdon 652a391223 Merge origin/main into cursor/html-comment-masking-linear-bcc0 2026-09-12 10:04:56 -06:00
Conor Bronsdon f760a6b56f fix: harden source offset composition
Compose retained source coordinates across blockquote and normalization passes. Cover interleaved and overlapping removals, preserve staged roleplay semantics, and avoid quadratic preprocessing.

Co-authored-by: Sharad. <sharadvc@users.noreply.github.com>
2026-09-12 09:40:22 -06:00
Sharad. eeff687e42 fix: linear HTML comment masking in rendered Markdown mode
Run maskCode once per document and advance a single comment-open scan.
Refresh code or list-aware views only when a removed comment contained
backticks or list markers, preserving #123 edge cases.

Fixes conorbronsdon/avoid-ai-writing#190

Co-authored-by: Sharad. <sharadvc@users.noreply.github.com>
2026-09-12 13:34:48 +00:00
sreevx 2ee4740ead fix: preserve source offsets through detector preprocessing 2026-09-12 15:51:10 +05:30
Sharad. 6c89503ed1 docs(detector): align contextMode comments with runtime behavior
Remove the nonexistent marketing boost from patterns.js comments.
Document all four accepted modes in CATEGORIES.md; only technical
changes flagging today. Regenerate mirrored detector copies.

Fixes conorbronsdon/avoid-ai-writing#173

Co-authored-by: Sharad. <sharadvc@users.noreply.github.com>
2026-09-12 03:12:07 +00:00
Conor Bronsdon e79254c892 fix: prepare CLI package release 2026-09-11 12:29:32 -07:00
Conor Bronsdon a0128758c9 docs: fix packaged house-style README link 2026-09-06 09:14:57 -07:00
Conor Bronsdon e1603b3d4d docs: correct detector context comment 2026-09-06 08:43:33 -07:00
Conor Bronsdon 66da51290c fix: treat em dashes as style-only guidance 2026-09-06 08:37:06 -07:00
Conor Bronsdon 28ba61bc9f fix: preserve prose quote boundaries around malformed bare URLs 2026-09-06 08:02:44 -07:00
Conor Bronsdon f654266ef9 Address release review: editable inputs, valid links, and catalog guidance 2026-09-05 13:04:13 -07:00
Conor Bronsdon 079392d042 Sync Claude bundle with delimiter-order fix 2026-09-05 12:55:05 -07:00
Conor Bronsdon 222fff80fc Sync Claude Markdown protection with final review fixes 2026-09-05 12:51:19 -07:00
Conor Bronsdon b902d8db71 Clarify Tier 1 exceptions and same-line load-bearing boundary 2026-09-05 12:45:01 -07:00
Conor Bronsdon 8856007ba8 Sync Claude bundle with single-quote preservation fix 2026-09-05 12:36:46 -07:00
Conor Bronsdon 9c1a048a45 Regenerate release bundles from repaired canonical sources 2026-09-05 12:35:39 -07:00
Conor Bronsdon 0aa0def0ee Split canonical skill and ship complete Claude bundle 2026-09-05 12:34:24 -07:00
UniversePeak 3dd72f5ec6 Fix automatic marks inference and malformed link scanning 2026-09-06 03:11:15 +08:00
Conor Bronsdon 7552fa3aa3 Keep explained transformations exempt from rewriting 2026-09-05 11:43:43 -07:00
Conor Bronsdon b43142a0e0 Include clarity rules in P2 triage and preserve explained summaries 2026-09-05 11:38:43 -07:00
Conor Bronsdon c5ec5b8a93 Complete catalog clarity rules with source-preserving guidance 2026-09-05 11:28:59 -07:00
Conor Bronsdon 081b495614 Narrow repeated reversal punchlines and integrate current main 2026-09-05 11:24:54 -07:00
odinfree d8c235186f Add detector trio from #108: launch intros, crowd contrast, fake-casual props (#128)
* Add detector trio from #108: launch intros, crowd contrast, fake-casual props

Three regex-detectable patterns from the welttowelt merged-system diff,
each with a must-fire and a must-not-fire fixture:

- launch-intro: sentence-initial imperative product introductions
  ("Enter X.", "Meet X, your new...", "Think X meets Y"). "Say hello
  to X" stays judgment-only ("Say hello to Grandma." is ordinary prose).
- crowd-contrast: the trailing "while everyone else..." clause, gated
  on a dismissive verb so literal simultaneity never fires.
- fake-casual-prop: stage directions, wink asides, and "because of
  course it does". Verdict closers and label-prefix openers stay
  LLM-judgment per the precision-over-recall policy.

Catalog 62 -> 65, engine 48 -> 51, word table unchanged at 112.

* Apply review fixes to the #128 detector trio

Addresses the three detector-surface items in the CHANGES_REQUESTED review,
plus the prose-scoping follow-up comment. Each item was reproduced against
the PR head before being changed.

1. `launch-intro`: the bare `Enter X.` surface is judgment-only. It matched
   "Enter Password." and "Enter Amount." verbatim, and "Enter Username —
   your work email." through the dash terminator; neither a terminator
   change nor a field-name denylist reaches the period-terminated class.
   The `Meet X, your new favorite…` and `Think X meets Y` surfaces stay.
   The three UI forms and "Enter Flowdesk." are pinned as must-not-fire.

2. `crowd-contrast`: the progressive branch is restricted to `-ing` forms.
   `(?:e|es|ed|ing)` let the adjective ("was still deliberate about"), the
   passive ("was still debated by pundits") and the bare present through;
   all three are now must-not-fire fixtures.

3. `fake-casual-prop`: `because of course …` is judgment-only. The present
   tense plus `did` gate does not hold — "The build failed because of
   course it did." is the ordinary human grumble in the same form. Two
   grumbles are pinned as must-not-fire.

Also scopes the prose claim: the "was still" marker gates the
debate/speculation branch only, and the two stereotyped variants
("wrote think-pieces", "played catch-up") match on their own wording.
SKILL.md, detector/CATEGORIES.md and the CHANGELOG say the same thing,
and the generated plugin/cursor copies are regenerated from SKILL.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Disclose the crowd-contrast residue that was measured, not assumed

Beyond the review. A fresh-eyes pass turned up sentences that fire and
that the entry's disclosure did not cover; every one was reproduced
against this branch before being written down.

- Branch one fires on any literal progressive use of its verbs, not only
  on "was still debating": "while the market was still speculating about
  the price" and "while others were still arguing about procedure" are
  ordinary wire copy and both fire.
- Branches two and three fire on literal contrasts of their own: "while
  everyone else wrote think-pieces from Washington" (a real reporting
  contrast) and "while everyone else played catch-up in the spring"
  (sports and classroom narrative).
- The wink-aside regex accepts all four of (yes|no) x (really|seriously);
  the entry named two.

No regex changes: these are accepted under precision-over-recall, and
narrowing them is the maintainer's call, not a review item. The prose now
states what the engine does instead of a tighter gate it does not enforce.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Tighten two surfaces and make every entry state what its regex does

Cross-family review (Codex / GPT-5.6). Each item was reproduced against
`land-pr-128` before the change and re-measured after.

Regex changes, both false positives on ordinary prose:

1. `fake-casual-prop`: `chef['’]?s\s+kiss` made the apostrophe optional, so
   "At midnight, *chefs kiss* their spouses goodbye" fired. The apostrophe
   is now required, straight or curly.
2. `launch-intro`: `the\s+new\s+(?:standard|way|home)\b` let the head noun
   swallow the first word of a compound noun — "Meet Rosa, the new home
   secretary" and "Meet Emma, the new way station manager" both fired. Each
   head now needs the tail launch copy actually has (`home of`, `way to`,
   `standard in|for`) or an end of clause. "Meet Riverside, the new home of
   our Saturday farmers market." still fires.

Not changed, disclosed instead: "Meet Alice, your new favorite aunt" and
"Think Alice meets Bob at noon" fire. They are person-name variants of the
two surfaces the maintainer asked to keep, so narrowing them would cost the
surfaces themselves.

Test change: the `(yes, really)` positive shared a fixture with
`*checks notes*` and only asserted that some `fake-casual-prop` existed, so
replacing the wink regex with a never-match left it green. It is now an
independent case asserting the matched text.

Prose: the three entries and the CATEGORIES rows now state the actual gates
— the four `Meet` heads and the one-token 2-30 character name; the
`was|were|is|are` + `still` + `-ing` branch, the `writing|wrote` branch and
the `play|plays|played|playing` branch; the six stage directions and four
parentheticals — plus the measured misses that follow from closed lists:
two-token names ("Meet North Star", "Think Google Docs meets Microsoft
Word"), crowds outside the list ("every competitor", "our rivals"), and
register neighbours ("*checks calendar*", "(yes, honestly)").

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: welttowelt <welttowelt@users.noreply.github.com>
Co-authored-by: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 12:24:44 -07:00
tech nasty 3bd64f19f4 Add seven rhetorical-tic categories from Simon Willison's LLM cliché highlighter (#138)
* Add seven rhetorical-tic categories from Simon Willison's LLM cliche highlighter

Three with detector types (performed-insight, negation-chain,
dev-blog-boilerplate) plus fixtures; four as skill-only judgment rules
with reasons recorded in CATEGORIES.md. Counts bumped per the CI
contracts (69 pattern categories, 51 engine types); version 3.28.0.

* Address review: sync 3.28.0 release artifacts, narrow detector precision

- Bump package.json, plugin.json, and regenerate the bundled skill and
  cursor rule (plugin-skill-sync now passes locally).
- negation-chain: the no-chain must open its sentence (mid-sentence
  inventories stay clean); the did-not chain is comma-joined with the
  subject elided (sequential narration stays clean).
- performed-insight: sit-with requires a boundary or 'for a moment'
  after that/this; the 'only X I trust' branch is dropped; sentence-
  initial 'Turns out' uses a lookbehind so the issue text and index no
  longer consume the previous sentence boundary.
- dev-blog-boilerplate: 'it just works' no longer matches the phrasal
  'it just works out'.
- Must-not-fire fixtures added for every reproduction in the review,
  plus a text/index regression assertion for Turns out.

* Narrow rhetorical-tic detector precision

* Align narrowed detector docs and slogan coverage

---------

Co-authored-by: tech nasty <262050521+t3chnaztea@users.noreply.github.com>
Co-authored-by: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com>
2026-08-28 21:15:27 -07:00
jeffhuen 4e79a67871 feat: add rendered Markdown source mode (#137)
* feat: add rendered Markdown source mode

* fix: address rendered Markdown review regressions

* fix: close rendered HTML comments before code masking

---------

Co-authored-by: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com>
2026-08-28 11:59:06 -07:00
宇宙的山峰 40328bd292 docs: restrict edit mode to prose files (#136)
Co-authored-by: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com>
2026-08-24 10:41:23 -07:00
Michael Hall 0bdab7bfbb docs: document npx skills as a universal install method (#131)
* docs: document npx skills as a universal install method

Points users at vercel-labs/skills (npx skills add) as the fastest way to
install this skill into any of its 75+ supported agents, with npx skills
update covering future updates. Existing manual per-platform steps stay as
a no-Node fallback.

* address review feedback: pin npx version, dedupe count, add changelog entry

- Pin npx skills invocations to @latest explicitly and note it runs
  third-party code, with a pointer to pinning a specific version instead.
- State the "75+ agents" figure once instead of in two places.
- Add the 3.25.1 CHANGELOG entry this repo's conventions expect for a
  notable change, even though no rule/detector content changed.

* address maintainer review: pin skills@1.5.23, fix behavior claims, add repository field

- Pin npx invocations to skills@1.5.23 instead of @latest for a reproducible
  install, and note the CLI's Node >=22.20.0 requirement.
- Correct the install description: for a root-level skill like this one, the
  CLI's GitHub blob fast path installs SKILL.md only, not the whole repo
  (per vercel-labs/skills src/blob.ts). git clone remains the documented
  route for the detector, tests, and other repo tooling. Drops the now-
  redundant raw-SKILL.md-URL example since the shorthand does the same thing.
- Correct the update description: skills update prompts for project/global
  scope and refreshes the selected scope, not every install across both.
- Add metadata.repository to SKILL.md's frontmatter so a copy installed via
  the SKILL.md-only fast path still links back to the project. Synced to the
  plugin copy via the existing sync-plugin-skill.sh.

* fix: vary sentence structure instead of repeating em-dash appositive

Self-audit with the avoid-ai-writing skill flagged the same "clause —
appositive clause" shape used six times in ~600 words across the new
Quick Install prose. Split into separate sentences, semicolons, and
commas as appropriate, keeping one dash for genuine variety instead of
zero.

* docs: clarify skills CLI install behavior

---------

Co-authored-by: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com>
2026-08-24 10:27:58 -07:00
Mahin Nadir ca2206c2f5 Bind voice-profile targets to Never-inject guardrails (fixes #100) (#133)
* Bind voice-profile targets to Never-inject guardrails (fixes #100)

* fix-ups for #133: sync generated copies, changelog, and formatting

- drop the unrelated Dockerfile
- give the guardrail intro line its own paragraph (leading space folded
  it into the previous one) and fix the professional-profile comma splice
- bump plugin.json to 3.25.1 and regenerate the bundled plugin skill and
  cursor rule via the sync scripts
- add the 3.25.1 changelog entry crediting @mahinNadir

npm test green (39/39 check-style, full suite passes).

---------

Co-authored-by: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com>
2026-08-21 18:09:23 -07:00
cland4449 8277224829 Add paraprosdokian detection category (v3.26.0)
LLMs deploy twist-ending sentences as a default wit move, clustering in
hooks, closers, and list-final items. The reversal substitutes a rhythm
trick for a claim, so flag it in analytical and professional prose.
Includes the scale-then-deflate marketing variant ("Four steps, and only
one of them is yours."). LLM-judgment rule with no detector type: spotting
a twist requires reading setup and payoff together, which a regex cannot.
Engine stays at 48 types and the word table at 112.

Catalog goes from 62 to 63 categories, P1 severity. Cursor port and
plugin copy synced, counts verified against CI scripts.
2026-08-17 10:02:12 +10:00
welttowelt 6f77e683cf Add catalog-only entries from #108: false agency, transformation crutch, domain-term collision
Judgment-only rules per the precision-over-recall policy, with the
carve-outs written as operative entry text per review:

- False agency: literal verbs and collective-actor shorthand ("the
  market rewards shipping") are protected in the entry itself.
- Transformation crutch: literal and mechanism-stated transformations
  stay unflagged.
- ZK/crypto domain-term collision ships as two genre-scoped Tier 3
  phrase rows (skipped by the deterministic engine); the cross-genre
  generalization is held as a note.
- "This matters because" joins the persuasive-authority tropes;
  the concession-fragment reassurance pair joins manufactured
  punchlines.

Catalog 65 -> 67, README Tier 3 phrase count 10 -> 12; engine stays
at 51, word table at 112.
2026-08-16 18:16:28 +02:00
welttowelt 7b5fdc73b2 Add detector trio from #108: launch intros, crowd contrast, fake-casual props
Three regex-detectable patterns from the welttowelt merged-system diff,
each with a must-fire and a must-not-fire fixture:

- launch-intro: sentence-initial imperative product introductions
  ("Enter X.", "Meet X, your new...", "Think X meets Y"). "Say hello
  to X" stays judgment-only ("Say hello to Grandma." is ordinary prose).
- crowd-contrast: the trailing "while everyone else..." clause, gated
  on a dismissive verb so literal simultaneity never fires.
- fake-casual-prop: stage directions, wink asides, and "because of
  course it does". Verdict closers and label-prefix openers stay
  LLM-judgment per the precision-over-recall policy.

Catalog 62 -> 65, engine 48 -> 51, word table unchanged at 112.
2026-08-16 18:14:31 +02:00
Conor Bronsdon 3c0fd8a266 Add actually as a cut-first writing signal (#126)
Co-authored-by: Codex <codex@openai.com>
2026-08-12 21:06:47 -07:00
Michael Hall 46087cea58 feat: detect unnecessary hyphenation (#120)
* feat: detect unnecessary hyphenation

* fix(detector): harden hyphenation masking

* fix(detector): mask long path components

* fix(detector): keep zero-weight copyedits out of AI highlights

buildSentenceRegions() mapped every text-bearing issue into
highlight_sentence_for_ai, so a copyedit-only document scored 0 and
classified HUMAN_ONLY while still reporting an AI-highlighted region.

Rename SUMMARY_ONLY_TYPES to NON_HIGHLIGHT_TYPES, since the set now
covers both anchorless summary signals and anchored style-only findings,
and add unnecessary-hyphenation to it. The finding stays in issues[] at
P2 with its weight of 0; scoring and trinary classification are
unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrhAmTQkVxzX6UkmTL2TCf

---------

Co-authored-by: Conor Bronsdon <cmbronsdon@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 17:45:12 -07:00
kkwanda b72b7c42b1 Write the openclaw emoji literally in SKILL.md frontmatter (#118)
Claude organization plugin sync rejects a skill whose SKILL.md frontmatter
contains a hex/unicode escape, because an escaped key can parse differently
across parsers. That makes the plugin unavailable to Team and Enterprise
organizations distributing it through Organization settings > Plugins.

Replaces the escaped form with the character it denotes. Same emoji, same
bytes once parsed; only the source representation changes.

Plugin copy regenerated with scripts/sync-plugin-skill.sh.
2026-08-06 11:57:18 -07:00
Conor Bronsdon a575602b3d release: 3.23.1 — constructor false-positive fix, automated npm publishing (#117)
Version bumped across all five sites (SKILL.md, plugin copy, plugin.json,
package.json, cursor rules regenerated via the sync scripts). First release
through the guarded publish pipeline from #113.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 12:24:51 -07:00
Clay f666087e52 feat: optional --style house-style layer (config-driven, points at Vale) (#99)
* feat: optional --style house-style layer (config-driven, points at Vale)

Implements the config-driven design recorded in #88, which #76 and the fork
review converged on. No bundled guides: the layer applies register/voice
directives and removes AI tells on top of whatever mechanics you enforce.

--style ./house.json applies a user-supplied JSON config (register +
mechanics) and scripts/check-style.js verifies the CHECKABLE mechanics
deterministically: quotes and latinAbbrev gate the exit code (0 clean /
1 hard / 2 tool error), headings, emDash and spellNumbersUpTo are advisory,
serialComma is never checked. The docs scope the verb accordingly rather
than claiming "mechanics are verified" flatly. examples/ ships two generic,
guide-neutral starters plus the schema, and links #88 for the licensing
rule.

A bare --style "APA" is a best-effort fallback, not a feature: SKILL.md
instructs the model to print a no-compliance-claim status line and not to
reproduce the guide's text. Those are instructions rather than checked
rules, and the docs say so instead of asserting the behaviour as fact.

--style is listed in the SKILL.md invocation line and has an explicit
composition rule with --voice/--context, since the file specifies
composition for every other axis.

README: the "House style is a different job" section said "There's no
--style input", which this makes false. The first paragraph now describes
the config input and reconciles with the heading (enforcing a PUBLISHED
guide is the different job). The Vale paragraph and the heading anchor are
unchanged.

cursor-rules: the ported rule would have told a Cursor user to run
scripts/check-style.js and read examples/, neither of which ships in the
.mdc. Two anchored spans in sync-cursor-rules.sh rewrite those the way
span 3 already handles detector/validate.js, so the port stays
self-contained. Worth noting the drift guard only covers edits to anchored
spans, not newly added SKILL.md sections that introduce repo references.

The checker went through three adversarial review rounds, and each round
found the previous round's fix had introduced a new way to hard-fail a
correct document or to miss a real one. The tests pin all of them: markdown
link titles, reference definitions (including escaped quotes in a title),
HTML attribute values, nested and tilde fences, BOM'd frontmatter, URLs
containing parentheses, parentheticals that wrap or span a code block, and
a stray "(" that must not suppress later findings, and prose containing a
comparison ("n<N ... >") that must not be mistaken for an HTML tag. Masking runs refdef ->
links -> tags, in that order, because masking a link destination first
makes an ordinary "[a](url): text" line look like a reference definition.
Link masking walks to the matching paren and scans linearly; the regex form
was quadratic (22.7s on a hostile line, now 4ms). resolveConfig decides
path-vs-name by shape so a cwd file can't shadow a bundled example, the CLI
parses by position, a config with "mechanics": null or [] exits 2 rather
than a false-green 0, and advisory findings print instead of hiding behind
--json.

Version 3.22.3 -> 3.23.0 in SKILL.md frontmatter, package.json and
plugin.json; CHANGELOG entry under a dated heading; plugin and cursor
copies regenerated.

No detector changes: catalog counts unchanged (61 / 112). 32 tests in
scripts/check-style.test.js under npm test; detector-test.yml path filters
extended to scripts/** and examples/**.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* style: mask indented code, tighten frontmatter open, announce the resolved mode

Review fixes applied on top of the submitted branch:

- Indented code blocks (4+ spaces / tab after a blank line, outside a list)
  are masked, so pasted terminal output can't hard-fail a correct document.
  Lazy continuation and list-item content stay checked; a code block nested
  in a list item needs a fence. Was the largest undocumented false positive.
- A document opening with a thematic break is no longer read as frontmatter
  (which silently swallowed everything to the next ---).
- The bare-name traversal regex was dead code its test never reached; a bare
  name is a single path segment by construction, so the guard is now a
  comment stating that argument, and a test pins dot-bearing names.
- Config runs open by naming the resolved config, mirroring the fallback's
  status line, so which of the two modes ran is always explicit in output.
- Known-limitations lists gain the double-backtick span and next-line
  reference-definition title gaps; examples/README.md joins the self-scan
  budgets; CHANGELOG updated; cursor-rule anchors follow the SKILL.md edit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com>
2026-08-03 17:48:46 -07:00
Conor Bronsdon 5897f7b497 skill: close five prose-contract gaps flagged in downstream review (#97)
* skill: close five prose-contract gaps flagged in downstream review

All five from the cubic review on davila7/claude-code-templates#773.
Prose only: no rule, threshold, or detector changes. Tables join the
edit-mode flag-don't-fix list (matching what validate.js already
enforced), the rewrite job line scopes 'all AI-isms removed' to
editable ones, edit mode gets an explicit instruction boundary, the
tracking-parameter fix leaves the rest of the query string alone, and
the second-pass audit must say when it supersedes section 2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* review: close the gaps the clarification pass itself opened

Adversarial pre-merge review caught four: two validate.js header
comments quoting the pre-fix sentences verbatim (refreshed,
comment-only), an 'only the listed parameters' exclusivity claim that
contradicted AI_URL_PARAMS and ai-utm-source coverage (reworded), and
seven stacked 'X, not Y' contrastive negations on one line where the
file's previous maximum was two (three rewritten as direct positives).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 11:59:15 -07:00
Conor Bronsdon ae1ecbebf0 detector: stop counting non-tag "#" forms as hashtags (#90) (#92)
* detector: stop counting non-tag "#" forms as hashtags (#90)

hashtag-stuff counted every #word and flagged at six, so a paragraph
citing six issue numbers scored as a stuffed tag block. Found when this
repo's own README linked issue #88 and the detector flagged the README.
The bug report cannot be written without triggering the bug either: the
prose of #90 scores hashtag-stuff on its own issue references.

Two subtractive changes, so the rule cannot start firing on anything it
does not already fire on.

maskCode() blanks fenced blocks and inline code spans, index-preserving,
before counting. fenceRanges() already existed but was wired only to
title-case-header, so a tag quoted in backticks counted as a tag used.
Fences are blanked before inline spans so a fence backtick cannot pair
with a later span and swallow the prose between them.

isSocialTag() subtracts forms that cannot be a tag: all-digit (#88), hex
colours of 3/4/6/8 digits, and C preprocessor directives. owner/repo#88,
URL fragments, shebangs, and Markdown headings already passed on the
rule's own anchor and are untouched.

Deliberately still counted: #main and #general. A CSS id, a channel, and
a tag are the same token. The only separator is an adjacency gate, and
measuring it showed it also stops the rule catching inline tag stuffing,
which is a real shape. Accepted cost of the hex carve-out: an all-hex
word tag (#dad, #cafe) stops counting, which only changes an outcome if
five real tags sit beside it.

Six fixtures: four that must not fire, two that must, per CONTRIBUTING.
Mutation-tested both halves -- disabling isSocialTag kills 3 tests,
disabling fence masking kills the suite. No new detector type, so
CATEGORIES.md is unchanged and the pattern count stays 61.

Gates green: npm test, check-pattern-count 61/112, self-scan SKILL.md
12/25, sync-plugin-skill 3.22.1.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Address adversarial review: a false negative, fake coverage, and no version bump

Three real problems, one of them worse than the bug being fixed.

The hex carve-out covered 3- and 4-digit forms, which silently deleted
them cost true positives on exactly the stuffed-block shape the rule
exists to catch -- two verified blocks that flagged on main went silent.
Trading a false positive for a false negative is the wrong direction in a
repo whose stated bias is precision over recall. Narrowed to 6 and 8
digits. Real palettes are dominated by 6-digit values, so the CSS
paragraph still lands under the threshold without the short forms.

The fixture named for inline-code masking did not test it. Four of its
six spans quoted forms isSocialTag already subtracts, so with masking
deleted the count was 3, under the threshold, and the test passed.
Rewritten with six spans that all survive the carve-outs.

No CHANGELOG entry and no version bump on a behavior change to an engine
downstreams vendor byte-identical. Two different patterns.js byte-streams
would both have called themselves 3.22.1. Now 3.22.2 across package.json,
SKILL.md frontmatter, and plugin.json, with an entry that records the two
recall changes (unclosed fence, stray backtick) rather than only the win.

Also mask indented code blocks. A fence nested in a list item is indented
past the three spaces fenceRanges anchors, so a README config block still
counted -- the exact shape masking was added for.

Mutation-tested all nine mutants now killed, including the inline-masking
one that previously survived: remove inline / fence / indented masking,
isSocialTag always true, drop digit / hex / cpp carve-out, widen hex to
3-8, threshold 6 to 7.

Gates green: npm test, self-scan:check (SKILL 12/25, CHANGELOG 29/40),
check-pattern-count 61/112, sync-plugin-skill 3.22.2.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Third review pass: drop the indented-code pass, and stop eating a-f words

Two false negatives and one artifact that never got the correction.

SKILL.md still said CSS hex colours "(#fff, #1a2b3c)" are subtracted.
The narrowing to 6/8 digits landed in patterns.js and in the CHANGELOG
and never traveled to SKILL.md -- the file agents load and downstreams
vendor. Verified wrong: seven 3-digit colours still flag. This is the
same shape as the two claims the first review pass caught, arriving a
third time through a different door.

Dropped the indented-code masking pass entirely rather than patching it.
Four spaces is a code block only at top level; under a list marker it is
a paragraph continuation, so blanking it silenced real tag blocks:

  - We shipped the detector ...
  (blank)
      #AI #Innovation #FutureOfWork #MachineLearning #Leadership #Growth

flagged on main and went silent on the branch. #90 reports fences and
inline spans; the indented pass was scope I added in response to review
pass two, and it was the sole source of eleven surviving mutants
including a real off-by-one in its offset arithmetic. Deleting it removes
the arithmetic and the state machine with it.

HEX_COLOUR now requires at least one digit. #decade, #facade, #deface
and #beaded are 6-8 char a-f words and real tags; every actual palette
value carries a digit. Same defect class as the 3/4-digit carve-out the
last pass removed, one size up, and not eliminated by that fix.

Fixtures for both regressions plus an 8-character RGBA case, which was
the one genuine coverage hole left: dropping the {8} alternative used to
survive. Two mutants remain undistinguishable (inline pass reading text
vs withoutFences, and an inclusive blank boundary); I probed for inputs
that separate them and found none, so they are equivalent rather than
uncovered.

Gates green: npm test, self-scan (CHANGELOG 29/40), check-pattern-count
61/112, sync-plugin-skill 3.22.2.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 22:49:51 -07:00
Conor Bronsdon 3b5bbc1193 Fix title-case-header on Markdown headings, and the FPs that fix introduced (#62)
Closes #62.

The pattern was anchored `^[A-Z][a-z]+`, so it never fired on a Markdown heading — the first character is `#`. It caught the bare-line form while missing the commonest way a heading is written.

Fixing that surfaced a larger false-positive class, found by adversarial review: `matchPatterns` reports `match[0]`, so `##` counted as a token and silently lowered the proper-noun guard from four content words to three. Separately, the guard's function-word test had no position constraint, so a leading `The` satisfied a rule whose own comment specifies a *mid-sentence* "And".

Measured on 81 files that provably predate LLMs: 13 false positives against zero on main, every one opening with `The`. Requiring the function word to be interior eliminates all 13 and leaves the target case firing.

Also rewrote fence detection — counting delimiters inverts on a four-backtick fence wrapping a three-backtick example, which is how you document fences — and removed an unreproducible score claim from the changelog rather than correcting it.

10 fixtures; 6 mutations run, all killed. #67 and #69 verified already fixed in 3.22.0 and closable.
2026-07-31 12:08:01 -07:00
Conor Bronsdon fba57d3c38 Enforcement, self-scan proof, and the first measured accuracy numbers (v3.22.0) (#68)
Adds a preservation validator, a self-scan with PROOF.md, over-correction guardrails, a false-positive intake, and a two-class control corpus with the project's first FPR, TPR, and AUC.

Splits Tier 1 into 1A frequency markers and 1B clarity edits, and fixes two detector defects the measurement found (#67, #69).

Findings and open decisions: #70, #71, #72, #73.
2026-07-30 22:53:01 -07:00
Conor Bronsdon 94998a9ff9 Add narrated-candor rule (v3.21.0) (#66)
* Add narrated-candor rule (v3.21.0)

Announcing your own disclosure instead of disclosing: "Two caveats I
would rather flag than let you discover later:", "I want to be upfront:",
"in the interest of full disclosure", "rather than bury this", "I could
have left this out, but". The content is "Two caveats:"; everything else
advertises the writer's forthrightness.

This completes a set. Chatbot artifacts perform helpfulness ("I hope this
helps!"), sycophantic tone flatters the reader ("Great question!"), and
this performs candor about oneself. All three are shapes assistant
training rewards, which is why they cluster in generated text. The
construction is usually a matched antithesis - flag rather than let you
discover, say plainly rather than bury - so the symmetry is doing work
the content should.

Precision carve-out: the disclosure itself is never matched, only the
separable clause about disclosing. "I haven't tested this on Windows",
"the numbers don't reproduce on my hardware", and "this is a mitigation,
not a fix" stay clean, as does a bare "Two caveats:". Bare "to be honest"
and "to be transparent" are left to the existing hollow-intensifier rule
rather than double-flagged. Four must-not-fire admissions are in the
fixtures alongside the positives, because a rule that discourages plain
admissions would be worse than no rule.

Found in the wild the ordinary way: it appeared in a PR comment I wrote,
Conor called it out as bad writing, and it wasn't in the catalog.

- SKILL.md: new rule + P1 severity entry, version 3.21.0
- detector: narrated-candor type, weight 3, medium severity
- CATEGORIES.md: direct-mapping row, 46 -> 47 types
- fixtures: three true positives, five must-not-fire cases
- README: 60 -> 61 pattern categories
- plugin manifest + bundled skill synced

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Demote narrated-candor to a judgment-only rule

An adversarial review of the first implementation found the regex set
unfixable at acceptable precision, so the detector type is withdrawn
before release and the rule ships as skill prose only. This follows the
precedent set by wall-of-text replies, which was tried as a detector and
reverted for the same reason.

The regexes flagged 13 of 16 careful-human sentences, including idiomatic
conflict-of-interest disclosure ("In the interest of full disclosure, I
own shares in the company discussed in this article") and the ordinary
English comparative ("I'd rather fix it than let you inherit the mess").
Neither is narrated candor: the first carries the material fact, and the
second is a preference about work. CONTRIBUTING is explicit that a rule
which wrongly flags ordinary human writing is worse than one that misses
a tell.

The bounded-repeat pattern also backtracked catastrophically. A nullable
subpattern inside {1,3} took 36 seconds on a 4 KB input and did not
finish on 20 KB. This engine scores untrusted pasted text, so that was a
denial-of-service defect independent of the rule's merits. Measured after
this change: 1 ms at 4 KB, 2 ms at 20 KB.

Two claims in the first version were false and are gone. The "carve-out"
fixtures only tested text the regexes could not match, which proved
nothing; and the documented handoff of bare "to be transparent" to the
hollow-intensifier rule did not exist, because that phrase is not in that
rule either. The prose now states both carve-outs explicitly, and README
gains the catalog row 55 that the count bump had been missing.

Engine returns to 46 types. Catalog stays at 61, since the rule is still
a section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 17:46:37 -07:00
Conor Bronsdon 3a34c68375 Add lingering-attention claims rule (v3.20.0) (#65)
The share-post frame that claims a thing has occupied the writer's mind
instead of saying anything about the thing: "the line I keep coming back
to," "I can't stop thinking about this," "rattling around in my head all
week."

Separate category from emotional flatline: flatline claims a feeling,
this claims duration of attention, which is unfalsifiable and
self-flattering in a way a feeling is not.

Precision carve-out: the bare "I keep coming back to X" does not fire,
since a following reason clause makes it legitimate and is not reliably
regex-detectable. Only the noun-anchored frame matches.

- SKILL.md: new rule + P1 severity entry, version 3.20.0
- detector: lingering-attention type, weight 3, medium severity
- CATEGORIES.md: direct-mapping row, 45 -> 46 types
- fixtures: two true positives + the must-not-fire carve-out
- README: 59 -> 60 pattern categories, catalog row 54
- plugin manifest + bundled skill synced

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 19:28:48 -07:00
mrlsecurity 22529c2338 Add "verbatim" as a Tier 3 (density-gated) word (#63)
Adds "verbatim" to the word table at Tier 3, so it flags only on overuse rather than always. Contributed by @mrlsecurity at Tier 1; moved to Tier 3 on review since the case is redundancy, not AI-specific frequency, and always-flagging over-fires on ordinary human writing.
2026-07-29 10:55:44 -07:00
delight-f 4d9e8510ea Add moral-adjective category errors and invented contrast-pair mirror (#61)
* Add moral-adjective category errors and invented contrast-pair mirroring rules

Two new AI-slop detection rules discovered during a real audit (bacoff project docs/Explanation.md):

1. Moral-adjective category errors: catches AI gluing moral/character
   adjectives (honest, genuine, faithful) onto non-agentic technical
   nouns (shape, number, representation) where the adjective cannot
   literally modify the noun. Also covers passive-voice moral adverbs
   ('described honestly'), ontological slop on assumptions ('stops
   being true'), and gratuitous universal quantifiers.

2. Invented contrast-pair mirroring: catches AI fabricating the second
   half of a contrast pair for symmetry ('false precision rather than
   genuine accuracy') where one term is a real term of art and the
   other is phantom.

Both added to P1 severity tier, tolerance matrix (relaxed for
technical-blog and docs), and skill-only section in CATEGORIES.md.
Version bumped to 3.19.0.

* Get #61 merge-ready: fix CI gates and clean up changelog

- README pattern count 57 -> 59 to match the two new SKILL.md categories
  (check-pattern-count.sh gate).
- plugin.json 3.18.0 -> 3.19.0 to match SKILL.md frontmatter, and regenerate
  the plugin SKILL.md mirror (sync-plugin-skill.sh gate).
- CHANGELOG: reset to main and re-author a single clean 3.19.0 entry. The
  submitted version had a wrong-year date (2025), a hyphen instead of the
  em-dash date format, and ~55 spurious blank lines inserted file-wide.

Rules and their prose left as authored. All gates green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 10:52:37 -07:00
Conor Bronsdon 27156c7ae6 v3.18.0: carve out definition-list separator em dashes (#60)
* v3.18.0: carve out definition-list separator em dashes

An em dash after a bolded lead term or markdown link opening a list item
("**Term** — description", "[label](url) — description") is typography,
not a prose splice, and no longer counts toward the 1-per-1,000-words
rate. The detector exclusion is line-anchored with an optional bullet
marker, so mid-sentence splices still count. Fixture pair added: a
definition-style feature list stays clean; the same dash density in
flowing prose still fires.

This repo's own README and changelog use the separator convention
throughout — that's the strict-context false positive in practice.
Same carve-out independently proposed upstream in blader/humanizer#190.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011MYk8KLh4KovTFwW2idcqb

* Require the list marker in the em-dash separator carve-out

Review found the optional bullet marker excluded any line merely
starting with a bold phrase or link before the dash — including the
line-initial "**Bold lead** — full sentence" splice, which is itself
an AI tell and an evasion path for the rate rule. The marker is now
required, matching the SKILL.md prose ("list-item separator"), and
the prose spells out the boundary: only the list-item form qualifies,
mid-sentence and markerless line-initial splices still count, and the
-- substitute is never carved out.

Two boundary fixtures added: numbered-list separators stay clean
(must-not-fire) and markerless bold-lead splices still flag
(must-fire — this one fails without the regex change).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011MYk8KLh4KovTFwW2idcqb

* Separator dashes no longer corroborate smart-punct-signature

The co-occurrence signature (curly quotes + em dash + Oxford comma +
zero typos) used bare em-dash presence for its dash leg, so a clean
definition list with separator-only dashes still completed the
signature the rate carve-out had just declared typography. The
separator regex is hoisted to a shared SEPARATOR_DASH_RE and the
signature's em-dash leg now requires a non-separator dash. Fixture:
a curly-quoted, Oxford-comma'd definition list (85 words, fired
before this change) no longer completes the signature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011MYk8KLh4KovTFwW2idcqb

* Cover the paren-numbered list style (1)) in the separator carve-out

Review residual: the marker alternation matched 1. but not 1), so a
paren-numbered definition list still counted toward the rate. Numbered
branch is now \d+[.)]; fixture extended with a 4) line.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011MYk8KLh4KovTFwW2idcqb

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-22 10:56:06 -07:00
Conor Bronsdon 3200004bc3 Rename "Subjectless fragments" to cover its passive-voice half; split 3.17.0 changelog entry (#59)
- "Subjectless fragments" → "Subjectless fragments and agentless passives":
  two of the three examples are agentless passives with grammatical subjects,
  and the old name hid that half of the rule's scope from anyone scanning
  category names. Renamed in SKILL.md (heading + tolerance matrix),
  detector/CATEGORIES.md, and CHANGELOG.md.
- Split the 3.17.0 "Added" entry from one dense paragraph into one bullet
  per category, matching the changelog's existing style (cf. 3.15.0).
- Re-ran sync-plugin-skill.sh; pattern count (57) and tests stay green.


Claude-Session: https://claude.ai/code/session_011MYk8KLh4KovTFwW2idcqb

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-22 09:37:28 -07:00
allanbeke-sys 193f4f6314 Harvest the remaining blader/humanizer patterns: 4 categories + 4 rule extensions (v3.17.0) (#58)
Cross-audit of blader/humanizer v2.8.2 against the catalog; most of its 33
patterns were already covered (P21/P26/P27 direct, P34-P43 via forks). This
adds the residue that survived a false-positive review:

New (53 -> 57): subjectless fragments (P13), diff-anchored writing (P30),
manufactured punchlines / staccato drama (P31), aphorism formulas (P32).
Extended: tailing negation on "It's not X - it's Y" (P9), fragmented
headers on Excessive structure (P29), fake-candid openers on Infomercial
hooks (P33), put-voice-back note on Tone calibration.

All judgment-only rules (no detector type); carve-outs per
precision-over-recall. npm test + check-pattern-count + plugin sync green.

Co-authored-by: Allan BEKE <abeke@cathay.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 09:31:48 -07:00