emrg: GUI interleaved text/tool message order (rant 21:57:10) - #554
Merged
Conversation
Fix: handleDelta reused one assistant node per request_id while tool rows
append independently to chat-view → all text segments stacked at top,
tool rows below. Now handleToolStart seals the current text segment and
subsequent deltas open a new assistant node, preserving TUI-style
text → tool → text → tool ordering; handleDone renders all segments.
- chat.js: groupNodes value → {node, nodes, hasText, sealed}
- handleToolStart seals when the group has text; handleDelta opens a new
segment when sealed; handleDone/clearTyping iterate all segments
- +1 renderer smoke test: alternating text/tool produces 5 nodes in order
text,tool,text,tool,text with each segment independent; doc counts 91→93
argszero
force-pushed
the
feature/gui-interleaved-msg-order
branch
from
August 7, 2026 14:07
18a87c6 to
e06ede1
Compare
argszero
commented
Aug 7, 2026
argszero
left a comment
Owner
Author
There was a problem hiding this comment.
✅ LGTM — cycle 20260807-220537 (1st angle: seal-semantics correctness)
Verified on head e06ede1 (rebased on 8dfe7f7):
- Trace of the rant's scenario (text1 → tool1 → text2 → tool2 → text3): handleDelta creates node A (hasText=true); handleToolStart seals it; next delta opens node B (old node stays in DOM at original position); second tool seals B; delta opens C; handleDone renders A/B/C in order → DOM order text,tool,text,tool,text = TUI parity. Correct.
- Edge cases: back-to-back tools (text→tool1→tool2) — second toolStart finds sealed already true (idempotent, no empty node); G104 tool-first (no text yet) — hasText=false so no seal, text lands in the existing node (pre-existing behavior, unchanged); done/clearTyping iterate all segments via group.nodes.
- doneRids residual-drop still guards stale deltas (14:11 behavior preserved).
- Test discriminative power: removing the seal logic → all text in node A → kinds join 'text,text,text,text,text' fails; concatenation also fails the per-segment assertion. Positive + negative states covered.
- Rebase resolved the README/Agent.md count conflict (Python 572 from #553 + GUI 93 from this PR); doc-count guard passes.
- 572 Python + 93 GUI tests pass locally; CI test workflow green (31186038422).
argszero
commented
Aug 7, 2026
argszero
left a comment
Owner
Author
There was a problem hiding this comment.
✅ LGTM — cycle 20260807-221125 (2nd angle: test discriminative power)
Verified on head e06ede1:
- Positive state: the new test passes (24 renderer smoke tests green, full GUI 93 green).
- Negative state (per #455 lesson): removed the seal → new-node logic and re-ran — the test FAILS (kinds becomes text,tool,tool / count 3 not 5 / segments concatenated). Restored → passes. So the test genuinely discriminates the fix from the old behavior.
- The 4 assertions cover independent failure modes: node count (seal absent → 3 instead of 5), interleaving order (kinds join), segment independence (per-segment texts), typing cleanup on done (all segments iterated).
- typingAfter uses className (sandbox classList mock overwrites className on remove → '' after clear; would stay 'msg-body typing' if done skipped a segment — discriminating).
- 572 Python + 93 GUI tests pass locally; CI test workflow green (31186038422).
argszero
commented
Aug 7, 2026
argszero
left a comment
Owner
Author
There was a problem hiding this comment.
✅ LGTM — cycle 20260807-221356 (3rd/final angle: regression vs 14:11 doneRids / G104 tool-first)
Verified on head e06ede1:
- 14:11 doneRids protection intact: residual delta after done still dropped (no orphan node) — the doneRids.has(rid) guard is unchanged at the top of handleDelta.
- G104 tool-first (tool_start before any text): hasText=false → no seal → subsequent text lands in the existing node (verified via sandbox simulation: afterTool=2 [assistant node + tool row], text appears in node 0, afterDone=2 [no orphan]). Pre-existing behavior preserved — no empty bubble introduced.
- Sealed idempotency: back-to-back tools (text→tool1→tool2) — second toolStart finds sealed already true, no double seal, no empty node.
- clearTyping iterates all segments (cancelled path covers every segment's typing cursor).
- 572 Python + 93 GUI tests pass; CI test workflow green (31186038422); two prior ✅ from distinct cycles on this head (220537 seal semantics, 221125 discriminative power). Ready to merge.
argszero
added a commit
that referenced
this pull request
Aug 7, 2026
This was referenced Aug 7, 2026
argszero
added a commit
that referenced
this pull request
Aug 7, 2026
…unt fixes (#553-#561) (#562) Version bump 0.2.11 → 0.2.12 across all 7 version sources (pyproject.toml / emrg/__init__.py / gui/package.json / gui/package-lock.json / uv.lock / build-runtime.sh / make-installer.sh). Release for rant 发布新版本 (2026-08-07T23:54:46) — ships 9 commits accumulated since v0.2.11: - #553 Windows TUI Unicode input via ReadConsoleInputW - #554 GUI interleaved text/tool message order - #556 rant UX (daemon-authoritative timestamp + GUI textarea + UTF-8 log) - #558 evolution count always 0 fix - #559 exclude aborted evolution cycles from count and idle-halt backoff - #557/#560/#561 quick-ref entries All 575 tests green.
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.
Summary
Fixes GUI message ordering for interleaved LLM text + tool calls (host rant
2026-08-07T21:57:10): a complete turn (text → tool → text → tool → text) displayed all text segments concatenated at the top with tool rows stacked below — the TUI interleaves them correctly, the GUI should too.Root cause
handleDelta(chat.js) reused the same assistant node perrequest_id(groupNodes.get(rid)→ append to that body), whilehandleToolStartappended tool rows directly to chat-view end, independent of the node grouping. So every later text segment kept appending to the first node (top of the view) instead of after the tool row that preceded it.Changes
emrg/gui/renderer/js/chat.jsgroupNodesvalue changed from a single node to{ node, nodes, hasText, sealed }(the active segment node + all segments for the rid in order).handleToolStart: if the rid group already has text, seal the current segment — a tool row has been inserted after it.handleDelta: if the group is sealed, open a new assistant node for the new text segment (old node stays in the DOM at its original position); appends continue on the active segment.handleDone: renders Markdown for all segments of the rid (not just one);clearTypingclears typing on all segments.emrg/gui/test/renderer.smoke.test.js: +1 test — alternating text/tool produces 5 nodes in ordertext,tool,text,tool,text, each text segment independent (not concatenated), and done removes typing on all segments.Acceptance (from the rant)
clearTypingiterates all segments ✅ (existing test passes)npm test93 pass; Python 572 pass