Skip to content

Show Magic Compose's questions, and say what a draft needs - #576

Open
richshaw wants to merge 11 commits into
claude/pensive-thompson-quytut-3-telemetryfrom
claude/pensive-thompson-quytut-4-screen
Open

richshaw wants to merge 11 commits into
claude/pensive-thompson-quytut-3-telemetryfrom
claude/pensive-thompson-quytut-4-screen

Conversation

@richshaw

@richshaw richshaw commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Part of #570, the fourth of five stacked PRs. It's based on #575 (activity events for the questions round).

Two things change for organizers as soon as this lands:

  • The helper line. Under "What are you organizing?" it now says "Say what people sign up for, when, and how many of each. We'll turn it into a title, blurb, and slots for you to edit."
  • The waiting screen. It reads "Reading your description" ("You'll get a draft, or a few quick questions if it needs more to go on.") until a draft arrives, then "Drafting your signup".

The questions screen itself is dormant until #577 makes the prompt ask.

The questions screen

MagicComposeRoot goes compose → reading → A few details first → drafting with the answers → builder. The page holds the answers, not the screen, so nothing typed is lost:

  • Cancel during the second call, or any error on it, returns to the questions with every pick, date and number kept. Errors (a refusal, "Couldn't draft from these answers…", the hourly limit) show in FormError above the card, and go away when an answer changes.
  • Back returns to the compose box with the answers added to the description as plain lines, for example What: Bake something to sell (no limit), Staff the table (2) and When: Saturday, November 14, 9am to 1pm. The next Draft my signup is a first call again.

Questions.tsx follows the design boards:

  • "← Back", heading and description. "← Back" sits at the top. The "A few details first" heading takes focus when the screen appears. "You wrote “…”" shows the description.
  • Only the asked questions:
    • What do people sign up for? The model's options are aria-pressed toggles in a labelled group. "Add your own" takes Enter or an Add button and makes chips with a × ("Remove Bring drinks"), up to 12 items. Focus stays in the box after an add and moves to the next chip after a remove.
    • When is it? One day / Several days / Not decided are native radios styled as a segmented control, so arrow keys work. One day shows a date and optional start and end times. An end before the start, or a year before 1900, is flagged on the page without calling the server.
    • How many people do you need? A labelled number box with − and + for each item. Empty means no limit.
  • The actions. Draft with placeholders, Draft my signup and Fill in by hand instead.
  • Target size. Every target is at least 44px tall.

While the second call runs, the drafting screen shows a Drafting from card: the description, each item with "2 people" or "No limit", and the date. A blank question reads "Not decided. The draft will use placeholder dates you can fill in later."

Reuse.

  • YouWrote.tsx is the one "You wrote" box, used by the waiting screen, the questions screen and the card. It replaces the old blockquote.
  • Items are tidied with the server's own tidyText, so the page and the server can't disagree about a duplicate.

What the browser sends

answers.ts builds the FollowUp (unit-tested), normalised before sending:

  • An empty "Several days" box, or "One day" with no date, goes as not_decided.
  • howMany is sent only when "how many" was asked and there are items: one entry per item, picked then added, null for an empty box.
  • Draft with placeholders sends every asked topic blank, plus placeholders: true.
  • Text left in "Add your own" counts as an item when the organizer presses Draft my signup or Back.

Page size

The questions screen and the Drafting from card load only when a reply asks for them. That keeps about 4 kB out of the first load of /app/signups/new. If their code can't load (say, after a deploy), the organizer goes back to the compose box with the description kept, under "The questions couldn't be shown. Reload the page and try again; copy your description first if you want to keep it."

Measured with a clean install, against main with #525 and #526:

page #575 this PR budget
/app/signups/new 113.8 kB 115.0 kB 114 → 116 kB
/ 132.9 kB 133.0 kB 133 → 134 kB
  • /app/signups/new grows by the reading/drafting copy, the "You wrote" box, the error boundary and focus handling.
  • / and every other page grow by about 0.1 kB: the shared runtime's map of the on-demand parts. / was already at 132.9 of 133.

