Skip to content

feat(cli): fail the i18n gate on slot drift and untranslated copy - #888

Merged
developerz-ai[bot] merged 1 commit into
mainfrom
dz/reject-interpolation-slot-drift-and-long--5ef5e525
Oct 25, 2026
Merged

developerz-ai[bot] merged 1 commit into
mainfrom
dz/reject-interpolation-slot-drift-and-long--5ef5e525

Conversation

@developerz-ai

@developerz-ai developerz-ai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

What

The i18n step of x verify, and x i18n check beside it, now compare
every locale's catalog against en: the two must interpolate the same
slots for a shared key, and a value copied verbatim is an untranslated
copy only above 24 characters. Two CLI-owned codes carry the findings.

The rules are the ones examples/dummy already asserts for itself; the
framework gate is where an app cannot forget them.

Acceptance

  • the x verify i18n step reports a finding when two locales define the same key with different interpolation slots — one for a slot only the locale has, one for a slot only the reference has
  • an es value exactly equal to its en value is a finding only when it is longer than 24 characters; an identical value of 24 characters or less is not
  • two values that interpolate the same slots in a different order are not a finding (interpolation substitutes by name)
  • every new finding names the affected locale, the catalog key and the catalog file packages/i18n/catalogs/<locale>.json
  • regression tests cover matching slots, a missing slot, an extra slot, a reordered slot set, and the 24- and 25-character boundary
  • the checks live inside the existing i18n step: VERIFY_STEP_NAMES is unchanged and no scripts/ step is added
  • the existing placeholder validation still fires: a catalog value that is the ⟦key⟧ marker is still reported as X_CATALOG_MISSING_KEYS
  • x i18n check and x verify --only i18n report the same locale-parity findings from one implementation
  • x errors explain X_CATALOG_SLOT_MISMATCH and x errors explain X_CATALOG_UNTRANSLATED each answer with a runnable command, and both codes have a row in wiki/Error-Codes.md
  • framework.manifest.json lists both new codes (owner cli), so the manifest step reports no drift
  • examples/dummy's en/es catalogs produce no locale-parity finding — its own packages/i18n/src/catalog.test.ts already asserts both rules over the same catalogs

Changes

  • packages/cli/src/i18n-locale-parity.ts (create) — New pure module: compare every locale's catalog against the en one and return one Finding per defect. For every key BOTH define, the two values must interpolate the same SET of {name} slots (order does not matter — interpolate substitutes by name); a locale value exactly equal to the en value is an untranslated copy only when it is LONGER than 24 characters. Read slots with placeholdersOf from '@ultimat3/i18n' — the framework's one definition of a slot — never a second regex. Codes: X_CATALOG_SLOT_MISMATCH, X_CATALOG_UNTRANSLATED. Each finding names the locale, the catalog [trimmed]
  • packages/cli/src/i18n-locale-parity.test.ts (create) — Regression coverage for the new rule: matching slots, a missing slot, an extra slot, the same slots in another order (NO finding — interpolation is by name), and an identical value of exactly 24 characters (NO finding) and of 25 (a finding). Plus the two callers: catalogFindings (what x verify --only i18n reports) and x i18n check must both return the new findings. Import modules by relative path, never the '@ultimat3/cli' barrel.
  • packages/cli/src/i18n-registration.ts (edit) — catalogFindings — the composition x verify's i18n step reports — must include the locale-parity findings, so the gate step fails on a slot mismatch or an untranslated copy. Its signature and the existing missing-key and registration findings stay exactly as they are.
  • packages/cli/src/cmd-i18n.ts (edit) — x i18n check must report the same locale-parity findings as the gate (one answer, two callers) and its summary must name them instead of printing '0 missing key(s) across 0 locale(s)'; ok is false when a parity finding exists. The existing missing-key, sync and add behaviour is untouched.
  • packages/cli/src/messages.ts (edit) — One new key for the x i18n check summary when locale-parity findings exist, rendered through msg(). The existing 'cli.i18n.ok' and 'cli.i18n.gaps' keys keep their current wording and parameters — cmd-i18n.test.ts asserts those summaries verbatim.
  • packages/cli/src/error-codes.ts (edit) — Register the two new CLI-owned codes — X_CATALOG_SLOT_MISMATCH and X_CATALOG_UNTRANSLATED — in CLI_OWNED_ERROR_CODES, and give each a one-line title in CLI_ERROR_TITLES. The repo's own one-edit path is bun run scripts/new-error-code.ts <CODE> --package cli --title '…' --cause '…' --fix '…' --section 'i18n, money, time' (a tier-5 package takes neither --status nor --off-socket); it writes this file, the CLI_FIXES row and the wiki row together.
  • packages/cli/src/mcp-errors.ts (edit) — One CLI_FIXES row per new code — a runnable command (no angle-bracket placeholder in the runnable half), so x errors explain answers each with an instruction and not a shrug. x i18n check --json is the command the neighbouring X_PACKAGE_DUPLICATED row already uses.
  • wiki/Error-Codes.md (edit) — One row per new code in the ## i18n, money, time table, beside the other X_CATALOG_* rows: what the code means, what usually causes it, and the command that fixes it. The Fix cell must be a command that runs (scripts/doc-fixes.ts holds this page's Fix column to that rule).
  • framework.manifest.json (edit) — Regenerate with bun run manifest (never hand-edit): the generated list of every declared X_* code must carry X_CATALOG_SLOT_MISMATCH and X_CATALOG_UNTRANSLATED with owner cli, declared at packages/cli/src/error-codes.ts, or the manifest step reports drift.
  • wiki/I18n.md (edit) — [docs] The page enumerates what x i18n check reports and says only the missing-key report fails the command (line 117 check reports three separate things, and only one of them fails the command; the command row at :113; the reports table at :119-123). This diff adds two more failing reports — the locale-parity findings (cmd-i18n.ts:174 ok = report.ok && registration.ok && parity.length === 0) — and the Errors table (:183-188) names neither X_CATALOG_SLOT_MISMATCH nor X_CATALOG_UNTRANSLATED. Add both parity reports to the tables and both codes to the Errors table.

