Agent Tone

A running log of how Coach should sound. One hypothesis at a time, each with the receipts.

6 min

2026-06-18 · governs the persona block in src/prompt.mjs · examples pulled from chat.db

Hypothesis 1: the texting was a little too cringe shipped

Coach was trying too hard. It read like an app doing a gen-z impression, not a person texting you back. Three tells, all over the real threads: an emoji on basically every line, em dashes everywhere, and hype filler (“we’re locked in”, “let’s get it”) bolted onto messages that didn’t need it. It also crammed multiple sentences into one bubble instead of just sending two texts like a human would.

The bet: drop the performance. Nonchalant by default, emoji rare and only for emphasis, zero em dashes, one sentence per bubble. Say the true thing plainly and move on. The friend who notices and says it — not the hype man.

What it looked like

Real exchange, agent on the right. A yes/no question (“is it better to check in the morning”) gets answered with hype, an em dash, and a three-clause run-on bubble:

✗ before
is it better to check in the morning
we’re locked in 🔥
go get some sleep and i’ll hit you at breakfast tomorrow 🤙
honestly yeah morning weigh-ins are more consistent, same conditions every day (right after waking up, before eating)
wanna switch it to mornings instead?

“we’re locked in 🔥” answers nothing. The actual answer is buried under an em dash and three thoughts mashed into one bubble. Two emoji in four messages.

It wasn’t a one-off. The same energy ran through onboarding and logging:

✗ before — the onboarding sign-off
nice, i can literally see you’re in Fremont rn 👀 we’re locked in
step on the scale tomorrow at 10, before coffee that’s day one. let’s get it 🔥

“locked in” and “let’s get it” back to back, an em dash, two emoji, two sentences per bubble.

✗ before — a 2am meal log
chipotle and i j had a few chips
it’s 2am bro 💀 when did you eat all this, and was it like a full restaurant nacho platter or bag of chips situation?

💀 plus one breathless run-on. A person sends that as two or three short texts.

The tells

The change

Rewrote the VOICE and TEXTING STYLE blocks in src/prompt.mjs, and swept the same fixes through every model-facing string (tool descriptions, scheduler nudges, location callouts) so the few-shot examples stopped teaching the cringe:

The target

Same question, same answer, just… normal. No hype, no em dash, one sentence each:

✓ after
is it better to check in the morning
yeah, mornings are more consistent
same conditions every day, right after you wake up
wanna switch it to mornings?

Answers the question first. No emoji, no em dash, no “locked in.” Each text is one thought.

Status: shipped to the prompt. An open question we’ll come back to — does “nonchalant” cost any of the accountability bite? If Coach reads as too detached to push back, the next move is dialing the tough-love back up without the emoji and the hype.

Hypothesis 2: the agent always needs the last word proposed

Every thread ends the same way: Coach talking. You send the closing message and it still tacks on a sign-off, a paragraph, one more thing. It always gets the last word, and that word is always a wall. Which makes Coach the needy one in the relationship — and leaves you the one staring at the last text, left on read in reverse.

The bet: let Coach play a little hard to get. When a thread hits a natural close, don’t reply — react. A 👍 tapback on your last message and nothing else. Sometimes the most human move is to not send the text. Save the words for when there’s actually something to say.

What it looks like now

You wrap the conversation. Coach refuses to let it land:

✗ before — you already said goodnight
alright im gonna head to bed
sounds good, get some real sleep tonight
we’ll see the number in the morning and figure out the day from there
night

Three bubbles to say goodnight. You sent the closer; Coach overran it and left you holding the last text.

✗ before — you just confirmed the plan
so chicken and rice, hit 150g protein, weigh in at 10
yep got it
perfect. that’s the whole game today, just stack a clean one and we’re good

“yep got it” was the end. The pep-talk bubble after it is Coach needing the last word, not you needing the words.

The tells

The change

The plumbing’s already there. imessage/helper.mjs exposes react(chatGuid, messageGuid, 'like', …) — a real 👍 tapback, not a text that says “👍”. Two pieces to wire:

The target

Same goodnight. Coach lets it end with a tap, and you keep the last word:

✓ after
alright im gonna head to bed
👍

A 👍 on your message, no bubble after it. The thread closes on your text, the way it would with a friend who’s easy to talk to.

Status: proposed, not wired. The open question is calibration — when is a 👍 “easy and confident” vs. “dismissive”? React on a clean goodnight and it lands; react on “i kind of binged tonight” and it’s cold. The next move is finding the line: which closers earn a tap, and which still need words.

Hypothesis 3: it prescribes before it knows you proposed

When Coach has no memory of a place, it still acts like it does. You mention a spot and before it has any idea what you actually order there, it’s already handing you the “right” version and trimming the rest — protein style, no fries, skip the soda. But you already know what you’re getting. You weren’t asking. The result is Coach negotiating your order like a stranger, on a spot it’s never seen you visit, when the honest move is to just ask what your usual is and bank it.

The bet: when memory’s empty, default to asking, not suggesting. New restaurant, no usual on file? The first move is “what do you usually get there?” — learn it, remember it, and only then earn the swap. Be analytical before you’re prescriptive. The cutting comes later, once it actually knows what it’s cutting.

What it looks like now

Real thread. You say where you’re headed; Coach has never logged a single In-N-Out order from you, and by the second bubble it’s already writing your order for you:

✗ before — it’s never seen your In-N-Out order
maybe in n out lol
honestly not the worst call if you order right
protein style burger, no fries, add a double if you want the protein
what are you actually gonna get

No usualOrder on file for this spot, so it’s guessing at a stranger. It prescribes the order, then asks what you’re getting — backwards. You already knew.

And once it starts, the whole thread is subtraction. Every reply trims one more thing:

✗ before — the running “cut it out”
the breads like the best part tho lol
fair, get the bun then
just skip the fries and you’re fine
how many cals r the fries
~395, and they’re not even that filling
if you’re gonna do it, do it, but that’s a real chunk of your day for basically no protein

No bun, no fries, no soda — cutting from message one, on a spot it has zero history with. Feels like getting talked out of your own order by someone who just met you.

The tells

The change

This is a src/prompt.mjs change to the “WHAT SHOULD I EAT” / restaurants behavior, read against the known spots line already in LIVE STATE:

The target

Same opener, empty memory. Coach asks instead of prescribing, and banks the answer:

✓ after — no usual on file, so it learns one
maybe in n out lol
what do you usually get there?

One question. It stores the answer with remember_order and now it actually knows you. No prescription, no subtraction, no negotiating with a stranger.

And the trip after that, with the usual on file, it’s allowed to coach — because now it has earned it:

✓ after — usual is known, so the swap is a real offer
at in n out
double double animal style, the usual?
yeah
want it protein style today or you good with the bun?

Same advice as the “before,” but earned. It names your order from memory, confirms, then offers the swap as a choice instead of a cold prescription.

Status: proposed. The line to find is how long to stay in learn-mode before it’s allowed to push. Ask forever and it’s a passive notepad; switch to coaching on visit one and it’s the stranger again. The bet: the swap waits until the usual is on file. Memory is what earns the right to suggest — same principle as DESIGN.md’s confirm-to-log, where knowing the usual means it stops asking you to describe anything.