Bulk Rule Operations
The ajat rule command group bulk-mutates Jira Cloud Automation rules from the command line, instead of clicking through the Jira UI rule by rule. It covers the bulk operations the Jira UI does not expose well: an emergency stop during an incident, freezing automation during a project-key rename or migration, cleaning up rules flagged by ajat report consistency, and rescoping rules in response to org changes.
Every ajat rule mutation acts against your live Jira Cloud
tenant. These commands are plan-first: the matched rules are printed
as a plan before any API call, and nothing is applied until you confirm. Use
—dry-run to preview and exit; use —yes to skip the
prompt in automation. Rules are selected from your local export
catalog, so always refresh the export first.
ajat targets Atlassian Jira Cloud Automation only. It does not support Jira Server or Jira Data Center.
Subcommands
Four mutating subcommands plus one read-only discovery subcommand share the same selector vocabulary and safety model:
| Subcommand | API endpoint | Effect |
|---|---|---|
ajat rule enable | PUT /rest/v1/rule/{uuid}/state | Enable matched rules so they fire again |
ajat rule disable | PUT /rest/v1/rule/{uuid}/state | Disable matched rules so they stop firing |
ajat rule delete | DELETE /rest/v1/rule/{uuid} | Delete matched rules (disabled rules only) |
ajat rule scope set | PUT /rest/v1/rule/{uuid}/rule-scope | Replace, add, or remove scope ARIs |
ajat rule manual list | POST /rest/v1/rule/manual/search | List manually-triggered rules (read-only) |
Selector vocabulary
Every mutating subcommand accepts the same selector flags. Selectors AND together, and at least one is required. The source of truth for matching is your local export catalog, passed with --input-dir — the selector evaluates against on-disk reality without an extra API round-trip.
| Flag | Description |
|---|---|
--uuid <uuid> | Target a specific rule by UUID. Repeatable. |
--from-file <path> | Newline-delimited file of UUIDs (blank lines and # comments ignored). |
--label <label> | Target rules carrying this label. Repeatable; ANDed. |
--name-pattern <glob> | Case-insensitive shell glob over the rule name (e.g. release-*). |
--author <accountId> | Target rules whose actor.accountId matches this value. |
--scope <ari> | Target rules whose ruleScopeARIs contains this ARI. |
--state ENABLED|DISABLED | Target rules in this state. |
--input-dir <path> | Local export directory used as the source of truth. Required. |
Because the selector reads from the local export, an export more than a few
hours old can produce a stale match. Re-export immediately before any
high-stakes mutation, then preview with —dry-run, then apply.
Safety model
Identical to ajat import — every mutation is gated on explicit confirmation:
- Plan first. The matched rules are printed as a plan before any API call.
--dry-runprints the plan and exits without prompting or mutating.- Interactive confirmation when stdin is a TTY. Answer
y/yesto proceed. --yesskips the prompt for CI runs. Required when stdin is not a TTY.- Zero matches is an error. A run that matches no rules exits non-zero, so a misconfigured selector fails loudly instead of silently doing nothing.
Exit codes
| Code | Meaning |
|---|---|
0 | Every target succeeded. |
1 | Bootstrap / configuration / no-match failure. |
2 | At least one rule failed during execution. |
130 | Interrupted by SIGINT / SIGTERM. |
Refreshing the catalog first
Because the selector reads from the local export, the intended workflow is to re-export immediately before a mutation, preview it, then apply:
# 1. Refresh the local catalog so the selector matches current reality.
ajat export --output-dir ./jira-automations-backup
# 2. Preview the plan without touching the tenant.
ajat rule disable --input-dir ./jira-automations-backup <selectors> --dry-run
# 3. Apply.
ajat rule disable --input-dir ./jira-automations-backup <selectors> --yesKeeping the export and mutation as separate steps also keeps the “what changed since I last looked” question answerable from the export state file (.ajat_state.json).
ajat rule disable / ajat rule enable
State toggles. disable turns matched rules off so they stop firing; enable turns them back on. The same state body is sent for every matched rule.
Typical uses for disable: freezing automation during a project-key rename or migration, an emergency stop for rules touching a downstream service that is in incident, or staging a cleanup (disable first, then delete once confirmed safe).
Flags
Both subcommands accept the shared selector flags plus:
| Flag | Default | Description |
|---|---|---|
--dry-run | false | Print the plan and exit without mutating the tenant. |
--yes | false | Skip the interactive confirmation prompt. |
--workers | 4 | Concurrent state updates. |
Examples
# Preview: which rules touch the ALPHA project?
ajat rule disable --input-dir ./jira-automations-backup \
--scope ari:cloud:jira:<tenant>:project/ALPHA --dry-run
# Pause every rule that touches ALPHA during a migration.
ajat rule disable --input-dir ./jira-automations-backup \
--scope ari:cloud:jira:<tenant>:project/ALPHA --yes
# Disable a single rule by UUID.
ajat rule disable --input-dir ./jira-automations-backup \
--uuid 11111111-2222-3333-4444-555555555555 --yes
# Pause every enabled rule labeled release-pauseable before a release window.
ajat rule disable --input-dir ./jira-automations-backup \
--state ENABLED --label release-pauseable --yes
# Re-enable everything once the migration finishes.
ajat rule enable --input-dir ./jira-automations-backup \
--scope ari:cloud:jira:<tenant>:project/ALPHA --yesajat rule delete
Permanently deletes matched rules from the tenant.
The Atlassian API only permits DELETE on disabled
rules, and ajat enforces the same constraint up front. Any ENABLED rule caught
by the selector is refused in the plan and excluded from
execution — it is listed separately rather than left for the server to reject
mid-run.
Because deletion is destructive and only applies to disabled rules, the recommended cleanup workflow is two steps plus a refresh:
# 1. Disable the rules first.
ajat rule disable --input-dir ./jira-automations-backup --label deprecated --yes
# 2. Refresh the export so .ajat_state.json reflects the DISABLED state.
ajat export --output-dir ./jira-automations-backup
# 3. Delete — restricting to DISABLED makes the intent explicit.
ajat rule delete --input-dir ./jira-automations-backup \
--label deprecated --state DISABLED --yesFlags
Shared selector flags plus:
| Flag | Default | Description |
|---|---|---|
--dry-run | false | Print the plan and exit without deleting anything. |
--yes | false | Skip the interactive confirmation prompt. |
--workers | 4 | Concurrent delete calls. |
Examples
# Preview: which disabled rules carry the 'deprecated' label?
ajat rule delete --input-dir ./jira-automations-backup \
--label deprecated --state DISABLED --dry-run
# Delete them for real.
ajat rule delete --input-dir ./jira-automations-backup \
--label deprecated --state DISABLED --yes
# Delete a single rule by UUID (must already be disabled).
ajat rule delete --input-dir ./jira-automations-backup \
--uuid 11111111-2222-3333-4444-555555555555 --yesajat rule scope set
Changes where matched rules apply by editing their ruleScopeARIs (global, project, or multi-project). Three mutually-constrained update modes are available:
| Mode | Effect |
|---|---|
--replace <ari> (repeatable) | Replace the entire scope list. Pass --replace with no value to move the rule to global scope (empty list). |
--add <ari> (repeatable) | Append the ARI to the current list (idempotent — no-op if already present). |
--remove <ari> (repeatable) | Drop the ARI from the current list (no-op if absent). |
--replace is mutually exclusive with --add / --remove; --add and --remove may be combined. The current scope list is read from the local catalog, so --add / --remove never fetch a rule before mutating it.
The plan includes a NEW SCOPE column, so you can confirm the
projected result for each rule before applying.
Flags
Shared selector flags plus:
| Flag | Default | Description |
|---|---|---|
--replace <ari> | [] | Replace ruleScopeARIs with the given ARI list (repeatable; pass without value for global). |
--add <ari> | [] | Append an ARI to ruleScopeARIs (repeatable, idempotent). |
--remove <ari> | [] | Remove an ARI from ruleScopeARIs (repeatable, no-op if absent). |
--dry-run | false | Print the plan and exit without mutating the tenant. |
--yes | false | Skip the interactive confirmation prompt. |
--workers | 4 | Concurrent scope updates. |
Examples
# Preview: add a project to every rule labeled shared-trigger.
ajat rule scope set --input-dir ./jira-automations-backup --label shared-trigger \
--add ari:cloud:jira:<tenant>:project/NEW-PROJ --dry-run
# Move a rule to global scope (empty scope list).
ajat rule scope set --input-dir ./jira-automations-backup \
--uuid 11111111-2222-3333-4444-555555555555 --replace --yes
# Replace the scope of one rule with two project ARIs.
ajat rule scope set --input-dir ./jira-automations-backup \
--uuid 11111111-2222-3333-4444-555555555555 \
--replace ari:cloud:jira:<tenant>:project/PROJ-A \
--replace ari:cloud:jira:<tenant>:project/PROJ-B --yesajat rule manual list
Manual rules — the ones surfaced as buttons in the Jira UI (“Trigger rule”) — are a subset of the full automation estate. ajat rule manual list enumerates them and is read-only: nothing is fired or modified.
The Automation API scopes the search to specific target ARIs; there is no server-side “list every manual rule” endpoint. Pass one or more --target values and/or a --from-file path. Each --target accepts either a Jira issue key or an object ARI — issue keys are resolved to ARIs before the API call.
Flags
| Flag | Default | Description |
|---|---|---|
--target <key|ari> | [] | Target ARI or Jira issue key. Repeatable. |
--from-file <path> | (none) | File with one target per line (blank lines and # comments ignored). |
--format table|json | table | Output format. |
--limit <n> | 0 | Stop after at most this many matches (0 = no cap). |
--page-size <n> | 100 | Manual rule page size (1..100). |
Examples
# Which manual rules apply to a single issue?
ajat rule manual list --target ACME-123
# Multiple targets, mixing issue keys and ARIs.
ajat rule manual list \
--target ACME-1 \
--target ari:cloud:jira:<tenant>:issue/10002
# Bulk targets from a file (one per line; # comments allowed).
ajat rule manual list --from-file ./targets.txt
# Machine-readable: extract the matched rule UUIDs.
ajat rule manual list --target ACME-1 --format json | jq '.matches[].uuid'Use the UUIDs in the output as the first positional argument to ajat invoke to actually fire a manual rule against a set of targets.
Related
- Export — produces the catalog that the selector reads; refresh it before every mutation.
- Reports —
report consistencyflags stale, mislabeled, or duplicated rules; pair it withrule disable/rule deleteto act on the recommendations. - Invoke — fire the manual rules discovered by
rule manual list. - Import — the other mutation command; shares this same plan-first safety model.