**The editor's point is right and larger than it looks.** `dados/` is Portuguese leaking into the
backend, and with more language editions coming the data layer has to be language-neutral: a
Portuguese key is fine while there is one edition and wrong the moment there are two. This release
does the preparation, proves it, and stops at a wall it may not climb.

**[`dados/en-migration.json`](../dados/en-migration.json) is the map, and it is data** — because
this repository's rule is that a classification is a published formula or it does not happen. 467
keys and 8 folder renames, and, as importantly, what it does **not** touch, each with a reason:
reader URLs (`artigos/`, `entidades/`, `empresas/` — in a multilingual future those are the
*Portuguese edition's* paths), the ontology's verbs (rule 6: the graph reads aloud in Portuguese),
the entity type names that appear in those URLs, and every value in every file. **The map renames
the vocabulary, never the content.**

**`build/migrate_to_english.py` applies it, and refuses to.** Dry run by default; `--apply` checks
the blockers first, prints what the editor has to change, and exits non-zero rather than doing
four-fifths of a rename.

## What running it four times against a scratch clone found

A repository-wide rename reads like a find-and-replace. It is not, and the only way to learn that
cheaply was to do it somewhere it did not matter. **Six classes of defect, every one of which would
have reached the live site:**

1. **Folder renames applied child-before-parent.** Renaming `fontes/congeladas` first *creates*
   `sources/`, so the later `fontes → sources` is skipped as "target exists" — leaving an empty
   `fontes/` and the move half-done.
2. **The pattern matched `"dados"` but not `"dados/historias.json"`**, nor `"fontes/congeladas"`
   with no trailing slash. The second is how nearly every builder refers to a folder.
3. **The key map was built from `dados/` alone.** Keys that live only in
   `artigos/**/comentarios.json` were renamed in the *code* and not in the *data*.
4. **120 keys came out half-translated.** `porque_existe_este_ficheiro` → `why_existe_este_file`;
   `nao_e_aconselhamento_juridico` → `not_and_aconselhamento_juridico`. They are phrases, not
   compounds. All 120 are now written by hand, and a rule that got those wrong is not trusted with
   the other 469 unchecked either.
5. **`redacao/` is a reader URL, not a backend folder.** `redacao/index.html` is the newsroom-floor
   page. Its subfolders move to `newsroom/`; the page stays. Found because the folder survived the
   migration holding exactly one file, and that file was a published page.
6. **Portuguese Python identifiers are invisible to a string-level rename.**
   `junta(agente=…, especie=…, porque=…)` builds a dict whose keys the map renames — so the
   function is handed `agente` and reads `agent`. 126 of them in `build/`. **Unsolved**, and the
   largest piece left; it is code-only, and needs Python's own parser rather than a regular
   expression.

## Why it stops here

`build/gates.py` and `build/entregas.py` hardcode the backend folder names **and 115 of the keys**,
and both are in the deny list of `.claude/settings.json`. An agent that can edit the gate that
stops it has no gate at all — so no agent working here may touch them, and renaming around them
turns the build permanently red through files no agent here can fix. `dados/aviso.json`, also
deny-listed, holds 33 more.

**That is a permissions decision, and it belongs to the editor of record.** The note is in
`redacao/correio/editor/entrada/`, with the two ways out and what each costs. A run that stops
honestly is a good run.

*Nothing was renamed in this release. The map, the script, the editor's note and this page are the
whole of it — and `docs/guidance/language.md`, which has said since v0.10.0 that this migration must
happen alone in one release, now links to the map and says how big "alone" turned out to be.*
