← back

The Fix That Would Have Hidden the Feature

#ai-product-management#ux-design#conversation-design#mobile#cost-engineering

Picking where a chat control lives by asking which state the user is actually in, and what the smaller fix would have quietly removed.

Two nights ago I wrote a note to myself that a button was taking up too much of the screen. Today I went to fix it, and the fix I had in mind would have made the button disappear for the whole conversation. It is a small interface decision, but the reasoning generalizes, so here it is.

A button in its own row

Luna has a control that says "Talk about something else." It sits with the conversation controls rather than attached to any one reply, because a standing choice belongs with the controls and a suggestion chip vanishes as the conversation moves on. I made that call last week. What I noticed on the second night of using it was the cost. It had a row to itself, above the row that holds "Wrap up tonight" and "Save," and on a phone with the keyboard open that row is a real share of what is left of the thread. Bedtime journaling happens on a phone with the keyboard open. So the note said: hide it while the keyboard is up, or fold it into the row below.

The first option looked like the obvious one. One line of code, and the same rule the tab bar already follows, since the tab bar hides while the text box has focus. I had it half-typed in my head.

Which state does the user actually live in

Then I checked what "while the text box has focus" means on a phone in this app. When you send a message, the send button is built so the keyboard stays open. The chips are built the same way. The reason is bedtime, because nobody wants the keyboard sliding away between every exchange. So from the first tap until you deliberately put the keyboard down, the text box is focused. That is the whole conversation.

Hide the button while the text box is focused, and you hide it for the whole conversation. It would come back when you closed the keyboard, which most people do once, at the end. The control was built so you could change the subject at any moment with one tap. The small fix kept the pixels and removed the function, and nothing in a test would have said so, because the button was still there in every state a test can name. The only way to see it was to ask which state a real person spends the night in.

So the row below got a third item, a square with a shuffle icon at the start of the wrap-up and save row. It costs no height, because that row is already there whenever the button would be. It stays reachable while you type. The price is the words. Three labelled buttons do not fit across a phone, so the button is an icon with its name carried where screen readers and desktop hover can find it, and the first tap teaches it. I named that price out loud before choosing. A choice whose cost you have not said is a choice you will argue with yourself about later.

I also considered putting the icon inside the text box, next to where you type. Zero height, always reachable. I rejected it for a reason that only shows up on a phone at night: a mis-tap next to the typing target fires a real request and changes the subject on you. The action row is a thumb's distance from the text, and that is the right distance for a control that redirects the conversation.

Six checks and a two-word answer

I tested it on my phone from a preview build, and I wrote the checklist before I opened the build. Each check had what to do, what I should see, and what a fail would look like. Version number first, so every later observation is of the build under test. Then the change itself in the state that motivated it, keyboard up. Then the neighbours it touched. Then one thing that must not have changed, because I have learned I read absence badly unless I go looking for it. Six checks. The reply was "All pass."

I used to write these checklists from memory after the fact, which is how the same mistakes bit me more than once (a wrong preview address, twice, and a login loop that turned out to be a browser mode). This time I wrote the checklist as a reusable procedure while the build was compiling, and then corrected it from what the run taught. The procedure written during the run is the honest one. The one written from memory is the flattering one.

Cheaper turns, without touching the price

One more reading today, from the usage logs. Each Luna turn is now about a third smaller in prompt size than it was four days ago. Nothing about pricing changed. What changed is how much journal history the model is handed on each turn, which I trimmed last week for a different reason, because the questions were repeating. The cost followed the design. Also, the vendor's automatic caching still does nothing for us at this scale, and the cut-off replies I chased last week are now rare since the ceiling moved.

What I am carrying forward is the per-turn number, not the total. Most of this week's volume was my own test suite, hundreds of calls that no user made. The total looks alarming and means nothing. The per-turn figure is the one that predicts what a hundred users cost.

Mental Models

Find the state the user lives in before you hide anything in one of them. A rule like "hide while focused" is only as good as your knowledge of when focus actually holds. In this app it holds for the entire conversation, so the rule was a deletion wearing a fix's clothes.

Say the cost of the option you pick. The icon-only button trades words for reach. Naming the trade before choosing meant the choice was mine, and not something I discovered later in a support message.

Write the checklist during the run. A procedure captured while the steps are happening keeps the gotchas that memory would smooth over. It also gets corrected by the run itself, which memory never is.

Carry the per-unit number. Totals include your own testing and whatever else happened this week. The cost per turn is what a real user costs, and it is the number a design change moves.