Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* text=auto
*.sh text eol=lf
*.py text eol=lf
21 changes: 19 additions & 2 deletions ATTRIBUTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,38 @@ This repository packages material from several upstream sources. Each is listed
below with its license and what was taken. If you redistribute this work, these
obligations travel with it.

## Required copyright notices

The MIT License requires the copyright notice to travel with the code, so the
notices themselves are reproduced here rather than only linked:

```
Copyright (c) 2025 Siqi Chen (blader/humanizer)
Copyright (c) 2026 Carter Johnson (jcarterjohnson/vibecoded-design-tells)
```

Each is MIT-licensed; the full text is in [LICENSE](LICENSE), which is the same
MIT text those projects use. `skills/humanizer/scripts/copy_scan.py` also carries
its upstream notice in its own docstring.

## Summary

| Component | Upstream | License | Obligation |
|---|---|---|---|
| `skills/humanizer/SKILL.md` | [blader/humanizer](https://github.com/blader/humanizer) | MIT | Keep copyright and license notice |
| ...its underlying pattern catalog | [Wikipedia:Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing) | CC BY-SA 4.0 | Attribute and share alike |
| `skills/humanizer/references/copy-tells.md` | [jcarterjohnson/vibecoded-design-tells](https://github.com/jcarterjohnson/vibecoded-design-tells) | MIT | Keep copyright and license notice |
| `scripts/copy_scan.py` | same as above (`devibe_scan.py`) | MIT | Keep copyright and license notice |
| `skills/humanizer/scripts/copy_scan.py` | same as above (`devibe_scan.py`) | MIT | Keep copyright and license notice |
| `skills/structural-humanizer/**` | original work, this repo | MIT | Cite the paper below if you build on it |
| StoryScope findings | Russell et al. 2026, arXiv:2604.03136 | academic citation | Cite, do not relicense |

## 1. humanizer (MIT, plus CC BY-SA upstream)

`skills/humanizer/SKILL.md` originates from the `humanizer` skill by
[@blader](https://github.com/blader/humanizer), released under the MIT License.
It tracks upstream **2.9.1** (the frontmatter records the version it was rebased
from). Local additions: the pass-1-of-2 scope note, the plain-emotion paragraph in
PERSONALITY AND SOUL, the public-facing-copy section, and the pipeline handoff.

That skill is in turn built from
[Wikipedia:Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing),
Expand All @@ -42,7 +59,7 @@ get your own legal read first. The rest of this repository is unaffected.
[jcarterjohnson/vibecoded-design-tells](https://github.com/jcarterjohnson/vibecoded-design-tells)
(MIT).

`scripts/copy_scan.py` is a port of the four copy rules (`copy-em-dash`,
`skills/humanizer/scripts/copy_scan.py` is a port of the four copy rules (`copy-em-dash`,
`copy-antithesis`, `hype-copy`, `copy-servile`) from that repo's `devibe_scan.py`,
narrowed to prose files and given a standalone CLI.

Expand Down
57 changes: 43 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,16 @@ Then the authors ran AI text through LAMP, a professional span-level rewriting s
that strips cliche, purple prose, and redundant exposition. Functionally, a very good
surface humanizer.

Detection dropped **1.6 points**.
Detection dropped **1.6 points** (95.5% to 93.9% macro-F1). Scope of that test: 278
Gemini stories, with Gemini itself as the rewriter.

Meanwhile the surface layer is eroding on its own. GPT 5.4 already cut its em-dash
usage sharply, and fine-tuning drops stylistic detection from 97% to 3%. Word-level
tells are a moving target. Structural tells require structural rewrites.
Read the claim precisely. Style is not a *weak* signal in that paper: style-only
features score 85.8% macro-F1 and text-based baselines separate human from AI at
99.8-99.9%. The point is durability. Style is the layer that a rewrite, a fine-tune, or
a model update can move, and it is already moving: GPT 5.4 cut its em-dash usage
sharply, and fine-tuning to mimic human style drops stylistic detection from 97% to 3%
(Chakrabarty et al. 2026). Structure is the layer that survives a surface rewrite, so
removing it takes a structural rewrite.

So: pass 1 fixes the words. Pass 2 fixes the shape. Run them in that order.

Expand All @@ -37,24 +42,34 @@ skills/
humanizer/ Pass 1: words and phrasing
SKILL.md
references/copy-tells.md Copy-specific tells (em dash, hype vocab, antithesis)
scripts/copy_scan.py Deterministic scanner for mechanical copy tells
structural-humanizer/ Pass 2: discourse structure
SKILL.md
references/
storyscope-findings.md The study distilled: 30 core features with rates
genre-calibration.md Which audits apply per genre
scripts/structural_scan.py Deterministic scanner for grep-able structural tells
scripts/
copy_scan.py Deterministic scanner for mechanical copy tells
docs/
PIPELINE.md How the passes chain, and what each one owns
tests/
run_tests.sh Fixture assertions for both scanners
```

Each scanner lives inside the skill that calls it, so both keep working after
`install.sh` puts the skills in `~/.claude/skills/`.

### Pass 1: `humanizer`

Vocabulary, punctuation, and phrasing. Inflated symbolism, promotional language,
superficial "-ing" analyses, vague attributions, em dash overuse, rule of three, AI
vocabulary, negative parallelism. Built from Wikipedia's
Vocabulary, punctuation, and phrasing: 33 patterns covering inflated symbolism,
promotional language, superficial "-ing" analyses, vague attributions, em and en dashes,
rule of three, AI vocabulary, negative parallelism, aphorism formulas, manufactured
punchlines, and more. Tracks [@blader/humanizer](https://github.com/blader/humanizer)
2.9.1, which is built from Wikipedia's
[Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing).
Two of its guardrails matter more than any pattern list: **never invent facts** (a
fabricated specific is a defect even when it reads more human than the vague original)
and **voice calibration** (a user-supplied writing sample outranks the style rules,
including the em-dash ban).

The `copy-tells.md` reference adds the tells that show up specifically in public copy,
ranked by a 3.2M-post Reddit analysis of what people actually flag.
Expand Down Expand Up @@ -119,15 +134,23 @@ Both scanners are deterministic and hook-friendly. They catch the pattern-matcha
slice only, and neither replaces the judgment work in the skills.

```bash
python3 scripts/copy_scan.py draft.md
python3 skills/humanizer/scripts/copy_scan.py draft.md
python3 skills/structural-humanizer/scripts/structural_scan.py draft.md

python3 scripts/copy_scan.py --json draft.md # machine-readable
python3 scripts/copy_scan.py --strict draft.md # exit 1 on any hit
cat draft.md | python3 scripts/copy_scan.py - # stdin
python3 skills/humanizer/scripts/copy_scan.py --json draft.md # machine-readable
python3 skills/humanizer/scripts/copy_scan.py --strict draft.md # exit 1 on any hit
cat draft.md | python3 skills/humanizer/scripts/copy_scan.py - # stdin
```

Mark a line `copy-ignore` to suppress an intentional usage.
`copy_scan.py --strict` exits 1 on any hit. `structural_scan.py --strict` exits 1 only
once a category reaches its threshold (2 hits per document), because one embodied-emotion
line is a choice and four are a habit.

Mark a line `copy-ignore` to suppress an intentional usage. Fenced code blocks, indented
code blocks, inline code spans, and comment lines are skipped automatically; headings,
blockquotes, bullets, and table cells are treated as copy and scanned.

Run `tests/run_tests.sh` after editing a rule; it asserts the fixture hit counts.

## Honest limits

Expand All @@ -139,6 +162,12 @@ Mark a line `copy-ignore` to suppress an intentional usage.
a person did.
- **The scanners catch maybe half.** Cadence, formulaic shape, and polished-but-empty
filler are only visible to a human reader.
- **The em dash ranking is contested in its own source.** It is the most-cited tell in
the Reddit corpus (7.1%), but one of that project's five auditors argued the count is
inflated by incidental punctuation rather than actual complaints. Treat it as a strong
signal, not a proven #1.
- **Nothing here is a detector.** The scanners flag surface patterns and the audits are
editorial judgment. Do not read a clean scan as "this will pass an AI checker."
- **Audit 3 contradicts standard writing advice.** "Show, don't tell" is now a machine
signature. That is what the data says, and it is worth sitting with before applying.

Expand Down
16 changes: 12 additions & 4 deletions docs/PIPELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ draft
|
v
[ pass 1: humanizer ] words, phrasing, punctuation, copy tells
| scanner: scripts/copy_scan.py
| scanner: skills/humanizer/scripts/copy_scan.py
v
[ pass 2: structural-humanizer ] discourse shape, arc, emotion mode, reference
| scanner: skills/structural-humanizer/scripts/structural_scan.py
Expand Down Expand Up @@ -71,13 +71,21 @@ Worth knowing what drafted the text, because each model converges differently.

## Hooking the scanners

Both scanners support `--strict`, which exits 1 on any hit. That makes them usable as a
pre-commit or pre-publish gate:
Both scanners support `--strict`, but they mean different things by it. `copy_scan.py`
exits 1 on any hit. `structural_scan.py` exits 1 only when a category reaches its
threshold (2 hits per document for embodied emotion, stated lesson, tidy closer, or
vague allusion), because a single embodied-emotion line is a choice and four are a habit.
Its paragraph-uniformity and number-density metrics are informational and never affect
the exit code.

```bash
python3 scripts/copy_scan.py --strict content/**/*.md \
python3 skills/humanizer/scripts/copy_scan.py --strict content/**/*.md \
&& python3 skills/structural-humanizer/scripts/structural_scan.py --strict content/**/*.md
```

Installed rather than cloned? The scanners ship inside their skills, so use
`~/.claude/skills/humanizer/scripts/copy_scan.py` and
`~/.claude/skills/structural-humanizer/scripts/structural_scan.py`.

Use this as a gate on outward-facing content only. Applied to internal docs it will fire
constantly on text that nobody needs to be human.
30 changes: 26 additions & 4 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ while [[ $# -gt 0 ]]; do
shift
done

IS_WINDOWS=0
case "$(uname -s)" in
MINGW* | MSYS* | CYGWIN*) IS_WINDOWS=1 ;;
esac

mkdir -p "$TARGET"
echo "installing into $TARGET (mode: $MODE)"

Expand All @@ -44,15 +49,32 @@ for skill in "${SKILLS[@]}"; do
fi

if [[ "$MODE" == link ]]; then
ln -s "$src" "$dst"
echo " linked $skill"
if [[ "$IS_WINDOWS" -eq 1 ]]; then
# Git Bash silently turns `ln -s` into a deep copy unless MSYS is
# configured for native symlinks, which breaks `git pull` updates
# without saying so. A directory junction is the reliable equivalent
# and needs no elevation.
win_src="$(cygpath -w "$src")"
win_dst="$(cygpath -w "$dst")"
if cmd //c mklink //J "$win_dst" "$win_src" > /dev/null 2>&1; then
echo " junctioned $skill"
else
cp -R "$src" "$dst"
echo " copied $skill (junction refused; re-run install.sh after a git pull)"
fi
elif ln -s "$src" "$dst" 2>/dev/null; then
echo " linked $skill"
else
cp -R "$src" "$dst"
echo " copied $skill (symlink refused; re-run install.sh after a git pull)"
fi
else
cp -R "$src" "$dst"
echo " copied $skill"
fi
done

chmod +x "$REPO/scripts/copy_scan.py" \
chmod +x "$REPO/skills/humanizer/scripts/copy_scan.py" \
"$REPO/skills/structural-humanizer/scripts/structural_scan.py" 2>/dev/null || true

cat <<EOF
Expand All @@ -61,6 +83,6 @@ done. verify with:
ls -la $TARGET | grep -E 'humanizer'

run the scanners:
python3 $REPO/scripts/copy_scan.py <file>
python3 $REPO/skills/humanizer/scripts/copy_scan.py <file>
python3 $REPO/skills/structural-humanizer/scripts/structural_scan.py <file>
EOF
Loading