Loading the screen with the page would cost about 4 kB more on /app/signups/new, so I've raised the two budgets by 1 kB each rather than drop the lazy loading.

Help

"Create and publish a signup" gets:

  • the new helper line;
  • "If you see A few details first, your site is asking a few questions before it fills in your signup. Pick what fits, add your own, or choose Draft with placeholders."

The walkthrough never reaches the questions (e2e runs without an AI), so .ui.ts says so and the article's unit test renders the screen and checks those two names. No screenshot shows the drafting screens, so none needed retaking.

Where it departs from the boards

  • Errors sit above the card (board D), not inside it (board C). On a phone the screen comes back scrolled to the top, where an error at the bottom would be out of sight.
  • Selected options use dark text on the blue tint. The board's darker blue isn't one of our colours, and our blue fails contrast on the tint (4.1:1).
  • Count boxes start empty (no limit). The board's demo starts them at 1.
  • Item case. Back keeps each item's case as typed rather than lower-casing it, which would break names like "Maple class".
  • Copy no board covers:
    • "All answered" / "One left blank" / "Two left blank" on the card;
    • "12 is the most. Remove one to add another.";
    • "The end time must be after the start time.";
    • "Check the date's year.";
    • after a follow-up times out, "AI drafting is taking longer than expected. Try again." (no "simplify your prompt", since there's no prompt to edit there).

Tests

  • Questions.test.tsx covers the issue's list:

    • picking options and adding your own;
    • a count row for each item;
    • Back adding the answers to the description;
    • Cancel and an error keeping every answer;
    • Draft with placeholders sending no answers.

    It also covers the first-call questions, heading focus, focus after Add, × and the 12th item, a short year, and errors clearing only on a real change.

  • answers.test.ts covers the FollowUp builder, the Back text, the year check and sameAnswers.

  • lazy-chunks.test.tsx covers the fallbacks when the code can't load.

  • MagicComposeRoot.test.tsx covers both watchdog messages.

  • Drafting.test.tsx covers the reading/drafting copy.

  • pnpm lint && pnpm typecheck && pnpm knip && pnpm test (1471) and pnpm build && pnpm budgets pass.

Tophat

This branch on its own, against a local fake OpenAI-compatible endpoint:

  • the helper line shows;
  • "Reading your description" stays up while a slow first call runs;
  • a thin description gets the questions;
  • a year typed as 0202 is caught on the page, and the error goes away once the date is fixed;
  • the answers draft a signup;
  • "Couldn't draft from these answers" goes away on the next pick.

With the questions screen's code blocked in the browser, the page goes back to the compose box with the description kept and the fallback message above it.

The whole stack, earlier, on desktop and phone: Cancel mid-draft, a refusal, Back, Draft with placeholders and the hourly limit.

Reviews

  • The first review, fixed in e372dc2:
    • no error boundary around the lazily loaded parts;
    • focus dropped to the page after Add or ×;
    • some controls were 40–42px tall.
  • A second review (/code-review), fixed in d4ffb57 and be2c366:
    • a pre-1900 year reached the server;
    • the fallback message was only true for a load failure;
    • an error stayed up after the answers were fixed;
    • the watchdog said "simplify your prompt" on the questions screen;
    • the page had its own copy of the server's text tidy-up.
  • Left as is:
    • Back can push a very long description past 4000 characters (the cap is unchanged and rare);
    • Cancel while the questions' code loads (it's preloaded, so the window is tiny);
    • overnight times (the issue asks for times in order);
    • commas inside items in Back's text.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks

