Skip to main content

Hiro Fukushima

Write-Like-Me

A Measurement Framework for Writing Voice

Applied AI
Case Study
AINLPStylometryComputational Linguistics
Write-Like-Me

01. What problem it solves

Text that an AI writes has habits a reader learns to recognize. It sets up a point as "X, not Y", calls a problem "the real weakness" or a result "genuine", joins clauses with em dashes, opens sentences with words such as "Importantly", and calls things "robust" or "innovative". It takes back a claim in the same breath with "that said" or "to be fair", ends on a caveat nobody asked for, writes short lines of abstract nouns that sound like a tagline, and adds names and numbers nobody gave it.

Six passages from the checker's own tests and the hit it reports for each, with its line number. Clarity, not clutter gets a review hit for COMPRESSED_ANTITHESIS, delete the tail if it adds nothing. The weights are fixed, joined to the next clause by an em dash, gets a block hit for EM_DASH, use a comma or a period. Importantly, the results held across all conditions gets a block hit for STANCE_OPENER, delete it. This innovative approach reduces computation time gets a block hit for EVALUATIVE_ADJECTIVE, replace it with the fact that earns it. That said, the plan holds gets a review hit for HEDGE_CONNECTIVE. Keep in mind that the handle gets warm, on line 5, gets a review hit for CLOSING_CAVEAT. A block hit fails the draft, and a review hit goes back to the writer with its fix.

An AI asked for a finished text, such as a product description, writes it the way that kind of text usually sounds, and the result can pass a checker that looks for banned words and still tell the reader nothing. Rules against these habits can sit in a skill, a set of instructions that an assistant such as Claude Code loads for a task, but the assistant often writes without loading it.

For anyone who publishes under their own name, this means the text speaks in the AI's voice, and a reader who knows these habits can tell that the person did not write it.

I built Write-Like-Me so that a voice is measured from the person's own writing and enforced in code. It measures about 50 features of how the person writes, such as sentence length, commas per sentence, how often they hedge or speak in the first person, and which punctuation they never use, and it measures each kind of writing on its own, so a casual message and a formal article do not become one averaged style. The measurements and any notes or corrections the writer already has become rules, the writer approves them, and they are saved with the number targets and three to five passages of the writer's own text as a voice profile. A checker in code reads every draft against that profile and the AI habits, reports each hit with its line number and a fix, and fails the draft on any rule that allows no exception.

For certain kinds of texts, such as an email, a post or a reply, the AI first explains the subject to the writer and, after approval, cuts the text from that explanation, with a check that the text contains no number or name the explanation did not have. Hooks in Claude Code recognize a request for such a text, add this step and run the checker when the text is saved.

02. The work

Write-Like-Me is a skill for Claude Code that works in two modes. When no voice exists yet, it builds one from the person's writing in seven steps, and once a voice exists, it writes in that voice. Its checker also runs on its own, on any text and without a voice, with nothing installed but Python.

The build asks for the writing, for any exported chat conversations and for any style notes the writer already keeps. From a chat export it keeps only the writer's own turns and shows five random samples, so the writer can confirm that nothing the AI wrote got in. Below 20,000 words every rule that comes from a count is marked as provisional, so a habit that is missing from a small sample stays open until the writer confirms the rule against a new text. It also runs its rules against AI habits over the writer's own texts and lists each rule the writer breaks, with a count and examples, such as a "not only ... but also", and the writer decides whether that habit stays.

Build mode in seven stages. 01 Discover, with the corpus, chat exports and style notes. 02 Extract and Preview, with the writer's own turns and 5 random samples. 03 Analyze, with stylometry.py for each kind of writing. 04 Mine Rules, with corpus_conflicts.py and provisional rules. 05 Review, a stop where a yes turns a rule into a check and a no removes it. 06 Emit, with the voice folder and claude-ai-skill.md. 07 Verify, with voice_eval.py verify --strict. Below, the corpus size after filtering. Under 5,000 words the build stops, from 5,000 to 20,000 words rules from counts are provisional until the writer confirms them, and from 20,000 words they are reliable.

Before anything is written, the skill stops, lists every proposed rule with its evidence and tells the writer that a yes turns the rule into a check and a no removes it. The writer also sets two or three of their own paragraphs aside. After approval it writes the voice, with one version that can be uploaded to Claude.ai, and tests it by having the AI write on the topics of the set-aside paragraphs and scoring the results strictly, so a sample fails on any flagged pattern.

