Human writing, for the jwt repository

SkillFiles & storage

Lets your agent write and revise natural-sounding Chinese articles, forum posts, answers, stories, and speeches.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Human writing, for the jwt repository skill

About this capability

Use when writing or editing any prose in this repository - the book's preface, its chapters and epilogue, the brand kit notes, the agent skill documents, the changelog, and the root README. Also use when editing a doc comment in a .go file here. Strips the tells of AI-drafted prose (em dashes as cla

What this skill tells your AI

The instructions your AI receives, as published by kataras/jwt in skill/human-writing/SKILL.md and read by ahel’s review.

This repository is read by people deciding whether to trust a security library with their authentication. It has to sound like an engineer wrote it, because one did. Prose that reads as generated invites the question of whether the code was too.

This skill is the standard, and scripts/scan-tells.ps1 is the check.

Scope

Every tracked markdown file: book/**.md, brand/BRAND.md, skill/**/*.md including this one, and the root README.md and CHANGELOG.md. That is exactly the set the scanner's -Md mode walks, so if the two disagree, one of them has a bug.

Untracked files are out of scope whatever they contain. Local agent configuration is not published, so holding it to a standard no reader will ever see is wasted effort.

Doc comments in .go files are in scope for the standard but not for the scanner. The scanner skips Go entirely, because separating a doc comment from a string literal from a code sample inside a comment is more machinery than it is worth. Those are edited by hand, and the rules below still apply to them.

book/output/ is generated. Never edit it.

book/README.md, the preface, is the reference voice. When you are unsure how a sentence should sound, read it.

Scan before you write, and again after

pwsh skill/human-writing/scripts/scan-tells.ps1 book/03-claims.md
pwsh skill/human-writing/scripts/scan-tells.ps1 -Md              # the whole corpus
pwsh skill/human-writing/scripts/scan-tells.ps1 -Md -Summary     # counts per rule
pwsh skill/human-writing/scripts/scan-tells.ps1 -Wrap book/      # long lines
skill/human-writing/scripts/scan-tells.sh --md --summary         # no pwsh needed

The scanner is fence aware. It never reports anything inside a fenced code block, and it blanks inline code spans, link targets and bare URLs before matching, so an identifier or a URL containing a double dash is not a finding. Exit code is 1 while findings remain.

Zero findings is the bar for every file here. There are no correct-in-context em dashes in this repository.

The scanner is the only gate. No CI job runs it and no hook runs it: the hooks in this repository sync artefacts, they do not lint prose. So finish every writing session with a repository-wide pass, whatever single file you came to edit, and treat findings you did not write as yours to fix.

Em dashes: none in prose

An em dash between clauses is the strongest single tell in this corpus. There is always a better mark. Pick by the job the dash was doing. The examples below are in code spans so that this document passes its own scanner, which is the point of the escape hatch at the bottom of this page:

Job the dash was doingReplacementExample
Interrupting appositive, a pair of dashes mid-sentenceA colon, or two sentencesthree failures — a nil key, a short nonce, a bad kid — each panicked becomes three failures panicked: a nil key, a short nonce and a bad kid
Trailing explanationA period and a new sentenceVerify the token — the signature proves the key. becomes Verify the token. The signature proves the key.
Definition or expansionA colon`exp` — the expiry claim becomes `exp` is the expiry claim
Subordinate asideParenthesesthe header (which is signed) cannot be edited
A simple pauseA comma

Never substitute mechanically. Choose per sentence.

The en dash is fine in a numeric range (16–48 px). It is not a clause separator either.

The rules

Second person, and no contractions. Write you, not we and not the developer. Write do not, never don't. Full sentences with a subject and a verb.

No emoji, anywhere.

No admonition syntax. Not > [!NOTE], not :::warning. Where you want a callout, write a paragraph whose first phrase is bold, the way this list does.

Banned shapes. These are the ones an assistant produces by default:

  • A ## Conclusion that restates what was just said. End on the last real point, or on a ## Summary that adds the shape of the argument rather than repeating it.
  • provides a robust and flexible way to
  • This guide will walk you through
  • By leveraging
  • allows developers to, enables you to, makes it easy to
  • Negative parallelism: not only X but also Y, X is not just Y, it is Z
  • Participial tails: , ensuring the token is valid, , allowing you to rotate keys
  • Let's dive in, deep dive
  • It is worth noting that. If it is, note it and skip the preamble.

Word swaps.

Instead ofWrite
comprehensivefull, or drop it
robustreliable, or drop it
seamless, seamlesslydrop it
leverageuse
utilizeuse
crucial, vitalimportant, or say what breaks without it
powerfulsay what it actually does
performantfast, or give the number

Technical adjectives that carry information stay. Constant-time comparison is a fact about how the code runs, and zero-dependency is a fact about go.mod.

One concrete specific per claim. A number, a default, a flag name, an error name, a line of output. Verification is fast is worth nothing. Verification costs 18 allocations and about 2.5 microseconds on this machine can be checked, and argued with.

Concede a trade-off now and then. Every design choice here cost something. Saying so is what makes the rest of the page believable. Enrich verifying its input costs eight allocations; the small-size brand cut drops a whole tooth; Merge splices raw JSON and can therefore emit a duplicate key. Say it.

Vary the rhythm. Three sentences of the same length in a row is the sound of generated text. Follow a long one with a short one.

Editing rather than rewriting

Edit surgically. Text that already complies is left alone, and no edit changes the meaning of a sentence to make it easier to phrase.

In a .go file you are editing comments only. Never touch code to fix prose. A doc comment must still begin with the name of the thing it documents, which is a Go convention the standard here does not override:

// Verify checks a token's signature and its standard claims.

not

// This function checks a token's signature.

Godoc has no emphasis syntax. **bold** in a doc comment renders as literal asterisks, and ## Heading renders as literal text, because a heading needs a space after a single #. This package once carried over a thousand ** markers that all rendered as punctuation.

When the scanner is wrong

It will be, occasionally. A quoted error string containing a banned word, a rule name, a literal example of the thing being warned against. Two ways out, in order of preference:

  1. Put the text in backticks. Inline code is exempt, and a quoted error string usually should be code anyway.
  2. Rephrase so the word is not needed.

Do not add an exemption to the scanner for one line. A rule with holes in it stops being a rule people trust.

Signals

GitHub stars
211
Forks
26
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
human-writing
Source
github.com/kataras/jwt