cmux-next: a not-sent prompt says why, and a refused Retry says so (hq#2569 item 5)

Merged
TE
16d ago
+606-15

Summary

Fixes manaflow-ai/cmuxterm-hq#2569 item 5. When the app's relay refused a prompt that no composer held (a New Tab ask, a seeded prompt, a queued one), the bubble said "Not sent. Press Retry to send it." with no reason. If Retry got refused the same way, the bubble came back identical, so Retry looked like it did nothing.

That text only comes from one refusal: transport.gesture_required. The relay sends it when no key press or click of the person's own is behind a session/prompt. Posted input, such as a script or a computer-use agent, never counts (AgentPanePersonInput, cx-ocdz). The audit drove a release nightly with cua-driver over SSH, so every send and every Retry click was refused this way. The sr 502 didn't cause this bubble. A turn the harness fails ends as a failed turn with its own error and Retry. Retry itself was wired and resent the prompt: chat.retryPrompt sent a second session/prompt, and the relay refused it again.

Now:

  • The bubble gives the reason beside the message: "Not sent: it needs your own key press or click." Any other refusal shows the words the daemon or harness gave ("Not sent: upstream request failed (502)"), as before.
  • When a Retry is refused again, the bubble says "Still not sent: …", so the click visibly did something. A Retry that goes through sends the same prompt and the failure goes away.
  • prompt.notSentGesture is gone. The new prompt.gestureReason and prompt.stillNotSent strings have all 21 languages (en and ja are translated, the rest are needs_review, matching the neighboring strings).

A person can still hit this: a prompt queued behind a slow harness start goes out without a ticket, and the person's gesture only lasts 30 s. Retry (a real click) sends it. This PR doesn't change the host's gesture timing.

Testing

Failing tests first (ecaf13f, 6e1b040 and 83920d3), then the fix. Everything ran on big-red with bun 1.4.0.

  • bun test src/agent-session/acpmux/failedPromptRetry.test.ts: on the base, 2 of 4 fail. The bubble reads "Not sent. Press Retry to send it." in both the gesture test and the refused-Retry test. On the head, 4 of 4 pass. The daemon-reason and Retry-resends tests pass on both, which shows Retry was never unwired.
  • CMUX_BROWSER_TESTS=1 bun test test/agent-pane-not-sent-retry.test.ts (real Chromium, the pane's dev entry, a stand-in acpmux that refuses session/prompt as the relay does): on the base, 0 of 2 pass. It received "Not sent. Press Retry to send it.\nRetry" exactly as in the capture. On the head, 2 of 2 pass. Clicking the bubble's Retry sends a second session/prompt, the turn runs, and the failure detaches. A second refusal shows "Still not sent: …" and keeps Retry.
  • Also green on the head: heldPromptGesture.test.ts, test/strings-build-output.test.ts, the browser tests agent-pane-secret and agent-pane-queue, vp check typecheck (1606 files), and vp lint/vp fmt --check on the changed files.

Observed / Not run

  • Observed: the commands above, red on 83920d3 and green on ed34300. After merging feat-cmux-next (d0e417b), I ran both again on big-red: failedPromptRetry.test.ts passed 4 of 4 and agent-pane-not-sent-retry.test.ts passed 2 of 2.
  • CI doesn't run the webview tests on this PR yet. On ed34300, react-apps-check stopped at the markdown page eager JS budget (1408245 > 1400000; the base is over too). On d0e417b, it stops earlier, in bun run check: check:browser-lanes flags test/agent-pane-older-rows-fade.test.ts, which came from the base (#20633) and is red there too. Test webviews is skipped after either failure.
  • Also red on the base: nightly-guards / release-notary ("NEXT post-publication dependency contract") and cmux-next checks (l10n hard ceiling and other steps). This PR changes no Swift and no .xcstrings.
  • test/agent-pane-locale.test.ts fails 3 of 5 identically on the base commit (it needs a built pane), so it isn't attributable to this change.
  • Not run: the native app (no Swift build on this Mac). The Swift relay is unchanged, and the stand-in reproduces its refusal shape (data.code: transport.gesture_required).

Changelog

Fixed: A message that wasn't sent now says why beside it, and a Retry that fails again says "Still not sent"

Proof

The browser test asserts the bubble text and the Retry round trip. There's no native capture because the fix is webview-only.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes the failure bubble on prompts the relay refuses because no personal gesture backed them. It used to say only "Not sent. Press Retry to send it." with no reason, and a Retry refused the same way drew an identical bubble, so Retry looked dead.

Now the bubble names the reason: the relay's transport.gesture_required refusal reads "Not sent: it needs your own key press or click.", any other refusal keeps the daemon's or harness's words, and a refusal with no message falls back to a generic "refused" reason. A Retry sent again and refused shows "Still not sent: …" — the Retry marks its prompt id (or a fresh one for an older row) while it sends, so only the second refusal gets the "Still" wording. Retry still resends the same prompt and clears the bubble once the send goes through.

  • Adds prompt.gestureReason and prompt.stillNotSent across all 21 languages, keeping the catalog insert-only against base; prompt.notSentGesture stays, unused.
  • Adds unit and real-Chromium browser tests covering the reason text and both Retry outcomes.