# Conversation links (Enlaces a conversaciones)

A dashboard widget can open the conversations it counts: a KPI gets «Ver {count} conversaciones →» under its value, a chart by tag or line opens the conversations of the bar the person clicks. The person lands in Conversaciones with the tag, the line and the period of the dashboard already filtered, and «Volver» brings them back.

## When to link

- Link a **count of conversations** (a group): «Quejas», «Conversaciones por etiqueta», «Chats de la línea Ventas».
- Do **not** link a median, an average, a percentile or a rate: they are not a group of conversations. Say so and move on.
- Countable today (`countable`): a tag, a field option, a line, the period. Coming soon (`soon`, «pronto»): the agent, unanswered for more than 24 h, campaign replies. Say «viene pronto» and link nothing for them.

## The order: inspect, preview, set

1. `bi_inspect_dashboard_links(dashboard_id)` — the facts: per widget the KPI aggregate guess, the columns, the values of its last result and how they match tags, field options and lines, the current link and `reads_tag` (whether its SQL reads the tag). Per slot a role guess; `ambiguous` when two date slots fit one role — ask the person which one.
2. `bi_preview_conversation_links(dashboard_id, links)` — the dry run: per link `valid` or an error code, the count with the dashboard's values (`seeded` marks the default dates), `capped`, matched and unmatched values, and `warnings`.
3. `bi_set_conversation_links(dashboard_id, links)` with the **same** `links`. In the copilot one link the person named applies at once with «Deshacer»; anything else is one card.

A link: `{"v": 1, "mode": "group", "target": {"kind": "tag", "tag_ids": [7]} | {"kind": "field", "key": "motivo", "value": "queja"}, "follow": {"period": {"range": "<slot>"} | {"start": "<slot>", "end": "<slot>"} | null, "line": "<slot>" | null}, "label": "Ver {count} conversaciones →"}`, or `{"v": 1, "mode": "per_value", "column": "<column>", "source": "tag" | "line" | {"field": "<key>"}, "follow": {...}}`. `link: null` removes it.

## Label rules

The label holds `{count}` exactly once, at most 60 characters, plain text (no `<` or `>`). Omit it for the default «Ver {count} conversaciones →».

## Limits

- At most **12** linked widgets per dashboard (`link_cap_exceeded`).
- A count above **500** contacts is not opened: the button reads «Más de 500 — acota la línea o el período» (`capped`).
- Errors (closed list): `link_widget_unknown`, `link_mode_invalid`, `link_label_invalid`, `link_target_unknown`, `link_column_unknown`, `link_slot_unknown`, `link_slot_type`, `link_line_unresolved`, `link_cap_exceeded`, `link_version_conflict`.

## The three tag cases

1. **The tag exists and is filled** → link it.
2. **The tag is filled only recently** (`tag_recent`: «Cuenta desde el …») **or the widget counts another way** (`widget_other_rule`: its SQL does not read the tag) → warn in one sentence and propose: count from that date, or make the widget read the tag.
3. **The tag is missing** → read `get_skill('etiquetas-y-campos')` and, only when `can_create_field` is true, create it as a field on its own card first (Paso 1 de 2). One card per turn, and wait for the person's click between them: (1) the field; (2) with a tagger (`tagger`), add the field to its list; (3) only when the person asked to fill the past and `can_fill_past` is true, `agent_review_past`; (4) last, the link (`flow="after_new_tag"`). Never two of these cards in one turn (a second one is refused `one_card_per_turn`). When `can_create_field` is false, say why and link nothing for that widget. Without a tagger, the field is filled by people only.

Fill the past only when `can_fill_past` is true, with the tagger's own estimate and card (`agent_review_past`).
