fix(ask): scroll ordinary question premises - #3687
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ddeffb02d2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ddeffb0 to
e2d5983
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e2d5983659
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
e2d5983 to
0df947c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0df947c3f3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
0df947c to
670df91
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 670df91c4a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
670df91 to
a2d2789
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2d27892e4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
a2d2789 to
4fc61a2
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4fc61a2737
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
4fc61a2 to
e756566
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e7565665db
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
e756566 to
b2ba6fe
Compare
Route ordinary ask selectors through the bounded title viewport already used by Deep Interview so long premises remain recoverable without changing terminal wheel scrollback. Issue: Yeachan-Heo#3675 Constraint: preserve Deep Interview behavior and terminal wheel handling Not-tested: tests and formatters intentionally skipped per assignment Confidence: high Scope-risk: narrow Reversibility: revert-commit
b2ba6fe to
8fffb6f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8fffb6fdfd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Yeachan-Heo
left a comment
There was a problem hiding this comment.
Hostile Review — MERGE_READY ✅
Reviewed commit 8fffb6fdfd1d28561bcf757f3ded93649151f2b6 on base 4c6e81706 (dev).
Attacked the diff for every named regression vector. Findings below.
Transcript admission regression — no regression
The selector continues to mount under the existing editorContainer swap path (detachChild → clear → addChild(hookSelector)), and hideHookSelector() restores the composer via the same restoreComposer() path. No transcript repaint or admission-state write was touched. showHookSelector still calls attachAbort() and returns the same finish(option | undefined) promise. Deep Interview routing is byte-identical (still DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS).
Ask viewport overflow — fixed, not regressed
ask.ts:1616 now routes all selector prompts (ordinary + deep-interview) through scrollTitleRows: DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS. extension-ui-controller.computeBudget() clamps that sentinel against availableTitleRows, so the title is bounded by the terminal budget and never pushed into scrollback. Verified: 20-row terminal renders ≤ 20 lines with Prompt row 1 and all options visible; the premise is fully recoverable via PgUp/PgDn/Ctrl+u/d.
Scrollback semantics — sound
- Terminal wheel scrollback is untouched: the selector renders within the viewport; nothing writes to scrollback.
▼ more/▲ moreindicators are now computed inside a bounded convergence loop (4 iterations) instead of baked into the last visible line. ThewasAtBottomre-anchor before the indicator pass prevents a countdown-timer repaint from resurrecting a stale▼ morerow — covered bykeeps the bottom premise page stable across a countdown repaint.- One-row title viewport (
scrollTitleRows: 1) slices cleanly without indicators (themaxRows < 3fast path). Verified bykeeps each premise line intact in a one-row title viewport.
Focus / prompt pinning — no regression
- PageUp/PageDown/Ctrl+u/Ctrl+d now page by
max(1, scrollTitleRows - 2)instead ofscrollTitleRows. This accounts for the indicator rows and prevents overshooting past content. Existing paging tests were updated from 8→16 page-downs to reflect the smaller page stride; this is correct, not gaming. - Paging is suppressed while the inline-editor autocomplete is open (
!this.#inlineEditor?.isAutocompleteOpen()) — a genuine fix so PageDn doesn't both scroll the title and move the autocomplete selection. Covered bykeeps an open autocomplete dropdown ahead of title paging. - Option focus (
selectedIndex) is never mutated by title scrolling — preserved.
Title reflow across resize — sound
ScrollableTitle.render() detects width changes via #lastRenderWidth and re-anchors the scroll offset to the same source-text span (graphemeStart/graphemeEnd from the #2031 viewport anchor system) rather than resetting to top. findLast is ES2023 — Bun runtime supports it (verified typeof === "function"). Covered by keeps the visible premise anchored across title reflow and the resize-handler test.
Resize listener lifecycle — clean
process.stdout.on("resize") is registered only when requestedTitleRows !== undefined, removed on hideHookSelector(), dispose(), and before re-registration. Test removes the bounded selector resize listener when disposed asserts listenerCount returns to baseline. No listener leak.
Contract drift — one widening, sound
Editor.setAutocompleteProvider widened from (provider: AutocompleteProvider) to (provider: AutocompleteProvider | undefined). The undefined branch cancels any open autocomplete and notifies — correct for the compact-mode path that disables @ autocomplete. All callsites remain valid (a concrete provider is still assignable to AutocompleteProvider | undefined). The EditorComponent interface in editor-component.ts:60 still declares the narrower signature — functionally sound (contravariant parameter widening on the implementer is assignable), but worth a follow-up to align the interface for clarity. Not blocking.
EditorComponent interface in editor-component.ts:60 still declares the narrower signature — functionally sound (contravariant parameter widening on the implementer is assignable), but worth a follow-up to align the interface for clarity. Not blocking.
#capFocusedSegments(budget === 1) behavior change — intentional, tested
The budget === 1 branch now returns truncateToWidth(rows[0]) instead of the … N wrapped rows omitted … marker. This keeps at least the first content row of a focused option visible under extreme compaction rather than replacing it entirely with an omission marker. The new sibling-clipping marker logic (mustMarkClippedSiblings) covers the position-indicator case. Tested by reserves a position marker when focused wrapping clips siblings.
Tests — comprehensive
The commit trailer Not-tested: tests and formatters intentionally skipped per assignment is stale/inaccurate — the PR actually adds substantial test coverage (309+ lines in hook-editor.test.ts, 171+ in hook-selector-overflow.test.ts, 43+ in hook-selector-inline-input.test.ts). I ran the full set: 154/154 green across the 4 affected files, plus 206/206 green across deep-interview-render-middleware, hook-input-timeout, interactive-mode-editor-component, editor-autocomplete-actions, and editor tests. The trailer should be corrected post-merge, but the actual coverage is strong.
Type/lint/build verification
bun --cwd=packages/coding-agent run check(tsc + biome): cleanbun --cwd=packages/tui run check: clean- CI: 14 SUCCESS, 2 SKIPPED (platform-gated), 0 failures
Verdict
The diff is maintainable, well-commented, and correctly addresses #3675 without regressing Deep Interview, transcript admission, scrollback, or focus pinning. The only nits (stale commit trailer, narrow EditorComponent interface signature) are non-blocking.
MERGE_READY — merging with squash.
Signed-off-by: GJC (hostile reviewer) — review grounded in 8fffb6fdfd on 4c6e81706, 154+206 tests re-run green, tsc+biome clean.
Problem
Fixes #3675. Ordinary
askdialogs did not opt into the existing bounded, scrollable question-title path. A long premise could leave the viewport with no PageUp/PageDown or Ctrl+U/Ctrl+D route to read it.This is intentionally separate from #3684 native terminal-scrollback work. Keyboard paging moves only the question; the wheel and terminal scrollbar keep their transcript behavior.
Reproduction
askwith a premise longer than the available title area, including wrapped text in a narrow terminal.Scope and behavior
ask; no new interaction model or public API is introduced.Other/clarification input transitions, including a countdown repaint on the bottom page.Ctrl+Gexternal-editor hint.@fallback when the dropdown itself cannot fit, and restores autocomplete after expansion. Budgets recompute in place on terminal resize without losing focus, input, or the semantically visible title premise. One-row option budgets retain a visible focused label, clipped windows retain an option-position marker, and one-row title view keeps the premise line intact rather than consuming it with an indicator.packages/coding-agentUnreleased changelog entry.Direct
HookSelectorcallers that do not requestscrollTitleRowsretain their previous behavior. No unrelated legacy-selector cleanup is included.Regression coverage
Ctrl+G, wrapped footer budgeting, bounded multiline drafts and an eight-item autocomplete list, the 20-row / 19-column compact literal-@fallback plus autocomplete restoration after expansion, one-row focused-label visibility, title offset preservation after timeout reset, and 30→20-row terminal resize while preserving an active draft.Verification
Focused automated coverage exercises the rendering and input contracts. Physical interactive-terminal dogfooding was not run; exact-head CI remains required.