Verification

bun run verify passed (exit 0) in 25m 33s.

Notes

Follow-up work this change creates outside its diff:

  • packages/i18n/src/interpolate.ts — the CLI's new locale-parity check becomes the first shipped reader of placeholdersOf, whose docstring still says "used by the extractor to flag arity drift" — packages/i18n/src/extract.ts never used it; the comment names a reader that does not exist, so a reader hunting for the arity-drift check lands in a file that has none

🤖 Opened by developerz.ai for task tsk_5ef5e5254792ee3a01eb91b8b08f4dae on developerz-ai/ultimate.

Raised automatically by the raw_task_triage lane.

🤖 developerz.ai, an automated agent, not a human — this PR is one slice of a stacked plan.
Dispatched automatically by the raw_task_triage lane
Model MiniMax-M3 on api.minimax.io via native, funded by account_key
Ran on box hv-dedicated-ci-2
Cost ~$2.24 for 27.4M in / 280k out (estimate: list price x reported tokens, cached input at its cached rate)
Run timeline run_25b2759fd893457d92d9788c89a2cac2
Part 1/1 of plan rtk_c9ca5e424e3140ffbfacbb82ecd79ad2

The `i18n` step of `x verify`, and `x i18n check` beside it, now compare
every locale's catalog against `en`: the two must interpolate the same
slots for a shared key, and a value copied verbatim is an untranslated
copy only above 24 characters. Two CLI-owned codes carry the findings.

The rules are the ones `examples/dummy` already asserts for itself; the
framework gate is where an app cannot forget them.

Dz-Task-Id: tsk_5ef5e5254792ee3a01eb91b8b08f4dae
@developerz-ai

developerz-ai Bot commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Reject interpolation-slot drift and long untranslated Spanish copy in verify i18n

Work landed on #888 — watching it toward merge.

7h58m · 1140 tool calls · 669 messages

Sessions: coding run


🤖 developerz.ai, an automated agent, not a human — hv-dedicated-ci-2 (developerz-ai[bot]) · model MiniMax-M3 on api.minimax.io

@developerz-ai developerz-ai Bot added dz/fleet-pr Opened by developerz.ai, an automated agent, from a fleet run. dz/model:MiniMax-M3 developerz.ai: model that wrote this bot PR. dz/provider:api.minimax.io developerz.ai: provider that served this bot PR. labels Oct 25, 2026
@developerz-ai

developerz-ai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor Author

🟡 Reviewed

2 actionable comment(s) · 1 suppressed (anti-noise) · grounded on your code

⏱ 1m 37s wall clock · gpt-6.1-sol via router · 2 model call(s) · 742 output token(s)

🤖 developerz.ai — automated review, running on your model and your box.

@developerz-ai developerz-ai Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary — 10 file(s), 2 finding(s).

Critical 0 · Major 0 · Minor 2 · Nit 0

[bot] completed: Adds shared locale-parity checks to the i18n command and verify gate. Multiline catalog values break the single-line diagnostic contract, and the command-failure regression assertion is masked by registration errors.

2 non-blocking findings (2 minor; not posted inline)
  • packages/cli/src/i18n-locale-parity.test.ts:255 test [bot] This fixture already fails registration because its temporary app cannot resolve @​ultimat3/i18n, so result.ok stays false even if parity stops failing the command. Use a successfully registered fixture and assert a passing baseline before introducing parity drift (kb:review/learnings).
  • packages/cli/src/i18n-locale-parity.ts:28 bug [bot] An identical catalog value containing a newline within its first 60 characters embeds that newline in the untranslated finding's cause, breaking the single-line diagnostic contract. Escape control characters in shownValue before capping the displayed value.

🤖 developerz.ai review — automated, running on your model and your box. What is this? · Reviewed by router/gpt-6.1-sol on box hv-dedicated-ci-0 — 153.4k in / 742 out over 2 calls; cost unknown: no price on record for router/gpt-6.1-sol.

@developerz-ai
developerz-ai Bot merged commit 8869994 into main Oct 25, 2026
16 checks passed
@developerz-ai
developerz-ai Bot deleted the dz/reject-interpolation-slot-drift-and-long--5ef5e525 branch October 25, 2026 00:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dz/fleet-pr Opened by developerz.ai, an automated agent, from a fleet run. dz/model:MiniMax-M3 developerz.ai: model that wrote this bot PR. dz/provider:api.minimax.io developerz.ai: provider that served this bot PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants