Shared spelling tools

Updated Oct 17, 2026

data/typos-oxendict-base.toml in this repository is the sole authority for estate-wide spelling policy, and main is the source consumers normally read. This repository curates that file; it no longer carries a generator, a phrase checker or a scanner of its own.

Run make spelling in this checkout to gate it the way a consumer is gated: the pinned typos-config-builder renders typos.toml from the dictionary and this repository's typos.local.toml overlay, runs the Typos binary it pins over every tracked file, and enforces the curated phrase corrections that Typos cannot apply because punctuation separates their word tokens. The recipe points --source at the working copy of the dictionary rather than at main, so a pull request that edits shared policy is gated against the policy it proposes.

Consumer repositories

A consumer repository carries no spelling tooling of its own: one command, pinned to a released tag.

uvx --from "git+https://github.com/leynos/[email protected]" \
  typos-config-builder gate

gate fetches data/typos-oxendict-base.toml live from this repository's main, merges the consumer's optional typos.local.toml overlay, rewrites typos.toml, runs the pinned typos binary over tracked files, and enforces the shared phrase corrections. Add --scope all where the whole tracked tree should be scanned rather than tracked Markdown alone. The fetched copy is cached in ignored .typos-oxendict-base.toml with freshness metadata in .typos-oxendict-base.json, so a valid cache still supports offline runs. A run with neither network access nor a valid cache falls back to the dictionary snapshot bundled with the pinned leynos/typos-config-builder release, which can omit exceptions added to main since that release. Add both to .gitignore; gate writes them as cache files, and neither is a policy source.

gate rewrites typos.toml on every run from the live dictionary, so continuous integration must never check it for drift: that would fail every consumer on every shared dictionary edit. A consumer therefore either leaves typos.toml untracked, by adding it to .gitignore and running git rm --cached typos.toml, or keeps it tracked only as a reviewable snapshot of the merged policy. This repository keeps it tracked because it curates the dictionary the snapshot is rendered from.

The builder's own users' guide and migration guide document the command and the one-time migration. Migrating an existing consumer means deleting its vendored generator and phrase-check scripts and their tests, replacing its spelling Makefile targets with the single gate call, and regenerating once.

Proposing a shared word

Adding an accepted word, a correction, or an ignore pattern to data/typos-oxendict-base.toml reaches every consumer on its next gate run. No consumer edit or tag bump is required, and the builder is not released again for a dictionary change. This repository tracks typos.toml, so a dictionary pull request also carries the snapshot make spelling regenerates.

Open a pull request against this repository that edits the dictionary. The gate runs against the edited file, so the pull request proves its own policy. Generic spellings belong in the shared dictionary; product names, quoted upstream terms and deliberate fixtures belong in a consumer's typos.local.toml overlay instead. A local [patterns] remove list withdraws an exact shared ignore expression for one repository that needs stricter checking, without weakening the policy anywhere else.

Evidence supports the proposal: a suffix match alone is not one because advertise, exercise, promise and Rust's usize all end the way an Oxford -ize family does. Gather it with the one spelling tool this repository still owns:

uv run --script scripts/oxford_form_harvest_cli.py --repository ../project

The command prints one JSON object per source line carrying a candidate -ise or -ize form, reading only Git-tracked UTF-8 text and skipping the paths the merged [files] exclude lists name. Tracked content that is not UTF-8 is skipped with a bounded informational record; any other read failure stops the harvest rather than reporting partial evidence.

The curated dictionary also normalizes nine common drift forms that replace s with z in otherwise, exercise and raise. Each drifted form gains exactly one canonical replacement, so consumers receive a single correction instead of competing candidates:

"otherwize" = "otherwise"
"exercize" = "exercise"
"exercized" = "exercised"
"exercizes" = "exercises"
"exercizing" = "exercising"
"raize" = "raise"
"raized" = "raised"
"raizes" = "raises"
"raizing" = "raising"

Inline code is checked by default so misspelled identifiers, flags, module paths and file names remain visible. Add exact identifier patterns to a repository's local [patterns] ignore list when an upstream name is intentionally spelled differently.