.kilo/plans/1779990397226-quiet-canyon.md
Fix JetBrains session scrolling so that when any inline prompt card appears while the scroll/jump button is already visible, the transcript scroll position is preserved and only the scroll button state changes. This applies to all current BaseQuestionView-backed session cards: questions, permissions, and login-required prompts. Preserve existing behavior that question navigation, review/back/next, and bottom-following content changes auto-scroll when they are supposed to.
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt, .../scroll/SessionScroll.kt, .../views/question/QuestionView.kt, .../views/permission/PermissionView.kt, .../views/LoginRequiredView.kt, .../views/base/BaseQuestionView.kt, and frontend/src/test/kotlin/ai/kilocode/client/session/SessionScrollTest.kt.QuestionView, PermissionView, and LoginRequiredView do not inherit from BaseQuestionView; they compose it as the shared card shell. Scroll behavior should stay outside BaseQuestionView because it is a pure visual component with no session scroll context.SessionUi.kt attempt changes QuestionView from follow = { scroll.following() } to follow = { scroll.atBottom() }.QuestionView.follow() is also used after custom editor growth, selection refresh, reply/reject, and other internal updates.QuestionView already intentionally calls scroll(true) in goBack, goForward, and goReview; those forced calls are the behavior that preserves auto-scroll when switching question pages.PermissionView.show() and LoginRequiredView.show() do not call scroll directly. They rely on SessionController.beforeUpdate / afterUpdate, which samples scroll.atBottom() before the state change and calls scroll.followBottom(follow) afterward. With the stale-tail fix, this path can preserve scroll for all non-question inline prompt cards.SessionScroll.followBottom(false) change that clears stale tail when the transcript is visible and not actually at bottom is directionally correct. It prevents later following() calls from resurrecting auto-follow after a non-follow decision.question / setQuestionPending and is only enabled for non-plan AwaitingQuestion. To cover all BaseQuestionView-backed prompts, this should become a generic pending-input/attention state and include AwaitingPermission and LoginRequired.test plan followup question keeps scroll icon), so preserve that exception unless product intent changes separately.build.gradle.kts and script/build-sign-check.sh changes are unrelated to this bug and should not be touched as part of this fix.QuestionView.kt, split the scroll-follow decision into two callbacks.follow callback for question-internal updates.follow so existing tests/call sites remain compatible.show(q), sample the new initial callback before making the question visible or rebuilding the card, then call scroll(tail) after syncPage() as today.scroll(true) in goBack, goForward, and goReview unchanged.scroll(follow()) calls in custom editor changes, option selection, reply, and reject paths unchanged.SessionUi.kt, wire the callbacks with distinct meanings.follow = { scroll.following() } for question-internal updates.{ scroll.atBottom() } for initial question appearance.scroll = { scroll.followBottom(it) }.SessionScroll.setQuestionPending and the backing question boolean to a generic name such as setInputPending / pending or setAttentionPending / pending.questionPending(state) in SessionUi.kt to match the broader meaning.true for non-plan SessionState.AwaitingQuestion, SessionState.AwaitingPermission, and SessionState.LoginRequired.false for plan follow-up questions so the existing plan-specific icon behavior is preserved.SessionScroll.kt, keep the stale-tail reconciliation in followBottom(false).follow is false, increment seq, and if the active view is the transcript and !atBottom(), set tail = false before updateJump().following() to use atBottom(), because tail is needed to preserve bottom-follow behavior across deferred layout/height growth.PermissionView, LoginRequiredView, or BaseQuestionView unless tests prove the controller-level path is insufficient.PermissionView and LoginRequiredView should remain retained visual views that show/hide themselves and refresh their parent.BaseQuestionView should stay a scroll-unaware card shell.SessionScrollTest.kt, update tests around question-style prompt appearance and preserved behavior.tail plus visible jump button if no public-only scenario can reproduce the failed state; keep the reflective helper local to this test file and avoid adding production test hooks.AwaitingPermission preserves scroll when away from bottom and follows when at bottom.LoginRequired preserves scroll when away from bottom and follows when at bottom.Preserve JetBrains chat scroll position when inline prompts appear while the scroll button is visible.packages/kilo-jetbrains/, run ./gradlew test --tests "ai.kilocode.client.session.SessionScrollTest".packages/kilo-jetbrains/, run ./gradlew typecheck.bun test.bar.value.