Skip to content

feat: question_shown: read the question the terminal shows (#323) - #327

Merged
Maxaubert merged 9 commits into
mainfrom
feat/question-follow
Oct 25, 2026
Merged

Maxaubert merged 9 commits into
mainfrom
feat/question-follow

Conversation

@Maxaubert

Copy link
Copy Markdown
Owner

Summary

Closes #323. Sonara side of the approved plan docs/plans/2026-10-16-question-follow.md; the PrismTerminal side follows in its repo.

Claude Code tells plugins nothing while the user moves between the questions of one AskUserQuestion. A terminal that draws the widget can: the new agent message question_shown (protocol 1.12) carries the question on screen, and Sonara reads it.

  • {channel | host_tab, text} or {..., review: true}. host_tab is the terminal tab the hook already reports (PRISM_TAB_ID), so PrismTerminal only needs its own tab id; the newest session of that tab with a match is used.
  • Matching: case and anything but letters and digits ignored; a screen cut short matches the question it starts (20 characters on).
  • With question_step on, the set lands on that question (as next_question); off, the reading goes on from that question's entry. The current question, an unknown text and a set already reviewed do nothing. The review screen says "Review your answers." once per set.
  • Reply: moved, plus channel, question, of (or review).
  • Both SDKs: questionShown (TS AgentApi) and question_shown (Python).

Tests

  • Rules: stepping set, read-in-order set, wrapped and cut-off texts, review once.
  • Agent: read when stepping (forward, back, review), jump in a set read in order.
  • Conformance: test_question_shown_follows_the_question_on_screen, test_question_shown_finds_the_session_by_its_terminal_tab. SDK namespace tests (TS 39/39, Python).
  • python packaging/gate.py --keep-going: all pass except the tests/embed curl example (local only).

Docs: protocol-v1 (message, 1.12), README (Prism follows the questions). Version 0.33.0. Releases are unsigned.

🤖 Generated with Claude Code

https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59

Maxaubert added a commit that referenced this pull request Oct 16, 2026
…apped tokens match

From the 2026-10-16 review of PR #327:
- #23: a question shown on screen is not read while mute_level is 1 or
  more (the set still follows the screen); "Review your answers." goes
  through the normal speak/store path, so it is stored while muted.
- #60: host_tab resolves to the newest session of the tab with a
  question set (stepping or read in order) and stops there, so an
  older session of the same tab is never acted on.
- #61: matching folds the text to letters and digits with no
  separators, so a long token the terminal cut mid-word still matches
  (the 20-character prefix minimum stays).
- A set read in order no longer restarts the question that is being
  read when the screen shows it.
- In a host tab the hook sends channel_open (with host_tab) before an
  AskUserQuestion's questions, so a runtime started mid-turn still
  knows the session's tab (new golden case, a deliberate mapping change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59
@Maxaubert

Copy link
Copy Markdown
Owner Author

Review fixes (2026-10-16 full code review), commit b4b82c0:

  • Daemon lifecycle: real shutdown/start commands, install stop-then-swap, uninstall ordering #23 question_shown spoke while muted: with mute_level >= 1 nothing is read (the set still follows the screen, the entry is still revised); "Review your answers." goes through Rules::speak, so it is stored while muted. Test: a_question_shown_on_screen_is_not_read_while_muted (stepping, review, in order).
  • Control cues wait out cold Chatterbox reloads; speak them via the instant Windows voice #60 host_tab fell through to older sessions of the tab: it now resolves to the newest session of the tab with a question set (stepping or read in order) and stops there. Test: host_tab_stops_at_the_newest_session_of_the_tab_with_a_question_set (sonarad).
  • Instant Windows-voice control cues (#60) #61 fold kept word boundaries: matching now folds to letters and digits with no separators, so a long token cut mid-word on screen matches; the 20-character prefix minimum stays. Test: a_long_token_wrapped_mid_word_on_screen_matches_its_question.
  • Unverified point 1, confirmed real: a set read in order restarted the question already being read when the screen showed it. Now it goes on. Test: a_question_shown_on_screen_does_not_restart_the_question_being_read.
  • Unverified point 2, confirmed real: host_tab only came with channel_open (SessionStart, UserPromptSubmit), so a runtime started mid-turn had the channel without its tab. In a host tab the hook now sends channel_open (with host_tab, keep_label) before an AskUserQuestion's questions. New golden case ask_user_question_in_a_host_tab_reopens_the_channel.json (a deliberate mapping change; existing goldens unchanged).
  • docs/protocol-v1.md question_shown row updated.

Tests: each new test failed first, then passed. cargo test -p sonara-agent / sonara-hook / sonarad all green, clippy clean, gate --quick all ok except the known local-only tests/embed curl example. No version bump.

https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59

Maxaubert added a commit that referenced this pull request Oct 16, 2026
…apped tokens match

From the 2026-10-16 review of PR #327:
- #23: a question shown on screen is not read while mute_level is 1 or
  more (the set still follows the screen); "Review your answers." goes
  through the normal speak/store path, so it is stored while muted.
- #60: host_tab resolves to the newest session of the tab with a
  question set (stepping or read in order) and stops there, so an
  older session of the same tab is never acted on.
- #61: matching folds the text to letters and digits with no
  separators, so a long token the terminal cut mid-word still matches
  (the 20-character prefix minimum stays).
- A set read in order no longer restarts the question that is being
  read when the screen shows it.
- In a host tab the hook sends channel_open (with host_tab) before an
  AskUserQuestion's questions, so a runtime started mid-turn still
  knows the session's tab (new golden case, a deliberate mapping change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59
@Maxaubert
Maxaubert force-pushed the feat/question-follow branch from b4b82c0 to 406b559 Compare October 16, 2026 08:12
Maxaubert added a commit that referenced this pull request Oct 17, 2026
…apped tokens match

From the 2026-10-16 review of PR #327:
- #23: a question shown on screen is not read while mute_level is 1 or
  more (the set still follows the screen); "Review your answers." goes
  through the normal speak/store path, so it is stored while muted.
- #60: host_tab resolves to the newest session of the tab with a
  question set (stepping or read in order) and stops there, so an
  older session of the same tab is never acted on.
- #61: matching folds the text to letters and digits with no
  separators, so a long token the terminal cut mid-word still matches
  (the 20-character prefix minimum stays).
- A set read in order no longer restarts the question that is being
  read when the screen shows it.
- In a host tab the hook sends channel_open (with host_tab) before an
  AskUserQuestion's questions, so a runtime started mid-turn still
  knows the session's tab (new golden case, a deliberate mapping change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59
@Maxaubert
Maxaubert force-pushed the feat/question-follow branch from 3ea08f8 to ee38d7e Compare October 17, 2026 01:10
Maxaubert and others added 9 commits October 22, 2026 06:04
…apped tokens match

From the 2026-10-16 review of PR #327:
- #23: a question shown on screen is not read while mute_level is 1 or
  more (the set still follows the screen); "Review your answers." goes
  through the normal speak/store path, so it is stored while muted.
- #60: host_tab resolves to the newest session of the tab with a
  question set (stepping or read in order) and stops there, so an
  older session of the same tab is never acted on.
- #61: matching folds the text to letters and digits with no
  separators, so a long token the terminal cut mid-word still matches
  (the 20-character prefix minimum stays).
- A set read in order no longer restarts the question that is being
  read when the screen shows it.
- In a host tab the hook sends channel_open (with host_tab) before an
  AskUserQuestion's questions, so a runtime started mid-turn still
  knows the session's tab (new golden case, a deliberate mapping change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59
Owner decision (2026-10-16): once a host reports the screen for a set read
in order (question_step off), only the question on screen is read. Sonara
stops at its end, never reads on into a question the screen does not show,
and a different question on screen stops the one being read at once. The
same question again does nothing; the review line is read only on the
review screen. A set no host reports is still read in order (#322); the
mute rules stay.

L2 gets entries set aside (Router::set_aside): kept in the batch, never fed
(not by a replay either), not unread. L3 sets aside every question of a
followed set but the one on screen (Rules::read_set_aside).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59
…n being read

Question-follow switched to the session whose question was shown, cut the message being read and read it again from the start once the question was answered. The set still follows the screen; the question is read when its session is.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59
…saves what is sent to the device

A key's sound (the flush sound) plays at once and was not in the earcon schedule, so the session_change chime and the announcement started under it. What follows a key's sound now waits for it and 0.3 s. SONARA_AUDIO_DUMP=<folder> writes every speech chunk and clip handed to the output as a WAV, with a timeline, to tell a clipped first word from one lost after the audio is sent.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59
…t the default rate

At Sonara's default rate (Kokoro speed 1.25) the model dropped an unstressed schwa that starts a sentence: "Agreed" was heard as "greed", "A real" as "real". The dumped audio showed the sound was missing before the output, not lost in it. That schwa now gets a primary stress.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cnwwWNiEmM5ghBD933h59
…'s end

The screen moving to another question (an answer, Tab, arrows) jumped
straight to it and skipped the rest of the text before the questions.
Now the message is read to its end and the question on screen follows,
in both question_step modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJ8AkowszQHZvUwSrUS7wp
@Maxaubert
Maxaubert force-pushed the feat/question-follow branch from ee38d7e to 250fe18 Compare October 25, 2026 10:37
@Maxaubert
Maxaubert merged commit cd05a48 into main Oct 25, 2026
5 checks passed
@Maxaubert
Maxaubert deleted the feat/question-follow branch October 25, 2026 10:43
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.

Follow the question on screen: question_shown from the terminal (PrismTerminal)

1 participant