fix(rag): stop the per-run blocks from folding — the container is the one disclosure
The owner refinement (2026-10-06): the per-run thinking and tool-call blocks inside the Reasoning bubble were individually collapsible details elements — a fold inside the fold. A tool frame closed the previous thinking block, so the visible thinking vanished mid-turn; and the pinned-tail follow died on the append of every run after the first (the pin was captured AFTER the new block's height growth contaminated the 32px reading). - app.js: the in-container runs are now plain divs — createThinkingRunEl (div.thinking + static .thinking-summary label + .thinking-text) and createToolRunEl (div.tool-calls-disclosure + static .tool-calls-summary label + the .tool-calls list); they never open or fold (the container is the ONE open/fold control, open through the turn, folded at settle — unchanged); the pin capture moves BEFORE ensureThinkingRun/ensureToolRun append (plus the first-frame edge: no container yet -> always pinned); closeCurrentRun just ends the run; closeAllRuns stays the defensive legacy guard; restoreTimeline/addSegments (shared.js) use the div builders; the legacy no-segments restore path keeps the old details-based blocks byte-identical - styles.css: the in-container runs are distinct by label + rail (brand-ink Thinking label on a brand-soft rail; accent-ink Tool call(s) (N) label on a neutral rail); the interactive treatment (cursor + chevron) is scoped to the legacy <summary>; the inner windows stay disabled (the container's .reasoning-body is the ONE live scroll surface) - unit + E2E: the D5-refinement contract is pinned — one details.reasoning disclosure, per-run blocks as plain divs, zero nested <details> inside the container, the container as the only open/fold control, the .reasoning-body window auto-follows the tail while streaming unit+integration 3738 passed (coverage 99%), ruff + pyright clean, all five affected E2E suites pass in isolation (reasoning_bubble 6, thinking_timeline 6, thinking_display 5, thinking_scroll 8, tool_call_compact 4), verified live: the follow holds through a long thinking stream, the tool-frame transition, and the second thinking run; restore renders the div structure without client-side errors LLM-Generated: true
This commit is contained in:
@@ -145,20 +145,25 @@ def test_thinking_event_is_a_first_class_turn_branch() -> None:
|
||||
def test_thinking_run_helpers_are_idempotent() -> None:
|
||||
"""Phase 158 (D1/D5 — the phase-109 D15 pin, replaced by owner
|
||||
direction 2026-10-04): ``ensureThinkingRun`` returns the active
|
||||
run's block or creates a NEW one OPEN (phase 161, D1: appended
|
||||
into the reasoning container's ``.reasoning-body`` — the
|
||||
per-run bubble anchor is superseded; a frame of a new kind
|
||||
closes the previous run first); ``closeCurrentRun`` is a no-op
|
||||
without a run and folds the active one (the D15 re-open of the
|
||||
shared block is gone — a post-delta thinking frame opens a NEW
|
||||
block)."""
|
||||
run's block or creates a NEW one (the D5 refinement, owner
|
||||
2026-10-06 — a plain ``div.thinking`` through the run builder,
|
||||
NEVER open/foldable: the container is the ONE disclosure); it is
|
||||
appended into the reasoning container's ``.reasoning-body`` (phase
|
||||
161, D1 — the per-run bubble anchor is superseded; a frame of a
|
||||
new kind ends the previous run first); ``closeCurrentRun`` is a
|
||||
no-op without a run and ends the active one (nothing folds — the
|
||||
D5 refinement; the D15 re-open of the shared block is gone — a
|
||||
post-delta thinking frame opens a NEW block)."""
|
||||
js = _js()
|
||||
fn = js.find("function ensureThinkingRun")
|
||||
assert fn != -1, "ensureThinkingRun must exist (near addTyping/removeTyping)"
|
||||
body = js[fn : js.find("\n}\n", fn)]
|
||||
assert "closeCurrentRun()" in body, ("a frame of a new kind closes the "
|
||||
assert "closeCurrentRun()" in body, ("a frame of a new kind ends the "
|
||||
"previous run (D1)")
|
||||
assert "block.open = true" in body, "D5: created open — the stream is the show"
|
||||
assert "createThinkingRunEl()" in body, (
|
||||
"D5 refinement: a plain div — never collapsible (the container "
|
||||
"is the ONE disclosure)"
|
||||
)
|
||||
assert (
|
||||
'ensureReasoningWrap(wrap).querySelector(".reasoning-body")'
|
||||
".appendChild(block)" in body
|
||||
@@ -169,15 +174,20 @@ def test_thinking_run_helpers_are_idempotent() -> None:
|
||||
fn2 = js.find("function closeCurrentRun")
|
||||
assert fn2 != -1, "closeCurrentRun must exist"
|
||||
body2 = js[fn2 : js.find("\n}\n", fn2)]
|
||||
assert "currentRun.el.open = false" in body2
|
||||
assert ".open" not in body2, (
|
||||
"D5 refinement: nothing folds — the run simply ends (the "
|
||||
"in-container runs are non-collapsible divs)"
|
||||
)
|
||||
assert "currentRun = null" in body2, "a no-op without a run (idempotent)"
|
||||
|
||||
|
||||
def test_delta_branch_collapses_block_and_transitions_to_streaming() -> None:
|
||||
"""The first answer delta transitions thinking → streaming (even when
|
||||
thinking created the wrap first) and folds the active run — phase 158
|
||||
(D5): ``closeCurrentRun()`` (a later thinking frame opens a NEW run;
|
||||
the D15 re-open of the shared block is gone)."""
|
||||
thinking created the wrap first) and ends the active run — phase 158
|
||||
(D5): ``closeCurrentRun()`` (the D5 refinement, owner 2026-10-06 —
|
||||
the in-container runs are non-collapsible divs, so nothing folds;
|
||||
a later thinking frame opens a NEW run; the D15 re-open of the
|
||||
shared block is gone)."""
|
||||
js = _js()
|
||||
delta_idx = js.find('ev.type === "delta"')
|
||||
done_idx = js.find('ev.type === "done"')
|
||||
@@ -190,8 +200,10 @@ def test_delta_branch_collapses_block_and_transitions_to_streaming() -> None:
|
||||
|
||||
def test_done_branch_sets_sawdone_and_closes_block() -> None:
|
||||
"""On `done` the turn marks itself complete (sawDone — the stream-drop
|
||||
guard keys off it) and settles EVERY open run closed (phase 158, D5:
|
||||
``closeAllRuns``)."""
|
||||
guard keys off it) and settles the runs (phase 158, D5:
|
||||
``closeAllRuns`` — the D5 refinement, owner 2026-10-06 — the
|
||||
in-container runs are non-collapsible divs, so the defensive
|
||||
details query no-ops in every live path)."""
|
||||
js = _js()
|
||||
done_idx = js.find('ev.type === "done"')
|
||||
error_idx = js.find('ev.type === "error"')
|
||||
|
||||
Reference in New Issue
Block a user