Stage 5, the review, stopped and waiting for the writer. Rule 1, no contractions, with the evidence of 0 contractions in the primary register, provisional under 20,000 words and answered yes. Rule 2, semicolons at review, with no semicolons in the corpus, provisional. Rule 3, a conflict with NOT_ONLY_BUT_ALSO, which blocks in the writer's own texts, to keep the habit or keep the rule. A yes makes the rule a check, and a no drops it. Stage 7, the verification, uses the two paragraphs set aside at stage 5. The AI writes one paragraph on the topic of each, voice_eval.py verify --strict fails a sample on a review hit too, and the result is VOICE_EVAL_OK.

In a test run, a new assistant with no knowledge of the project built a voice this way for an invented author from eight essays, went through all seven steps, and then wrote in that voice, explaining first and cutting the text after approval.

03. My role

I designed and built Write-Like-Me. I decided that every feature it measures has to rest on published research on how writing styles differ, so that what the skill reports can be defended by pointing to that research. I decided that the voice is enforced by a checker written in code, that the checker has its own tests, from unit tests to sets of example passages with known results, and that a change to a rule ships only with a new example passage and after every test passes again.

04. Key decisions

Message 1, the explanation, plain sentences as told to the writer, marked approved. Message 2, the text, cut from the explanation and shorter. Below them, the check run with --explanation reports ADDITION for any number or name the explanation lacks.
01

The explanation comes first

Testing a voice that Write-Like-Me had built from my own writing, I noticed that an AI explains a subject in plain, natural sentences when it talks to a person, and that it switches into a promotional voice when it writes the same subject for readers. An analysis of about 384 times I had rejected or corrected what the AI produced confirmed it, and almost none of them came from a rule the checker had. More checker rules would not have reached the switch, so for the kinds of texts other people read, I made the explanation the first step of the method.

The writer's prompt, Can you write a reply to this email?, with write and email marked, a request word and then within 60 characters a kind of text. The UserPromptSubmit hook runs before the AI reads the prompt and adds the method. The PostToolUse hook runs when the text is saved and runs the checker.
02

Hooks start the skill

The same analysis showed that the skill holding my voice was often skipped for the texts it was made for, and I asked for it by name 30 times when that happened. A skill that has to be remembered gets skipped, so I put its start into hooks that run on their own.

Eight essays, each with a count of 0 sentences of five words or fewer. The earlier version turned that count into the ban Never use a sentence of five words or fewer, which is crossed out and stops before the voice, profile.json. The provisional rule, open until the writer confirms it against a new text, is the one that reaches the voice.
03

A missing habit is not a rule yet

In a walkthrough of the earlier version, eight essays by an invented author without a single sentence of five words or fewer produced the rule never to write one, and the author had to reject it by hand. A habit missing from a small sample is weak evidence, which is why such rules are now provisional.

TRICOLON, lists of three items. 74 runs in which it was the only flag were overridden or ignored. Of the three levels, block fails the draft, review goes back to the writer and info is reported and leaves the verdict alone. A line runs from review, crossed out and marked before, down to info, marked now.
04

A flag that is mostly overridden becomes a note

The checker flagged lists of three items for review, and 74 runs in which that was the only flag were overridden or ignored. I lowered the rule to a note that is still reported and leaves the verdict alone.

05. How it works

One request through the system in six steps. 01, the writer's prompt in Claude Code, Can you write a reply to this email? 02, the UserPromptSubmit hook adds the method to the AI's instructions. 03, the AI explains first and writes after approval, cutting the reply from the explanation. 04, the PostToolUse hook reads the path and extension to decide that it is a text for readers and which kind of writing. 05, voice_check.py runs with the voice, the approved explanation and the rejected phrases. 06, the verdict BLOCK, REVIEW or PASS goes back to the AI. Inside the checker, it reads lines of prose and skips code, tables and sources, sets each rule to block, review or note, blocks REPEATED_PHRASE for a phrase from the rejected list, reviews ADDITION for a number or name the explanation lacks and reviews drift in sentence measurements from 150 words. The hook returns Fix every block hit, then write the file again for BLOCK, Resolve or justify each review hit in one line for REVIEW, and one line of status for PASS.

One request through the system

When a writer asks Claude Code "Can you write a reply to this email?", a hook reads the prompt before the AI does. It looks for a word such as "write", "draft" or "reply" followed within 60 characters by a kind of text such as "email", "post" or "description", and when it finds one, it adds the method to the AI's instructions. A question about a text, such as "is this good", also sets it off, and then the AI answers yes or no first and changes nothing until the writer asks.

