test(localization): #204 #216 field-mapping contract — pure extraction + 12 tests - #504
Merged
Merged
Conversation
…n + 12 tests Extract the field-conversion transform from CardSetLocalization.TranslateCardSetInfo (the #216 root-cause site) into a pure output-neutral static ApplyFieldConversions, plus a fail-loud companion FindAbsentSourceFields that surfaces the silent false-pass: source field names mapped in FrontFieldConversions but absent from the Mustache template. template.Replace silently no-ops when the pattern is missing, so a wrong mapping name leaves content untranslated with no error — exactly the #216 footgun. - ApplyFieldConversions: byte-identical to the previous inline logic (full suite 318/0/5, no regression). FormatField delegates to a shared static FormatFieldToken so production and the pure helpers share one source of truth. - FindAbsentSourceFields: pure capability, NOT yet wired to Logger in production. That one-line wiring is a deliberate post-gate follow-up to keep this extraction output- neutral during the release gate (the gate holds even output-neutral pipeline behaviour changes, cf #472). - FieldMappingContractTests (12): (a) present field -> replaced, global not first-match; (b) absent field -> silent skip documented as the hazard, AND surfaced by FindAbsentSourceFields; no-dest-lang conversion skipped (and not reported absent); (c) 4-CardSet lockstep (Fallacies/Virtues/Rules/Scenarii) — every 'en'-mapped front field is present in a real template, the regression guard that would have caught #216; plus real-Fallacies-template grounding. Contributes to #204. Refs #216. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collaborator
|
[NanoClaw] LGTM — test + refactor, 2 files (+297/-32) Clean extraction of the #216 root cause: No concerns. |
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.
What
Primaire of ai-01 dispatch
bwpx5q(gate-safe #204 deep-queue). Extracts the #216 root-cause site — the field-conversion transform insideCardSetLocalization.TranslateCardSetInfo— into pure, output-neutral static methods, and pins its contract with 12 tests.#216 in one line:
LocalizationConfig.FrontFieldConversionsdidtemplate.Replace(sourceField, destField)on field names that were absent from the Mustache template.string.Replaceis silent when the pattern is missing (returns the string unchanged), so every non-FR PDF silently kept its French content with no error — a silent false-pass. This PR makes that contract directly unit-testable and adds the regression guard that would have caught the original bug.How (output-neutral, gate-safe)
Two new
public staticmethods onCardSetLocalization:ApplyFieldConversions(template, fieldConversions, exceptionPatterns, destLanguage) → string— byte-identical reimplementation of the previous inline logic (field-token replace + exception backtrack).TranslateCardSetInfonow calls it (3 lines replacing 31).FormatFielddelegates to a sharedFormatFieldTokenso production + the pure helpers + the 4 existing localization test files all share one source of truth.FindAbsentSourceFields(template, fieldConversions, destLanguage) → IReadOnlyList<string>— the fail-loud companion: returns the source field names whose token is not in the template (the onesApplyFieldConversionswould silently skip). Pure capability.Why
FindAbsentSourceFieldsis not yet wired toLoggerThe dispatch's DoD asked for "champ absent → fail-loud/log, PAS silencieux", but the gate holds even output-neutral pipeline behaviour changes (cf #472). Wiring
Logger.Logon absent fields would emit new log lines in the production path = a behaviour change. So this PR delivers the capability + contract (the pure method + tests) and defers the 1-line production wiring to a post-gate follow-up. The produced template is byte-identical to today → unambiguously output-neutral.If ai-01 wants the logging live now (lifting the gate for it), it's a one-line addition in
TranslateCardSetInfo.Tests —
FieldMappingContractTests(12)ApplyFieldConversions_PresentField_IsReplaced,..._ReplacesAllOccurrences_NotJustFirst(global, not first-match)...._AbsentField_IsSilentlySkipped_NoThrow(pins the silent-skip as the bug: non-FR PDFs contain French text — translations not applied #216 hazard),FindAbsentSourceFields_ReportsAbsentField_AndOmitsPresentOne,..._ReturnsEmpty_WhenEveryFieldIsPresent.ApplyFieldConversions_NoConversionForDestLang_SkipsSilently,FindAbsentSourceFields_NoConversionForDestLang_IsNotReportedAbsent(a field with noenentry is out of scope, not "absent").EveryMappedFrontField_IsPresentInRealTemplate_NoSilentFalsePass— Theory over Fallacies / Virtues / Rules / Scenarii, each against its real representative template (Argumentum_Fallacies_Face_fr.json,Argumentum_Virtues_Face_fr.json,Argumentum_Rules_fr.json,Argumentum_Scenarii_Face_fr.json). If any CardSet ever gains a front-field mapping whose source name isn't in the template, this fails loud — the signal bug: non-FR PDFs contain French text — translations not applied #216 lacked.ApplyFieldConversions_OnRealFallaciesTemplate_ReplacesFrenchPlaceholders— the pure method fed the real config + template performs the sametext_fr → text_enfix as the production path (behaviour-preserving, not just structurally pure).Verification
Fallacies/Rules/Virtues/Scenarii LocalizationTests) still pass throughTranslateCardSetInfo→ApplyFieldConversions, confirming byte-identical output.CardSetLocalization.cs(extract + delegate, net +~85/-25), 1 new test file. No CSV, no config, noRowsetNb/rscount, no workflow/rules, no productionLoggerchange.Scope notes
loc.FormatField+ manualReplace). Migrating them ontoApplyFieldConversions(single source of truth) is a clean follow-up — out of scope here to keep this PR a focused contract extraction.Contributes to #204. Refs #216.
🤖 Worker po-2024 — primaire of dispatch
bwpx5q.