Question cards: per-question and whole-card skip, skip ≠ blank answer, Slack parity (OPE-153) - #600
Open
devikaverma wants to merge 2 commits into
Open
Question cards: per-question and whole-card skip, skip ≠ blank answer, Slack parity (OPE-153)#600devikaverma wants to merge 2 commits into
devikaverma wants to merge 2 commits into
Conversation
…card
Questions were the only Inbox item kind with no way out. Approvals have Deny,
directory requests have Deny, plans have Reject, notifications have Dismiss —
a question had nothing, so a user who couldn't answer had to invent an answer
that misleads the agent or abandon the session. Worst on a card with exhaustive
options and allow_text:false: literally no control but the options themselves.
The card now carries Skip, and a grouped card carries both "Skip question" (this
one, advance) and "Skip all" (resolve the rest). Skip all KEEPS answers already
given — it means "don't ask me the rest", not "discard". Skip is present whatever
allow_text says, since that's exactly where the user was stuck.
The button was the easy half. answer_result() returned {"answer": ""} both for a
blank resolution and for anything unparseable, so a skip could not be told from an
empty answer. A skipped question now answers null — never "" — alongside `skipped`
naming what was declined. Two consequences in the engine: an all-skipped card reads
"denied" rather than "ok" (a non-empty answers map is no longer proof anyone
answered), and _note_ask_replies drops nulls, else str(None) files the literal
"None" as something the user said.
What the agent is told about a skip is deliberately asymmetric. "Do not put this
question back to them" is absolute — that anti-loop guarantee is what the skip is
for. "Pick a default" is conditional: the same note fires for "you choose the chart
colour" and for "Staging or Production?", and a model that follows instructions
literally would deploy off a shrug. Opus 4.8 refused that guess on its own; smaller
models are likelier to comply, so proceeding without deciding is offered as a third
path rather than left to whichever model is driving.
Verified against a live model, not just fixtures: the options-only card skipped
cleanly, and a grouped card kept its answered step while reporting the skipped ones
as skipped. Four e2e specs and four Python tests cover both surfaces.
An unattended session mirrors its questions to a bound channel, and buttons_for() rendered one button per option and nothing else. So the dead end this issue closed in the app was still wide open in Slack: a reader whose answer wasn't on the list could only click a wrong option or leave the agent suspended. Skip now trails the options, carrying the sentinel answer_result() already maps to a null answer — that text-only-surface branch was written for exactly this caller and had a test before it had a sender. It grants nobody new authority. Anyone who can click an option in that channel can already decide the question; this only adds declining to what they could already do, which is the gentler of the two. Grouped questions stay button-less on purpose. Any button at all flips the mirror from the plain-text path to the interactive one, which would cost them the "(Open the app to respond.)" hint — and the app is where their per-question skip lives anyway.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
allow_text: a card with exhaustive options and no free-text escape was the one place with no control on screen but the options themselves. Both surfaces inherit it — the inline attended card and the parked Inbox card are the same component.