Taking the Machine Fingerprint Off Agent Output
Three portable agent skills that strip the tells out of AI prose and code. Why the signal sits above the word level, why every rule carries an evidence grade, and why I refused to build it against a detector.
Models write toward the statistical center. Every token is a bet on the likeliest continuation, so the output clusters in a narrow region that no individual writer or engineer actually occupies. That clustering is what a reader notices, and it is why proofreading does not catch it: nothing in any individual sentence is wrong.
I built a set of agent skills to fix that. Three of them, portable markdown against the Agent Skills open standard, so the same files load in Claude Code, Codex CLI, Cursor, Gemini CLI, Copilot and the rest without modification. No dependencies and no build step.
The repository is private at the moment. I am making it public shortly, at github.com/iurman/notARobot. If that link is dead when you read this, I have not flipped it yet.
The three
humanize-writing rewrites prose to remove AI tells. Around 45 catalogued patterns across four tiers, each carrying an evidence grade so you can see which rules are backed by measurement and which are folklore.
humanize-code covers three failure families: the convergent visual aesthetic, backend anti-patterns ordered by measured frequency, and dependency version currency.
voice-profile mines your own words from local agent transcripts, git history and documents, then generates a portable profile any harness can load.
Three things I did differently
Era-segmented vocabulary
The AI word list drifts as models are retrained. "Delve" and "tapestry" now read as dated, which is a worse failure than sounding artificial, because it dates your text to a specific model generation. The current set is smaller and much harder to notice: showcasing, leverage, robust, seamless. A skill that only bans delve is fighting the last war, so the catalogues segment their lists by era and expect to be updated.
A structural tier
This is the one I would defend hardest. Sentence-length variance survives every lexical fix, and rhythm is what a reader registers before they can name why something feels off. Research on separating human from AI fiction found that discourse-level narrative features alone retain over 97 percent of the detection performance of models that also use stylistic cues. Most of the signal sits above the word level.
Which means a humanizer that only fixes vocabulary is working on a minority of the problem. On the sample in the repo's before-and-after doc, raising sentence-length standard deviation from 6.5 to 9.5 changed the read more than any word swap, while the word count barely moved.
Calibration instead of a hard ban
The default is a ban on em dashes. That is not a claim that em dashes are bad. It is the no-information default, and it gets replaced the moment there is information.
If you have generated a voice profile, your measured rate replaces the ban automatically. If you want something other than what you measurably do, a preference file overrides both. Somebody who genuinely writes with em dashes should not have to configure anything to stop being stripped, and with a profile they do not.
Every run reports what it did and why, with markers showing which rules were overridden by a profile and which by an explicit preference. You should always be able to see why a tell survived.
The version rule
The strongest-evidence part of the whole project is one line: never write a version number from memory.
Not in a manifest, not in a Dockerfile, not in an answer to "what version should I use." Studies on deprecated API usage in generated code put it between a quarter and a third across common Python libraries. Version-conditioned generation, where the model is explicitly told which version to target, still lands correct only about half the time.
That second number is the one that matters, because it kills the two obvious rebuttals at once. Use a newer model does not fix it. Just tell it the version does not fix it. The version has to be resolved from a registry, not recalled, so the skill carries the exact endpoints per ecosystem. If there is no network, it says so and leaves the version unset, because an unresolved placeholder is a visible problem and a confidently wrong version is an invisible one.
What it deliberately is not
Not a detector, and not detection evasion. Humanizers and classifiers are not inverses. Text can pass a rules-based cleanup and still classify as machine-written, and text tuned to fool a classifier can still read wrong to a person. Detection papers are cited in the repo as corroboration, but no rule derives from them, on purpose. Building rewrite rules from a classifier's feature weights would make this a detector-evasion tool, which optimizes against the wrong target entirely. These skills optimize for the human reader.
Not a fabrication license. Every skill carries a hard no-invention rule. Never invent facts, names, dates, statistics, citations, or metrics to satisfy a style rule. If a vague attribution cannot be resolved to a real source, the tell stays and gets reported.
Not a design dictator. The visual slop catalogue flags rather than fixes, because visual defaults belong to whoever owns the product. Two things get fixed outright: accessibility failures, because those are defects and not taste, and fabricated testimonials or metrics, because those are false claims about the world.
Proving it does anything
Structural checks prove the skills are well formed. They say nothing about whether they change behavior. So there is an eval suite with deterministic assertions and no model in the grading loop, which means the same output always scores the same.
Four of the eight cases check that a skill does not do something: strip a direct quotation, invent a citation to satisfy a style rule, silently redesign your page, or improvise a voice it has no profile for. Those matter more than the positive cases, and no structural check catches them. There is also a self-test proving the grader rejects known-bad output, because an assertion that accepts everything is worse than none.
The repo also dogfoods. Its own prose has to pass the em dash gate, which is how I found 26 of them in my own bibliography.
The honest part
Not every claim carries the same weight, and the docs say which is which. Some things are verified against reality. Some are fixture-tested against a documented schema, which is not the same as confirming the schema is current. One transcript path is a lead rather than a procedure, and says so. The frontend tells have mechanism but no frequency data, which is exactly why they are flagged and left for a human.
Grading your own evidence is uncomfortable and it is the thing that makes the rest trustworthy. A rule that says "observed" is a legitimate rule. A rule with no source is not.