· 9 min read

Teaching my Claude Code skills Simplified Technical English without losing my voice


A lot of the people reading the Block Developer Cookbook don’t speak English as a first language. I write those recipes with a handful of Claude Code skills that are pretty good at sounding like me. That voice is great in an intro. It’s less great in step 4 of a setup guide, when someone is pasting a sentence into a translator to figure out what “spin up” means.

So I wanted the steps readers follow to be dead simple, and everything else to still sound like me. I also didn’t want to guess whether it worked. I wanted numbers. I built it, ran it 81 times headlessly, and found two changes that made things worse.

What Simplified Technical English is

ASD-STE100 Simplified Technical English (STE) is a controlled version of English from aerospace maintenance manuals. It exists so a mechanic reading in their second language can’t misread an instruction. The core rules are short:

  • Procedural sentences are 20 words max.
  • One instruction per step.
  • Imperative mood, active voice.
  • Warnings come before the step they apply to.
  • One word, one meaning (“select” every time, not “select” then “choose” then “pick”).

The full standard includes a licensed dictionary that’s far too big to load into a skill. What I built is STE-informed, not certified STE.

Voice zones and action zones

Applying STE to everything would make my posts read like a torque spec. The fix was to split every piece of content into two kinds of zone:

ZoneRulesContains
Action zoneSTE winsNumbered steps, prerequisites, warnings, UI click paths, troubleshooting tables
Voice zoneMy voice winsIntros, “why this matters”, reasoning, transitions, closings

The rules live in one card, ste-card.md, inside a new ste-pass skill. Every other skill reads it live by path instead of keeping a copy:

${CLAUDE_SKILL_DIR}/../ste-pass/references/ste-card.md

That’s the same pattern my YouTube and What’s New for Developers skills already use for the shared voice card, and it’s why the voices don’t drift. write-article reads the STE card for tutorials. wp-workshop-scaffold uses it for the steps attendees follow. sounds-like-me, my voice-pass skill, reads it so it knows which lines to leave alone.

How I measured it

I froze three copies of the plugin:

  • baseline: before any of this.
  • ste: STE added.
  • optimized: STE plus a round of context trimming (more on that below).

Then I ran the same prompts through each one headlessly:

prompt="$(cat cases/team-post.md)"
claude -p "$prompt" \
  --plugin-dir snapshots/optimized \
  --settings '{"enabledPlugins":{"devrel@ryan-claude-skills":false}}' \
  --strict-mcp-config --model claude-opus-5 \
  --output-format stream-json --verbose \
  --permission-mode acceptEdits \
  --disallowedTools AskUserQuestion WebFetch WebSearch Agent

Two of those flags matter more than the rest. The --settings line turns off my installed copy of the plugin, so only the snapshot loads and the two don’t fight over the same skill names. And disallowing AskUserQuestion means the model can’t stop and ask me anything, which is why every prompt has to answer the intake questions up front.

The stream-json trace gives you token usage, cost, turns, and every file the model read. A small check.py grades each run with regex, hashes, and diffs: step lengths, whether code blocks changed, whether the STE card got read. That was eight cases with three reps each. Not every case ran on every snapshot, since ste-pass doesn’t exist in baseline, so it came to 66 runs for about $33. A follow-up added 15 more.

Three reps per cell isn’t a lot, so I’d treat small differences as noise. The whole harness is in evals/ste-voice in my claude-skills repo.

What STE did to the writing

Here’s write-article drafting a Developer Blog tutorial on adding a toolbar button with BlockControls:

baselinesteoptimized
Numbered steps022.319.0
Steps at 20 words or fewer100%100%
“Make sure that…” checks0.35.04.0
First person outside steps (per 100 words)0.50.90.8
Cost per draft$0.32$0.46$0.43

Without the card, the model wrote the whole tutorial as prose with code blocks. Not one numbered step. With the card, every run produced short, checkable steps, and the intro still opened like me: “I see a lot of custom blocks that store an image and give users no clean way to remove it.” Not every line was clean. One draft opened with “The block toolbar is prime real estate”, and nothing flagged it, so I still read every output.

It isn’t free, though. The first STE version made tutorial drafts cost about 44% more than baseline. After the trimming, they still cost 34% more, mostly because they’re longer. More steps and more checks mean more output.

Say when before where

The first STE version had a bug.

When I write an internal post for my team, STE should be off. It isn’t a tutorial. But the ste version of write-article read the STE card in 3 of 3 team-post runs, and one draft picked up a **NOTE:** block. That’s STE formatting leaking into a post for my teammates.

The skill said this:

The rules live in the shared card — read it live, never copy it here:

${CLAUDE_SKILL_DIR}/../ste-pass/references/ste-card.md

Pick the mode from the card's **Destination defaults** (hybrid for the
Developer Blog ...; off for ... opinion posts).

Look at the order. It tells the model where the file is before it says when to use it. So the model reads the file first, then decides it didn’t need it. One sentence fixed it:

When STE is off, don't read the card.
Team postbaselinesteoptimized
STE card read0/33/30/3
Cost$0.22$0.28$0.21

