Skip to content

fix(control): stop offering back the prompts nobody reuses, and the clamp that never clamped - #482

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/control-noise
Sep 4, 2026
Merged

fix(control): stop offering back the prompts nobody reuses, and the clamp that never clamped#482
github-actions[bot] merged 1 commit into
mainfrom
fix/control-noise

Conversation

@catomean

@catomean catomean commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Found by using /control in 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:

distinct custom prompts 1859
sent exactly once 1823 (98%)
ever reused 36

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.

2. The clamp I shipped in #479 was inert, and I could not see it by eye

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 — fine until the sentence is long.

Checked live: a 460-character next_step grew 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-audit now flags a 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)

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:

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 from when grok was the default, in a message that already had agent in 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.json and claude setup-token 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.

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 catches src/db/queries/ — data access, not the shape of the data. So this branch, which edits one SELECT, 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

…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
github-actions Bot merged commit 5e4192e into main Sep 4, 2026
3 checks passed
@github-actions
github-actions Bot deleted the fix/control-noise branch September 4, 2026 12:20
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>
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