Skip to content

Release v0.51.341 — Release LE (stale thinking-dot placeholder fix #3869/#3876)#3886

Merged
nesquena-hermes merged 1 commit into
masterfrom
stage-v0.51.341
Jun 9, 2026
Merged

Release v0.51.341 — Release LE (stale thinking-dot placeholder fix #3869/#3876)#3886
nesquena-hermes merged 1 commit into
masterfrom
stage-v0.51.341

Conversation

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Release v0.51.341 — Release LE (stale thinking-dot placeholder fix)

Brick-prepass release. Absorbs contributor PR #3876 (@franksong2702) which fixes the confirmed #3401 regression #3869.

Fixed

Root cause

The live-to-final redesign (#3401, v0.51.294) made the thinking-card-row wrapper class unconditional on every thinking row, including the dots-only spinner. But finalizeThinkingCard() used that same class as its "has real content" signal:

const hasContent = row.querySelector('.thinking-card') || row.classList.contains('thinking-card-row');

Since the wrapper now always carries thinking-card-row, hasContent was always truthy, so the dots-only-removal branch went dead and empty spinners stacked across turns. Narrowed to the actual content element:

const hasContent = !!row.querySelector('.thinking-card');

Real Worklog Thinking Cards (which contain a .thinking-card) are preserved; dots-only spinners are removed on finalize.

Files

  • static/ui.js — 1-line change in finalizeThinkingCard()
  • tests/test_issue3869_thinking_dots.py — NEW regression test (asserts the narrowed check + that real thinking cards are not removed)
  • CHANGELOG.md — release entry

Gate

  • Full suite: 8470 passed, 9 skipped, 3 xpassed, 0 failures (pytest -p no:xdist)
  • Codex advisor: SAFE TO SHIP — confirmed scoped to the legacy path; verified no other production code treats thinking-card-row as a content signal (the remaining use at ui.js:5189 is a positional insertion anchor)
  • Opus advisor: correct, complete, ship-safe — Worklog branch structurally untouched, no regression risk to the live-to-final render family
  • node -c clean, regression test green, rebase fidelity verified identical to PR head

Closes #3869.

Co-authored-by: franksong2702 franksong2702@users.noreply.github.com

/#3876)

Fixes #3869: empty legacy three-dot thinking spinners piled up as stale
rows after the agent finished thinking. The live-to-final redesign (#3401)
made the thinking-card-row wrapper class unconditional, which broke
finalizeThinkingCard()'s dots-only detection — it treated the wrapper class
itself as a "has content" signal, so the dots-only removal branch went dead.
Narrow hasContent to the actual .thinking-card element so dots-only spinners
are removed on finalize while real Worklog Thinking Cards are preserved.

Includes #3869 regression coverage (brace-walks finalizeThinkingCard, asserts
the narrowed check + that real thinking cards are not removed).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
@nesquena-hermes nesquena-hermes merged commit 3509863 into master Jun 9, 2026
11 checks passed
@nesquena-hermes nesquena-hermes deleted the stage-v0.51.341 branch June 9, 2026 17:19
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown

Greptile Summary

This release fixes a regression introduced in v0.51.294 where empty "thinking dots" spinner rows were never cleaned up, causing stale three-dot placeholders to accumulate across turns. The root cause was that finalizeThinkingCard() used row.classList.contains('thinking-card-row') as a "has content" signal, but that class was unconditionally applied to every thinking row — including dots-only spinners — making the removal branch permanently unreachable.

  • static/ui.js: One-line narrowing of hasContent from querySelector('.thinking-card') || classList.contains('thinking-card-row') to !!querySelector('.thinking-card'), correctly scoping the check to actual rendered content.
  • tests/test_issue3869_thinking_dots.py: New regression test using substring matching against the extracted function body to verify the fix is present and that the Worklog path does not call active.remove().
  • CHANGELOG.md: Release entry added; the \u26d4 HELD annotation on v0.51.340 is silently removed without an explanation.

Confidence Score: 4/5

The one-line fix is correctly scoped and safe to merge.

The JavaScript change is minimal and correct. The regression test validates the fix and the Worklog path preservation. Two non-blocking style concerns keep the score from being perfect: the test assertions are exact substring matches that would break on any whitespace reformat of ui.js, and the silent removal of the HELD annotation on v0.51.340 leaves the changelog audit trail ambiguous.

tests/test_issue3869_thinking_dots.py — substring assertions are brittle against formatting changes to ui.js. CHANGELOG.md — the dropped HELD annotation on v0.51.340 warrants confirmation that the hold was intentionally cleared.

Important Files Changed

Filename Overview
static/ui.js Single-line fix in finalizeThinkingCard(): removes the always-truthy `
tests/test_issue3869_thinking_dots.py New regression test using string-matching against the extracted function body; validates fix is present and that the Worklog branch does not call active.remove(). Functionally correct but tightly coupled to exact source formatting.
CHANGELOG.md Adds v0.51.341 release entry and silently removes the ⛔ HELD pending independent review annotation from v0.51.340 without explanation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[finalizeThinkingCard called] --> B{Session guard OK?}
    B -- No --> Z[return - wrong session]
    B -- Yes --> C{isSimplifiedToolCalling?}

    C -- No - legacy path --> D[row = thinkingRow element]
    D --> E{row exists?}
    E -- No --> Z2[return]
    E -- Yes --> F["hasContent = row.querySelector('.thinking-card') != null\nFIX: removed always-truthy OR clause"]

    F --> G{hasContent false AND data-thinking-active eq 1?}
    G -- Yes dots-only spinner --> H[row.remove - stale placeholder gone]
    G -- No real thinking card --> I[reset scrollTop, strip id and active attr]

    C -- Yes Worklog path --> J[get liveAssistantTurn and tool-call group]
    J --> K{group exists?}
    K -- No --> Z3[return]
    K -- Yes --> L[strip data-worklog-reason-active]
    L --> M["forEach .agent-activity-thinking active\nstrip data-thinking-active + data-live-thinking\nactive.remove NOT called - cards preserved"]
    M --> N[syncToolCallGroupSummary]
Loading

Reviews (1): Last reviewed commit: "Release v0.51.341 — Release LE (stale th..." | Re-trigger Greptile

Comment on lines +27 to +35
"""#3869: an empty legacy thinking row is only a spinner, not durable content."""
body = _function_body(UI_JS, "finalizeThinkingCard")

assert "const hasContent=!!row.querySelector('.thinking-card');" in body
assert "row.classList.contains('thinking-card-row')" not in body
assert "if(!hasContent && row.getAttribute('data-thinking-active')==='1'){" in body
assert "row.remove();" in body


Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Test is tightly coupled to exact source formatting

The assertions match against verbatim substrings like "const hasContent=!!row.querySelector('.thinking-card');" (no spaces around =). Any cosmetic reformat of ui.js — a prettier run, a minifier pass, or even an editor auto-format — will cause these assertions to fail even when the logic is identical. Consider relaxing the check to a regex or normalizing whitespace before asserting, so the test survives formatting-only changes.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread CHANGELOG.md
### Fixed

- **Legacy thinking placeholders no longer pile up as stale three-dot rows.** Empty pre-stream thinking spinners are now removed when the thinking phase finalizes instead of being mistaken for durable Thinking content. The live-to-final redesign (#3401) made the `thinking-card-row` wrapper class unconditional, which broke `finalizeThinkingCard()`'s dots-only detection (it treated the wrapper class itself as a "has content" signal); the check is now narrowed to the actual `.thinking-card` element so dots-only spinners are removed on finalize while real Worklog Thinking Cards are preserved. (#3869, #3876)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Silent removal of the v0.51.340 HELD annotation

The previous entry carried — ⛔ HELD pending independent review, which is dropped here without a note explaining why the hold was cleared (or whether it still applies). If v0.51.340 was genuinely unblocked, a brief inline remark like (independent review complete) preserves the audit trail for anyone reading git history later.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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.

Thinking animation dots persist as multiple stale rows after agent finishes thinking

1 participant