phase: 161_reasoning_bubble
All gates verified. Final check — AGENTS.md rules against the project (commands, gates, protocols) — the quick-reference commands all exist and the rules (PLAN §2/§7/§9 references, phase protocol, No CDN, Debugpy, git gates) accurately describe the project; the reasoning bubble is a pure UI change with no new commands or env vars, so no AGENTS.md change is needed.
**Verification complete — full results:**
- **Verified** (implemented by prior task passes, all in working tree): `createReasoningWrap`/`ensureReasoningWrap` live container (D1), `settleReasoning` fold at the 3 settle sites + not at delta (D2), 320px `.reasoning-body` window + pinned-tail follow (D3), zero persistence change (D4), collapsed restore + shared-page parity with D6 step counts, `styles.css` family treatment + reduced-motion still, mock `REASONING_MIDROUND_TRIGGER` flow, 6-test story suite, phase-158 pin updates
- **Full suite:** `uv run pytest` → **3738 passed, 0 failed** · `uv run pytest --cov=app --cov-report=term-missing` → **TOTAL 99% (>90%)** · `uv run ruff check .` → all checks passed · `uv run pyright` → **0 errors, 0 warnings**
- **E2E in isolation** (DB up; port-8123 collision with an unrelated `undercooked` service → `E2E_APP_PORT=8125`/`E2E_MOCK_PORT=8902`, no live change made): `test_reasoning_bubble.py` **6/6** · `test_thinking_timeline.py` ✓ · `test_thinking_display.py` ✓ · `test_tool_call_compact.py` ✓ · `test_big_read_progress.py` ✓ · `test_turn_progress_loader.py` ✓ · `test_share_chat.py` ✓ · `test_chat_persistence.py` ✓ · `test_smoke.py` ✓ (all exit 0, zero failures)
- **Completion criteria:** all 9 checked against the code + test outcomes — container open/live/nested ✓, stays open past mid-round delta ✓, folds at done ✓, restore + shared collapsed with "3 steps" ✓, plain turn = no container ✓, regression suites green ✓, suite/coverage/lint green ✓, diff scope limited to `frontend/assets/{app.js,shared.js,styles.css}` + `tests/` + `README.md` + phase files (no `app/` — D4) ✓
- **Documentation:** README.md "Thinking" section names the Reasoning bubble accurately (320px window, open-until-settle incl. mid-round deltas, "N steps", byte-identical no-container, collapsed on restore/shared) — verified consistent, no changes needed; AGENTS.md — verified consistent, no changes needed; all `.env.example` vars documented
- **Decisions:** commit + phase-dir move left to the harness (per pass rules — no git index/history commands executed); `undercooked` dev server on port 8123 untouched, E2E used the env port overrides
- **Next pending phase:** none — `161_reasoning_bubble` is the last in `todo/`
This commit is contained in:
@@ -438,16 +438,31 @@ whole git-source refresh in one click:
|
||||
|
||||
The self-hosted `turbo` model reasons before it answers. That reasoning is
|
||||
streamed with the turn as `thinking` SSE events and shown in **
|
||||
collapsible "Thinking" blocks** above the answer bubble — one block per
|
||||
contiguous thinking run, in arrival order (phase 158): the turn renders
|
||||
as a chronological timeline (Thinking → Tool call (1) → Thinking → … →
|
||||
answer). Each block opens and fills in live while its run streams,
|
||||
and the runs tuck themselves away when the first answer token lands,
|
||||
when the turn ends, or when the turn is stopped/failed — and every
|
||||
block and tool-call disclosure stays click-toggleable afterwards. A
|
||||
thinking run that starts after the answer began (the model replied,
|
||||
called a tool, then thinks again) gets its own block above the answer
|
||||
instead of resuming on top of it.
|
||||
collapsible "Thinking" blocks** — one block per
|
||||
contiguous thinking run, in arrival order (phase 158) — all wrapped in
|
||||
**one collapsible "Reasoning" bubble** above the answer bubble
|
||||
(phase 161): the turn renders as a chronological timeline
|
||||
(Thinking → Tool call (1) → Thinking → … → answer), condensed into a
|
||||
single bounded live window (320 px, one scroll surface) that follows
|
||||
the tail while you watch.
|
||||
|
||||
The bubble is created on the turn's first thinking (or tool) frame and
|
||||
stays **open** from creation until the turn settles — including a
|
||||
mid-round reply (the model answered, called a tool, then thinks again:
|
||||
the reply streams into the answer while the bubble stays open, and the
|
||||
new thinking run gets its own block inside it instead of resuming on
|
||||
top of it). It folds when the turn ends, is stopped, or fails. While
|
||||
open, its summary carries a live status ("Thinking…" /
|
||||
"Calling <name>…") and reads "Reasoning — N steps" once the turn
|
||||
settles; a turn with no thinking and no tools renders no bubble at all
|
||||
(the answer layout is byte-identical to pre-phase).
|
||||
|
||||
The per-run blocks keep their own open/fold behavior: each block opens
|
||||
and fills in live while its run streams, and the runs tuck themselves
|
||||
away when the first answer token lands, when the turn ends, or when the
|
||||
turn is stopped/failed — and every block and tool-call disclosure stays
|
||||
click-toggleable afterwards (the bubble's summary is the at-rest entry
|
||||
point).
|
||||
|
||||
The tool calls ride the same timeline: each contiguous run of tool
|
||||
calls gets its own collapsed-when-idle "Tool call(s) (N)" disclosure
|
||||
@@ -457,7 +472,8 @@ suffix targets the newest line of the active run.
|
||||
The timeline is persisted with the saved turn (an optional `segments`
|
||||
array — the per-run order; the concatenated `thinking` and the `tools`
|
||||
record stay for history and older clients), so a reload and the shared
|
||||
page re-render the same timeline, collapsed.
|
||||
page re-render the same timeline **collapsed inside the Reasoning
|
||||
bubble**, with the step-count summary.
|
||||
|
||||
To hide it, set `BOR_STREAM_THINKING=0`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user