OneRead, a compiler for Simplified Technical English

Pull-quote: "A prompt is a suggestion. A compiler is a gate. STE was built for a reader who cannot ask a question. That reader is now an agent."
A language model writes like a consultant. An aircraft mechanic writes so a tired colleague cannot misread a step. Those are not the same job, and the second job already has a standard. ASD-STE100 Simplified Technical English is the controlled language aerospace and defense manufacturers use for maintenance documentation. Issue 9 is dated 15 January 2025. It contains 53 writing rules. The official dictionary is copyrighted by ASD and is a free download from the STEMG site. ASD-STE100 is an EU trademark of ASD (trade mark 017966390). Nothing in this post, and nothing in the repository it points to, is affiliated with or endorsed by ASD or STEMG.
The reason this belongs on an AI engineering desk is simple. Models rotate synonyms so the prose does not repeat. They hedge with should, may, and could. They put the condition after the command. They write 40-word sentences. Those habits are rewarded in training. They are punished in a hangar, on an on-call rotation, and in a tool description that an agent will parse without a human in the loop.
People have already packaged those rules as agent skills. A skill is still a prompt. The model will try. The next call drifts. CI cannot fail a prompt. That is the problem OneRead is built to close.
OneRead is the layer a skill cannot be. It is a compiler for technical English. Parse the prose. Type-check the mechanical rules. Lower the document to one intermediate representation, ORIR. Emit that IR to Markdown, HTML, SARIF, GitHub annotations, Slack Block Kit, OpenAPI, SKILL.md, MCP tool descriptions, RFC 9457 problem details, and an aviation AMM skeleton. The repository is public at github.com/zorost/oneread. The interactive rule explorer is at zorost.github.io/oneread. This post is the why, the before and after, and the honest limits.
This piece is unofficial. No tool can guarantee ASD-STE100 compliance. Final approval rests with the writer. The official wording is in Issue 9.
What you will be able to do
- Explain why Simplified Technical English exists, who publishes it, and why a default language model writes the opposite of it.
- Separate a prompt skill from a compiler, and say why CI can fail one and cannot fail the other.
- Classify a passage as procedural or descriptive, apply the 20-word and 25-word limits, and put a required condition before its command.
- Run OneRead as a CLI, a GitHub Action, an MCP server, and a skill adapter, and emit the same IR to ten surfaces.
- Rewrite error messages, runbooks, OpenAPI text, incident reports, agent prompts, and aviation procedures so a tired reader survives one pass.
- Place AeroFarr correctly: the intelligence layer for aviation operations, not a writing tool, at aerofarr.com and on the AeroFarr platform page.
What is ASD-STE100, and why does a software team care?
STE is a controlled natural language. It was developed in the late 1970s by AECMA, now ASD, with support from the Aerospace Industries Association of America, at the request of European airlines. The goal was aircraft maintenance documentation that a technician with a basic command of English could not misread. The AECMA Simplified English Guide shipped in 1986. It became an international specification in 2005. In 2025 it became an international standard: ASD-STE100 Issue 9, 15 January 2025. That history is on the STEMG site.
Issue 9 added no new rule numbers. It revised the wording of 31 of the 53 rules. The official site lists current use in aerospace and defense, computer science and information technology, industry and services, and academia. By the end of Issue 8, public STEMG commentary put 64 percent of registered users outside aerospace and defense. The dictionary is not reproduced here. It is copyrighted. OneRead enforces mechanical writing rules and a one-word-one-meaning discipline. Full vocabulary compliance still needs Issue 9.
A software team cares because the tired reader never left. The 02:00 on-call engineer is that reader. The non-native contractor reading your runbook is that reader. The agent that will call your MCP tool, with no chance to ask what "should" meant, is that reader. STE was designed for a human who cannot ask. Agents are that constraint with a smaller patience budget. The same properties transfer: one meaning per word, short sentences, condition-first commands, active voice, simple tenses. Tool design for agents is the contract half of that problem. This post is the prose half.
Do not apply STE to marketing, brand, or social posts. It deletes persuasion on purpose. This Signals page is not written in STE. The docs it points at are.

