# Runbook — the keyword doesn't trigger (or triggers on the wrong thing)

> **Symptom.** *"la keyword no dispara cuando comentan"* · *"puse la palabra y no pasa nada"* ·
> *"the keyword trigger does not fire on comments"* · *"dispara con cualquier cosa"*.

## What you need first

- The exact keyword, and the exact message the lead sent — verbatim, including punctuation.
- Whether it was a **DM** or a **comment**. They behave differently and the operator rarely says.

---

### H1 — The keyword is not registered

Obvious and still first, because it is the answer more often than anything else. Operators publish a
CTA and assume the word is live.

- **Test:** `list_triggers` and compare against the CTA the operator published.
- **Stop condition:** the word is not there → **you're done**.
- **Fix:** deploy the keyword set with `deploy_agent_sdk` — it writes the config and re-syncs the
  live triggers in one call, so the two cannot drift apart. `sync_keyword_triggers` is the surgical
  alternative when keywords are genuinely the only thing you are touching. Either way the write
  **deletes** any keyword absent from what you send, so send the complete set, never a partial one.

*Evidence: a post whose CTA read "Comenta YO" with `yo` never registered. The lead commented exactly
as instructed, the agent replied with a dead-end social message, and the conversation was lost.*

### H2 — The lead did not use the keyword at all

Leads arriving from an ad or a DM campaign write "quiero más información" or "me interesa". In
keyword-only mode that is correctly ignored, and to the operator it looks broken.

- **Test:** read the opening messages of ten recent conversations. If most contain no keyword, this
  is a mode question, not a keyword bug.
- **Stop condition:** leads are not using keywords → **you're done**.
- **Fix:** decide with the operator whether the agent should answer all DMs. That changes volume, so
  it is their call, not yours.

### H3 — It fired, but the reply was not what the operator expected

The opening message on a keyword lives in the **trigger config**, not the prompt. If they disagree,
the trigger wins, and the operator concludes the keyword "didn't work" when it did.

- **Test:** read the trigger's literal response and compare it to what the lead received.
- **Stop condition:** they match, and the operator dislikes the copy → **you're done**, this is a
  copy change, not a defect.
- **Fix:** update the trigger. See `repeated-or-wrong-opener.md` H4 — a prompt-only edit changes
  nothing here.

### H4 — Comments and DMs are not the same surface

A trigger can work in DMs and not on comments, and some content simply cannot be delivered as the
first message on a comment-triggered conversation — audio openers in particular. A keyword whose
literal response is empty because the audio is meant to be the opener will produce silence on the
comment path.

- **Test:** `list_triggers` and check the surface. Then check whether the keyword's opener depends
  on a media type.
- **Stop condition:** the keyword has no text opener and the path cannot deliver the media →
  **you're done**.
- **Fix:** give the comment-triggered variant a text opener of its own.

*Evidence: a creator's DM agent used audio as the opener with an empty literal response; the
comment-triggered mirror could not deliver audio first and needed its own text opener.*

### H5 — Typo tolerance on a short keyword

Fuzzy matching on a two- or three-letter keyword matches far more than intended. This presents as
the opposite symptom — the trigger firing constantly on unrelated messages — and it is much harder
to see on comments than in DMs.

- **Test:** check whether typo tolerance is enabled, and how short the keyword is.
- **Stop condition:** tolerance on a very short keyword → **you're done**.
- **Fix:** disable tolerance on short keywords, or lengthen them.

*Evidence: typo tolerance on a very short keyword; DM misfires were rare enough to look acceptable,
while the bulk of the damage was on comments under a high-view video where nobody was counting.*

### H6 — Two triggers claim the same message

Overlapping keywords, or a keyword plus a catch-all, produce ambiguity. A routing map written before
a newer campaign keyword existed will keep claiming messages the new one should own.

- **Test:** run the lead's message against every registered keyword, not just the one in question.
- **Stop condition:** more than one matches → **you're done**.
- **Fix:** remove the overlap rather than adding a tiebreak rule in the prompt. A fired keyword
  always delivers its own literal resource first, so prompt-level ordering cannot resolve it.

*Evidence: a topic map authored before a campaign keyword went live; the overlap sent an unrelated
resource with no context until the ambiguous route was deleted outright.*

### H7 — The sync never landed

Keyword edits reach production through a sync that can fail without surfacing an error, so everyone
keeps believing the last known state.

- **Test:** re-run the sync for this agent and read its output, not its exit status. Then confirm
  against `list_triggers`.
- **Stop condition:** the live set differs from the source file → **you're done**.
- **Fix:** re-sync. Treat a silently failing sync as a defect of its own and report it.

*Evidence: a keyword sync failing silently for days, found only when a fix from four days earlier
was verified in production and turned out never to have shipped.*

### H8 — Stale flags on the keyword itself

Keyword entries carry flags that outlive the decision that set them — a qualification bypass from an
older standing instruction, for example. The keyword fires correctly and then does the wrong thing,
which the operator describes as the keyword being broken.

- **Test:** read the full keyword entry, not just its trigger word and response.
- **Fix:** remove the stale flag, and check whether the prompt echoes it — these usually exist in
  both places.

*Evidence: two keywords carrying a qualification bypass from an instruction superseded months
earlier, with the prompt still echoing the bypass in two sections.*

---

## When to stop

If the keyword is registered, live, unambiguous, matched the message, and still produced nothing,
capture it: the trigger, the exact inbound text, the surface (DM or comment), and the conversation
id. Send that with `report_feedback`.
