Before submitting
Both checks were completed by the submitting assistant before publication, not by the reporter. Duplicate-search evidence: open and closed issue searches for questionnaire answer change, ask_user_question shift+tab, ask_user_question answer, questionnaire navigate, questionnaire change answer, and questionnaire tab returned no report of this problem. The nearest matches are #1273 (pointer click semantics only), #833 (the questionnaire feature itself), #1340 (a long preview growing the panel), and closed #1141 (transcript scrolling). The exact published text was privacy-scanned before the single create attempt.
Problem
The questionnaire ends itself the moment the last unanswered question receives an answer. From that instant every key is inert, so a user cannot go back to change any answer they already committed.
Answering one to four questions in the natural order (Enter on each) therefore commits the last one and completes the questionnaire in the same keystroke. There is no review step and no Submit action, so nothing tells the user the answers have just become final and unchangeable.
Root cause is lib/questionnaire/questionnaire-view.ts:
afterCommit() (lines 547-557) calls finish(...) as soon as every state has an answer, instead of leaving completion to an explicit action.
finish() (lines 565-571) sets completed = true, and handleInput() (line 193) returns immediately on completed, so Tab, Shift-Tab, arrows, Enter, and space all become no-ops at that instant.
The in-code comment at lines 552-553 documents the opposite intent:
// Advance to the first unanswered question so a commit is visible and the
// tab strip keeps moving; committed answers stay reachable with Tab.
"Reachable with Tab" holds only while at least one question is still unanswered (verified below). Once every question is answered the questionnaire has already completed, so Tab cannot reach anything.
Navigation itself is not broken: Tab cycles through all questions and wraps around (verified). The defect is exclusively the automatic completion, which closes the only window in which a committed answer is still changeable.
Impact: ask_user_question is how the harness collects batched user decisions, and the shipped prompt guidance in extensions/ask-user-question.ts tells the model to "Use ask_user_question to collect decisions in one batch: ask one to four questions at a time, each with two to four options." A user who wants to correct one answer must instead press Esc to cancel the whole questionnaire, losing every other committed answer and forcing the model to ask all of them again. The one workaround that does exist — deliberately leave a question unanswered, Tab back, correct it, then answer the last one — is not discoverable from the UI, whose only hint is ↑↓ move · enter select · tab switch · esc cancel.
For traceability, without claiming a formal contract breach: the approved feature request #833 lists "Next explicitly commits a valid current answer and advances; the final Submit commits the explicit current answer and completes" as an acceptance criterion, and #833 is still open. The delivered TUI has neither a Next row nor a Submit row; a grep over lib/questionnaire/ finds no such affordance, and its only submit is the internal handler of the free-text editor. Whether the delivered interaction was meant to satisfy that criterion is a maintainer call.
Secondary finding (same file, same interaction lifecycle; can be split into its own issue on request)
On a multiSelect question, committing with zero options toggled is a silent no-op. commit() returns at line 472 (if (toggled.length === 0) return;) without changing the answer, without advancing, and without any message, while the footer hint still advertises "enter select". This is reachable by tabbing back to an answered multiSelect question, un-toggling its options, and pressing Enter: nothing happens and nothing explains why. If the no-op is intentional (a multiSelect answer must be non-empty), it needs user-visible feedback rather than silence.
Steps to reproduce
In the live TUI
- Let the agent ask a questionnaire with two or more questions (for example two single-select questions).
- Press Enter on each question in order, accepting the first option every time.
- The panel completes on the last Enter.
- Press Tab, Shift-Tab, arrows, space, Enter: nothing happens, and no committed answer can be changed.
Minimal programmatic reproduction
QuestionnaireView can be driven directly with real key sequences. Node refuses --experimental-strip-types for files inside node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), so copy lib/questionnaire/ outside node_modules and import it from there.
import { QuestionnaireView } from "./lib/questionnaire/questionnaire-view.ts";
const theme = { fg: (_color, text) => text, bold: (text) => text };
const option = (label) => ({ label, description: `${label} description` });
const questions = [
{ question: "First?", header: "First", options: [option("Alpha"), option("Beta")] },
{ question: "Second?", header: "Second", options: [option("Gamma"), option("Delta")] },
];
const completed = [];
const view = new QuestionnaireView({ questions, theme, onComplete: (result) => completed.push(result) });
view.handleInput("\r"); // commit First = Alpha; focus advances to Second
view.handleInput("\r"); // commit Second = Gamma; the questionnaire completes here
console.log("onComplete calls:", completed.length);
// Every one of these is now inert, so First cannot be changed to Beta.
for (const key of ["\t", "\x1b[Z", "\x1b[A", "\x1b[B", "\r", " "]) view.handleInput(key);
console.log("answers:", JSON.stringify(completed[0].answers.map((a) => a.answer)));
Observed on the released package:
onComplete calls: 1
answers: ["Alpha","Gamma"]
The same view does allow the correction while one question is still unanswered, which isolates the cause to the automatic completion rather than to navigation:
"\r" -> First = Alpha, active tab 1
"\x1b[Z" -> active tab 0 (Shift-Tab back)
"\x1b[B" -> cursor moves to Beta
"\r" -> First = Beta, active tab 1
Tab wraparound was verified separately with three questions: active tab 0 -> 1 -> 2 -> 0 -> 1.
Expected and actual behavior
Expected: an answer stays changeable until the user completes the questionnaire as an explicit action. Tab back to an answered question, commit a different option, and the questionnaire completes only when the user asks it to — consistent with the intent recorded in the afterCommit() comment. A commit rejected because nothing is selected reports why instead of doing nothing.
Actual: answering the last question completes the questionnaire in the same keystroke, nothing announces that, and from that instant every key is inert, so no committed answer can be changed.
gentle-pi version
3.5.0 (installed package). Verified still present on main: lib/questionnaire/questionnaire-view.ts on main (package.json version 3.6.0, commit c0aadd8b218c250218769f4fc39af698c23df2df) is byte-identical to the installed 3.5.0 copy, with the same line numbers.
Pi version
0.87.0
Operating system
Windows (WSL)
Relevant logs or error output (optional)
Repro A: two single-select questions, natural order
before any key : active tab 0
"\r" (Enter) : First = Alpha, active tab 1
"\r" (Enter) : Second = Gamma -> FINISHED (onComplete fired once)
then "\t", "\x1b[Z", "\x1b[A", "\x1b[B", "\r", " ": all inert
final answers : ["Alpha","Gamma"] <- First cannot be changed to Beta
Repro B: two single-select questions, correction while one is unanswered
"\r" : First = Alpha, active tab 1
"\x1b[Z" : active tab 0
"\x1b[B" : cursor -> Beta
"\r" : First = Beta, active tab 1 <- correction succeeds
Repro C: one single-select question
"\r" : FINISHED on the first Enter
Repro D: Tab wraparound with three questions
active tab: 0 -> 1 -> 2 -> 0 -> 1
Repro E: multiSelect, every option un-toggled, then Enter
render changed : false
answers unchanged: [["One"]] <- silent no-op, hint still says "enter select"
Before submitting
Both checks were completed by the submitting assistant before publication, not by the reporter. Duplicate-search evidence: open and closed issue searches for
questionnaire answer change,ask_user_question shift+tab,ask_user_question answer,questionnaire navigate,questionnaire change answer, andquestionnaire tabreturned no report of this problem. The nearest matches are #1273 (pointer click semantics only), #833 (the questionnaire feature itself), #1340 (a long preview growing the panel), and closed #1141 (transcript scrolling). The exact published text was privacy-scanned before the single create attempt.Problem
The questionnaire ends itself the moment the last unanswered question receives an answer. From that instant every key is inert, so a user cannot go back to change any answer they already committed.
Answering one to four questions in the natural order (Enter on each) therefore commits the last one and completes the questionnaire in the same keystroke. There is no review step and no Submit action, so nothing tells the user the answers have just become final and unchangeable.
Root cause is
lib/questionnaire/questionnaire-view.ts:afterCommit()(lines 547-557) callsfinish(...)as soon as every state has an answer, instead of leaving completion to an explicit action.finish()(lines 565-571) setscompleted = true, andhandleInput()(line 193) returns immediately oncompleted, so Tab, Shift-Tab, arrows, Enter, and space all become no-ops at that instant.The in-code comment at lines 552-553 documents the opposite intent:
"Reachable with Tab" holds only while at least one question is still unanswered (verified below). Once every question is answered the questionnaire has already completed, so Tab cannot reach anything.
Navigation itself is not broken: Tab cycles through all questions and wraps around (verified). The defect is exclusively the automatic completion, which closes the only window in which a committed answer is still changeable.
Impact:
ask_user_questionis how the harness collects batched user decisions, and the shipped prompt guidance inextensions/ask-user-question.tstells the model to "Use ask_user_question to collect decisions in one batch: ask one to four questions at a time, each with two to four options." A user who wants to correct one answer must instead press Esc to cancel the whole questionnaire, losing every other committed answer and forcing the model to ask all of them again. The one workaround that does exist — deliberately leave a question unanswered, Tab back, correct it, then answer the last one — is not discoverable from the UI, whose only hint is↑↓ move · enter select · tab switch · esc cancel.For traceability, without claiming a formal contract breach: the approved feature request #833 lists "Next explicitly commits a valid current answer and advances; the final Submit commits the explicit current answer and completes" as an acceptance criterion, and #833 is still open. The delivered TUI has neither a Next row nor a Submit row; a grep over
lib/questionnaire/finds no such affordance, and its onlysubmitis the internal handler of the free-text editor. Whether the delivered interaction was meant to satisfy that criterion is a maintainer call.Secondary finding (same file, same interaction lifecycle; can be split into its own issue on request)
On a
multiSelectquestion, committing with zero options toggled is a silent no-op.commit()returns at line 472 (if (toggled.length === 0) return;) without changing the answer, without advancing, and without any message, while the footer hint still advertises "enter select". This is reachable by tabbing back to an answeredmultiSelectquestion, un-toggling its options, and pressing Enter: nothing happens and nothing explains why. If the no-op is intentional (amultiSelectanswer must be non-empty), it needs user-visible feedback rather than silence.Steps to reproduce
In the live TUI
Minimal programmatic reproduction
QuestionnaireViewcan be driven directly with real key sequences. Node refuses--experimental-strip-typesfor files insidenode_modules(ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), so copylib/questionnaire/outsidenode_modulesand import it from there.Observed on the released package:
The same view does allow the correction while one question is still unanswered, which isolates the cause to the automatic completion rather than to navigation:
Tab wraparound was verified separately with three questions: active tab 0 -> 1 -> 2 -> 0 -> 1.
Expected and actual behavior
Expected: an answer stays changeable until the user completes the questionnaire as an explicit action. Tab back to an answered question, commit a different option, and the questionnaire completes only when the user asks it to — consistent with the intent recorded in the
afterCommit()comment. A commit rejected because nothing is selected reports why instead of doing nothing.Actual: answering the last question completes the questionnaire in the same keystroke, nothing announces that, and from that instant every key is inert, so no committed answer can be changed.
gentle-pi version
3.5.0 (installed package). Verified still present on main:
lib/questionnaire/questionnaire-view.tson main (package.jsonversion 3.6.0, commitc0aadd8b218c250218769f4fc39af698c23df2df) is byte-identical to the installed 3.5.0 copy, with the same line numbers.Pi version
0.87.0
Operating system
Windows (WSL)
Relevant logs or error output (optional)