Why a compiler, not another skill?
A skill tells the model the rules. The Agent Skills standard means the same folder loads in Cursor, Claude Code, Codex, Gemini CLI, OpenCode, and the rest of the hosts that speak agentskills.io. That is real distribution. It is also the ceiling. The model will try. The next call will put "check", "verify", and "ensure" back in as three operations. CI cannot fail a suggestion. A prompt has no type. A failing test needs a type.
A compiler type-checks. OneRead's linter is a regex, not a grammar parser. It undercounts passive voice and part of speech. Numbers are comparable between two texts run through the same version. They are not a compliance verdict. That honesty is the point. You can fail a pull request on mechanical findings: contractions, banned modals, present perfect, -ing verb clauses, semicolons, em dashes, Latin abbreviations, courtesy filler, trailing conditions, synonym rotation. You can rewrite the subset a program can prove. You can mark the rest needs_model and let a model or a human finish it. You can emit SARIF so GitHub code scanning shows the line.
That is the reframe. Authoring checkers in the HyperSTE class sit in a writer's editor and often need a license. They do not sit in an agent loop. They do not emit OpenAPI. They do not wrap an AMM page. A skill sits in the agent loop and stops at prose. OneRead sits in the agent loop and in CI, and it emits the artifact your surface already expects.
The repository keeps a skill adapter. The skill's job is to tell the model to run the compiler, not to pretend it is one.
How the compiler is shaped
Four stages after the optional skill.
- Parse. Split passages. Classify each as procedural, descriptive, mixed, or protected. Code fences, headings, identifiers, URLs, and quoted errors are protected. Rule 8.6 counts a backticked command as one word, which is why
sqlpipe run --config sqlpipe.yamldoes not blow a 20-word budget. - Type-check. Mechanical rules from Issue 9, paraphrased. Procedural limit 20 words. Descriptive limit 25. One instruction per sentence. Condition before command when the reader must know it first. Approved modals: can, will, must. Banned: should, would, may, might, could. No present perfect. No
-ingas a verb. No contractions. Keep articles. Keep "that". - Lower to ORIR. JSON. Schema in the repo. A passage carries kind and sentences. A sentence carries word count, limit, and protected tokens. Findings hang off the document. Emitters read ORIR. They do not re-parse prose.
- Emit. Markdown, HTML, SARIF, GitHub annotations, Slack Block Kit, OpenAPI
summaryanddescription, SKILL.md, MCP tool description, RFC 9457titleanddetail, aviation AMM skeleton.
The mechanical compile pass rewrites what a program can prove: contractions, e.g. / i.e. / etc., please and oops, in order to, prior to, semicolons, em dashes, make sure without that, a few phrasal verbs. Residual keys stay for a model or a human: sentence splits, banned modals that change meaning, -ing clauses, trailing conditions, synonym rotation. That split is the difference between a linter and a rewriter that lies.

