I built the localization checker before the localization. It still missed three defects. A developer building Parlotype, an open-source local voice-to-text desktop app in .NET 10 and Avalonia 12, used Claude Code to extract roughly 450 hardcoded strings into 389 localization keys across three languages, changing 122 files. The developer built parity scripts, xUnit tests, and Claude Code hooks before the extraction began, yet three user-visible defects still shipped, each representing a distinct category of blind spot rather than a translation error. The work is documented as a postmortem on the project's GitHub repository. Parlotype's UI was English-only, with the copy baked into markup and C : 211 literal attributes across 26 .axaml files, roughly 200 string literals across 48 view models, plus the tray menu, the dialogs and the toasts. I wanted Russian and Spanish, and I wanted the ninth language to cost one translation file and nothing else. Most of that work is mechanical, which makes it a good fit for an agent. I ran it as a directed session with Claude Code: I made the architectural calls and reviewed everything, the agent did the extraction, the translations, the tests and most of the implementation. The result is on master. 389 keys in three languages, 122 files changed, +11,467 / -618. I built the guardrails before the bulk work: a parity script, an xUnit mirror of it, two Claude Code hooks and a skill file. Three user-visible defects shipped past all of them anyway. Each one was a different category of blind spot, and none was a translation error. That is what this post is about. Some context on me: twenty years across C, C++, Java and Scala, about two years into .NET. This is the second postmortem from Parlotype https://github.com/mdemin729/parlotype , a local voice-to-text desktop app I build in the open with .NET 10 and Avalonia 12. The obvious order is: extract the strings, then write something that checks them. I inverted it. The checker, the test, the hooks and the skill all landed in phase 2, before a single one of the ~450 keys moved. The reason is specific to agent work. An agent doing bulk mechanical extraction across 26 markup files will drift: miss an attribute, invent a key naming scheme halfway through, translate one locale and forget the other. A human reviewer catches that at review time, which is the expensive moment. A checker catches it in the loop, which is free. What went in first: {0} counts must match, or string.Format throws in front of a user , every {loc:Tr} key in markup resolves, and a scan for hardcoded literals still in .axaml . dotnet test and the release gate enforce them too. A hook only covers sessions that go through Claude. PostToolUse and a Stop hook The baseline file is the interesting piece. scripts/localization-baseline.json https://github.com/mdemin729/parlotype/blob/v0.5.0/scripts/localization-baseline.json records how many hardcoded literals each .axaml still has. Counts may shrink, never grow. During extraction that turns "the check is red" into a useful signal instead of a wall. The hook distinguishes the two cases explicitly: php Extraction progress baseline stale, nothing broken - tell the agent to re-baseline. Actual regression new literal, missing key, placeholder mismatch - exit 2, block. Exit code 2 is what makes a Claude Code hook blocking. The guard also exits 0 on any internal failure of its own, so a bug in the tripwire can never wedge a session. I verified every check by breaking it on purpose before trusting it: deleting a key from one locale, adding a literal to markup, mismatching a placeholder. That habit is also what exposed the first blind spot. Two constraints in this codebase pointed in opposite directions. x:CompileBindings="True" is mandatory and {ReflectionBinding} is banned. But live language switching wants a binding to some localizer lookup, which is exactly the indexer-or-method shape that ban targets. I went in expecting to write a narrow, documented exemption. Avalonia 12 made the exemption unnecessary. CompiledBinding.Create