Skip to content

fix(configio,codec): record export locale and refuse cross-locale silent label remap#20

Merged
ale-rinaldi merged 1 commit into
mainfrom
fix/issue-4-locale-aware-yaml
May 15, 2026
Merged

fix(configio,codec): record export locale and refuse cross-locale silent label remap#20
ale-rinaldi merged 1 commit into
mainfrom
fix/issue-4-locale-aware-yaml

Conversation

@ale-rinaldi

Copy link
Copy Markdown
Contributor

Closes #4. The design rationale lives in issue #4's pinned comment; this PR is the implementation.

What changed

Loud failure on unresolvable enum labelspkg/codec/compound.go:enumNum now returns (int, error). encodeVar and EncodeCompound propagate. Previously a label that didn't exist in the loaded template's enum (because of locale drift, a typo, or a locale-translated string) silently returned 0, so a compound write went through with the zero entry in place of the intended value — exactly the SOGLIA-flips-MINIMA↔MASSIMA silent-data-loss path the issue flags.

Locale recorded in YAML, reconciled at importpkg/configio/export.go writes device.locale into the YAML header (omitempty, so legacy exports still round-trip). cmd/mythy/import.go, cmd/mythy/diff.go, and cmd/mythy/validate.go consult a shared reconcileLocale helper after parsing the YAML and before opening the Modbus session:

YAML device.locale CLI --locale behaviour
absent (legacy) absent use the global default (en)
absent (legacy) passed use the CLI value
present absent use the YAML value (info-line not added — silent adopt is the typical path)
present matches use the CLI value (= YAML value)
present differs return LocaleMismatchError; require --force-locale to proceed

LocaleMismatchError is typed (lives in pkg/configio/parse.go next to ProductMismatchError) so callers can errors.As. The error message names both locales and points at the --force-locale escape hatch.

Tests

  • pkg/codec/compound_test.go:TestEncodeCompoundErrorsOnUnresolvableEnumLabel — passes an Italian label into a US-locale-shaped enum and asserts the error names the offending VAR and quotes the bad label.
  • pkg/configio/export_test.go:TestExportRecordsLocaleInHeader — verifies Locale: "it" lands in YAML as locale: it, and that an empty Locale is omitted (back-compat with pre-fix exports).
  • cmd/mythy/locale_compat_test.go — five cases covering the full reconciliation table (legacy / implicit adopt / explicit match / explicit mismatch errors / --force-locale bypass).

What's in scope vs not

In scope: import, diff, validate. All three carry the new --force-locale flag.

Out of scope: a label-resolution fallback that scans every installed locale until one matches. The locale-recording approach is enough to eliminate the silent path for the typical round-trip; broader cross-locale label tolerance would need separate design.

🤖 Generated with Claude Code

…ent label remap

Two changes that compose to close issue #4's silent-data-loss path:

1. enumNum no longer returns 0 when a compound sub-field's enum label
   can't be resolved. It returns an error and encodeVar / EncodeCompound
   propagate up to the user. This matches the loud behaviour
   resolveEnumWriteValue already had for top-level ENUMs and removes
   the case where a YAML label translated to a different locale
   silently writes the zero entry (SOGLIA Tipo flipping MINIMA↔MASSIMA
   was the worked example).

2. Export records `device.locale` in the YAML header. Import / diff /
   validate read it back through a small reconcileLocale helper:

     * file has no locale (legacy) → CLI default unchanged;
     * file has locale, user didn't pass --locale → adopt file's;
     * file has locale, user passed matching --locale → use it;
     * file has locale, user passed different --locale → return
       LocaleMismatchError unless --force-locale is set.

This keeps YAML files readable in the locale they were produced under
while making cross-locale round-trips a loud, opt-in event rather
than a silent corruption. The error names both locales and points
at --force-locale so the operator can either drop --locale (adopting
the file's) or keep theirs explicitly.

Closes #4

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ale-rinaldi ale-rinaldi merged commit f156c87 into main May 15, 2026
2 checks passed
@ale-rinaldi ale-rinaldi deleted the fix/issue-4-locale-aware-yaml branch May 15, 2026 11:35
ale-rinaldi added a commit that referenced this pull request May 15, 2026
…le-aware round-trip (#25)

The export / import section was lagging by two v1.2.0 affordances:

  * --include-disabled-modules and the post-export per-category skip
    summary lines, both from #23. The previous paragraph claimed
    --all was "shorthand for all three" includes; it now bundles all
    four, including the module-gate override.
  * device.locale recording and the import/diff/validate-side
    reconciliation against --locale (with --force-locale to override),
    both from #20. Earlier the README only mentioned --locale as a
    template-picking knob; the YAML-round-trip story wasn't covered.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

Locale-dependent YAML round-trip: enum labels from one --locale silently default to 0 in another locale

1 participant