Assert a chat turn ends in exactly one earned terminal - #858
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: WaylandYang <wayland0916@gmail.com>
This was referenced Sep 21, 2026
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.
Which record
None. This adds a test; no decision changes and no status line needs updating. From #857.
Why
#845, #850, #851 and #852 each close one case of the chat path reporting success when something underneath it failed, and each ships its own regression. Four tests that each catch one leak do not catch the fifth, which is written the same way by whoever next adds a path that can fail after the first byte is out.
This asserts the rule instead of the instances. One table, one row per injection point, the same three assertions every time:
event: donereaches the clientdoneanderrortogether occur onceerrorwith empty data fails tooAdding a path that can fail costs one row. Not being able to write that row means the path has not decided how it ends, which is the thing review should catch.
The table today
doneerrorerrorerrorRows whose fix has not landed keep a
pending: Some("#845")field, print a skip line, and are left in the table rather than deleted — a known hole should be visible. Deleting that field when the PR lands is what starts enforcing the row.The pending rows do fail on
dev. Settingpending: Noneon both against79caca04:So the row is not decorative; it catches the live bug and will go green when #845 lands.
Three choices worth explaining
A sibling module, not another
modline inchat_empty_reply_tests.rs. That file is where #848, #849, #850 and #852 all collide (see my note on #852). This declareschat_terminal_testsfromchat.rsinstead, so it adds nothing to the contested spot.The fixture is reused, not copied. Seven lines in
chat_empty_reply_tests.rswidenReply,Scripted,Fx,fixture,askandcleanuptopub(super). All of them are declaration lines, far from themodblock the other PRs touch. Copying the fixture would have been the parallel-implementation problem I raised on #827.Terminals are counted by frame, not by substring. A model can legitimately discuss SSE and write
event: doneinside an answer.a_terminal_is_a_frame_not_a_substringpins that separately and needs no database — it is the floor the other three assertions stand on.Not in the table yet
The injection手段 for these arrive with their own PRs, and each becomes one more row rather than another test file:
Validation
Linux/Windows-agnostic; run here against an isolated database templated from a clean base at migration 69.
cargo fmt --all -- --checkclean,cargo clippy -p utopia-server --all-targets -- -D warningsclean.No CI step is added — this runs inside the existing
utopia-servertest target, and my note on #852 argues the five separate chat steps there should collapse rather than grow.🤖 Generated with Claude Code