A description like "Bake sale" can now come back with questions instead
of a draft, so the page needs somewhere to answer them. "A few details
first" shows only the topics the reply asks about: the AI's options for
what people sign up for plus the organizer's own, a day or several or
not decided, and a count for each item. The page sends exact values
(FollowUp) built and tidied by one pure function, so an empty "Several
days" box or a day with no date goes as not decided, and Draft with
placeholders sends every asked topic skipped.

The answers live in the page rather than the screen, so Cancel and any
error on the call with answers come back to the questions with every
pick, date and number kept. Back writes the answers into the description
as plain lines, items as typed, and the next draft starts fresh.

Until a reply arrives the waiting screen no longer promises a draft,
and the compose helper line says what a draft needs. The description is
quoted by one "You wrote" component on the waiting screen, the questions
and the new Drafting from card.

The questions screen and the card load only when a reply asks, which
keeps about 4 kB out of the page's first load. What is left, the "You
wrote" box and the new phases, takes /app/signups/new from 113.7 to
114.7 kB, so its budget goes from 114 to 115.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks
The help page's drafting note now gives the new helper line, and says
what to do on "A few details first": pick what fits, add your own, or
choose Draft with placeholders. The walkthrough can't reach that screen
because CI runs without an AI, so the article's unit test renders it and
checks the two new names instead, as it already does for the compose
screen. No screenshot changes: none shows the drafting screens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks
Formatting had re-sorted Tailwind classes and re-wrapped help text in
lines this change doesn't otherwise touch. Put those back as they were,
so the diff shows only the helper line, the compose box's label, the
reading and drafting copy, the shared "You wrote" box and the help
article's new sentence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks
… the questions

If a deploy replaces the page's code while an organizer has the new signup
page open, the questions screen's code can fail to load when the questions
arrive. That error reached Next's "client-side exception" screen and took the
description with it. Now the questions and the Drafting from card each sit
inside an error boundary: a questions screen that can't load sends the
organizer back to the compose box, description still in it, with a note to
copy it and reload, and a Drafting from card that can't load shows the
description box instead. The preloads no longer leave a rejected promise
unhandled, and the drafting screen stays up while the questions' code loads
rather than going blank.

Add and a chip's remove button disappear once used, which dropped keyboard
focus to the page. Focus now stays in "Add your own" after Add, moves to the
next chip's remove button (or the box) after a remove, and moves to the line
about the limit when the twelfth item takes the box away.

The number control's buttons and box, and the two time boxes, were 40 to 42px
tall. They are now 44px, the size every target on this screen is meant to be.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks
A date with a year before 1900, an easy slip in a date box (202 becomes
0202), went to the server, which refused it with "The date must be a real
day, as YYYY-MM-DD". The page now says "Check the date's year." before
sending, and the date box starts at 1900, the server's earliest year.

An error stayed on screen while the organizer changed their answers, so
answers they had fixed still looked wrong. It now goes once an answer
actually changes, and the next try brings its own.

When drafting from answers ran too long, the message said to simplify a
prompt the questions screen doesn't let them edit; it now says only to try
again. When the questions can't be shown, the message no longer says the
page was updated, since an error while rendering them lands there too.

The page also tidies typed text with the server's own function now, so the
two can't drift.

Part of #570.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks
It was cut to "Reload" to stay a few bytes under the page's budget.
The words come first; the budget is raised in the next commit, with
its reason.

Part of #570.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks
Measured with a clean install against main (with #525 and #526):

- /app/signups/new goes from 113.8 kB (the telemetry branch) to 115.0
  kB: the reading and drafting copy, the shared "You wrote" box, the
  error boundary and focus handling. The questions screen and the
  Drafting from card already load only when a reply asks for them;
  loading them with the page would add about 4 kB more.
- Loading them on demand adds about 0.1 kB to the shared runtime every
  page loads (its map of chunks). / was at 132.9 of 133 kB on main and
  goes just over.

Budgets: /app/signups/new 115 -> 116, / 133 -> 134.

Part of #570.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UW9ZpEu4MWkeqTv2Qe14ks
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.

2 participants