Skip to content

fix: reliability batch 1 — language identity, atomic model switch, structured outcomes, NFC dictionary - #80

Merged
GalaxyRuler merged 5 commits into
mainfrom
codex/reliability-batch-1
Jul 20, 2026
Merged

GalaxyRuler merged 5 commits into
mainfrom
codex/reliability-batch-1

Conversation

@GalaxyRuler

Copy link
Copy Markdown
Owner

Before Submitting This PR

  • I have searched existing issues and PRs to ensure this isn't a duplicate
  • I have read CONTRIBUTING.md

Stacked on #79 → #78. Base is codex/reliability-batch-0b; retarget to main after the earlier batches merge.

Human Written Description

This is the semantic-foundations layer for the language and dictionary reliability work — plumbing and data migrations that the pipeline fixes build on, with no new end-user behavior beyond correcting wrong outputs. The bilingual bugs mattered most to me: language tags getting case-mangled (zh-Hans → zh-hans, breaking Chinese conversion), a locked language silently reverting to Auto after a failed model switch, and Arabic dictionary entries not matching when composed vs decomposed Unicode forms differed.

What's in the batch

  1. BCP-47 casing + language-mode invariant. One normalize_bcp47 helper: primary subtag lowercased, script subtag Title-case (zh-Hans), region UPPER (en-US), used everywhere a code is stored. Legacy stores with a concrete language but missing mode now resolve to Single, and the frontend treats the backend mode as authoritative.
  2. Atomic model switch. Snapshots selected_model + selected_language + dictation_language_mode + adaptive_language_shortlist; a failed load restores all four (previously only the model). When a lock is genuinely incompatible with the new model, lock+mode+shortlist clear together and a translated reason is surfaced instead of a silent revert to Auto.
  3. Structured language outcome (plumbing only). New LanguageOutcome { requested, effective, supported: Supported|Unsupported|Unknown, detected, detection_confidence, translation_requested, translation_performed }, populated truthfully — empty capability list ⇒ Unknown (not "accept any"), and engines that ignore the language param leave translation_performed=false. The guard/history are not rewired to consume it yet (that's the next batch).
  4. NFC dictionary identity. Adds unicode-normalization; one canonical key (NFC + case-fold + whitespace + smart-punct) used by learning, exact/fuzzy match, suppression, and the mirror. Display strings are never normalized. A v3 migration collapses canonically-equal entries (manual-tier wins, else first), preserving order and recording any discarded replacement_of as a review candidate rather than dropping it. Forced-save + rollback like v2.

Testing

  • Full Rust suite: 756 passed, 0 failed (implementation + independent review run). A combining-mark punctuation regression was caught by the suite and fixed (dedicated dictionary_suffix_punctuation helper).
  • tsc, ESLint, Prettier, translations (870 keys × 19 locales), generated bindings, frontend language-mode suite (8 tests), settings-lock coverage, and cargo-deny (advisories/bans/licenses/sources) all pass. unicode-normalization 0.1.25 is the only new dep.

Needs device/runtime verification (follow-up, not blocking desktop review):

  • Real-provider detected-language and confidence values — transcribe-rs does not currently expose them, so detected/detection_confidence are honestly left None.
  • Android Kotlin matcher parity for composed/decomposed Arabic and accented Latin (Android has its own matcher, untouched here).

AI Assistance

  • AI was used (please describe below)

If AI was used:

  • Tools used: Codex (implementation), Claude Code (audit, adversarial review, code review)
  • How extensively: implementation and review AI-driven from a human-approved plan; all four items code-reviewed against spec, full suite re-run independently

🤖 Generated with Claude Code

Use unicode-normalization because Rust's standard library does not provide NFC normalization. Reconcile persisted v3 dictionary identity deterministically while preserving display strings and recording discarded replacement rules for review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dcbc2072f9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +226 to +228
if is_manual_trust_entry(&group[index]) && !is_manual_trust_entry(&group[winner_index])
{
winner_index = index;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve active dictionary entries during NFC merge

When a user has NFC-equivalent duplicates where the active entry is auto-learned and a later manual/imported duplicate has been quarantined or disabled, this winner selection chooses the inactive manual-tier entry solely because of its source. The migration then drops the active entry and rebuilds custom_words from the retained inactive entry, so that phrase stops being used for prompts/replacements after upgrade even though the user still had an active dictionary entry for it.

Useful? React with 👍 / 👎.

Batch 1 changed set_active_model to return ModelSwitchOutcome { reason }.
The Playwright mock still returned the pre-batch unit (null) shape, so
modelStore.selectModel's result.data.reason access threw during first-run
onboarding, stalling the model->shortcut step. Update the mock to the real
shape and null-guard the frontend read defensively.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@GalaxyRuler
GalaxyRuler merged commit 13702bb into main Jul 20, 2026
15 checks passed
@GalaxyRuler
GalaxyRuler deleted the codex/reliability-batch-1 branch July 20, 2026 20:34
@GalaxyRuler GalaxyRuler mentioned this pull request Jul 20, 2026
3 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant