fix(reflect): fail when a run produces no answer instead of storing a placeholder (#2959) (#3761)
* fix(reflect): fail when a run produces no answer instead of storing a placeholder (#2959)
Reflect substituted a human-readable placeholder whenever a terminal path
came up empty: "No answer provided." for a done tool call with a blank
answer, and an iteration-limit sentence when the loop fell through. Both
are non-empty strings, so every downstream emptiness check read them as a
real answer.
refresh_mental_model's guard ("refuse to overwrite existing content with
an empty render") tests `not final_content.strip()`, so the placeholder
passed it and was written over the document. The refresh then returned
normally and the async operation was recorded as completed with no error:
a mental model built across months of refreshes became a 19-character
string with nothing in the operation log to alert on.
A run that produced no answer is a failure, so it now raises
ReflectNoAnswerError at the three points where that is known -- a blank
done answer, an empty final synthesis, and the unreachable iteration-limit
fall-through -- rather than inventing text for callers to store. Callers
that write what reflect returns never reach the write, so the previous
content is preserved by construction and no new guard is needed on the
refresh path. The HTTP endpoint maps it to a 500 beside the existing
ReflectToolCallError handler (#3013 set that precedent); the MCP tool
already surfaces exceptions as an error payload.
populated_content no longer special-cases the no-answer stub: nothing can
produce it, so what is stored and what is reported cannot drift.
* test(reflect): cover that a no-answer run leaves the stored document untouched
Adds the end-to-end half of #2959: the agent tests prove reflect raises,
these prove refresh_mental_model never reaches its write when it does --
content and both watermarks are byte-identical after the failure.
Also drops the now-dead `and answer` from the structured-output gate:
past the new guard the answer is always non-empty. N
Nicolò Boschi committed
f4a9369509ca0642158a8efe6d4bb3701c5d8685
Parent: 1e45834
Committed by GitHub <noreply@github.com>
on 8/24/2026, 11:21:39 AM