Stop recording a verdict the translation validator never reached - #726
Open
mircealungu wants to merge 1 commit into
Open
mircealungu wants to merge 1 commit into
mircealungu wants to merge 1 commit into
Conversation
The translation validator failed open in four places: an API exception, a batch API exception, an unparseable reply, and a batch response with fewer lines than items all returned is_valid=True. validation_service then wrote validated=VALID on the meaning — and because it short-circuits on VALID, that meaning is never looked at again. A transient Anthropic outage therefore permanently blessed whatever translations were in flight, which is the opposite of what a validator is for. ValidationResult now carries a three-state outcome (VALID / INVALID / UNAVAILABLE) instead of a boolean, with is_valid kept as a property so the existing call site reads the same. On UNAVAILABLE the service persists nothing and returns the user_word, leaving it NOT_VALIDATED and in scope for the re-scanning tools. Parsing is now layout-driven rather than positional. The prompts declare two different pipe-delimited layouts (the batch one asks for no CEFR level) but both were parsed by the same positional reader, so batch replies had every field after the second one read as the wrong field. Each layout now has its own field list, split with a bounded maxsplit so a stray "|" inside free text is absorbed by the last field instead of shifting everything after it, and closed-vocabulary fields are dropped when out of vocabulary rather than written through. That also fixes phrase_type "arbitrary_multi_word", which PhraseType.from_string() has always returned None for. A FIX verdict carrying no correction is now UNAVAILABLE too: it is not actionable, and _fix_bookmark would have read it as "no correction provided" and marked the meaning permanently INVALID. Co-Authored-By: Claude Opus 5 <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.

The bug
The translation validator failed open in four places — an API exception, a batch API exception, an unparseable reply, and a batch response with fewer lines than items — all returning
is_valid=True.validation_servicethen wrotevalidated = VALIDon the meaning, and since it short-circuits on VALID, that meaning is never re-examined.So a transient Anthropic outage permanently blessed whatever translations happened to be in flight. That is the opposite of what a validator is for.
What changed
ValidationResultcarriesVALID/INVALID/UNAVAILABLEinstead of a boolean.is_validsurvives as a property, so the one call site reads unchanged. OnUNAVAILABLEthe service persists nothing and returns theuser_word, leaving itNOT_VALIDATEDand in scope for the re-scanning tools.|inside free text is absorbed by the final field instead of shifting every field after it. Closed-vocabulary fields (frequency / cefr_level / phrase_type) are dropped when out of vocabulary rather than written through — an out-of-vocabulary value is what a shifted line looks like.arbitrary_multi_wordalias. The prompt's vocabulary saysarbitrary_multi_word;PhraseType.ARBITRARY_MULTI_WORD.valueismulti_word, sofrom_string()has always returnedNonefor it, andexclude_because_multi_wordnever fired.FIXwith no correction isUNAVAILABLE. It is not actionable, and_fix_bookmarkwould have read it as "no correction provided" and marked the meaning permanentlyINVALID.Verification
Only
validation_serviceconsumesValidationResult, and theis_validproperty keeps that call site working — checked by grep acrosszeeguu/andtools/.The parser was exercised by hand over valid / fix / no-correction / garbage / empty / aliased / out-of-vocabulary / embedded-pipe inputs, and each produced the intended outcome. There are no automated tests yet — the parse layer is now pure and easy to test, and that is the obvious next commit on this branch.
Open question, not addressed here
This stops new bad writes. It does nothing about rows already stamped by the old behaviour:
meaning.validatedAn unknown share of those 13,482 were never actually checked. There is no marker distinguishing them, so the options are to re-validate the lot, leave them, or find a proxy (e.g. rows written during a known outage window). Worth deciding separately.
🤖 Generated with Claude Code