fix(control): stop offering back the prompts nobody reuses, and the clamp that never clamped - #482
Merged
Merged
Conversation
…lamp that never clamped Found by using /control in a browser as a user, on a phone-width window, start to finish: pick a project, type a prompt, send it, follow the outcome. 1. "PASTE FROM HISTORY" WAS SHOWING THE OPPOSITE OF WHAT IT PROMISES The card offered back conversational asides — "you do it. open in browser and do it" — as reusable work. Not a rendering bug: those really were dispatches. The selection was wrong. Measured on prod, 30 days: 1859 distinct custom prompts, of which 1823 (98%) were sent exactly ONCE. The query ordered by recency, so it surfaced the three most recent one-offs — by definition the prompts least likely to be wanted again — while the genuinely reused ones sat unoffered: "continue" 115×, a git-diff review prompt 63×, "go ahead" 14×, "keep going" 8×. Requiring two uses cuts the pool from 1859 to 36 and leaves exactly what the feature is named for. Nothing is lost: Activity keeps every dispatch with a re-run control. One-offs just stop competing for space on the card. 2. THE CLAMP I SHIPPED IN #479 WAS INERT, AND I COULD NOT SEE IT `block ... line-clamp-2` computes to `display: block; -webkit-line-clamp: 2`. `-webkit-line-clamp` does nothing without `display: -webkit-box`, and Tailwind's `block` utility overrode it. It LOOKED fixed because an unclamped box just wraps — which reads fine until the sentence is long. Checked live in the browser: a 460-character next_step grew that box from 48px to 96px and pushed the composer, the thing you act with, off the bottom of the viewport. So my "it renders fully now" was measuring the ABSENCE of a clamp. Dropping `block` makes it work; three lines, not two, so a typical next_step (~256 chars) still reads in full and a pathological one is bounded and reachable (clicking loads the whole text into the composer). THE GATE: responsive-audit now flags a line-clamp that is declared but not enforced. It cannot test `display === "-webkit-box"` — Chrome reported `flow-root` for a clamp working correctly — so it tests BEHAVIOUR: an element declaring N lines that renders more than N. Run against live prod it says: "Suggested next (profile)…"[ui-link-subtle-button.block.line-clamp-2] declared 2 lines, renders 4 (display:block) naming the offending class. That is this exact bug, caught in shipped code. 3. A FAILURE MESSAGE THAT NAMED THE WRONG AGENT A real dispatch returned: "launched claude (pty) + injected, but the agent isn't generating yet … Retry, or switch the project agent away from grok if this repeats." One sentence naming the agent that ran and, as advice, one that did not — a literal left over from when grok was the default. `agent` was in scope; the auth branch above it interpolates it correctly. Gated in failure-message-pairing: a failure message that NAMES an agent must interpolate ${agent}. Stated that way rather than "no agent ids", because the auth message legitimately says `~/.claude/.credentials.json` and `claude setup-token` — a real path and a real command — and already names the right agent. The check joins `+` continuations so a message split across two lines is judged whole; per-line it got that one wrong. Mutation-proven: restoring the "grok" literal → 20 passed, 1 failed, naming the line. Restored → 21/21. pnpm run verify passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P
github-actions Bot
pushed a commit
that referenced
this pull request
Sep 4, 2026
Today a dependabot bump turned main red and stopped the merge queue. The gate was right; its TIMING was racy, and the race is the whole bug. What happened, in order: #482 merges, mints fleet-runner-v0.8.16. #411 (dependabot, touches ONLY desktop/package-lock.json) had already run its CI while v0.8.15 was newest. Against v0.8.15 its base ALREADY read 0.8.16, so "version is ahead of released" held — CI PASSED. #411 merges. Newest tag is now v0.8.16, the only desktop change since it is that lockfile, and the identical check goes RED on main. Both evaluations were correct. The gap is between them, and everyone else pays: the sweep merges nothing onto a red base, so every unrelated PR stalls behind a bot's dependency bump. #483 failed CI on a diff that touched no desktop file. The existing question is cumulative — "has anything changed since the last tag" — and its answer moves when a tag is minted, i.e. for reasons outside the branch. So ask a second question that has no race in it: does the diff of THIS BRANCH touch desktop/, and if so does THIS BRANCH bump desktop/package.json? Decided entirely within the branch, identically before and after any tag appears. It fires on the PR — one blocked bot PR a human can see — instead of after the merge, where it is a red main and a stopped queue. On main itself the branch diff is empty and the rule does not apply; the cumulative check still guards there. Mutation-proven: desktop change, no bump -> exit 1, "this branch changes desktop/ but does not bump desktop/package.json", naming the file + bump, no changelog -> exit 1 on the pre-existing changelog rule (the two compose; a bump alone is still not publishable) neither -> passes, rule correctly silent This is the fix I would rather have than the automation I had offered — opening a bump PR after the fact repairs the outage; this prevents it. pnpm run verify passes. Claude-Session: https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P Co-authored-by: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found by using
/controlin a browser as a user, at phone width, start to finish: pick a project, type a prompt, send it, follow the outcome.1. "Paste from history" was showing the opposite of what it promises
The card offered back conversational asides — "you do it. open in browser and do it" — as reusable work. Not a rendering bug: those really were dispatches. The selection was wrong.
Measured on prod, 30 days:
The query ordered by recency, so it surfaced the three most recent one-offs — by definition the prompts least likely to be wanted again — while the genuinely reused ones sat unoffered:
continue115×, a git-diff review prompt 63×,go ahead14×,keep going8×.Requiring two uses cuts the pool from 1859 to 36 and leaves exactly what the feature is named for. Nothing is lost — Activity keeps every dispatch with a re-run control.
2. The clamp I shipped in #479 was inert, and I could not see it by eye
block ... line-clamp-2computes todisplay: block; -webkit-line-clamp: 2.-webkit-line-clampdoes nothing withoutdisplay: -webkit-box, and Tailwind'sblockutility overrode it. It looked fixed because an unclamped box just wraps — fine until the sentence is long.Checked live: a 460-character
next_stepgrew the box 48px → 96px and pushed the composer, the thing you act with, off the bottom of the viewport. My "it renders fully now" was measuring the absence of a clamp.The gate.
responsive-auditnow flags a clamp that is declared but not enforced. It cannot testdisplay === "-webkit-box"— Chrome reportedflow-rootfor a clamp working correctly — so it tests behaviour: an element declaring N lines that renders more than N. Run against live prod it says:That is this exact bug, caught in shipped code, naming the offending class.
3. A failure message that named the wrong agent
A real dispatch returned:
One sentence naming the agent that ran and, as advice, one that did not — a literal left from when grok was the default, in a message that already had
agentin scope.Gated: a failure message that names an agent must interpolate
${agent}. Stated that way rather than "no agent ids", because the auth message legitimately says~/.claude/.credentials.jsonandclaude setup-tokenand already names the right agent. The check joins+continuations so a message split across two lines is judged whole — per-line it got that one wrong.Mutation-proven: restoring the
grokliteral → 20 passed, 1 failed, naming the line. Restored → 21/21.4. A pre-push guard that blocked on the wrong directory
The schema-drift guard is deliberately scoped: "if this branch changes schema, a mismatch is yours and blocks; if not, report and move on." But it matched
^src/db/, which also catchessrc/db/queries/— data access, not the shape of the data. So this branch, which edits oneSELECT, was treated as schema-touching and a stale local scratch DB blocked the push. Narrowed to^(src/db/schema/|drizzle/); it now correctly reports the mismatch as ambient.Verification
pnpm run verify— exit 0. Desktop version bumped to 0.8.16 with a changelog entry (the release-drift gate caught the missing bump, correctly).🤖 Generated with Claude Code
https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P