fix(configio,codec): record export locale and refuse cross-locale silent label remap#20
Merged
Merged
Conversation
…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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4. The design rationale lives in issue #4's pinned comment; this PR is the implementation.
What changed
Loud failure on unresolvable enum labels —
pkg/codec/compound.go:enumNumnow returns(int, error).encodeVarandEncodeCompoundpropagate. 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 returned0, 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 import —
pkg/configio/export.gowritesdevice.localeinto the YAML header (omitempty, so legacy exports still round-trip).cmd/mythy/import.go,cmd/mythy/diff.go, andcmd/mythy/validate.goconsult a sharedreconcileLocalehelper after parsing the YAML and before opening the Modbus session:device.locale--localeen)LocaleMismatchError; require--force-localeto proceedLocaleMismatchErroris typed (lives inpkg/configio/parse.gonext toProductMismatchError) so callers canerrors.As. The error message names both locales and points at the--force-localeescape 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— verifiesLocale: "it"lands in YAML aslocale: 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-localebypass).What's in scope vs not
In scope: import, diff, validate. All three carry the new
--force-localeflag.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