Skip to content

design: what a question's answer actually chose - #293

Merged
stupart merged 1 commit into
mainfrom
feat/question-collapse
Sep 16, 2026
Merged

stupart merged 1 commit into
mainfrom
feat/question-collapse

Conversation

@stupart

@stupart stupart commented Sep 16, 2026

Copy link
Copy Markdown
Owner

§3: "Questions: a single hairline block… Once answered it collapses to 'Legacy keys · you chose Keep them under legacy'." Today an answered question keeps drawing its whole block — header, question, and a list of options nobody can press any more. It is the largest thing in a finished transcript saying the least.

This is the rule only; the view wiring follows separately — the same order as SessionGrouping, StageMode and ToolFolding.

Where the answer actually lives

The wire never states a choice. AgentQuestion is exactly header, question, options, multiSelect on both sides, and nothing records a selection. But an answered AskUserQuestion is a completed tool call: conversation.ts attaches the tool_result to it with status: "done" and result: claudeResultText(...). So the choice is recovered by matching the options' own labels against that result text.

I checked that rather than assuming it — earlier tonight I concluded from a nested struct that tool timing wasn't on the wire, and was wrong. This time the interface, the decoder and the attach site were all read.

Two things the rule has to get right

  • A shorter label inside a longer one is not a second choice. With options "Keep" and "Keep them under legacy", an answer naming the latter names one thing. Labels claim their span longest-first, and an overlapping shorter match is refused.
  • An unrecognisable answer claims nothing. No match, no result, empty result → no summary, and the row keeps exactly the shape it has today. Guessing at a person's decision is worse than not summarising it.

Mutation battery

mutation caught by
a shorter label inside a longer one counts as a second choice testAShorterLabelInsideALongerOneIsNotASecondChoice
the shortest label claims its span first same test
matching becomes case-sensitive 4 tests
an unanswered question still gets a collapsed line testTheCollapsedLineIsTheOne3Describes
choices come back in match order rather than the question's testMultipleChoicesComeBackInTheQuestionsOrder

Gate

swift test 131/0 (from 125) · bun test exit 0 · tsc --noEmit exit 0 · Mac xcodebuild BUILD SUCCEEDED · iOS xcodebuild BUILD SUCCEEDED (it links ConchDesign).

🤖 Generated with Claude Code

https://claude.ai/code/session_01SfEX6bfwCGXtThk1JRNmsE

§3 collapses an answered question to "Legacy keys · you chose Keep them
under legacy". Today it keeps drawing the whole block -- header, question,
and options nobody can press -- which is the largest thing in a finished
transcript saying the least.

The rule only; the view follows separately.

Where the answer lives: the wire never states a choice, and AgentQuestion is
exactly header/question/options/multiSelect on both sides. But an answered
AskUserQuestion is a completed tool call, and conversation.ts attaches the
tool_result to it with status "done" and result text. So the choice is
recovered by matching the options' own labels against that result -- checked
at the interface, the decoder and the attach site, because earlier tonight I
concluded from a nested struct that tool timing was not on the wire and was
wrong.

Two things it must get right: a shorter label inside a longer one is not a
second choice (options "Keep" and "Keep them under legacy" -- an answer
naming the latter names one thing), so labels claim their span longest-first;
and an answer matching nothing claims nothing, leaving the row as it is.
Guessing at a person's decision is worse than not summarising it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SfEX6bfwCGXtThk1JRNmsE
@stupart
stupart merged commit f52a97f into main Sep 16, 2026
2 checks passed
@stupart
stupart deleted the feat/question-collapse branch September 16, 2026 17:49
stupart added a commit that referenced this pull request Sep 16, 2026
§3's collapse, wired onto #293's rule. An answered question was redrawing
its header, the whole question, and every option greyed at 0.58 -- the
largest block in a finished transcript, saying the least. It becomes one
quiet line naming the decision.

A running question is untouched: it is the row the session is blocked on and
every option stays pressable. An answer naming no option collapses nothing,
since the wire never states a choice and it is recovered by matching the
options' labels against the finished call's result -- when nothing matches
the block stays as it was, and that fallback is guarded.

The line is not a button. There is nothing left to do to it, and leaving an
answered question tappable is how an earlier choice gets sent in answer to a
later prompt, which is why the existing code already disables those options.


Claude-Session: https://claude.ai/code/session_01SfEX6bfwCGXtThk1JRNmsE

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant