# Combot Automation Compact v3: LLM instructions and format reference

Give this entire file to a model together with your rule request. It describes the format imported by the Automation editor. The rule language is Compact v3 JSON, not Python, YAML, JavaScript, older Triggers v2, or arbitrary “if → then” pseudocode.
Interface labels are localized; JSON values are not. For example, `Known Combot links` corresponds to “Combot invite links” in the editor, and `Combot custom admins` to “Combot admins”. Never replace these JSON values with interface labels.

Document revision: September 9, 2026. The reference was checked against the Automation editor and handler. Completed import and Telegram checks are listed separately at the end: inspecting source code does not establish the version of a running service.

## 1. Instructions for the model

Translate the person's request into an Automation rule without changing its meaning.

1. Establish the event, conditions, exclusions, action, action recipient, and reply destination. For a schedule, establish the time zone; for a command, establish whether it is sent on its own or as a reply. Do not add moderation, rewards, randomness, or deletion unless requested.
2. Do not guess user IDs, topics, chat addresses, invitations, ranks, or enabled features. A topic name is not its ID. Ask questions when essential information is missing. You may suggest assumptions about reply wording separately, but must not present an incomplete rule as an exact solution.
3. Always generate new rules with `en: false`. This is a safe preparation convention, not an Automation limitation. Omitting `en` means enabled.
4. Use only documented fields. For nested filters marked “use an export,” ask for a real export of a similar setup; do not invent the structure. If a scenario is unsupported, explain the limitation rather than hiding it by removing conditions.
5. Respond with a brief explanation → one importable JSON block → manual settings and checks. JSON must contain no comments, ellipses, placeholder IDs, explanatory lines outside fields, trailing commas, or invented keys such as `when`, `if`, `then`, `conditions`, `actions`, or `event`.
6. Before returning the result, reason through one matching and one nonmatching case. For a reply command, check the command sender separately from the original message's author. Do not claim to have tested a rule in Combot unless that actually happened.

The accompanying JSON Schema is a generation aid for new disabled rules. Its required fields are intentionally stricter than import, and it does not accept every older export. Complex nested filters are checked only as objects. Passing the schema does not prove condition compatibility, resource availability, plan compliance, or successful Telegram actions.

## 2. What to paste into Import

The root is an object with numeric `v: 3` and a rule array `t`. Usually return one rule. Do not return a bare array or `{"triggers": [...]}`: those are not the import dialog's format.

A complete minimal example:

```json
{
  "v": 3,
  "t": [
    {
      "n": "Course information command",
      "en": false,
      "k": "c",
      "ctm": "p",
      "cm": ["/course_info"],
      "a": {
        "m": "a",
        "r": [
          {
            "i": "course_reply",
            "t": "s",
            "v": {
              "tx": "Class recordings are in the chat's pinned message.",
              "rp": "r"
            }
          }
        ]
      }
    }
  ]
}
```

`dv: "t3.compact.3"` is an optional schema marker. New results only need `v` and `t`. Existing import recognizes a `bundle` wrapper, but generation does not need it.

Do not add a server `id`, `revision`, `chat_id`, computed `ck`, or `$schema` inside the import bundle. The server assigns a new rule ID. The action's `i` is different: it is a local row identifier such as `course_reply`. It must be unique within that rule's actions and contain 1–80 Latin letters, digits, `_`, or `-`.

The same short key means different things at different levels: root `t` is the rule list, `a.r[].t` is the action type, and `a.r[].v` holds action parameters. Do not mix levels.

## 3. Main rule fields

| Field | Meaning |
| --- | --- |
| `n` | Nonempty name, up to 80 characters in the editor |
| `d` | Optional description, up to 280 characters in the editor |
| `en` | `false` for a new disabled rule |
| `e` | Event-code array, unless this is a command |
| `k`, `cm`, `ctm` | Command mode; see below |
| `a` | Action plan: `{"m":"a","r":[...]}` or `{"m":"r","r":[...]}` |
| `s`, `ti` | Where to check the event: source chat, General, or selected topics |
| `at` | When matching is allowed; a time filter, not a timer |
| `wh`, `wx`, `wm` | Event actor: included groups, exclusions, and how to combine groups |
| `cgr` | Numeric conditions: time in chat, messages, warnings, XP, reputation |
| `ua`, `ux`, `uar`, `ul`, `ulx` | Member attributes, field values, and language |
| `twh`, `twx`, `tcg`, `tua`, `tux`, `tur`, `tlg`, `tlx` | Checks for the event's other user, such as the replied-to author or joining member |
| `tv`, `tr`, `ty`, `cs`, `lmin`, `lmax` | Text conditions |
| `mti`, `mtx`, `me`, `mex` | Message types and text entities |
| `il`, `ilc`, `ilx` | Invitation links for join requests |
| `rct`, `rnt`, `rnx`, `rcy`, `rcx`, `rcn`, `rcm` | Reaction conditions |
| `chl` | Direct link to a particular checklist for its task-change events |
| `cl` | Default previous-reply cleanup: `n` or `ps` |
| `ov` | OR alternative condition sets; use an export for complex cases |

Do not fill every field. Add only requested conditions relevant to the chosen event. Joining does not need message keywords, for example, and an aggregate reaction counter has no specific user to reward automatically.

## 4. Events

| `e` | When it runs |
| --- | --- |
| `["m"]` | New chat message |
| `["em"]` | Message edit |
| `["m","em"]` | New message or edit |
| `["lc"]`, `["el"]`, `["lc","el"]` | Linked-channel comment, edit, or both |
| `["cp"]`, `["ec"]`, `["cp","ec"]` | Post in the linked-channel context, edit, or both |
| `["jr"]` | Join request |
| `["nm"]` | Member joined |
| `["ml"]` | Member left or was removed |
| `["cb"]`, `["rb"]` | Boost added or removed |
| `["mr"]` | A specific user's reactions changed |
| `["rc"]` | Aggregate anonymous reaction counters updated in the linked-channel context |
| `["ck"]` | New checklist |
| `["cd"]` | Checklist tasks marked complete |
| `["ca"]` | Checklist tasks added |

Do not combine unrelated event types such as `["m","nm"]`; use separate rules. The listed new-plus-edited pairs are allowed. Do not generate hidden guest-bot mode `gm`.

An edit is a separate trigger. Do not automatically add it to XP, reputation, warnings, or any action the person expects to happen once.

## 5. Plain commands and reply commands

A plain command, such as an information reply:

```json
{"k":"c","ctm":"p","cm":["/course_info"]}
```

A command a moderator sends as a reply to a member's message:

```json
{"k":"c","cm":["/team_pause"]}
```

These are rule-field fragments, not standalone import bundles.

Do not add `e` to a command. Supply a nonempty list of custom lowercase slash commands in `cm`, using Latin letters, digits, and `_`. Do not present a made-up command as built into Combot. For reply commands, omit `ctm`; do not write `reply_target`, `r`, or a person's name there.

A plain command has no recipient for user actions. Use a message send and, only if explicitly requested, deletion of the command message. Mutes, warnings, XP, and other actions affecting a person require a reply command.

In a reply command, `wh` checks the command sender, while `twh` and other `t...` fields check the replied-to message's author. Use `v.tg: "t"` to act on the latter. If Anna replies `/team_pause` to Ilya, restrict Ilya, not Anna.

Checking only `twh` does not restrict who can call the command. A moderator command must include `wh` according to the request. Action `d` deletes the command itself, not Ilya's message.

Do not replace command mode with a `/team_pause` string search in `tv`. Do not add command conditions hidden by the editor, such as schedules, without a verified export.

## 6. Members and exclusions

`wh` and `wx` use these exact strings, not translated labels:

`New members`, `Regular members`, `Ex-members`, `Whitelisted users`, `Admins`, `Telegram admins`, `Combot custom admins`, `Owner`.

An empty inclusion list imposes no group restriction. You can usually omit `Anyone`. `wm: "o"` requires one matching group; `wm: "a"` requires all selected groups. Exclusions in `wx` prevent a match regardless of positive groups.

Example: anyone except administrators can send the matching message:

```json
{"wx":["Telegram admins","Combot custom admins","Owner"]}
```

Example: only a Telegram administrator or owner may call the command:

```json
{"wh":["Telegram admins","Owner"],"wm":"o"}
```

Do not confuse roles. `Combot custom admins` describes Combot permissions, not Telegram administrators. In the handler, `Admins`, like `Telegram admins`, checks Telegram administrators including the owner; it does not add Combot administrators. To allow both groups, list `Telegram admins` and `Combot custom admins` with `wm: "o"`. `Regular members` means known current members, not people with a long membership or many messages. `New members` uses the chat's existing newcomer settings and exemptions; it is not a fixed “first seven days.” Use `cgr` for an exact duration.

Do not generate `Core members` or `Non-members`: reliable definitions are not provided for new rules. Missing member information does not prove someone has never belonged to the chat.

For the target, use `twh`, `twx`, and `twm`. Take additional contextual values such as `Target self`, `Target bots`, `Target Combot`, and `Target linked channel post` from a suitable export; do not guess them in sender filters.

For `nm` and `ml`, `wh`/`wx` describe whoever initiated the join or removal; `twh`/`twx` describe the member whose status changed. If Anna adds Ilya, newcomer conditions belong to Ilya via `t...`, not Anna. Choosing `v.tg` on an action does not swap these conditions.

`am: "o"` checks for a Telegram administrator; `am: "n"` excludes one. This is not a substitute for combining roles in `wh`/`wx`. Prefer explicit groups for new rules.

## 7. Time in chat, activity, XP, and reputation

Numeric conditions go in `cgr`, or `tcg` for the target. This fragment means the member has been in the chat for at most one hour and has at most five recorded messages:

```json
{
  "cgr": {
    "logic": "All",
    "statistics": {
      "rules": [
        {"metric":"joinedDays","unit":"h","op":"lte","value":"1"},
        {"metric":"messageCount","unit":"c","join":"and","op":"lte","value":"5"}
      ]
    }
  }
}
```

Operators: `eq` means equals, `gte` at least, and `lte` at most. Always state the operator explicitly. Do not translate “fewer than five” as `lte: 5`: for an integer counter it is `lte: 4`. Do not use an exact-duration `eq` check when the person means “at least”: time keeps passing.

Statistics metrics:

- `joinedDays`: time since joining this chat, not account age or a person's age. Units: `s`, `m`, `h`, `d`, `w`, `mo`. A day is 24 hours, a week 7 days, and a month here 30 days. New rules must always include a unit.
- `messageCount`: recorded messages in this chat; unit `c`.
- `warns`: active warnings; unit `c`.

“One recorded message” is `{"metric":"messageCount","unit":"c","op":"eq","value":"1"}`. It does not guarantee exactly-once execution: counter updates and event processing can happen separately.

Other sections:

```json
{
  "cgr": {
    "logic": "All",
    "xp": {
      "xp": {"rules":[{"op":"gte","value":"100"}]}
    },
    "reputation": {
      "reputation": {"rules":[{"op":"gte","value":"5"}]}
    }
  }
}
```

Rank thresholds use `cgr.xp.rank.rules`, but their values must come from the chat's real ranks, not invented names. Rows support `join: "and" | "or"` and `mode: "include" | "exclude"`. Use explicit AND for simple conditions and a real export for complex AND/OR chains. `cgr.logic: "All" | "Any"` combines sections. An excluding numeric row is a prohibition: a positive match in another section of the same condition variant cannot bypass it.

Do not create time-since-first-message or first-other-activity checks: `firstMessageAge` and `firstOtherActivityAge` are unavailable. Do not promise “last week's activity” using total `messageCount`. Unavailable data must not automatically become zero.

## 8. User attributes and fields

`ua` requires attributes; `ux` excludes them. Exact values: `Any username`, `Telegram Premium`, `Bot account`, `Any last name`. For example, `{"ux":["Bot account"]}` excludes bots.

`ul` and `ulx` include and exclude user language codes, if Telegram supplied them. This is not the language of a message or nationality. Do not infer language from a member's name.

Conditions on specific values go in `uar`, or `tur` for the target. Supported fields: `user_id`, `name`, `username`, `last_name`, `bio`. Bio belongs to the join-request context; it is not a continuously available profile field for every member.

A username condition has this shape:

```json
{
  "uar": {
    "username": {
      "rules": [
        {"mode":"include","matchType":"Exact match","values":["example_member"]}
      ]
    }
  }
}
```

This demonstrates structure, not a real member. For an actual request, use only the username supplied by the person, without `@`; ask if missing. Supply IDs as strings in `values`. For access control, prefer an explicitly supplied ID: names and usernames can change.

Here `matchType` uses full strings `Exact match`, `Partial match`, and `Regular expression`, not message-text codes `f`, `p`, and `r`. Values in one row are alternatives; rows have `join`. Do not invent `Starts with` or `Ends with` for these fields.

`ual` and `tul`: `a` means all rules; `o` means any group. Use an export for group structures `uag` and `tug` in this generation profile.

## 9. Text and message type

Use `tv`, an array of strings, to search for phrases. One matching string is enough. For example:

```json
{"tv":["where is the recording","class recording"],"tr":"p","ty":"p","cs":false}
```

`tr`: `p` means phrases, `w` words, and `r` regular expressions. Word mode does not require every listed word. Ordinary partial matching does not guarantee word boundaries either: “cat” can match inside “catalog.”

`ty`: `f` means the entire text, `p` part of the text, `s` the start, `e` the end, and `r` a regular expression. `cs: true` makes matching case-sensitive; `false` or omission disables that. A simple FAQ only needs `tv`: case-insensitive phrase search is the default.

`lmin` and `lmax` limit text length. An empty or zero boundary imposes no limit on that side. Do not confuse text length with word count.

Use regular expressions only when simple matching is insufficient. Supply the pattern as a JSON string, escaping backslashes; do not automatically add JavaScript `/.../i` delimiters. For exact-word requirements or complex negation, explain matching and nonmatching examples.

`mti` includes message types; `mtx` excludes them. Main values: `photo`, `video`, `animation`, `audio`, `document`, `sticker`, `voice`, `video_note`, `contact`, `location`, `poll`, `dice`, `game`, `paid_media`. For example, `{"mti":["voice"]}` means voice messages; `{"mti":["photo","video"]}` means photo OR video, not both attachments at once.

The editor also has `text` and `caption`. They participate in text-content settings; do not treat `caption` as an independent Telegram attachment type. For “photo captions only” or a strict distinction between text and captions, obtain an export of that setup. The codec may omit defaults, including a lone `text`; that value in the input JSON alone does not prove the restriction survives import.

`me` and `mex` require or exclude text entities. Values: `bot_command`, `url`, `text_link`, `mention`, `hashtag`, `cashtag`, `email`, `phone_number`, `emoji`, `custom_emoji`, `bold`, `italic`, `underline`, `strikethrough`, `code`, `pre`, `spoiler`, `blockquote`, `expandable_blockquote`. `mel: "a"` requires all included entities; `"o"` requires at least one. For any visible or hidden link: `{"me":["url","text_link"],"mel":"o"}`.

`mef` narrows specific links, commands, and other entities; `mmo` describes media properties; `mtg` and `mog` are condition groups; `csx`/`cse` are character sets. Generate these complex fields from a real export. Do not invent replacements such as `max_file_size`, `allowed_domains`, `mime`, `language`, or `contains_all`.

## 10. Time and chat topics

`s`: `sc` means source chat, `gn` General, and `st` selected topics. For the latter, `ti` contains positive topic IDs.

Import resets selected source topics to the entire source chat, and selected send topics to the current topic. This happens even with correct IDs and also applies to OR alternatives. If the scenario depends on a topic, name it in the explanation and mandatory manual settings. Do not call the result ready to enable until topics have been selected again.

`at` sets the allowed event time; it does not schedule an independent send. “Answer a question in the evening” is possible. “Post every day at 19:00 without an incoming event” needs a scheduler, not this trigger.

The bot checks the current time while processing the event, not the original message timestamp. An edit in the morning checks morning hours even if the original message was written at night.

A weekly window, Monday–Friday, 09:00–18:00 UTC:

```json
{"at":{"m":"w","wd":[0,1,2,3,4],"tw":[{"startMinute":540,"endMinute":1080}]}}
```

`wd`: 0 is Monday and 6 Sunday. Times are whole minutes since midnight UTC, from 0 to 1439. Weekly windows include the final minute. An overnight window can have a start greater than its end; the weekday uses the current UTC date, not the day the “shift” started. Check both sides of midnight instead of guessing the required days.

Moscow 09:00 and 18:00 correspond to 06:00 and 15:00 UTC: minutes 360 and 900. To stop matching at exactly 18:00, the final allowed minute is 14:59 UTC, or `endMinute: 899`. For other time zones, account for the offset and possible daylight saving changes. Do not enter local time as UTC without conversion.

A date interval:

```json
{"at":{"m":"d","s":"2026-10-01T00:00:00Z","e":"2026-10-07T23:59:59Z"}}
```

These illustrate the shape, not the person's campaign dates. The start must precede the end. Both boundaries are included in this mode. `Z` means UTC.

