# Runbook — the agent repeats itself, or opens with the wrong message

> **Symptom.** *"repite el mismo mensaje de bienvenida"* · *"manda el saludo dos veces"* · *"me
> saluda de nuevo a alguien que ya venía hablando"* · *"el opener no es el que yo mando"* · *"it
> greets everyone with the same robotic line"*.

## Split the symptom first

Two different bugs hide under one complaint, and they have nothing in common:

- **Same opener sent twice** to one lead → hypotheses 1–3.
- **The opener is wrong, stale, or robotic** for everyone → hypotheses 4–6.

Ask which one, or read two affected conversations. Guessing here costs the whole session.

---

## The same opener fires twice

### H1 — The opener was never recorded as sent

A once-per-conversation guard only works if the send is recorded. If the opener goes out through a
path that does not write that record, the guard has nothing to check and fires again on the next
inbound. In our experience the guards themselves are rarely broken — the recording is.

- **Test:** `get_conversations` for an affected conversation, then find the first opener and check
  whether it carries a trigger attribution.
- **Stop condition:** the opener has no trigger attribution but is clearly the trigger's copy →
  **you're done**.
- **Fix:** platform-side. File it with the conversation id rather than adding a prompt rule; a rule
  telling the agent "don't greet twice" cannot work, because the agent has no memory of the
  unrecorded send.

### H2 — The label and the slug do not match

When the trigger's human label and its internal identifier drift apart, the lookup that should
recognize "this trigger already ran" misses, and the trigger is free to run again.

- **Test:** `list_triggers` and compare each trigger's display label against its identifier.
- **Stop condition:** a mismatch on the trigger that produced the repeated opener → **you're done**.
- **Fix:** `update_trigger` so they agree, then re-test with a fresh conversation.

### H3 — Two triggers own the same entry point

A keyword and a catch-all, or two keywords that both match the lead's first message, will each
produce an opener. This looks like a duplicate but is two correct sends.

- **Test:** `list_triggers`, then check the lead's first message against every registered keyword —
  including partial and accent-normalized matches.
- **Stop condition:** two triggers match the same message → **you're done**.
- **Fix:** narrow one keyword or remove it. Note that a fired keyword always delivers its literal
  resource first, so the overlap is not resolved by ordering rules in the prompt.

---

## The opener is wrong for everyone

### H4 — `keywords.json` was not updated with `system.md`

**The most common cause of "the opener isn't what I asked for".** The opening message lives in the
trigger config, not only in the prompt. A deploy that edits the prompt's greeting and leaves the
trigger's literal response untouched changes nothing the lead actually sees on first contact.

- **Test:** read the trigger's literal response and compare it to the prompt's opener.
- **Stop condition:** they disagree → **you're done**, the trigger wins.
- **Fix:** `sync_keyword_triggers` after updating the keyword config. Treat the two as one change:
  never ship an opener edit that touches only one of them.

*Evidence: 2026-05-18 — a deploy introduced a new narrative opener in `system.md` and left
`keywords.json` untouched, so the change was invisible in production. A wellness-retreat agent — the
prompt's welcome message did not match what the creator actually sends to new followers, and the
agent was not triggering on the replies at all because the platform keywords were not aligned.*

### H5 — The copy is duplicated across files and went stale in some of them

When the same offer or framing is written into the prompt, the examples, the objections, the
keyword responses and the JSON data files, an update lands in some and not others. The agent then
mixes current and stale copy in the same conversation, which reads to the operator as "it says
different things every time".

- **Test:** search the agent's SDK for the stale phrase across **all** files, not just the prompt.
- **Stop condition:** the phrase appears in more than one file with different wording → **you're
  done**.
- **Fix:** update every occurrence in one change, then reduce the duplication so the next update has
  one place to land. Instruction duplication across files is the defect; style duplication is fine.

*Evidence: a wellness-retreat agent, 2026-07-01 and 2026-07-15 — a live-event framing shipped as
temporary, duplicated across six SDK files, and went stale in all six at once. Two separate fixes,
same cause.*

### H6 — A fixed phrase is hardcoded as the default

An empathy token or greeting written into the prompt as *the* opener will be repeated verbatim
forever. Replacing one hardcoded phrase with another hardcoded phrase does not fix this — it moves
the robotic feeling one phrase to the right.

- **Test:** grep the prompt for the repeated phrase. If it appears as an instruction rather than
  inside an example, that is the cause.
- **Fix:** delete the instruction and demonstrate the register in several examples instead. Variety
  comes from examples; a rule cannot produce it.

*Evidence: a personal-brand agent — a fixed empathetic opener hardcoded in four places, then a
mirroring rule that made the agent echo the lead word for word. Three versions before the fix moved
from rules to examples.*

---

## When to stop

If the opener is correct in the trigger, sent once, and still feels wrong to the operator, this is
not a defect — it is a copy disagreement. Take the wording they want and change the trigger's
literal response. Do not open a ticket for it.

Otherwise put in `report_feedback`: the conversation id, the trigger involved, both openers the lead
received, and which of H1–H3 you ruled out.
