Skip to content

usertest: voice-moderated sessions with a Claude moderator - #185

Merged
michielbdejong merged 4 commits into
mainfrom
claude/usertest-interview
Sep 28, 2026
Merged

michielbdejong merged 4 commits into
mainfrom
claude/usertest-interview

Conversation

@michielbdejong

Copy link
Copy Markdown
Contributor

Adds voice-moderated user-testing sessions to the test instance from #182, so testers can do a session on their own laptop without anyone from the team on a call.

What a tester gets

One invite link: https://plugins.<base-domain>/usertest/?code=…, Chrome or Edge, headphones. The page (usertest/page/):

  1. explains the session and asks consent for what is recorded and where it goes: screen and microphone to the test server, speech through Google's recognizer, text and the app's error log to Anthropic's API;
  2. sets the test catalog (same origin as the data-browser), opens a fresh drive in a second window, and records the shared screen and the microphone in 10-second chunks;
  3. listens with SpeechRecognition and hands a turn to the moderator after a pause (3 s after a question, 9 s otherwise), after 60 s of silence, or when the collector logs a new error; then speaks the answer with speechSynthesis. Recognition pauses while the moderator speaks.

The moderator

usertest/moderator/server.mjs, with @anthropic-ai/sdk 0.128.0:

  • claude-opus-5, output_config.effort: "low" for short pauses, server-side refusal fallback (fallbacks: "default"), top-level prompt caching;
  • the interview script usertest/moderator/script.md: spoken one-or-two-sentence turns, mostly listening ([WAIT]), no hints unless the tester is stuck and asks, neutral about the app, four tasks (import calendar events, change one and send it back, explore other apps, wrap-up), then [END];
  • each turn includes the collector's error, warning, feedback and sync lines since the previous one;
  • per session /var/lib/usertest-sessions/<id>/: meta.json, transcript.jsonl (both sides, the log lines each turn saw, token usage) and recording.webm.

The endpoints spend API money and are public, so they need the invite code (x-usertest-code, from /etc/usertest-moderator.env). There are at most 120 turns per session and 20 sessions per UTC day. The API key stays in /etc/anthropic.env, read only by the moderator container.

Checked

On the droplet, over the real API:

  • the opening turn (welcome plus first task) in about 3 s;
  • thinking aloud answered with [WAIT] (nothing spoken);
  • "which button should I press?" answered with a question, not a hint;
  • cache_read_input_tokens 989 to 1088 from the second turn on;
  • requests without the code get 403.

oxlint and oxfmt over the new files are clean. node --check passes for both scripts.

Not verified yet: a full session with a real tester, screen and microphone permissions in the page, speech recognition quality in Dutch, and Edge.

🤖 Generated with Claude Code

michielbdejong and others added 2 commits September 28, 2026 14:25
Testers get one invite link to /usertest/ on the test instance. The page
asks consent for what is recorded and where it goes, sets the test
catalog, opens a fresh drive in a second window, records the shared screen
and the microphone, listens with the browser's speech recognition and
speaks the moderator's answers with speech synthesis.

The moderator (moderator/server.mjs, @anthropic-ai/sdk, claude-opus-5 at
effort low, server-side refusal fallback) follows moderator/script.md:
short spoken questions, mostly listening ([WAIT]), no help unless the
tester is stuck and asks. Every turn also carries the collector's error,
warning, feedback and sync lines since the previous one. Per session it
keeps the transcript and the recording. The endpoints need the invite
code, and sessions and turns are capped, because they spend API money.

Checked on the droplet: about 3 s per turn, [WAIT] while the tester
thinks aloud, a question instead of a hint when asked which button to
press, and prompt-cache reads from the second turn on. Not yet run by a
real tester.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#184)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@michielbdejong
michielbdejong force-pushed the claude/usertest-interview branch from 22e5700 to 68b967f Compare September 28, 2026 12:26
michielbdejong and others added 2 commits September 28, 2026 15:20
From the first real session (2026-09-28, Michiel):
- Dutch was chosen at the start, but the moderator switched language on
  its own while the voice stayed; and before Chrome's voice list loaded,
  the first line had a different voice. Now English only (page,
  recognizer, script) and one voice picked once the list has loaded.
- Questions did not hand over: Chrome's recognizer adds no "?", so every
  question waited the full 9 s. Questions are now recognized by their
  words (2 s), other speech hands over after 5 s, and an "Ask the
  moderator" button hands over at once.
- The moderator could not see the screen. Each turn now carries a
  1280-pixel JPEG of the shared screen (the history keeps only the text),
  saved per turn as screen-NNN.jpg; the consent text says so.

Checked on the droplet: a turn with a JPEG is accepted and answered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 'wait'

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@michielbdejong
michielbdejong merged commit 99c676a into main Sep 28, 2026
18 checks passed
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