Closing hours use the chat's existing settings: `{"at":{"m":"c"}}`; outside those hours: `{"at":{"m":"c","i":true}}`. Closing-hours mode includes the start and excludes the end. The trigger does not configure the chat's closing schedule. Without a valid schedule, do not promise either the direct or inverted condition will work.

## 11. Invitation links, reactions, and checklists

### Invitations

For join requests `["jr"]`, `il` accepts `Known Combot links`, `External invite link`, or `Any source`. Omit it when no restriction is needed.

`Known Combot links` means links in this chat's Combot catalog. `ilc` and `ilx` list included and excluded link codes within that catalog; take exact codes from an export or supplied data. Empty lists leave the overall category check in place.

`External invite link` means an identified link outside the entire Combot catalog, not “everything except my two selected links.” An unknown source or unavailable catalog cannot be treated as an external link. Do not promise this condition will catch every request without a known invitation.

### User reactions

Event `["mr"]`. `rct` contains `added` and/or `removed`. `rnt` and `rnx` include and exclude reactions in the user's new state. If either is populated, `rct` must include `added`.

The handler runs `mr` only when Telegram supplies `user`. A reaction sent as a channel or anonymous administrator with `actor_chat` does not trigger this event. This differs from aggregate reaction counts `rc`.

```json
{"e":["mr"],"rct":["added"],"rnt":["👍"]}
```

Reaction values are ordinary emoji, string custom-emoji IDs, or `paid`. `rnt` checks the complete new state, not just the difference. If 👍 was already present and the user adds another reaction, the condition can match again. Action recipient `u` here is the person who changed the reaction; it does not automatically reward the message author.

### Aggregate reaction counts

Event `["rc"]`. `rcy`/`rcx` choose counted reaction types. `rcn`/`rcm` set lower and upper count boundaries as nonnegative integers. Zero or omission means no boundary on that side.

```json
{"e":["rc"],"rcy":["👍"],"rcn":10}
```

This checks the current value on an update, not “reached ten for the first time.” Another matching update can run the action again. The aggregate anonymous counter has no specific reacting user.

### Checklists

For `["cd"]` and `["ca"]`, `chl` limits the rule to one checklist. If the person wants a particular list, ask for its direct message link. If they want all matching checklists in a chat or topic, do not add `chl`.

Direct Telegram links, including topic-number links, are supported; links with `?comment=` are not. Do not invent an address. For a public link, the event handler must know the chat username; a superficially valid URL alone does not establish a match.

## 12. Actions

`a.m: "a"` runs every row in order. `a.m: "r"` runs all rows marked `fr: 1` and exactly one random unmarked row, if any exist. Selected rows retain their original order. Use mode `a` unless randomness was requested; do not add `fr` outside random mode.

| Code `a.r[].t` | Action | Parameters `v` |
| --- | --- | --- |
| `s` | Send a message | `tx` and formatting; see below |
| `d` | Delete the message that triggered the rule | No parameters |
| `w` | Add warnings | `tg`, positive `c`, usually 1 |
| `rw` | Remove warnings | `tg`, positive `c`, usually 1 |
| `m` | Restrict posting | `tg`, duration `du` in seconds |
| `b` | Ban | `tg`, duration `du` in seconds |
| `k` | Remove from the chat with rejoining allowed | `tg` |
| `um` | Lift posting restrictions | `tg` |
| `ub` | Lift a ban | `tg` |
| `du` | Delete stored user messages available to the bot | `tg` |
| `x` | Change XP | `tg`, nonzero integer `v` from −99999 to 99999 |
| `r` | Change reputation | `tg`, nonzero integer `v` from −999 to 999 |
| `ja` | Approve a join request | Event `jr` only, no parameters |
| `jd` | Decline a join request | Event `jr` only, no parameters |

Do not confuse action code `du` with duration field `v.du`. Duration is in seconds, not minutes: one hour is 3600. Zero for a mute or ban means no specified end; never substitute it for an unknown duration. A reason can be written in a separate message if requested; do not promise a customizable sanction reason via an undocumented field.

User-action recipient `v.tg`:

- `u`: the participant who caused the event.
- `t`: the target defined by context, such as the replied-to message's author. Not a literal ID.
- `l`: the Combot-catalog invitation link's creator, in a join-request context.
- `b`: both available participants in the relevant context. Not “everyone in the chat.”

Always specify the recipient. Plain commands do not support user actions. Reply commands support sender, target, and both. For a join request, use separate actions for the applicant (`u`) and known link creator (`l`) if both are required: `b` does not mean applicant plus link creator. The editor offers the link creator for join requests; do not offer the same choice in step-by-step member-join instructions. For other events, use only recipients available in that event.

An unknown target must not turn an action into a punishment for the command sender. Do not promise fallback to another person. Action `du` does not guarantee deletion of a member's entire history: the bot is limited by available messages and Telegram capabilities.

Warnings, levels, and reputation must be enabled where the scenario depends on them, and moderation needs the appropriate bot permissions. Unmuting, unbanning, and approving a join request are separate actions.

Join-request events allow message sends, actions on available users, and `ja`/`jd`; message deletion `d` is unsuitable. For events without a message, do not add deletion or a reply to a nonexistent message.

## 13. Reply text and destination

Parameters for action `s`:

| Field | Meaning |
| --- | --- |
| `tx` | Nonempty text up to 4096 characters; supported Telegram HTML is allowed |
| `d` | `sc`: source chat; `lg`: configured log channel; default `sc` |
| `tp` | `ct`: current topic; `gn`: General; `st`: selected topics; default `ct` |
| `to` | Positive selected-topic IDs; reset by import |
| `rp` | `r`: reply to the triggering message; omission: standalone message |
| `cl` | `n`: keep the previous reply; `ps`: delete this row's previous reply |
| `bt` | URL-button rows: array of arrays of objects with `text` and `url` |
| `ph` | Array of image URLs for link previews |
| `pa` | `true`: put the preview above the text |

`rp: "r"` is retained for the source chat and current topic. Do not promise the same reply linkage in logs or another topic. Do not put arbitrary external chats, channels, or private messages in `d`.

If row-level `cl` is absent, it inherits the rule setting. Rule-level `cl: "ps"` enables previous-reply cleanup; explicit row-level `cl: "n"` overrides it. Cleanup belongs to a particular rule, action row, and destination. Different random rows do not become one shared “last welcome.”

For a normal reply, you can use `<b>Course information</b>\nRecordings are in the pinned message.` Do not supply Markdown as HTML. Escape links and text correctly. Add buttons and images only with real URLs supplied by the person; `ph` does not send a photo album.

Confirmed message-context variables: `{name}`, `{uid}`, `{name_link}`, `{from.id}`, `{from.title}`, `{from.username}`, `{chat.id}`, `{chat.title}`, `{chat.username}`, `{message.id}`, `{message.timestamp}`, `{timestamp}`, `{group_name}`, `{reply_to_uid}`, `{reply_to_name}`, `{reply_to_name_link}`. Availability depends on the event. Do not invent `{target.name}`, `{user.first_name}`, `{reaction_count}`, or similar variables.

In a reply command, `{from.title}` describes the command sender, not the punished or rewarded member. Choosing `tg: "t"` does not change variable meanings. If you are unsure the event provides the required name, use neutral wording.

On a join, `{from.title}` does not guarantee the newcomer's name either: if Anna adds Ilya, Anna is the actor. Use a generic welcome such as “Welcome to {chat.title}!” without a name.

Actions are not a transaction. A message following a mute does not prove Telegram applied the mute. Do not phrase unconditional confirmation of a sanction as a verified result: no separate success-check condition is documented here.

## 14. Alternatives, limits, and unsupported promises

`ov` contains alternative conditions for the same rule. They share the parent's event and actions, rather than defining independent scenarios. An exclusion in one alternative is not global. If a prohibition must always apply, retain it in every alternative. An alternative with `en: false` does not participate in matching.

Use real editor exports for new complex `ov`, `mtg`, `mog`, `uag`, `tug`, `mef`, and `mmo`. Do not create recursive OR trees or put separate action chains inside alternatives expecting independent execution.

Compatibility fields `mt`, `mtl`, `t`, `rmi`, and `rme` may appear in exports. Do not add them instead of the documented main settings without a reason. For simple reply restrictions, use `rm`: `a` any message, `r` replies only, `rb` replies to a bot, `rc` replies to Combot, `nr` non-replies. Do not use `rm: "cr"` instead of the new command fields.

Do not generate old or internal fields `lf`, `fc`, `lo`, or UI fields `actions`, `destination`, `topic`, `applyTarget`, and `alwaysRun`. Compact format represents them differently, and some settings are not active features at all.

Planning limits:

- Free: up to 2 saved rules; Pro: up to 50; Business: up to 100. Disabled rules count too. Account for existing rules when assessing remaining slots.
- Additional OR alternatives per rule: Free 0, Pro 2, Business 5.
- Up to 100 action rows per rule, with a separate calculated action budget of 20 per plan execution.
- A send costs 1 per unique destination, or 2 with previous-reply cleanup. User-facing Telegram actions are counted by recipient, so `b` may reserve twice as much. XP and reputation cost 0 in this calculation. Random mode counts required rows plus the most expensive random choice.
- This is an internal calculation, not a promise of exactly twenty network requests including all helper operations. If the next row exceeds the remaining budget, execution stops; completed actions are not rolled back.

Do not promise timer-only execution without an event, “at most once an hour,” a one-time reaction reward, protection from repeated-event reward abuse, the first threshold crossing only, exactly-once execution, arbitrary external-chat processing, account age, or activity statistics over an arbitrary past period. If one of these is essential, explain that the described rule alone is insufficient.

Import adds new rule copies; it does not migrate old Triggers v2. You may rewrite an old rule's meaning in Compact v3, but cannot present its old JSON as a ready import.

## 15. Acceptance scenarios for the examples

The companion `examples` directory contains six independent bundles, each with one disabled rule:

| File | Matching case | Additional checks |
| --- | --- | --- |
| `01-course-command.json` | A member enters `/course_info` | Plain text without the command should not trigger a reply |
| `02-recording-faq.json` | Text contains “where is the recording” | An unrelated question should not trigger a reply; partial search can match a longer phrase |
| `03-random-welcome.json` | A new member joins | One welcome should be chosen, not all three |
| `04-moderator-reply-mute.json` | A Telegram administrator or owner replies `/team_pause` to a member | Regular members cannot call it; administrator targets are excluded; a command without a reply must not restrict its sender |
| `05-known-link-join-request.json` | A request uses a link from Combot's catalog | External or unknown sources must not match; all known links match unless specific ones are selected |
| `06-thumbs-up-reaction.json` | A user adds a reaction and the new state contains 👍 | Removing 👍 does not match; repeated matching changes can send another message |

These are acceptance scenarios for your chat, not a claim they have all been executed. Completed checks follow below. Every example needs a settings and capability check in your chat before enabling.

## 16. Format sources and completed verification

The format was checked against Rails `0d80ce4788ef5adc5c7c1b79e85c6b8be27cc00e` and bot `ddc1a8a6b6ab99bd2517d783870495990c7058e5`. These main-branch revisions include the coordinated editor and Automation fixes. Rails PR #78 and bot PR #56 merges were confirmed on Codeberg; exact deployed process revisions were not independently established.

Main sources: `trigger_compact_codec_helpers.js`, `trigger_import_export_helpers.js`, `trigger_validation_helpers.js`, event and action registries, `user_attribute_model_helpers.js`, `AutomationController`, the detailed Automation contract in `docs/MONGODB.md`, and condition/action handling in `automation.py`.

The six original examples were checked in memory using the editor's source codec: decode, rebuild, decode again, and normalized-result stability. Actions and disabled state were retained. The codec omits some defaults, so comparison used normalized results rather than byte-for-byte equality with the input JSON.

On September 8, the disabled state of all six original examples was checked after import through the production editor on combot.org, server save, and page reload. This does not replace verification of every condition and action in Telegram.

Command and FAQ cases were checked in Telegram: matching requests received the intended replies and controls did not. Only `/course_info` was changed to a unique test name allowed by the tester's safeguards; the other example settings were retained. These results do not automatically establish other events and actions.

For `06-thumbs-up-reaction.json`, adding 👍 from a personal profile produced one expected reply; removing it produced none. Identity and old/new reaction sets were confirmed through a separate Bot API observer. Random welcome on joining, mute application, and approval of a real join request were not tested in that run; import does not prove action execution.

Created test rules were removed and restoration of the original list checked; result messages remain in the test group. The JSON Schema is companion documentation, not connected to the product or presented as an existing built-in validator.

On September 9, the editorial reference was checked against Rails `0d80ce4788ef5adc5c7c1b79e85c6b8be27cc00e` and bot `0a491a6ee50c57e98c1d9de04819253d15214506`. Clarifications cover the editor's name limit, administrator groups, event participants, action recipients, and optional checklist links. This is source-based documentation review, not a new Telegram run. The localized example wording has not been independently live-tested.
