* 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>
* 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>
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.
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>
* 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>
* 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>
* 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>
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.
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.
* 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>
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>
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.
* 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>
* 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>
- "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>
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>
Adds metaphorical "load-bearing" as a Tier 1 phrase — an LLM tell for labeling any dependency an argument rests on ("load-bearing assumption/claim/invariant"). Matches the hyphenated compound only; unhyphenated "load bearing" is ordinary English.
Construction carve-out exempts attributive use before a literal structural noun (wall, beam, column, joist, truss, member, footing, slab, stud, partition, masonry, lintel, pier, rafter, girder, capacity), with an optional material/position adjective in between. Abstract-capable nouns (structure, element, frame, foundation) are excluded so the metaphor still fires on "the load-bearing structure of his argument".
Known gap: predicative use ("the wall is load-bearing") still flags — the tell lives in the subject, which a lookahead cannot reach. Carve-out design tracked in #56.
Sources: Marek Suppa, Yaniv Bernstein, Developers Digest, HN 48905248, r/ClaudeAI.
* v3.15.0: conversational-register patterns (wall-of-text, recap-flattery)
Adds three catalog entries prompted by a real-world GitHub issue exchange
where a maintainer called out an assisted-sounding reply with "I prefer
to talk human to human" (name/repo withheld):
- Wall-of-text replies: reply-length text delivered as one unbroken
paragraph with no line breaks, where humans in conversational registers
break at thought boundaries instead. Documented as an LLM-judgment
rule after a first-pass detector implementation broke an existing
calibration fixture and, on review, would have fired on any ordinary
short paragraph rather than anything AI-specific.
- Recap-flattery opener: summarizing the reader's own work back at them
with praise before getting to the point. LLM-judgment rule.
- Immaculate typography in casual registers: folded into the existing
Formatting section at the same weak-signal tier as curly quotes, plus
its inverse (preserve a human's typos when editing their casual text).
Catalog goes from 51 to 53 detection categories; the deterministic
engine stays at 45 types (no new type — see the CATEGORIES.md §C note
and the NOTE in patterns.js for why the detector attempt was reverted).
Also catches the Cursor port up from v3.12.0 to v3.15.0, closing a gap
where it had missed the 3.13.0 and 3.14.0 rule changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Review fixups: recap-flattery dedup line + wall-of-text carve-out
Two neighbor-standard gaps found in adversarial review of the new
conversational-register rules:
- Recap-flattery opener now explicitly distinguishes from Sycophantic
tone and Acknowledgment loops (every adjacent category carries a
"Distinct from X" clause; without it the skill's audience would file
dupes against those two).
- Wall-of-text replies now carries an explicit legitimate-use carve-out
(a single dense paragraph is correct in blog/docs/formal registers),
per CONTRIBUTING's precision-over-recall rule. Notes why a tolerance
matrix row is the wrong scoping home: a plain issue comment
auto-detects to the `blog` profile, so a skip-on-blog cell would
suppress the rule in the exact register it targets.
Ported to cursor-rules/.mdc and synced to the plugin copy.
Contracts still green: npm test (45 detector types), pattern count 53.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The proof-point half of #39 (raised by @hiSandog): manufacturing
credibility by pointing at an *unnamed* external authority plus a
generic superlative -- 'an outside party measuring the same models
everyone runs and putting us on top.' The inverse of Notability
name-dropping, which over-names specific sources.
Judgment-only rule (high false-positive surface, no regex). Carve-out
keeps specifically-attributed, checkable validation unflagged -- the
tell is the vagueness, not the citation. Catalog 50 -> 51. Version
3.13.0 -> 3.14.0.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Add speculative-scenario-opener detector + tropes.fyi gap-fill (#45)
Reviews tropes.fyi/directory against the skill. Most of the 33 catalogued
tropes were already covered; this adds the gaps that survived a
false-positive review.
New detection category (49 -> 50) + detector type (44 -> 45):
- Speculative scenario openers ("Imagine a world where...") — gated to the
world/future/reality object so instructional "imagine you have an array"
and analytical "consider a scenario where..." stay clean.
LLM-judgment / vocabulary additions (no new category):
- "deeply" added to Tier 2 (magic-adverbs trope), mirrored into the engine.
- Multi-negation countdown folded into the "It's not X, it's Y" rule.
- Invented concept labels folded into Novelty inflation.
- Historical analogy stacking noted under Notability name-dropping.
Version 3.13.0; plugin.json + bundled skill synced; README count bumped;
CATEGORIES.md updated to 45 types; fixtures cover a true positive and a
must-not-fire case for the new detector.
Credit: tropes.fyi and https://gist.github.com/ossa-ma/f3baa9d25154c33095e22272c631f5a1
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Gate "deeply" on significance collocations; review fixes
Adversarial review showed unconditional "deeply" in Tier 2 flags
clean human prose (deeply nested JSON + crucial, cares deeply +
nuanced) and can tip a borderline human document across the
HUMAN_ONLY boundary. New TIER2_CONDITIONAL mechanism: everyday
words join a cluster only when their AI significance collocation
matches; literal uses never count. Fixtures pin both sides.
Also: comma-interrupted speculative opener variant ("Imagine, for
a moment, a world where") + fixture; document the accepted
fiction/thought-experiment FP in the regex comment and changelog;
list the Sentence structure contrastive rule under Skill-only in
CATEGORIES.md and mark the invented-labels sub-rule judgment-only
(anti-drift contract gaps found in review).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Add quietly to Tier 2 word table
* Sync bundled plugin skill to 3.12.0
Regenerate plugins/.../SKILL.md from root SKILL.md via
scripts/sync-plugin-skill.sh and bump plugin.json to match.
Absorbs the missed 3.11.0 sync (plugin.json was still 3.10.0
on main) plus this PR's quietly addition. Fixes the red
plugin-skill-sync check.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The word table half-covered morphological variants and was inconsistent
about it (some rows list `x / xly`, most don't), so an auditor reading
`genuine` / `leverage` / `robust` had no instruction to extend to
`genuinely` / `leveraging` / `robustly`. Rather than enumerate every
inflection in every row, add one interpretive rule: each entry covers the
listed word and its variants (adverb, gerund, plural, comparative, verb
conjugations) unless a variant has a distinct legitimate sense, judged by
context. Future-proofs new entries and closes the gap that let "genuinely"
through. Applies to the LLM/prose path; detector regex parity is a
separate follow-up.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 spots in SKILL.md (mirrored in cursor-rules and the synced plugin copy)
had the Unicode replacement character where an em dash belonged, from a
lossy cp1252 round-trip somewhere in the file's history. Affected the
rhetorical-question/list-intro tells, the six voice-profile definitions
(linkedin/blog/technical-blog/investor-email/docs/casual), and one
profile-matrix table cell. Restored each to an em dash, matching the
surviving uncorrupted twin at the `technical` profile line. Byte-level
substitution only; no other content or line-ending changes.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The detector regex already matched /\bgenuine(?:ly)?\b/, but the SKILL.md
prose and word-removal table only named the adjective "genuine", so
LLM-driven /clean-ai-writing audits could miss the adverb. Spell it out
across SKILL.md, the cursor-rules mirror, and the synced plugin copy.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Add bold list-label periods detection category (#31)
LLMs end bold bullet-list labels with a period (**Intros.**) where a
person writes a colon (**Intros:**). Adds it as a new judgment rule next
to inline-header lists, bumps the catalog from 48 to 49 categories
(README count bullet + plugin manifest version), and records it in the
changelog.
Closes#31
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Broaden list-label periods beyond bold labels
Review feedback: an unbolded list heading ending in a period is the same
tell, just weaker. Renamed the category, added the unbolded form with a
tighter carve-out (1-4 word noun-phrase labels only), aligned README and
CHANGELOG.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Add social endorsement closer detection (#29)
LLMs commonly close LinkedIn/X share posts with a generic curatorial
sign-off teeing up a link — "This one is worth your time:", "Do yourself
a favor and read this", "Thank me later". The endorsement is
demonstrative-anchored and could sit under any link, which is the tell.
- New detector `type` social-cta-closer (engine 43 → 44 types), weighted
like formulaic-opener (8) so the length divisor doesn't wash it out on
a short post. Severity high (P1).
- New SKILL.md "Social endorsement closers" section (catalog 47 → 48),
plus P1-list and tolerance-matrix rows (strict on linkedin).
- Demonstrative anchors ("this one", "do yourself a favor", "won't want
to miss") keep it off plain human endorsements ("the book is worth
reading, but the middle drags"). Distinct from the bare "worth
[verb]ing" word-table entry and from infomercial mid-flow hooks.
- Tests: 3 fixtures (LinkedIn share post, bare-CTA variants, human-prose
negative). Mirrored into cursor-rules + synced plugin skill; counts
updated across README/CATEGORIES/package.json; version 3.8.0 → 3.9.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Harden social-cta-closer regexes against review findings
Two-agent review (code-reviewer + test-analyzer) found that 6 of the 10
closer patterns dropped their anchor and fired on ordinary prose, and
that the contraction branches missed curly apostrophes — so the detector
skipped the exact "you won't" form its own docs ship as the canonical
example (LinkedIn/Word/macOS auto-curl to U+2019).
Precision fixes:
- Apostrophe classes now admit U+2019 across every contraction branch
(won't / don't / can't / one's / you'll), matching the house style of
the existing smart-punct detector.
- "do yourself a favor and read THIS/IT" — gated to a demonstrative
object, so "...read the runbook" no longer matches.
- "miss this" / "bookmark this" — trailing-terminal lookahead, so the
closing-line shape fires but "miss this meeting" / "bookmark this page"
don't.
- "thank me later" / "save this for later" — sentence-initial lookbehind
(kept match text clean), so "she will thank me later" / "I usually save
this for later" don't.
- "trust me, ..." — dropped the bare "this one" branch that caught
"trust me, this one took all afternoon."
Tests: branch-coverage fixture (one positive per regex so a typo fails
loudly), a curly-apostrophe fixture, and a 7-case literal-prose negative
table built from the reviewers' false-positive probes. 66 checks green;
count + plugin-sync gates pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Promote the 'Self-labeling significance' pattern into the SSOT. It was
authored in a downstream copy and never pushed upstream: back-pointing
labels that flag which item in a list is supposed to matter ('That last
move is the contrarian one') instead of writing the list so the right
item carries the weight on its own. Distinct from confidence calibration
(front-loads the cue) and emotional flatline (prefaces a single claim).
LLM-judgment rule, no detector type. Catalog 46 -> 47.
- SKILL.md: new ### section, version 3.7.2 -> 3.8.0
- README.md: count 46 -> 47, catalog row 50, section label v3.5-3.8
- CHANGELOG.md: 3.8.0 entry
- detector/CATEGORIES.md: documented in section C (skill-only)
- plugin skill + plugin.json regenerated to 3.8.0
Guards: pattern-count in sync (47), plugin sync OK, npm test green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The #15 merge shipped the un-tightened rule and missed the inline review
suggestions. Applies them now:
- Reframe curly quotes/apostrophes from a "strong" tell to a WEAK,
corroborating signal — meaningful mainly in plain-text contexts
(code, commit messages, plaintext drafts), since Word/Docs/macOS/iOS
auto-curl by default.
- Stop flagging curly apostrophes (U+2019) standalone (every
contraction has one — discriminates nothing).
- Fix the German low-9 example; keep consistent with the deterministic
detector's co-occurrence logic (#16).
Verbatim from @conorbronsdon's #15 inline suggestions. Mirrored to the
Cursor rule, version 3.7.1 -> 3.7.2, bundled skill re-synced.
Co-authored-by: augustasas <augustasas@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings @augustasas's curly-quotation-marks rule up to date with main and
reconciles it with the overlapping "mixed straight/curly punctuation"
bullet that shipped in 3.7.0: keeps his stronger fingerprint framing
(curly-in-otherwise-ASCII = copy-paste-from-chat tell) plus his
publication-typography and locale carve-outs, and folds the mixed-case
note into it as one clause. One coherent Formatting rule, not two.
Mirrored to the Cursor rule, version 3.7.0 -> 3.7.1, bundled skill
re-synced.
Co-authored-by: augustasas <augustasas@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes#22. Ports the four catalog candidates from competitive research:
- Formatting: mixed straight/curly punctuation (paste-from-chat tell)
- New: hyphenated-pair overuse (density + attributive/predicate error)
- New: speculative gap-filling (hedged guesses dressed as background)
- Confidence calibration: extended with persuasive-authority tropes
Adapted from blader/humanizer (P21/P26/P27) + Wikipedia "Signs of AI
writing." Mirrored to the Cursor rule, version 3.6.0 -> 3.7.0,
CHANGELOG, bundled skill re-synced.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* v3.6.0: voice profiles + edit mode + iterate-to-convergence
Implements the low-risk product features from the competitive feature
spec (P1.C). Deferred: --score (#21) and 4 catalog candidates (#22).
- Voice profiles: optional persona axis (casual/professional/technical/
warm/blunt), orthogonal to the audience context profiles, with
concrete craft-backed targets (sentence length, contractions, hedging,
jargon, rhythm) + optional calibration to a writing sample + a
composition rule (voice = target, context = strictness, resolve
toward stricter).
- Edit mode: third mode that edits a named file in place via the Edit
tool with minimal, human-preserving changes, then verifies.
- Iterate to convergence: capped (N=2) audit->rewrite loop generalizing
the existing second pass.
- Documented optional flags alongside natural-language triggers.
Mirrored to the Cursor rule, version 3.5.0 -> 3.6.0, CHANGELOG, bundled
skill re-synced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Address sub-agent review of #23
- Voice/context composition: fix the worked example that contradicted
its own "resolve toward stricter" rule (blunt+blog em-dashes), and
state explicitly that a voice positive-target applies even where a
context profile skips that category. Also drops a copula-avoidance
"means" the skill flags elsewhere.
- Edit mode: add guardrail — don't edit quoted/code/third-party text
(flag instead); confirm scope on large files.
- Iterate: clarify the built-in corrective pass IS pass 2 (--iterate
doesn't stack), and cut hedge/filler phrasing.
- Docs staleness: README "Two modes" -> "Three modes" (+ Edit bullet,
voice note, description) and CLAUDE.md "two modes" -> "three modes".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* v3.5.0: add P38/P41/P43 catalog entries + sharpen P40
Ports the unambiguous catalog wins from the competitive audit of
Aboudjem/humanizer-skill (the P33-P35 fingerprints already shipped in
#11; these are the remaining "P1.B" spec adds):
- Infomercial engagement hooks (P41) — "The catch?", "The kicker?",
"Here's the thing." — distinct from rhetorical-question openers.
- Paragraph-reshuffle immunity (P38) — writer-side structure test.
- Treadmill effect / low information density (P43) — writer-side
content test, "what's actually new here?"
- Superficial -ing analyses — extended to the declarative
meaning-telling variant (P40).
Mirrored to the Cursor rule, version bumped 3.4.0 -> 3.5.0 (SKILL.md +
plugin.json + CLAUDE.md), bundled plugin skill re-synced, CHANGELOG
updated with source attribution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Tighten P43 entry: cut aphoristic "density is the antidote" line
Self-audit flagged it as mildly significance-inflating (adjacent to the
"generic conclusions" pattern the skill itself flags) and redundant with
the preceding sentence. Dogfooding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: distribute as a Claude Code / Cowork plugin
Package the skill as a single-plugin marketplace so it can be installed
via /plugin instead of a bare clone. This is the only path that works in
Claude Cowork, which loads skills exclusively from installed plugins
(see #12).
Root SKILL.md stays the single source of truth; the plugin's bundled
copy at plugins/avoid-ai-writing/skills/avoid-ai-writing/SKILL.md is
generated by scripts/sync-plugin-skill.sh. A CI workflow regenerates it
and fails on drift or a plugin.json/SKILL.md version mismatch.
Validated with `claude plugin validate` and a full local
add-marketplace / install / inspect / uninstall cycle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: parse plugin version safely in sync script
* fix: harden plugin version parsing in sync script
* harden skill-version parsing + plugin install docs
Builds on Copilot's plugin.json JSON-parser fix:
- sync-plugin-skill.sh: read the version only from the SKILL.md
frontmatter block (not anywhere in the file), strip CR, and fail with
a clear message if it's unparseable — closes the loose/false-green
side of the version check that the JSON parser already closed for
plugin.json.
- .gitattributes: pin *.md to LF so a CRLF checkout can't forge a
version mismatch or mask skill drift.
- README: add /reload-plugins to the install steps (skills don't
activate until reload/restart) and note updates are version-gated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>