Skip to content

emrg: GUI interleaved text/tool message order (rant 21:57:10) - #554

Merged
argszero merged 1 commit into
masterfrom
feature/gui-interleaved-msg-order
Aug 7, 2026
Merged

emrg: GUI interleaved text/tool message order (rant 21:57:10)#554
argszero merged 1 commit into
masterfrom
feature/gui-interleaved-msg-order

Conversation

@argszero

@argszero argszero commented Aug 7, 2026

Copy link
Copy Markdown
Owner

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 per request_id (groupNodes.get(rid) → append to that body), while handleToolStart appended 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.js
    • groupNodes value 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); clearTyping clears typing on all segments.
    • Empty-segment guard: a tool that starts before any text does not seal (G104 case — text lands in the existing node).
  • emrg/gui/test/renderer.smoke.test.js: +1 test — alternating text/tool produces 5 nodes in order text,tool,text,tool,text, each text segment independent (not concatenated), and done removes typing on all segments.
  • Doc counts: GUI 91 → 93 (renderer smoke 22 → 24).

Acceptance (from the rant)

  • LLM alternating text/tool output shows interleaved in order (matches TUI) ✅ (tested)
  • Each text segment is its own block ✅ (tested)
  • All segments Markdown-render after done ✅ (tested)
  • Streaming delta appends to the current segment (no flicker) — unchanged path ✅
  • cancelled/error path leaves no typing cursor — clearTyping iterates all segments ✅ (existing test passes)
  • Existing GUI tests all green: npm test 93 pass; Python 572 pass

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
argszero force-pushed the feature/gui-interleaved-msg-order branch from 18a87c6 to e06ede1 Compare August 7, 2026 14:07

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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
argszero merged commit 9b0a832 into master Aug 7, 2026
1 check passed
@argszero
argszero deleted the feature/gui-interleaved-msg-order branch August 7, 2026 14:15
argszero added a commit that referenced this pull request Aug 7, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant