{{ message }}
Repository navigation
Read the kind-word binding reply in the shapes the model actually writes so a whole base no longer stays untyped in silence - #894
Merged
Conversation
…tes so a whole base no longer stays untyped in silence Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Wayland Yang <wayland0916@gmail.com>
WaylandYang
added a commit
that referenced
this pull request
Sep 26, 2026
…tch needed the repair (#919) gemini-3.5-flash through an OpenAI-compatible gateway writes a raw newline inside a quoted reason. serde refuses it ("control character (\u0000-\u001F) found while parsing a string"), the adjudicate_entities job retries three times on the same reply, and the whole batch is marked failed; on a 100-document Re-DocRED base 2903 resolution_reviews stayed pending. utopia-extract: parse_adjudication_repairing tries serde first; on failure it escapes bare control characters inside string literals (\n, \r, \t, \u00XX) and parses again, returning whether the repair was needed. parse_adjudication keeps its signature and output. Newlines between tokens are untouched; \" does not end a string; already-escaped sequences stay as they are. utopia-server: adjudication.rs and governance.rs use the repairing parser and log a WARN with the kb (and run) id, the batch size and the first pair's names when the fallback was taken, the way #894/#895 report a reshaped alignment reply. Tests: a well-formed reply is not repaired and reads the same as before; a reply with a raw newline and a raw tab inside "reason"/"why" strings parses with the newline kept in the reason; the escaper only touches control characters inside strings. Signed-off-by: Wayland Yang <wayland0916@gmail.com> Co-authored-by: Claude Fable 5.1 <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.

What happened
Two of twelve identity-bench rounds ended with every document
done, 30+ entities, 50+ facts, and everyentities.type_idNULL, so all 21 anchors (located by class) weremissing. The server log had no WARN.The surviving KB (
guard-run7 reverse) shows 13align_typesjobs, alldone, nolast_error, each taking 10–50 s, and zerotype_bindingsrows. Reproducing with the same entities against SiliconFlowdeepseek-ai/DeepSeek-V3.2showed why: at temperature 0 the model often answers the kind-word prompt not as the documented{"b": [[id, key]]}but as an id-keyed object, in several variants:parse_kind_word_responseread any object without abarray as "no pairs, nothing malformed", so every item counted as unanswered. One unanswered vote out of two means no decision, nothing is written, nothing is logged, and nothing is re-enqueued (only call/parse failures were). The same prompt returns the same shape every round, so the base stayed untyped through all twelve documents. The "good" sibling round only bound anything on 2 of its 12 runs, for the same reason.What changed
utopia-extract/align.rs: the parser reads all the shapes above, plusbas an object,{"id":..,"key":..}pairs and a bare top-level array. Unreadable ids/keys count as malformed instead of absent.type_alignment.rs: a pass that parses to zero choices is treated as a failed pass and logged at WARN withfinish_reason, char count and a snippet of the reply. The completion line reportsunanswered, and a WARN follows when it is non-zero. Unfinished runs (failed or unanswered) re-enqueue themselves with areaskcounter in the payload, at mostMAX_REASK(3) times with a growing delay; new words or stale bindings still re-enqueue as before.main.rs: the job dispatcher passesreaskthrough.an_id_keyed_reply_still_binds_the_kind_wordreproduces the all-NULL case through the scripted model and asserts the class lands;an_unreadable_reply_is_asked_again_a_bounded_number_of_timesasserts no decision, onereask=1job, and no more once the budget is spent.Resolution, adjudication and governance are untouched.
Verification
cargo test --workspace,cargo clippy --workspace --all-targets -D warnings,cargo fmt --checkgreen locally against a dedicated database.typed 21/29).Earlier rounds had 3–9 anchors missing. The f1 numbers are not directly comparable with those rounds: with more anchors usable, the pre-existing INST/LN/P2 splits now enter the score (pair-for-pair identical to
guard-run7 forward, which also had 2 missing). Those splits are candidate-generation gaps on the identity side, out of scope here.Follow-ups noted separately: the phrase-binding parser has the same silent path;
scripts/e2e_type_drift.mjsis stale (documents.findon a{docs: [...]}response) independent of this change.🤖 Generated with Claude Code