-
Notifications
You must be signed in to change notification settings - Fork 1.1k
docs(readme): resync every non-English README and gate them against drift #4151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5caf519
922b674
f65712f
ba5c78b
7ecbd82
f646e31
06e7071
cb609ac
e115efd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # README i18n parity — plan | ||
|
|
||
| ## Objective | ||
|
|
||
| Resync the seven non-English READMEs under `readme/` to the current `README.md`, and add a | ||
| mechanical guard so the same drift cannot accumulate silently again. | ||
|
|
||
| ## Current state (evidence) | ||
|
|
||
| `README.md` last moved at `2f3f82680`. Every `readme/README.*.md` last moved at `8bc9e4ee2`, | ||
| seven README-touching commits earlier: | ||
|
|
||
| ```text | ||
| 2f3f82680 feat(sponsors): PackyCode Standard sponsor preset, README row and docs (#3915) | ||
| 037137a50 fix(readme): show the OrcaRouter sponsor logo on every branch (#4097) | ||
| 4b379b9ec feat(sponsors): OrcaRouter placement, overview introduction and links (#3914) | ||
| 615c5c62c feat(provider): add Qoder CN PAT provider | ||
| 124c57b1f feat(provider): add Qoder Global PAT provider | ||
| 17d2a1715 docs(readme): one sponsor line pointing at SPONSORS.md (#3923) | ||
| aeefb3ab5 docs(readme): one-line Sponsors slot, sponsorship summary and contact (#3918) | ||
| ^ last shared ancestor: 8bc9e4ee2 docs: publish the sponsorship rule set (#3910) | ||
| ``` | ||
|
|
||
| The divergence is structural, not cosmetic. The English README was reorganized into a | ||
| four-product hero table, a `## Quick start` section holding `### Personal install`, | ||
| `### Sponsors` and three `<details>` blocks (Docker Compose, install from source, for agents), | ||
| a `### Health and readiness` subsection, and a memory-ownership `<details>` block. Five of the | ||
| seven locales — ko, ja, ru, zh-CN, zh-TW — still carry the pre-reorganization outline with | ||
| sections the English file no longer has (`## Adding a provider`, | ||
| `## OpenAI provider account modes`, `## Configuration`), and none of the seven carries the | ||
| sponsor table, the Docker Compose block or the memory-ownership block. `README.fr.md` is the | ||
| closest: it has the four-product hero and the readiness section but predates the sponsor and | ||
| Docker work. `README.tr.md` is the shortest at 172 lines against 399 English. | ||
|
|
||
| ## Constraints | ||
|
|
||
| - `README.md` is the source of truth and must not change in this unit. | ||
| - Write scope: `readme/*.md`, `readme/i18n-manifest.json`, | ||
| `tests/ci-workflows/docs-readme-translation-parity.test.ts`, | ||
| `scripts/test-layout/layout.json`, `tests/fixtures/test-layout-expected.json`, | ||
| and this unit directory. | ||
| - Out of scope: `docs-site/` translations, GUI i18n, runtime source, release, merge, deploy. | ||
| - The user forbade the local product suite, typecheck and build for this task. The only local | ||
| execution is the new guard test file; remote CI on the pushed head is the authoritative | ||
| evidence, and the push uses `--no-verify`. | ||
| - Translation drafting is delegated to parallel `xai/grok-4.6` subagents, one locale per agent, | ||
| disjoint write sets. Korean additionally passes the `cxc-kwrite` four-pass revision. | ||
|
|
||
| ## Work-phase map | ||
|
|
||
| | Phase | Doc | Outcome | Depends on | | ||
| |---|---|---|---| | ||
| | wp1 | this unit | roadmap locked | — | | ||
| | wp2 | `010_phase1_parity_guard.md` | manifest + guard test + layout registration | wp1 | | ||
| | wp3 | `020_phase2_locale_resync.md` | seven locales resynced, guard green | wp2 | | ||
| | wp4 | `030_phase3_delivery.md` | branch pushed, PR open against `dev` | wp3 | | ||
|
|
||
| The guard lands before the translations on purpose: it is the executable specification the | ||
| seven drafts are integrated against, so an incomplete draft fails a check instead of a review. | ||
|
|
||
| ## Why a manifest and not only a structural diff | ||
|
|
||
| A structural comparison catches a missing section. It cannot catch a paragraph rewritten in | ||
| English inside a section every locale still has — which is most of what accumulated here. A | ||
| recorded per-locale `sourceSha256` of the English file catches exactly that case, and names | ||
| which locales are stale rather than failing as one opaque check. The structural checks stay | ||
| because the hash alone can be satisfied by editing one JSON field. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| # wp2 — parity guard (diff level) | ||
|
|
||
| ## NEW `readme/i18n-manifest.json` | ||
|
|
||
| ```json | ||
| { | ||
| "source": "README.md", | ||
| "note": "sourceSha256 is the LF-normalized SHA-256 of README.md that each locale was last resynced against. Update it in the same commit that resyncs the locale file.", | ||
| "locales": { | ||
| "fr": { "file": "readme/README.fr.md", "label": "Français", "docsPath": "fr", "sourceSha256": "<sha>" }, | ||
| "ko": { "file": "readme/README.ko.md", "label": "한국어", "docsPath": "ko", "sourceSha256": "<sha>" }, | ||
| "zh-CN": { "file": "readme/README.zh-CN.md", "label": "简体中文", "docsPath": "zh-cn", "sourceSha256": "<sha>" }, | ||
| "zh-TW": { "file": "readme/README.zh-TW.md", "label": "繁體中文", "docsPath": "zh-tw", "sourceSha256": "<sha>" }, | ||
| "ru": { "file": "readme/README.ru.md", "label": "Русский", "docsPath": "ru", "sourceSha256": "<sha>" }, | ||
| "ja": { "file": "readme/README.ja.md", "label": "日本語", "docsPath": "ja", "sourceSha256": "<sha>" }, | ||
| "tr": { "file": "readme/README.tr.md", "label": "Türkçe", "docsPath": "tr", "sourceSha256": "<sha>" } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| `docsPath` matches the Starlight locale directory in `docs-site/astro.config.mjs` | ||
| (`fr`, `ko`, `zh-cn`, `zh-tw`, `ru`, `ja`, `tr`), which is what `opencodex.me` serves. | ||
| Locale order is the order of the language nav line in `README.md`. | ||
|
|
||
| ## NEW `tests/ci-workflows/docs-readme-translation-parity.test.ts` | ||
|
|
||
| Imports `repoPath` from `../helpers/repo-root`, reads with `node:fs`, hashes with | ||
| `node:crypto`. All content is LF-normalized before parsing or hashing, so a CRLF checkout | ||
| cannot flip a hash on Windows CI. | ||
|
|
||
| ### Structural token stream | ||
|
|
||
| `tokens(markdown)` walks the file line by line and emits, in order: | ||
|
|
||
| | Line shape | Token | | ||
| |---|---| | ||
| | `## ` heading | `h2` | | ||
| | `### ` heading | `h3` | | ||
| | `<details>` | `details` | | ||
| | `</details>` | `/details` | | ||
| | opening fence `\`\`\`lang` | `fence:lang` (bare fence -> `fence:`) | | ||
|
|
||
| Heading text is deliberately ignored: it is translated. Fence bodies are skipped so a | ||
| `#`-commented shell line is never mistaken for a heading. The English stream is 38 tokens | ||
| beginning `fence:bash, h3, h3, h3, h3, h2, h3, fence:bash, h3, details, ...`; each locale | ||
| must produce the identical stream, and the failure message prints the first differing index | ||
| with both tokens. | ||
|
|
||
| ### Checks | ||
|
|
||
| 1. **Registry integrity** — the set of `readme/README.*.md` files on disk equals the manifest | ||
| locale set. A new locale file with no manifest entry fails here, not silently later. | ||
| 2. **Freshness** — `sha256(README.md)` equals every `sourceSha256`. The failure names the stale | ||
| locales and prints the current hash to paste after resyncing. | ||
| 3. **Skeleton** — token stream equality against `README.md`. | ||
| 4. **Commands** — for each `bash`/`powershell` fence, the command part of every line (text | ||
| before an inline ` #` comment, trailing whitespace trimmed) matches the English fence at the | ||
| same index, line for line. Comments stay translatable; commands do not drift. | ||
| A double-quoted argument containing a space collapses to a placeholder first: the three | ||
| example prompts in the model-routing block (`"Explain this stack trace"` and friends) are | ||
| sentences a translator is supposed to translate, and every existing locale already did. | ||
| A quoted argument without a space stays exact, so `"anthropic/claude-opus-5"` is still | ||
| frozen. This was an audit FAIL: without it the guard would have rejected every correct | ||
| translation. | ||
| Non-ASCII content counts as prose on the same footing. Japanese and Chinese do not put | ||
| spaces between words, so the whitespace-only version of this rule read | ||
| `"このスタックトレースを説明して"` as an identifier and demanded it equal the English sentence. | ||
| Every token that must stay frozen in these fences is ASCII, so the widening costs nothing. | ||
| Found by running the guard against the finished Japanese file, not by review. | ||
| 5. **Assets** — every asset path referenced in `README.md` (`assets/...`, including the raw | ||
| `githubusercontent` forms) appears in each locale by its repository-relative suffix, so | ||
| `assets/demo.gif` and `../assets/demo.gif` both satisfy it. | ||
|
Comment on lines
+70
to
+72
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Require
🤖 Prompt for AI Agents |
||
| 6. **Sponsor markers** — the ordered list of `<!-- sponsors:<key>` markers matches English, and | ||
| every sponsor destination URL in the English sponsor table appears in each locale. | ||
| 7. **Doc links** — every `https://opencodex.me/<path>` in English appears in the locale either | ||
| verbatim or with its `docsPath` prefix inserted. Every other absolute URL in English must | ||
| appear verbatim, **except** URLs whose path contains `/assets/`: English serves three hero | ||
| gifs from `raw.githubusercontent.com` while locale files use `../assets/<name>.gif`, so | ||
| asset references are owned by check 5 and excluded here. Trailing markdown punctuation | ||
| (`*`, `.`, `,`, `)`, `]`) is stripped before comparison — English contains | ||
| `http://localhost:10100**` inside a bold span. | ||
| 9. **Repo-relative links** — every `./<path>` link target in `README.md` (`./SPONSORS.md`, | ||
| `./AGENTS_INSTALL.md`, `./docs-site`, `./structure`, `./CONTRIBUTING.md`, `./SECURITY.md`, | ||
| `./CREDITS.md`) must appear in the locale as `../<path>`. The locale files live one directory | ||
| down, so a copied `./SPONSORS.md` is a 404 on GitHub and nothing else would catch it. | ||
| 8. **Nav line** — the locale file links `../README.md` for English, links all six sibling | ||
| locales by filename, and marks itself with `<b>`, never as a link to itself. | ||
|
|
||
| ### Non-vacuity | ||
|
|
||
| Each check is driven red once against a mutated in-memory copy before the unit closes, and the | ||
| observed failure text is recorded in `030`. A guard nobody has seen fail is a guard nobody | ||
| knows works. | ||
|
|
||
| ## MODIFY `scripts/test-layout/layout.json` | ||
|
|
||
| Add to `explicit`, in sorted position among the other `docs-` entries: | ||
| `"docs-readme-translation-parity.test.ts": "ci-workflows"`. The `docs-` regex seed for | ||
| `ci-workflows` would already place the file, so this entry is belt-and-braces rather than | ||
| strictly required; it keeps the file's domain pinned if the seed regex is ever narrowed. | ||
|
|
||
| ## MODIFY `tests/fixtures/test-layout-expected.json` | ||
|
|
||
| Add the same key/value pair in sorted position. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| # wp3 — locale resync (spec + delegation packet) | ||
|
|
||
| ## Canonical outline every locale must reproduce | ||
|
|
||
| `README.md`, in order. Prose is translated; structure, commands, links and assets are not. | ||
|
|
||
| 1. `<h3 align="center">make codex open!</h3>` and the two-line tagline. | ||
| 2. The four shields badges, unchanged except badge `alt` text. | ||
| 3. `\`\`\`bash` — `npm install -g @bitkyc08/opencodex` / `ocx start`. | ||
| 4. Hero `<table>`: four rows, each with `### <product>, running any model` and its gif. | ||
| Products: Claude Code, Codex, Claude Desktop, Grok Build. Locale files reference the gifs | ||
| as `../assets/<name>.gif`. | ||
| 5. Language nav line: English links `../README.md`, the six siblings link `README.<code>.md`, | ||
| the file itself is `<b>...</b>`, and the docs link points at `https://opencodex.me/<docsPath>/`. | ||
| 6. Intro paragraph: what the proxy translates, which clients, and the ChatGPT account pool. | ||
| 7. `## Quick start` | ||
| - `### Personal install` — bash fence, `ocx service` note, dashboard paragraph, account-pool | ||
| paragraph including quota routing, thread affinity and the selection-order sentence. | ||
| - `### Sponsors` — the SPONSORS.md pointer, the `<!-- sponsors:main -->` and | ||
| `<!-- sponsors:standard -->` markers verbatim, and the two-row sponsor table. The PackyCode | ||
| row keeps its Simplified Chinese `<sub>` block in every locale. | ||
| - `---` | ||
| - `<details>` Docker Compose, `<details>` install from source (bash + powershell), | ||
| `<details>` for agents (bash + the agent-consent blockquote). | ||
| 8. `## Supported platforms` — three-row table, Node 18+ paragraph. | ||
| 9. `## Highlights` — the bullet list including the `<!-- sponsors:main-first-mention -->` marker, | ||
| the provider-policy blockquote, and the memory-ownership `<details>` block. | ||
| 10. `## Model routing` — bash fence, prefix-omission paragraph. | ||
| 11. `## Providers & adapters` — the second `<!-- sponsors:main-first-mention -->` marker and the | ||
| provider prose. | ||
| 12. `## CLI` — bash fence, port note, then `### Health and readiness` (with the exit-code table), | ||
| `### Autostart: service vs shim`, `### Uninstall`. | ||
| 13. `## Remote access`, `## Documentation`, `## Development`, `## Disclaimer`, `## License`. | ||
|
|
||
| ## What must be byte-identical | ||
|
|
||
| - Every command line inside a `bash`/`powershell` fence. Only the trailing `# comment` is translated. | ||
| - Every HTML comment marker, sponsor URL, badge URL and asset filename. | ||
| - Every absolute URL except `opencodex.me`, which takes the locale prefix, and except the three | ||
| hero gifs, which locale files reference as `../assets/<name>.gif` instead of the English | ||
| `raw.githubusercontent.com` form. | ||
| - Repository links are rewritten one level up, because locale files live in `readme/`: | ||
| `./SPONSORS.md` -> `../SPONSORS.md`, and the same for `./AGENTS_INSTALL.md`, `./docs-site`, | ||
| `./structure`, `./CONTRIBUTING.md`, `./SECURITY.md`, `./CREDITS.md`. A copied `./` link is a | ||
| 404 on GitHub. | ||
| - Product, provider, model and CLI identifiers: `ocx`, `opencodex`, `Codex`, `Claude Code`, | ||
| `Claude Desktop`, `Grok Build`, `/healthz`, `/readyz`, `x-opencodex-api-key`. | ||
|
|
||
| ## Delegation packet (one agent per locale) | ||
|
|
||
| Model `xai/grok-4.6`, seven agents dispatched in one round, disjoint write sets: agent *N* owns | ||
| exactly `readme/README.<code>.md` and nothing else. Each packet carries the full English | ||
| `README.md`, this outline, the byte-identical list, the file's existing translation for | ||
| terminology continuity, and the locale's `docsPath`. | ||
|
|
||
| Standing instruction in every packet: translate for a reader of that language, not word by | ||
| word. Keep the English file's register — direct, technical, unhedged. Do not add sections, | ||
| do not drop sections, do not add marketing. | ||
|
|
||
| ## Korean | ||
|
|
||
| `README.ko.md` gets the `cxc-kwrite` four-pass revision in the main session after the draft | ||
| lands: register consistency end to end, translationese and AI idioms removed | ||
| (`~에 대해`, `~를 통해`, `~함으로써`, `결론적으로`, `기대된다`), no `첫째/둘째` enumeration, no stacked | ||
| sentence-initial connectives, and concrete endings rather than abstract ones. Meaning stays | ||
| frozen: the pass edits detected spans only. | ||
|
|
||
| ## Integration | ||
|
|
||
| Drafts are checked against the wp2 guard, not read for vibes. A locale that fails the token | ||
| stream is repaired against the reported index; a locale that fails the command check is | ||
| repaired against the English fence. `sourceSha256` is refreshed for all seven only once every | ||
| structural check is green. | ||
|
|
||
| ## Outcome | ||
|
|
||
| Five locales — fr, ko, ru, zh-CN, zh-TW — came from the parallel `xai/grok-4.6` round and passed | ||
| the guard on their own. The ja and tr agents died mid-run and were finished in the main session. | ||
|
|
||
| The delegation had a cost worth recording. Two of the agents wrote their file by passing the | ||
| document through a double-quoted `python3 -c` string. The README contains inline code spans such | ||
| as `` `ocx service` ``, `` `ocx stop` `` and `` `ocx service uninstall` ``, and inside a | ||
| double-quoted shell string a backtick is command substitution — so those commands ran, against the | ||
| user's live proxy, repeatedly. A translation task took down a running service four times before | ||
| anyone connected the two. Any future agent writing these files must use a file-editing tool, never | ||
| a shell string; a quoted heredoc is the only safe shell form, and even that is worse than not | ||
| going through a shell at all. | ||
|
|
||
| ## Documentation anchors are locale-owned | ||
|
|
||
| The first draft of this spec said to translate `https://opencodex.me/<path>` by inserting the | ||
| locale prefix and otherwise copying the URL. That produced fourteen dead links: Starlight derives | ||
| a heading id from the heading text, and the localized pages translate their headings, so | ||
| `#docker-compose` exists only on the English Remote Hub page — the French one is | ||
| `## Docker, retour arrière et dépannage`, the Korean one is `## Docker`. The locale files now link | ||
| the localized page without a fragment, and the guard compares the page rather than the fragment. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # wp4 — delivery | ||
|
|
||
| ## Branch and commits | ||
|
|
||
| Branch `codex/readme-i18n-parity` off the current `dev` head (`5669b96b7`), in the managed | ||
| worktree `/Users/jun/.codex/worktrees/c0a4/opencodex`, which starts detached. Adopt in place | ||
| with `git switch -c`; do not move or recreate the worktree. | ||
|
|
||
| Three scoped commits: | ||
|
|
||
| 1. `test(readme): guard non-English READMEs against drift` — the manifest, the guard test and | ||
| both test-layout registrations. | ||
| 2. `docs(readme): resync every non-English README to the current English source` — the seven | ||
| locale files and their refreshed `sourceSha256`. | ||
| 3. `docs(devlog): record the README i18n parity unit` — this unit. | ||
|
|
||
| ## Verification contract | ||
|
|
||
| The user forbade the local product suite for this task and asked for a `--no-verify` push. | ||
| What that means concretely, and what the PR description must say: | ||
|
|
||
| | Check | Status | | ||
| |---|---| | ||
| | `bun run test` (full suite, ~850 files) | NOT RUN — forbidden for this task | | ||
| | `bun run typecheck` | NOT RUN — forbidden for this task | | ||
| | `bun run build:gui`, `bun install` | NOT RUN — forbidden for this task | | ||
| | `bun test tests/ci-workflows/docs-readme-translation-parity.test.ts` | run — the new guard only | | ||
| | Remote CI on the pushed head | authoritative evidence | | ||
|
|
||
| Running the one new file is not the local suite: it is the smallest proof that the guard this | ||
| PR adds is not vacuous, and shipping an unexecuted guard would spend more of the user's time | ||
| than it saves. Everything else stays NOT RUN and is labelled as such rather than implied green. | ||
|
|
||
| ## Push and PR | ||
|
|
||
| Landed as [#4151](https://github.com/lidge-jun/opencodex/pull/4151), base `dev`, head | ||
| `codex/readme-i18n-parity`, pushed with `--no-verify`. CI dispatched on the pushed head as run | ||
| `34405975400`. | ||
|
|
||
| `git push --no-verify -u origin codex/readme-i18n-parity`, then a pull request against `dev` | ||
| — never `main` — with `.github/PULL_REQUEST_TEMPLATE.md` filled: Summary, Verification, | ||
| Checklist. The Verification section carries the table above verbatim, including the NOT RUN | ||
| rows. No screenshot is required: the PR touches no `gui` surface. | ||
|
|
||
| Out of scope for this unit: merging, releasing, promoting to `main` or `preview`, and touching | ||
| `docs-site/` translations. If review asks for the docs site, that is a new work-phase. | ||
|
|
||
| ## Guard non-vacuity record | ||
|
|
||
| Observed, not predicted. | ||
|
|
||
| | Mutation | Observed failure | | ||
| |---|---| | ||
| | the seven stale locale files, before the resync | 10 pass / 43 fail, each message naming the locale and the divergence | | ||
| | `sourceSha256` set to a dummy value for ko and ja | freshness failed naming both locales and printing the current README.md hash | | ||
| | `tr` removed from the manifest | registry failed naming the orphan file | | ||
| | a changed model id inside a fence | command parity differs while the translated prompt beside it does not | | ||
|
|
||
| ## Two guard defects the locales found | ||
|
|
||
| Both were found by running the guard against a finished translation, not by review: | ||
|
|
||
| - The command-parity rule classified a quoted argument as prose only when it contained a space. | ||
| Japanese and Chinese do not put spaces between words, so a translated example prompt read as an | ||
| identifier and had to equal the English sentence. Non-ASCII now counts as prose. | ||
| - The link check required the English fragment on a localized URL, which no localized page has. | ||
| It now compares the page and leaves the fragment to the locale. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Reject extra commands in localized shell fences.
structure()skips fence bodies, and the command check iterates only the Englishblock.lines. Therefore, a localizedbashorpowershellfence can append a command without failing either check. The existing structural check already validates fence count, order, and language. Add a line-count check after the missing-block check; no parser change is required.Suggested fix
🧰 Tools
🪛 markdownlint-cli2 (0.23.2)
[warning] 57-57: Spaces inside code span elements
(MD038, no-space-in-code)
🤖 Prompt for AI Agents