githubnext/chopinPublic

Let's compose rich plans together

Show a Planner question as waiting on a decision in chat #285

MergedMaggieAppletonmerged 2 commits intomainfromrefs/pull/285/head
  1. MA

    The Planner’s running tools use readable activity labels. An open `ask` displays “Waiting on your decision” and an Open action for the matching decision card, then returns to the completed summary when answered.

    Integration preserves main’s current research-offer placement and Chat naming. The obsolete embedded offer card from the old branch was omitted while retaining the new decision wiring.

    Local validation passes: checks, types, 144 chat unit tests, and all 16 smoke browser tests, including waiting, opening the card, and completion. Final GitHub CI is running.

    <details><summary>Original proposal and screenshots</summary>

    ## Why

    While a Planner tool runs, the chat showed a spinner, the raw tool name in monospace and a "0 done" count (for example "⟳ Questions 0 done"). The most common long run is the Planner asking a question: `ask` stays running until someone answers in Decisions, so the row spun forever and read like debug output, even after the Planner had written its follow-up below it.

    ## What changed

    - Running tools read as a sentence-case activity in the normal sans face ("Reading a file · 2 done"). The count appears only once something has finished. The quaternary colour and loader stay. - A running `ask` is shown as waiting, not working: the decision icon, "Waiting on your decision" (or "Waiting on N decisions"), and a small ghost **Open** button. No spinner. - **Open** reuses the existing `TranscriptDecisions.onOpenCard` plumbing, so it reveals and focuses the matching card. The card is found by matching the `ask` call's question prompts against open cards, because the questionnaire ids are minted on the server after the call starts. If nothing matches, the row still says it is waiting, just without the button. - Once the question is answered the tool finishes, and the row falls back to the usual collapsed "N tools · time" summary. - Pure helpers (`runningLabel`, `summarize` waiting state, `waitingCards`, `waitingText`) live in `chat/model.ts` with unit tests. The `ToolRun` diff is kept tight to avoid conflicts with #268.

    ## Screenshots

    Before (desktop) ![Before, desktop](https://github.com/user-attachments/assets/747dd709-f37b-47cc-ba84-f41422acccff)

    After (desktop) ![After, desktop](https://github.com/user-attachments/assets/e0a8d2ce-fb9c-4dd2-b3cc-98d2882915f1)

    Before (phone) ![Before, phone](https://github.com/user-attachments/assets/37e9d825-0764-4bc8-9aab-b8a40aa3fada)

    After (phone) ![After, phone](https://github.com/user-attachments/assets/d29e046f-4800-4404-b2fe-ebafb8c1bf3a)

    After clicking Open, the matching card is focused ![After Open](https://github.com/user-attachments/assets/d91edd1e-7184-4e41-a98f-213e634d8466)

    ## Testing

    - `bun test apps/web/src/chat`: 140 pass - `bun run types`: pass - `bun run ci`: pass - e2e: updated `e2e/smoke.e2e.ts` to the new running labels and added "a Planner question waits on people and opens its card" (waiting text, no loader, Open focuses the card, and the row collapses once the tool finishes). Local e2e is paused for this audit, so CI's browser job is the check here: CI e2e, validation and container jobs all pass. - Checked manually in a real browser by injecting chat history (a running tool and a running `ask` matched to a seeded open decision) at 1440×900 and 390×844.

    🤖 Generated with [Claude Code](https://claude.com/claude-code)

    </details>

  2. MA
    Author

    **Review:** Looks good

    Checked in a real browser (own headless Chromium, `NO_PLANNER` server, injected `chat:history` with a running `read_repository_file` and a running `ask` against the seeded decisions) at 1440x900 and 390x844. `bun test apps/web/src/chat` (140 pass) and `bun run ci` pass on the branch. The waiting row reads well and sits quietly (icon, "Waiting on your decision", ghost Open). The running row ("Reading a file · 1 done") is a clear improvement over the monospace row. Open reveals and focuses the matching card on desktop, and on phone it switches from Chat to Document and focuses the card. An unmatched prompt shows the wait text with no button, as described.

    Nothing blocking. Suggestions, roughly by value:

    1. **Matching only looks at the card's first question** (`transcript.tsx` WaitingRun, `value.questions[0]?.prompt`). One `ask` call creates a single questionnaire holding all its questions (`apps/server/src/agent/tools.ts` ask -> `Questions.identify`), so a two-question ask is one card. It matches only through its first prompt; the second prompt is unmatched. It works by accident (one card -> "your decision"). But if the card is not found, `ids.length || prompts.length` falls back to "Waiting on 2 decisions" for one card. Suggest counting cards, not prompts, in that fallback (or always "your decision" when nothing matched), and matching a card if any of its questions equals a prompt. The `["Which database?", "Which database?"] -> ["db","old"]` unit test also encodes a one-prompt-per-card model that the server never produces. 2. **Text matching is acceptable but is a heuristic.** Equal-text collisions are handled by `findLast` open card, which is the right pick for a re-ask. A card answered or discarded by someone else drops out of `open`, so Open disappears until the tool finishes, which is correct. The card arriving after the tool call is fine because the row subscribes to the store (verified: reload with the tool already running, then card sync). A residual false positive is an older open card with identical first-question text from a different ask. Rare enough to accept. If the prompt is ever normalised server-side (trim/whitespace), matching would silently fail, so consider comparing trimmed text. 3. **`cardStatus(value)` throws on an invalid status** (`@chopin/dialect` questionnaire-fields.ts:61) and is now called during render in the transcript. A corrupt record would take the chat down; a try/catch treating it as not open is cheap. 4. **Accessibility.** The button's name is just "Open" and is repeated per waiting row; give it `aria-label="Open decision"` (or "Open the decision the Planner is waiting on"). The row appears and swaps without any live-region semantics; since it is a status change, `role="status"` on the row (or the existing chat live region) would announce "Waiting on your decision". The decorative icon is correctly `aria-hidden`; the loader is also hidden, so the plain running row announces nothing either way (pre-existing). 5. **Label vocabulary.** The running label says "Reading the document" / "Editing the document" (matches AGENTS.md terminology), but the expanded finished list still uses `toolCopy`, so the same call reads "Read plan" / "Edit plan" afterwards (`transcript.tsx` ~118). Worth aligning in a follow-up, or mapping both through one table. All 17 `PLANNER_TOOL_NAMES` are covered. Tools outside the table fall back to `toolCopy`, so GitHub MCP names come out as "Github get file contents" (the unit test asserts this) and the job tools as "Draft heading"/"Refine decision", which are imperative and miscased. Either add `-ing` entries for the job tools or special-case the `github/` prefix ("Reading from GitHub"). "Reading tasks"/"Editing tasks" for the implementation graph is fine given the child surface has no tasks, but note it is the only place the user sees "tasks". 6. **Conflict with #268.** The `transcript.tsx` hunks do not overlap (#268 touches SystemEntry, MessageBody label, MessageGroup name/timestamp). A trial merge conflicts only in `e2e/smoke.e2e.ts` (the `Stop Planner`/`Stop Chopin` and `Edit plan` vs `Editing the document` lines around 620). Whichever lands second needs a trivial rebase there: keep `Editing the document` and `Stop Chopin`. Note that #268 renames Planner to "Chopin", so "Waiting on your decision" stays consistent.

    Threading of `decisions` is correct (Transcript -> MessageGroup -> MessageBody -> ToolRun -> WaitingRun) and reuses `onOpenCard`; the `useSyncExternalStore` fallbacks are stable. No unrelated changes.

  3. MA
    Author

    Follow-ups applied in f65d2fc7:

    1. Cards, not prompts: a card matches when any of its questions equals one of the ask's prompts (trimmed). The count is distinct cards, or distinct asked prompts until a card arrives. Unit tests cover multi-question cards, closed cards, and the fallback. 2. `cardStatus()` is wrapped in `isOpen()`, so an invalid stored status counts as not open instead of throwing in render. 3. The waiting row has `role="status"`; the button is labelled "Open decision" (e2e updated). 4. Progressive labels for `github/*` tools ("Reading a GitHub file", unknown ones read "Using GitHub") and the job tools (`draft_heading`, `refine_decision`, `write_decision_prose`). Anything else reads "Using <tool>" rather than an imperative name.

    `bun test apps/web/src/chat` (141 pass), `bun run types`, `bun run ci` pass locally; CI e2e, validation and container all pass.

  4. lavaman131referenced this in [Design] Rebuild the workflow run card with assistant-ui Elements #307last month on GitHub

  5. lavaman131referenced this in [Design] Replace the composer @ mention picker with assistant-ui mentions #308last month on GitHub

  6. lavaman131referenced this in [Design] Conversation controls with assistant-ui Elements: message queue, stopped run, error state #309last month on GitHub

  7. lavaman131referenced this in [Design] Research and references with assistant-ui Elements #310last month on GitHub

  8. lavaman131referenced this in [Design] Decisions display with assistant-ui Elements (option list, question flow) #311last month on GitHub

  9. MA
    Author

    <!-- pr-rebase:f65d2fc782ea0125185f1eed990070bb44517427:964535bb81cb00d3583301d3ec90d55393c278ca --> PR babysitter could not rebase this branch cleanly onto main. The branch is unchanged. Please resolve the rebase conflict locally; automation will try again after the branch changes.

  10. MaggieAppletonauto-rebase-enabledlast month on GitHub

  11. MA
    Author

    <!-- pr-rebase:f65d2fc782ea0125185f1eed990070bb44517427:8675f8e57182b28d0b2dc34c8089d0d928d00191 --> PR babysitter could not rebase this branch cleanly onto main. The branch is unchanged. Please resolve the rebase conflict locally; automation will try again after the branch changes.

  12. MA
    Author

    <!-- pr-rebase:f65d2fc782ea0125185f1eed990070bb44517427:e6b7bb333ce8948d0dac0c8aff61cf3d4fb8dc8b --> PR babysitter could not rebase this branch cleanly onto main. The branch is unchanged. Please resolve the rebase conflict locally; automation will try again after the branch changes.

  13. MA
    Author

    <!-- pr-rebase:f65d2fc782ea0125185f1eed990070bb44517427:b919317f14f29799be257eee49ba5ab753592371 --> PR babysitter could not rebase this branch cleanly onto main. The branch is unchanged. Please resolve the rebase conflict locally; automation will try again after the branch changes.

  14. MaggieAppletonhead-ref-force-pushed c2e5d8889e3clast month on GitHub

  15. MaggieAppletonhead-ref-force-pushed 4e9b4b51bd95last month on GitHub

  16. MaggieAppletonreferenced this in Make navigation dialogs compact and palette-style #286last month on GitHub

  17. MaggieAppletonmerged 63dfeaf62e5dlast month on GitHub

  18. MaggieAppletonclosedlast month on GitHub

  19. MaggieAppletonhead-ref-deletedlast month on GitHub

  20. MaggieAppletonreferenced this in Show Chopin's work as live stages with inspectable tools #296last month on GitHub