Runtime dependencies are zero. Python 3.10. The MCP server is stdio JSON-RPC with the stdlib. The GitHub Action runs the same CLI. The playground at zorost.github.io/oneread ports the linter to the browser so a rule is clickable and a paste is live. A change to the Python linter that is not mirrored in the playground is an incomplete change.
The 53 rules, as a working set
The official wording is in Issue 9. This table is a working paraphrase with software examples. Click-through versions, with before and after on every rule, live in the playground.
| Section | What it governs | The failure you will see in AI output |
|---|---|---|
| 1. Words (1.1 to 1.14) | One word, one meaning, one part of speech. Domain nouns and verbs are legal. | config here, settings there. webhook used as a verb. |
| 2. Multi-word nouns | Chains of three words or fewer. | the connection pool timeout configuration value |
| 3. Verbs | Simple tenses. Active voice. No -ing as a verb. Modals: can, will, must. | has completed, making it easy, you should |
| 4. Sentences | Short, complete grammar, lists, connecting words, articles. | Contractions. Telegraph style. Semicolons. |
| 5. Procedures | 20 words. One instruction. Imperative. Condition first. Notes are not commands. | Read the log if the build fails. |
| 6. Descriptions | 25 words. One fact per sentence. One topic per paragraph. Six sentences max. | A 33-word sentence with three facts. |
| 7. Safety | WARNING, CAUTION, NOTE. Command first, then the risk. | Note that data loss may occur if you use --force. |
| 8. Punctuation and count | No semicolon. Identifiers count as one word. | Em dashes. e.g. A command that looks long and is not. |
| 9. Practice | Restructure, do not swap. No phrasal verbs. One style. | set up, carry out, Before accept of the unit |
Safety mapping for software: WARNING if a person can be harmed (industrial control, vehicle, medical device). CAUTION for data loss, irreversible schema change, production outage, credential leak. NOTE for information only, never a limit and never a command. Repeat "Do not" in each prohibited list item. One leading negative must not govern a long list.
Untouchables, in every genre: code, identifiers, CLI commands, flags, paths, URLs, quoted errors, UI labels, CVE and CWE identifiers, hashes, version numbers, legal wording. STE the prose around them.
Before and after, by genre
The "before" lines are typical unedited model output. The OpenAPI, ADR, methods, agent-prompt, and aviation pairs are constructed to cover genres a README sample does not. Tokens in backticks stay exact. Constructed model-family inputs are labeled in fixtures/models/.
README (descriptive)
Before: sqlpipe is a command-line tool for syncing PostgreSQL tables to Amazon S3 as Parquet files. It's built for teams who need a lightweight way to move data out of Postgres into a data lake without standing up a full ETL platform. Configuration lives in a single YAML file, making it easy to version-control your sync definitions alongside the rest of your infrastructure code.
After: sqlpipe is a command-line tool that copies PostgreSQL tables to Amazon S3 as Parquet files. It moves data into a data lake without a full ETL platform. The configuration is one YAML file. You can keep this file in version control with your infrastructure code.
Error message (procedural)
Before: Oops! Something went wrong while attempting to establish a connection to the database. Please ensure your credentials have been properly configured and try again.
After: Connection to the database failed. The password for user app was not correct. Set DB_PASSWORD to the correct value, then connect again.
Runbook (procedural, condition first)
Before: You'll want to grab the API key from the dashboard before configuring the client, which you can do under Settings. If sqlpipe hangs or fails with a timeout, check that the host can reach the Postgres port.
After: Get the API key from the dashboard, under Settings. Then configure the client with this key. sqlpipe stops with a timeout when it cannot reach the Postgres port. 1. Make sure that the host that runs sqlpipe can reach the Postgres port.
OpenAPI description
Before: This endpoint allows you to seamlessly retrieve a user by ID, leveraging our robust lookup pipeline. You should ensure the ID is valid; otherwise an error may be returned.
After: Returns the user with the given id. If id is not a UUID, the API returns HTTP 400. If no user has this id, the API returns HTTP 404.
Incident report (descriptive, simple past)
Before: We have identified an issue that may have impacted some users' ability to access the service. We sincerely apologize for any inconvenience this may have caused.
After: Between 14:02 and 14:31 UTC, 12% of requests to the API failed with HTTP 502. A deploy at 14:00 removed the cache warmup step. We reverted the deploy at 14:27. All requests succeeded from 14:31.
Agent prompt
Before: You should check the build, verify the tests, and ensure the configuration is valid before you deploy, making it easy to catch errors.
After: 1. Make sure that the build is complete. 2. Make sure that the tests pass. 3. Make sure that the configuration is valid. 4. Then deploy the service.
Aviation procedure
Before: Prior to applying power, the technician should verify that the hydraulic lines have been properly connected, making sure there is no leakage, and/or that the reservoir is filled. See above.
After: CAUTION: Do not apply electrical power until this task is complete. Wrong power can damage the unit. 1. Make sure that the hydraulic lines are connected. 2. Make sure that there is no leak. 3. Make sure that the reservoir is full. 4. Then apply power.
The same pattern holds for release notes (breaking change: command first, then the risk), ADRs (descriptive: decision, then consequences as facts), academic methods (simple past, no "it could be argued"), CLI help (STE the sentences, leave the synopsis), and security advisories (protect the CVE, state impact as fact, mitigation as command). Full pairs ship in fixtures/ of the repo.

Before and after, by model family
Do not read this as a ranked bake-off. The samples are constructed to show the failure mode those families commonly produce, and they are labeled as constructed in fixtures/models/. The point is not which vendor is worse. The point is that the failure is shared, and a prompt does not hold it.
| Family | What the prose does | What the compiler catches first |
|---|---|---|
| README-shaped | Long sentences, contraction, -ing clause | Sentence limit, Rule 4.2, Rule 3.5 |
| GPT-shaped | You'll want to, prior to, e.g., etc. | Courtesy and wordy phrases, Latin abbreviations |
| Gemini-shaped | comprehensive, robust, effortlessly, leveraging | Slop words, Rule 1.3 |
| Kimi-shaped | It is worth noting, could, we have attempted | Banned modal, present perfect, filler |
A skill can reduce those counts on a good call. The next call puts them back. A compiler makes the residual visible on the next pull request, on the next MCP tool description, and on the next OpenAPI field. That is the evaluation story, and it is why evaluation and guardrails sit on this post. You cannot evaluate what you cannot fail.
MCP tool descriptions are the highest-value software target. An agent that cannot ask a clarifying question is the tired reader. MCP, RAG, and skills solve three different problems. OneRead does not replace any of those layers. It type-checks the prose each layer already emits: the skill body, the tool description, the retrieved passage you are about to show a human.
Ten surfaces from one IR
A skill stops at prose. OneRead ships the artifact.
| Emitter | Surface | Why it exists |
|---|---|---|
markdown / html | README, runbook, ADR, this site | The default human page |
sarif / github | Code scanning, workflow warnings | CI can fail a finding |
openapi | summary and description | The contract an SDK will quote |
rfc9457 | Problem Details title / detail | Errors a client can show |
skill | SKILL.md | Procedure the agent loads on a match |
mcp | Tool description plus inputSchema | The tired reader with no human |
slack | Block Kit | The on-call channel |
amm | WARNING / CAUTION / NOTE / PROCEDURE / RESULT | The hangar page STE was built for |
Install paths: pip install -e . for the CLI. Copy adapters/skills/ for Cursor, Claude Code, Codex, Gemini CLI, and OpenCode. Point an MCP host at adapters/mcp/server.py. Add zorost/oneread/adapters/github-action to a workflow. Open the playground and click a rule. All of that is in the repository README.

