Files
ducoterra dbf2af26c6 refactor(agents): migrate .agent/ planning tree to .agents/
Standardize on the .agents/ directory (shared with project skills):
phases/, user_stories/, reports/, screenshots/, validate.sh, and
phase-sessions/ + pipeline.log all move to .agents/ (git mv preserves
history; runtime artifacts move alongside).

Updates every reference in AGENTS.md, README.md, .gitignore, app
docstrings, and test story headers. Historical KB content in data/
and the runtime pipeline.log transcript are left untouched.
2026-09-05 10:57:07 -04:00

3.8 KiB

Phase 43 — Thinking scroll back (user scroll + generate-time autoscroll)

Source: TODO.md L7 — "Add scrolling back to the thinking block, but have it autoscroll while thinking content is generating." Story: .agents/user_stories/thinking-scroll-back.md Context: Phase 17 streams reasoning into the collapsible .thinking block with a per-chunk bottom-pin (textEl.scrollTop = textEl.scrollHeight in the thinking SSE handler). Phase 21 (owner choice 2026-08-24) made .thinking-text a no-scroll live tail: overflow-y: hidden (the JS pin is the sole scroller). The owner now reverses phase 21: the window is user-scrollable again, and the pin becomes gated — follow the tail only while the user is pinned near the window's bottom. This is the window-level successor of the phase-18 pattern (the page-level one is removed in phase 42, which runs first and touches the same thinking handler line — order matters).

Objective

The Thinking block follows its live tail while reasoning is generating and the user is at the bottom; a scrolled-up user is never yanked down, and returning to the bottom resumes following.

Dependencies

  • 42_no_reply_autoscroll (todo) — must run first: it strips the page-level scroll from the same thinking handler; this phase then reworks the window pin in the cleaned-up handler.
  • 17_thinking_display (complete) — the block, the pin, the auto-collapse on first delta.
  • 21_thinking_no_scroll (complete) — the overflow-y: hidden + 320px window being reversed (the 320px clip is kept).

Tasks

  1. 01_window_user_scrollable.md — CSS: overflow-y: auto back, comment replaced (owner direction 2026-08-27).
  2. 02_gated_bottom_pin.md — app.js: THINKING_NEAR_BOTTOM_PX = 32 + gated pin; unit pin rewritten (phase-21 file replaced).
  3. 03_thinking_scroll_e2e_and_commit.md — replace the phase-21 E2E with the new-contract suite + regressions + commit.

Testing & Quality

  • Unit: tests/unit/test_thinking_no_scroll.py deleted, replaced by tests/unit/test_thinking_scroll.py — pins: overflow-y: auto + max-height: 320px in the .thinking-text rule; the 2026-08-27 owner-direction comment; export const THINKING_NEAR_BOTTOM_PX = 32; the pin is gated on isThinkingNearBottom(textEl) (no unconditional pin).
  • Coverage: frontend-only — app/ TOTAL unchanged, >90%.
  • E2E (mandatory, A16): tests/e2e/test_thinking_scroll.py, run in isolation. tests/e2e/test_thinking_no_scroll.py is deleted (behavior intentionally reversed).

Completion Criteria

  • Wheel/drag/keyboard move .thinking-text (frozen-tail state); computed overflow-y: auto, max-height: 320px.
  • While pinned at the window bottom: each chunk re-pins to the tail (±1px). Scrolled up: no re-pin across chunks. Return to bottom: following resumes.
  • Auto-collapse on first delta, reduced-motion stillness, answer-bubble scroll (phase 11), restored-collapsed block (phase 17) all unchanged.
  • uv run pytest green; coverage TOTAL unchanged.
  • uv run pytest tests/e2e/test_thinking_scroll.py -v --no-cov green in isolation (DB up).
  • Regression E2E suites green in isolation: test_thinking_display.py, test_chat_persistence.py, test_no_reply_autoscroll.py, test_smoke.py.
  • uv run ruff check . && uv run pyright clean.
  • One --no-gpg-sign commit; phase dir moved to .agents/phases/complete/.

Locked decisions

  • Owner direction (2026-08-27, roadmap A2) reverses the phase-21 owner choice (2026-08-24): the window is user-scrollable again; autoscroll only while pinned near the bottom (32px band). The 320px clip is kept (owner-confirmed).
  • A15 unchanged — SSE contract untouched; pure client-side.
  • A16/A17 honoured — one story E2E suite (replacing the removed one), one atomic commit.