Skip to content
Back to skills

558 Reserch 4fb53593

ASecurity

self.mount(widget, before=self._current_stream) elif self._insertion_anchor is not None: self.mount(widget, before=self._insertion_anchor) else: self.mount(widget) if self._auto_scroll: self.scroll_end(animate=False) Acceptance Test: Tool panel arriving after end_stream() appears before the finalized agent response, not at bottom. Dependencies: Task 1 Milestone: M2 Task 3: Clear insertion anchor on new request Summary: Reset anchor when starting a new stream to prevent stale positioning. File...

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
devopsgotestingapi

Works with

  • api

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 558-reserch_4fb53593 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 558 Reserch 4fb53593?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 558 Reserch 4fb53593
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-558-reserch-4fb53593/badge)](https://www.skillsdirectory.com/skills/tools-only-558-reserch-4fb53593)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
Goal
Fix the race condition where tool panels append to the end instead of inline when tools complete after stream cancellation or end.
Non-Goals
No deployment/ops changes
No observability/metrics additions
No new dependencies
Scope & Assumptions
In Scope:

Fix insert_before_stream() to correctly position tool panels even after stream ends
Track insertion point when stream ends so late-arriving tool panels insert correctly
Handle cancel scenario where tools complete post-cancel
Out of Scope:

Tool cancellation signal propagation (separate concern)
Throttling/batching of tool panels (LOW priority per research)
Assumptions:

Textual's mount(before=widget) works correctly when widget exists
Tool panels arrive via post_message() which is async (confirmed in research)
Single-threaded event loop; no concurrent message handling
Deliverables
Modified ChatContainer class with insertion point tracking
Updated insert_before_stream() to use tracked position
Updated end_stream() and cancel_stream() to preserve insertion context
Readiness
Research complete with flow analysis and root cause identified
Code locations verified in chat.py (lines 137-268)
No external dependencies required
Milestones
M1: Insertion point tracking infrastructure
M2: Fix insert_before_stream() logic
M3: Basic test scenario (manual verification)
Work Breakdown (Tasks)
Task 1: Add insertion anchor tracking to ChatContainer
Summary: Track the last widget before which tool panels should insert, persisting after stream ends.

Files: src/tunacode/ui/widgets/chat.py

Changes:

Add _insertion_anchor: Widget | None attribute in __init__
In start_stream(): set _insertion_anchor = None (panels go before stream widget)
In end_stream(): capture the finalized _current_stream widget as _insertion_anchor BEFORE setting _current_stream = None
In cancel_stream(): capture widget reference before removal, set _insertion_anchor to next sibling if exists
Acceptance Test: After calling end_stream(), _insertion_anchor holds reference to the finalized message widget.

Dependencies: None Milestone: M1

Task 2: Update insert_before_stream() to use insertion anchor
Summary: Modify logic to insert before anchor when stream is None but anchor exists.

Files: src/tunacode/ui/widgets/chat.py

Changes:

def insert_before_stream(self, renderable: RenderableType) -> None:
    widget = Static(renderable)
    widget.add_class("chat-message")

    if self._current_stream is not None:
        # Active stream: insert before streaming widget
        self.mount(widget, before=self._current_stream)
    elif self._insertion_anchor is not None:
        # Stream ended: insert before the finalized message
        self.mount(widget, before=self._insertion_anchor)
    else:
        # No context: append
        self.mount(widget)

    if self._auto_scroll:
        self.scroll_end(animate=False)
Acceptance Test: Tool panel arriving after end_stream() appears before the finalized agent response, not at bottom.

Dependencies: Task 1 Milestone: M2

Task 3: Clear insertion anchor on new request
Summary: Reset anchor when starting a new stream to prevent stale positioning.

Files: src/tunacode/ui/widgets/chat.py

Changes:

In start_stream(): set _insertion_anchor = None at the start
In clear(): set _insertion_anchor = None
Acceptance Test: New request does not use stale anchor from previous request.

Dependencies: Task 1 Milestone: M2

Task 4: Handle cancel scenario insertion anchor
Summary: On cancel, preserve insertion context for late-arriving tool panels.

Files: src/tunacode/ui/widgets/chat.py

Changes:

In cancel_stream(): before removing _current_stream, find its next sibling
Set _insertion_anchor to next sibling (or None if last child)
Alternative: Keep _current_stream hidden instead of removing? (research: simpler to just track position)
Design Decision: Track position via index, not sibling reference. Siblings can be fragile.

Better approach:

def cancel_stream(self) -> None:
    if self._current_stream is not None:
        # Find the index of the stream widget
        children = list(self.children)
        try:
            idx = children.index(self._current_stream)
            # Anchor is widget that will be at this position after removal
            if idx > 0:
                self._insertion_anchor = children[idx - 1]
        except ValueError:
            pass
        self._current_stream.remove()
        self._current_stream = None
    self.remove_class("streaming")
Wait - this anchors to widget BEFORE, but we want to insert AFTER it. Let me reconsider.

Revised approach: Use after= parameter for mount instead:

def cancel_stream(self) -> None:
    if self._current_stream is not None:
        children = list(self.children)
        try:
            idx = children.index(self._current_stream)
            if idx > 0:
                self._insertion_after_anchor = children[idx - 1]
        except ValueError:
            pass
        self._current_stream.remove()
        self._current_stream = None
    self.remove_class("streaming")
Then in insert_before_stream():

elif self._insertion_after_anchor is not None:
    self.mount(widget, after=self._insertion_after_anchor)
Acceptance Test: Tool panel arriving after cancel_stream() appears at the position where streaming was occurring.

Dependencies: Task 2 Milestone: M2

Task 5: Manual verification
Summary: Verify fix with real tool calls in TUI.

Files: None (manual testing)

Steps:

Start tunacode TUI
Run request that triggers tool (e.g., "read file X")
Wait for tool panel to appear inline during streaming
Cancel mid-stream (Ctrl+C or escape)
Observe: any late tool panels should NOT appear at bottom
Acceptance Test: Visual confirmation that tool panels maintain correct ordering in all scenarios.

Dependencies: Tasks 1-4 Milestone: M3

Risks & Mitigations
Risk	Likelihood	Impact	Mitigation
Textual after= behavior differs from expected	Low	Medium	Verify Textual docs; test in isolation
Removed widget reference causes issues	Low	Low	Clear anchor when widget removed by other means
Stale anchor persists across sessions	Low	Low	Clear in clear() and start_stream()
Test Strategy
Task 2 acceptance: Create unit test that mounts ChatContainer, starts stream, ends stream, then calls insert_before_stream() and verifies widget order.
Single test file: tests/ui/test_chat_container.py

References
Research doc: memory-bank/research/2026-01-27_insert-before-stream-investigation.md
Code: src/tunacode/ui/widgets/chat.py:244-261 (current implementation)
Textual mount docs: https://textual.textualize.io/api/widget/#textual.widget.Widget.mount
Final Gate
Plan path: memory-bank/plan/2026-01-27_17-41-42_insert-before-stream-fix.md
Milestone count: 3
Tasks ready for coding: 5
Next command: /context-engineer:execute "memory-bank/plan/2026-01-27_17-41-42_insert-before-stream-fix.md"

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…