Aviation, and where AeroFarr sits
STE was requested by airlines so a technician with basic English could not misread a maintenance step. That origin is not nostalgia. It is the acceptance test. If a procedure cannot survive one read in a noisy hangar, it is not done. OneRead's amm emitter forces the page shape: signal word, command, risk, numbered steps, a NOTE that contains no command. It cannot certify an AMM. The manufacturer publication spec and the official dictionary still govern.
The procedure is not the operation. Delay, cancellation, diversion, cascade through the network, and the safety report that explains a pattern, are a different system. AeroFarr is Zorost's aviation intelligence platform. It fuses live surveillance, weather, and years of public-domain flight records into calibrated pre-departure disruption forecasts, verified causal chains, and network cascade, with retrieval over public aviation safety corpora and citations. The public product site is aerofarr.com. The Zorost page is AeroFarr on the platform index.
OneRead makes the procedure readable. AeroFarr makes the operation visible. They are not the same product. They meet where aviation still writes steps for humans and still needs a picture of what the network will do next. If you are here for the compiler, use the repo. If you are here for the airspace picture, use AeroFarr.
Where this goes wrong
You paste the skill into a marketing page and the persuasion dies. STE is the wrong tool for brand. Use it on the docs the landing page links to.
You treat the linter as a certificate. It is a regex. It will miss passive voice and part of speech. It will over-flag an -ing technical noun if you stretch it. The disclaimer in every report is not decoration.
You rewrite a precise technical noun into a vaguer approved word. p-value, idempotent, and webhook stay. Rule 1.5 exists so you do not "simplify" meaning away.
You emit an AMM skeleton and ship it as a maintenance page. The emitter is a shape. Certification is a human with Issue 9 and the manufacturer spec.
You add three more MCP servers because the tool descriptions were vague. The descriptions were the bug. Type-check them. Do not grow the catalogue to hide prose. The same confusion is diagnosed in the MCP versus skills post.
You run the compiler on legal or statutory wording. Do not. Offer a separate STE explanation if a reader asks.
Common questions
Is this affiliated with ASD or STEMG?
No. OneRead is unofficial. ASD-STE100 is a trademark of ASD. The official standard is a free download at asd-ste100.org. No tool can guarantee compliance.
Is this just another agent skill?
No. Skills that tell a model to write in STE already exist. A skill is a prompt. The next call drifts. CI cannot fail a prompt. OneRead is a compiler: parse, type-check, lower to ORIR, emit. The skill adapter in the repo tells the model to run that compiler. The skill writes. The compiler gates.
Does OneRead include the official dictionary?
No. The dictionary is copyrighted. Strict vocabulary still needs Issue 9. Mechanical rules and one-word-one-meaning discipline do not.
Which Python version, and what are the dependencies?
Python 3.10 or newer. Runtime dependencies: none. The MCP server uses the stdlib. The playground is static HTML.
Can I use this on agent-to-agent messages?
Yes. That is one of the highest-value targets. A tool description, an error detail, and an inter-agent instruction are all procedures executed by a reader that cannot ask. Classify them procedural. Put the condition first. Ban "should".
Where does AeroFarr fit?
AeroFarr is not a writing tool. It is the aviation intelligence platform: calibrated disruption forecasts, causal explanation, cascade, cited safety retrieval. Live at aerofarr.com. OneRead is the language compiler. Use AeroFarr when the question is what the airspace will do. Use OneRead when the question is whether a human or an agent can misread the step.
Next steps
Clone github.com/zorost/oneread and run python3 -m oneread.cli self-test. Then lint fixtures/slop/readme.md. Then open the playground and click Rule 5.4 until condition-first is a reflex.
If the scarce resource is which layer an agent should use, start with MCP, RAG, and skills. If the scarce resource is the contract on a tool, start with tool design for agents. If the scarce resource is the airspace picture, start with AeroFarr. The rest of the library sits on the Signals index and in the AI Lab.