When the reply is saved, a second hook reads the file's path and extension to decide whether it is a text for readers and which kind of writing it is. It then runs the checker with the voice, the approved explanation and a list of phrases the writer has rejected before.

Inside the checker

The checker splits the file into lines of prose and leaves out code, tables and a list of sources, so its rules read only the sentences a reader reads. The voice sets every rule for each kind of writing to one of three levels, block for a rule that allows no exception, review for a pattern the writer decides on, and a note that is reported and leaves the verdict alone. A phrase from the rejected list always blocks. A number or a name in the reply that the explanation lacks is a review hit. For a text of 150 words or more, the checker also measures how the sentences are written, such as their length, the commas in them, hedging and first person, and reports every measurement outside the limits of the voice as a review line.

When the checker blocks, the hook blocks and tells the AI to fix every block hit and write the file again. Review hits go back to the AI with the instruction to resolve or justify each one in one line before the text is shown, and a clean text gets one line of status.

Several voices and a rebuild

One skill holds several voices, each in its own folder, and the hook checks the reply against the voice named in its settings. A voice built from a published author sets patterns to follow, while the writer's own voice sets rules. A voice is built again when the writer's writing grows or a rule keeps being corrected, the earlier version is kept, and the new rules replace it only after they pass the checker tests against the earlier record and the writer confirms the change.

How the checker is tested

The checker is tested against 78 example passages, 42 for the AI habits and 36 for the rules on texts for readers, and each passage is marked as one that must pass, must be flagged for review or must be blocked. A scoring run counts how many of the passages that must be caught were caught and how many clean passages were blocked, and it compares both numbers with a saved record, so a rule change that catches less or blocks a clean passage fails. A separate test runs the checker on a file full of AI habits, which has to be blocked, and on a clean file, which has to pass, so the checker is shown to be able to fail.

06. Evidence

120tests of the scripts, all pass
43tests of the hooks, all pass
78example passages, all caught
9block hits on the file of AI habits
The 78 example passages, one square each. Of the 42 for AI habits, 12 must be blocked, 7 must be flagged and 23 must pass. Of the 36 for texts for readers, 4 must be blocked, 16 must be flagged and 16 must pass. The checker caught 39 of 39 and blocked 0 of 39 clean passages, VOICE_EVAL_OK. Below, a probe of the installed checker. probe.md has three lines, The checker is a gate, not a suggestion, It reads every draft against the voice, so you can publish faster, and Keep in mind that the profile needs approval. With a writer's voice, the checker reports COMPRESSED_ANTITHESIS on line 1 and BENEFIT_TAIL on line 3 as review hits and HEDGE_CONNECTIVE on line 5 as a block hit, verdict BLOCK with one block and two review hits.

Every test passes

In the installed version, all 120 tests of the scripts that build a voice and check a draft pass, and so do all 43 tests of the hooks.

The checker catches what it must

On the 78 example passages, the checker blocked or flagged every passage that it had to catch, and it blocked none of the clean ones.

The checker can fail

The file full of AI habits came back blocked with 9 hits from rules that allow no exception, and the clean file passed with none.

A probe of the installed checker

A short text with an "X, not Y" label, a sentence ending in "so you can" and a closing "keep in mind" went through the installed checker with a writer's voice. The checker named all three patterns, and because that voice allows no exception for the caveat phrase, it blocked the text.

07. What I learned

My corrections were mostly about AI habits

Before the analysis of my corrections, I expected that most of them were about habits of AI writing that most readers reject, more than about my own taste, and the analysis confirmed it. Only three things were specific to me, among them never using contractions. So I built checks for these habits into Write-Like-Me for every writer, and kept my writing and my corrections out of the skill, with made-up examples for its tests. For anyone who corrects what an AI writes, a correction about a habit most readers reject can become a check that works for every writer.

Five patterns from the analysis of my corrections, with how many corrections named each and the check Write-Like-Me has for it. Adding what was not asked for, 78, ADDITION. Two extremes of length, 43 long and 21 short, LENGTH_BAND. Repeating a phrase after a correction, 63, REPEATED_PHRASE. Switching into a promotional voice, 45, TAGLINE. Hedged answers and caveats, 29, CLOSING_CAVEAT. Specific to me were three things, among them no contractions.