The ste version cost about 27% more than baseline on a team post. With that sentence added (and the trimming below), it’s back to baseline.

The conflict I planned for never showed up

My biggest worry was the voice pass. sounds-like-me rewrites anything that doesn’t sound like me, and plain STE steps definitely don’t sound like me. I figured it would “fix” them right back into chatty prose, so I added a rule telling it to skip action zones.

Then I measured it on a hybrid recipe with every finding pre-approved:

baselinesteoptimized
Action-zone lines rewritten000
Voice-zone lines rewritten4.04.03.7

Even the old version without the rule left the steps alone. I’m keeping the rule as a guardrail, but I can’t claim it prevented anything. I wrote that rule on a hunch, and without the eval I’d never have known it wasn’t doing anything.

Trimming what the skills load

While I had the harness, I went after context cost in the voice skills. Shorter skill descriptions cut what loads in every session from 1,156 tokens to 676, but most of the savings came from two changes.

  • Refinement logs moved out of the voice cards. I log every correction with a date, and the card says every entry is already distilled into its rules. But every run was still loading the log. It now lives in a write-only refinement-log.md next to the card.
  • No full transcripts. wnd-script told the model to read a full episode transcript before writing. youtube-script had an optional “deep soak” that could pull in a 15–20k-token livestream transcript. Both now read curated excerpts.

The static estimate for youtube-script looked great on paper: worst case down from 31k tokens to 10k, a 67% cut. In practice it changed nothing, because the model never took that optional path in any run.

The required read is where the real savings were:

wnd-scriptsteoptimized
Full transcript read3/30/3
Total input tokens215,699175,422 (-19%)
Cost$0.69$0.62
Branded greeting + “have a good one”3/33/3

So now I cut what the model has to read before I touch what it might read. Static estimates can’t tell you the difference.

Moving the counting into a script

Models are bad at counting words, so ste-pass got a small Python linter that prints only the mechanical violations: steps over 20 words, swap words, phrasal verbs, cautions placed after their step, procedures with no final check.

python3 ${CLAUDE_SKILL_DIR}/scripts/ste-lint.py recipe.md --lines 13-60,160-170
ste-lint: 2 hits in recipe.md (0 numbered steps; marked lines + ranges 13-60,160-170)
L13 [swap 'choose' -> 'select'] You can choose to either use the repository which provides...
L13 [R13 hedge 'just'] You can choose to either use the repository which provides...

The --lines flag matters. The recipe I tested with doesn’t have a single numbered step, because all its instructions are prose. Without line ranges, the linter finds nothing.

The test recipe had three problems I planted on purpose, so I could tell whether an audit actually found them. With the linter, audits got faster and cheaper, and found more:

ste-pass auditste (Opus)optimized (Opus + lint)
Cost$0.50$0.40
Duration130s74s
Findings3038
Caught all 3 planted problems3/33/3

The Sonnet fork that made things worse

The 15-run follow-up tested ste-pass with model: sonnet in its frontmatter. Audits dropped to $0.27 and still caught every planted problem. Rewrites dropped to $0.48 from $0.85, but only because Sonnet did less: 13 lines changed against 74 for Opus, prose instructions stayed prose, and a doubled “completed completed” survived. It passed every pass/fail check anyway. Only the diff size gave it away.

There was a second catch. model: sonnet only took effect when I started the skill with the slash command. When the model triggered ste-pass on its own from a plain-English request, the session stayed on Opus.

So the obvious next move was to put the audit in a forked helper pinned to Sonnet, and keep Opus for talking to me and applying edits.

---
name: ste-audit
description: Internal forked helper for ste-pass. Maps zones in one file, runs ste-lint.py, and returns Simplified Technical English findings with proposed rewrites. Not for direct use.
context: fork
model: sonnet
user-invocable: false
allowed-tools: Bash(python3 *) Read
---

The fork did fix the model problem: Sonnet ran in 3 of 3 auto-triggered runs. And then it cost more, which was not the part I was going for:

optimized (Opus inline + lint)Sonnet fork
Audit cost$0.40$0.57 (+43%)
Audit duration74s175s
Tokens inside the fork210,774
Rewrite cost$0.85$0.74

A fork is a second full context, and it re-sends that context on every turn. It pays off when it keeps a huge payload out of the main session, like a 20k-token transcript. An STE audit’s payload is small, so the fork was all overhead. The rewrite did come in cheaper, but the audit is the job the fork was for, and that’s the part that got slower and more expensive.

Looking back, the two changes I was surest about were the two the numbers pushed back on. The voice-pass rule I wrote on a hunch never did anything, and the Sonnet fork I called the obvious next move made the audit 43% more expensive. The best fix of the whole round was one sentence, and I only went looking for it because the STE card showed up in 3 of 3 runs where it had no business being.

I doubt I’m the only one with a list like this. If a skill you’ve written or used has done something weird, like ignoring its own frontmatter, reading a file it didn’t need, or passing every check while doing half the work, let me know in the comments. I want to hear what tripped you up.

Leave a Reply

Your email address will not be published. Required fields are marked *


Keep going