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:
@@ -144,6 +144,10 @@ SUMMARY = "#messages .msg.brain .tool-calls-summary"
|
||||
DISCLOSURE = "#messages .msg.brain .tool-calls-disclosure"
|
||||
LINES = "#messages .msg.brain .tool-call"
|
||||
|
||||
#: Phase 161 (D1/D2): the ONE reasoning container — created on the
|
||||
#: turn's first tool frame, OPEN live, folded at the settle (D2).
|
||||
REASONING = "#messages .msg.brain details.reasoning"
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# DB seeding (TRUNCATE-then-seed, cf. test_agent_document_tools.py)
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -261,7 +265,12 @@ def _submit_tools_turn(page: Page, app_url: str) -> None:
|
||||
|
||||
def _expand_disclosure(page: Page) -> None:
|
||||
"""Tap the summary — the native ``<summary>`` is a real focusable
|
||||
toggle (AA), so a plain click is the user contract."""
|
||||
toggle (AA), so a plain click is the user contract. Phase 161
|
||||
(D2): at rest the disclosure sits INSIDE the folded reasoning
|
||||
container, so the user flow is two native toggles — open the
|
||||
container first (its own summary — a direct child, so the inner
|
||||
runs' summaries are not matched), then the disclosure's summary."""
|
||||
page.locator(f"{REASONING} > summary").click()
|
||||
page.locator(SUMMARY).click()
|
||||
|
||||
|
||||
@@ -274,16 +283,27 @@ def test_tool_calls_fold_to_one_line_after_turn(
|
||||
page: Page, app_url: str, mock_llm: int, db_ready: None
|
||||
) -> None:
|
||||
"""Pin 1 (the space fix): after the deterministic ``ls`` →
|
||||
``ls(scoped)`` → ``read`` turn COMPLETES, the record is ONE visible
|
||||
"Tool calls (3)" summary — the disclosure is CLOSED (no ``open``
|
||||
``ls(scoped)`` → ``read`` turn COMPLETES, the record is ONE
|
||||
visible summary line — the disclosure is CLOSED (no ``open``
|
||||
attribute: the first answer delta folded it, D3), the three
|
||||
``.tool-call`` lines are still in the DOM (the permanent record)
|
||||
but hidden (the first line is not visible), and the answer bubble
|
||||
is present."""
|
||||
is present. Phase 161 (D2): the at-rest record is wrapped in the
|
||||
FOLDED reasoning container — the at-rest visible line is the
|
||||
container's "Reasoning" summary; the disclosure's "Tool calls (3)"
|
||||
count is the in-container record, revealed by opening it."""
|
||||
page.set_default_timeout(30_000)
|
||||
_reset_db(_seed)
|
||||
_submit_tools_turn(page, app_url)
|
||||
|
||||
# Phase 161 (D2): the container exists (the tool frames created
|
||||
# it) and folds at done — the at-rest visible line is its summary.
|
||||
container = page.locator(REASONING)
|
||||
expect(container).to_have_count(1)
|
||||
expect(container).not_to_have_attribute("open")
|
||||
page.locator(f"{REASONING} > summary").click()
|
||||
expect(container).to_have_attribute("open", "")
|
||||
|
||||
# One compact summary line, visible, with the live count.
|
||||
summary = page.locator(SUMMARY)
|
||||
expect(summary).to_be_visible()
|
||||
@@ -396,10 +416,15 @@ def test_restored_turn_renders_folded(
|
||||
record is written at the save points, so the reload sees the full
|
||||
conversation), a same-context RELOAD re-renders the persisted
|
||||
record through the restore path (``renderStoredMessage`` →
|
||||
``appendToolLine`` → the after-loop fold): the disclosure is
|
||||
``restoreTimeline`` → the after-loop fold): the disclosure is
|
||||
present, CLOSED (no ``open`` attribute — no auto-expand on load),
|
||||
the summary reads "Tool calls (3)", the three lines are in the DOM
|
||||
and the first is hidden (folded on load)."""
|
||||
and the first is hidden (folded on load). Phase 161 (D2/D5): the
|
||||
restored record is wrapped in the FOLDED reasoning container —
|
||||
the at-rest visible line is the container's "Reasoning" summary;
|
||||
the disclosure's "Tool calls (3)" count is the in-container
|
||||
record, revealed by opening the container (the same two-native-
|
||||
toggle user flow as the live at-rest pins)."""
|
||||
page.set_default_timeout(30_000)
|
||||
_reset_db(_seed)
|
||||
_submit_tools_turn(page, app_url)
|
||||
@@ -413,10 +438,22 @@ def test_restored_turn_renders_folded(
|
||||
page.locator(".msg.brain .bubble").last
|
||||
).to_contain_text(MOCK_ANSWER_MARKER, timeout=30_000)
|
||||
|
||||
# Folded on load: one compact summary line, disclosure closed.
|
||||
# Folded on load (phase 161, D2/D5): the record is wrapped in the
|
||||
# FOLDED reasoning container — the at-rest visible line is the
|
||||
# container's "Reasoning" summary.
|
||||
container = page.locator(REASONING)
|
||||
expect(container).to_have_count(1)
|
||||
expect(container).not_to_have_attribute("open")
|
||||
expect(container.locator("> summary")).to_be_visible()
|
||||
expect(container.locator("> summary")).to_contain_text("Reasoning")
|
||||
|
||||
# The disclosure is CLOSED (no auto-expand on load) — and the
|
||||
# in-container record is revealed by opening the container (the
|
||||
# two-native-toggle user flow).
|
||||
assert page.get_attribute(DISCLOSURE, "open") is None
|
||||
page.locator(f"{REASONING} > summary").click()
|
||||
expect(page.locator(SUMMARY)).to_be_visible()
|
||||
expect(page.locator(SUMMARY)).to_contain_text("Tool calls (3)")
|
||||
assert page.get_attribute(DISCLOSURE, "open") is None
|
||||
|
||||
# The three lines are in the DOM (the permanent record) but folded:
|
||||
# the first line is not rendered — no auto-expand.
|
||||
|
||||
Reference in New Issue
Block a user