Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/components/control/project-intent-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,15 @@ export function IntentButtonPanel({
</button>

{/* Secondary intents: compact chips + More toggle.
On phones, these three (Test & fix / Quality / Commit) plus the
primary CTA above plus composer/mic/send below put 8-10 tap
targets on screen before scrolling past a single card — the
"wall of buttons" the mobile UX audit named as this surface's
worst offender. Hidden under `sm` until "More" is tapped, same
as MORE_INTENTS already was; one extra tap trades for a card
that opens with ONE clear action instead of ten. Unchanged at
sm and up, where the room to show them was never the problem. */}
This row used to carry Test & fix / Quality / Commit, hidden under
`sm` but always shown above it — "the room to show them was never
the problem" at desktop widths. Room was not the problem; ATTENTION
was. Counted over all of orchestration_runs, those three have one
real dispatch between them ever, against next_best's 185, so they
now live in `more` (see config/control-intents.ts for the counts).
ACTION_INTENTS is consequently empty and this map renders nothing —
the group is kept, not deleted, because the fix is a config change
and reassigning an intent back is one word. */}
<div className="flex flex-wrap gap-1.5">
{ACTION_INTENTS.map(({ id, label }) => (
<button
Expand Down
32 changes: 29 additions & 3 deletions src/config/control-intents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,38 @@ type IntentGroup = "primary" | "action" | "more";
// UI presentation config for control-panel intent buttons.
// Only intents shown as group buttons are listed here.
// Keys must exist in ORCHESTRATION_INTENTS — TypeScript enforces this.
//
// WHICH GROUP AN INTENT BELONGS IN IS A USAGE QUESTION, AND WE HAVE THE USAGE.
//
// Counted over the whole history of orchestration_runs (2026-09-04):
//
// next_best 185 runs <- primary, and it earns it
// custom 99 runs
// test_and_fix 3 runs <- of which 2 are seed rows
// quality 1 run <- a seed row
// commit_push 0 runs <- never once, by anyone
//
// The three "action" chips sat directly under the primary CTA on every project
// card at every width above `sm`. Between them they have ONE real dispatch in
// the product's entire history — Bitbaum/test_and_fix on 2026-07-31, which
// ended `error/timeout`. The other five rows were inserted at 04:20:02.844
// through .850 — five rows inside six milliseconds, on projects (ledgerpost,
// harbourlight, kestrel) that are not in the fleet. That is a seed script, not
// a person.
//
// So they move to `more`, behind the disclosure that already exists and already
// hid them below `sm`. Nothing is removed: every intent is still one tap away,
// and a rarely-used action one tap deeper is a fair trade for a card that opens
// with the one action that is actually used. This is what CLAUDE.md asks of
// this surface — "at most one button" — applied with numbers instead of taste.
//
// If usage changes, move them back. The counts are the argument, not the layout.
const INTENT_UI: Partial<Record<OrchestrationTaskIntentId, { label: string; group: IntentGroup }>> =
{
next_best: { label: "Next best", group: "primary" },
test_and_fix: { label: "Test & fix", group: "action" },
quality: { label: "Quality", group: "action" },
commit_push: { label: "Commit", group: "action" },
test_and_fix: { label: "Test & fix", group: "more" },
quality: { label: "Quality", group: "more" },
commit_push: { label: "Commit", group: "more" },
full_audit: { label: "Full audit", group: "more" },
product: { label: "Product review", group: "more" },
ux_review: { label: "UX review", group: "more" },
Expand Down
Loading