Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ Supports Claude Code, Cursor, Codex, OpenCode, and [other supported agents](http

### Workflow

| Skill | Description |
|---|---|
| `cx-cost-optimization` | Analyze and reduce Coralogix data costs - usage analysis, TCO policies, retention, archive |
| `cx-cases` | Manage Coralogix Cases - list, inspect, assign/acknowledge/resolve/close, set priority overrides |
| `cx-slos` | Manage SLO definitions - list, inspect, check error budgets, create/update/delete |
| `cx-data-pipeline` | Configure data processing - parsing rules, enrichments, Events2Metrics, recording rules |
| `cx-platform-admin` | Manage access and security - API keys, roles, users, groups, IP access |
| `cx-observability-setup` | Set up monitoring - saved views, webhooks, notifications, integrations |
| `cx-cli` | Cross-cutting `cx` behavior that applies to every command, such as update notifications |
| Skill | Description |
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `cx-cost-optimization` | Analyze and reduce Coralogix data costs - usage analysis, TCO policies, retention, archive |
| `cx-cases` | Manage Coralogix Cases - list, inspect, assign/acknowledge/resolve/close, set priority overrides, read parent-child links and case provenance |
| `cx-slos` | Manage SLO definitions - list, inspect, check error budgets, create/update/delete |
| `cx-data-pipeline` | Configure data processing - parsing rules, enrichments, Events2Metrics, recording rules |
| `cx-platform-admin` | Manage access and security - API keys, roles, users, groups, IP access |
| `cx-observability-setup` | Set up monitoring - saved views, webhooks, notifications, integrations |
| `cx-cli` | Cross-cutting `cx` behavior that applies to every command, such as update notifications |

## Requirements

Expand Down
80 changes: 73 additions & 7 deletions skills/cx-cases/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
---
name: cx-cases
description: >
Triage and manage Coralogix Cases with the `cx cases` CLI — e.g. acknowledge,
assign, resolve, or re-prioritize a case, or inspect its event timeline or
notification deliveries.
This skill should be used when the user asks to "triage a case", "acknowledge a
case", "assign a case", "resolve a case", "close a case", "comment on a case",
"re-prioritize a case", "check case status", "see a case timeline", "check case
notifications", "create a case", "open a case", "link cases", "find the parent
case", "find child cases", "find cases Olly opened", "find cases from a scheduled
task", or wants to inspect or drive Coralogix Cases through their lifecycle using
the cx CLI.
metadata:
version: "0.1.0"
version: "0.2.0"
---

# Cases Management Skill

A **Case** groups related alert events into one investigation unit with a status,
priority, category, and assignee. Use this skill to inspect cases and drive them
through their lifecycle (active → acknowledged → resolved → closed).
A **Case** groups related indicators — usually alert events, sometimes an external
source — into one investigation unit with a status, priority, category, and
assignee. Use this skill to inspect cases and drive them through their lifecycle
(active → acknowledged → resolved → closed).

## CLI Commands

Expand All @@ -33,6 +38,20 @@ through their lifecycle (active → acknowledged → resolved → closed).
| `cx cases events get <event-id>` | A single event — drill in, e.g. to expand a comment thread |
| `cx cases notifications <case-id> [<case-id> ...]` | Notification deliveries (connector, status, time) |

## What the CLI cannot do

Tell the user plainly and point at the alternative — do not improvise a command
that does not exist.

| Not available | Why, and what to do instead |
|---|---|
| **Creating a case** | Cases are opened by the Cases service from an indicator — an alert event, an Error Tracking issue, or an Olly scheduled task. Create exists only on the *internal* API, so there is no `cx cases create`. To get a case for something: ask Olly to open one (`cx olly`, which can), fix the alert that should have caught it (`cx-alerts`), or open one in the Cases UI. |
| **Listing / searching cases** | `cx cases list` was removed. Case discovery lives in the dataset: query `system/labs.cases.state_updates` with `cx dataprime`. See [`references/case-analytics.md`](references/case-analytics.md). |
| **Linking / unlinking cases** | Parent-child links are created in the Cases UI or through the API, not the CLI. You can still *read* the hierarchy from the dataset — see "Case relationships" below. |
| **Muting a case** | No CLI verb. Mute in the UI. Muting stops notifications without changing status, which is the honest alternative to resolving work nobody has finished. |
| **Changing labels** | Labels are fixed when the case is created. `cx cases update` patches only the title and resolution reason. |
| **Reopening** | `RESOLVED` → `CLOSED` only. A recurrence of the same underlying indicator reopens the case automatically; a person cannot. |

## Case Lifecycle

```
Expand Down Expand Up @@ -90,6 +109,46 @@ If uncertain, stay in `ACKNOWLEDGED` (reversible via `unacknowledge`) until
confident. For non-resolution edits (title, post-hoc postmortem link), use
`cx cases update`.

## Case relationships (parent / child)

Cases can be linked into a **one-level** parent-child hierarchy: the parent is the
incident of record and children hang under it. A case has at most one parent, is
either a parent or a child but never both, and a parent has at most 100 children —
so there are no grandparents and no cycles. There is no "related" or "duplicate"
link type.

Linking a case as a child **mutes it** by default, so the parent becomes the single
notifying case. Expect a child to be quiet; that is the design, not a delivery
failure.

`cx cases get <id>` returns the case's `relationship`: absent for a standalone case,
`{"type": "PARENT"}` for a parent, `{"type": "CHILD", "parentCaseId": "<uuid>"}` for
a child. A parent does not inline its children — to list them, see
[Case relationships](references/case-analytics.md#case-relationships-parent--child)
in the analytics reference.

When triaging a child, read the parent first — the investigation usually lives
there. When resolving a parent, check its children: they do not resolve with it.

## Cases opened by Olly

A case has **no top-level source field**. Provenance lives on its indicators, so a
case Olly opened carries a generic indicator with
`indicatorType == "OLLY_SCHEDULED_TASK"` and a free-form `metadata` object naming the
task and run it came from. That also means `cx cases` cannot filter for Olly's cases
— see
[Generic indicators](references/case-analytics.md#generic-indicators--non-alert-sources-including-olly)
in the analytics reference for the query.

Triage one the same way as any other case, with one addition: open the run link in
the indicator's `metadata` to see the evidence Olly based the case on, and judge that
evidence rather than taking the case's own summary at face value.

The indicator's `externalId` is the dedup key. One unresolved case exists per key, and
a recurrence reopens the resolved case rather than forking a new one — so a case with
a non-null `lastReactivatedAt` is a repeat, which is usually the more interesting
signal than the current occurrence.

## Bulk Operations

There are no bulk endpoints. To act on many cases, pipe IDs through a loop, e.g.
Expand All @@ -102,6 +161,13 @@ There are no bulk endpoints. To act on many cases, pipe IDs through a loop, e.g.
for false alarms `close` from `ACTIVE` directly.
- **Always supply a resolution reason** unless `--no-reason` truly applies.
- **`P1`-style shorthand** is accepted anywhere a priority/status/category is expected.
- **Acknowledging means a person is on it** — escalation, PagerDuty and ServiceNow
all read that fact, so never acknowledge on someone's behalf to quiet a case.
Assign it, or mute it in the UI.
- **Prefer muting over resolving** when the work is real but nobody is on it. A
resolved case is a claim the system is healthy.
- **A case has no source field** — provenance is on its indicators. See
"Cases opened by Olly".
- **Multi-profile fan-out** with `-p <profile>` (repeatable) for cross-environment triage.
- **Link to a specific case** — build `<base>/cases?id=<case_id>`, where
`<base>` is the console URL already seen in a `View in Coralogix:
Expand Down
72 changes: 72 additions & 0 deletions skills/cx-cases/references/case-analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,65 @@ Per-element fields:
| `state` | `TRIGGERED` (still firing) / `RESOLVED` / `NO_DATA` (signal dropped, **not** a recovery) / `MUTED`. |
| `triggeredAt` / `resolvedAt` | Indicator-side timing (different from case-level resolution). |

#### Generic indicators — non-alert sources, including Olly

`indicators.genericIndicators[]` — indicators from a source that is not a Coralogix alert. This is where a case's non-alert provenance lives; there is **no top-level `source` field on a case**, so this array is the only way to tell what opened it.

| Field | Meaning |
|---|---|
| `indicatorType` | `ERROR_TRACKING` or `OLLY_SCHEDULED_TASK`. |
| `externalId` | The originating system's stable key for the problem. It is also the dedup key: one unresolved case per `(team, indicatorType, externalId)`, and a recurrence reopens rather than forking. |
| `instanceId` | The indicator's own UUID. |
| `status` | `TRIGGERED` / `RESOLVED`. |
| `priority` | `P1`–`P5`, indicator-side (independent of case priority). |
| `labels` | Flat `{key: value}` map. |
| `metadata` | **Free-form JSON object**, shaped by the producing system — not a string map, so values may be nested objects, numbers or lists. For an Olly scheduled task it carries the task and run the finding came from (`taskId`, `runUrl` and similar). Deliberately separate from `labels` so source provenance does not pollute the routing namespace. |
| `entityLinks` | URLs to the originating entity. |
| `lastTriggeredAt` / `lastResolvedAt` | Indicator-side timing. |

`metadata` is set at creation and there is no API to change it afterwards, so treat it as a fixed record of where the case came from.

**Cases opened by an Olly scheduled task, most recent first:**

```
source system/labs.cases.state_updates
| dedupeby caseId orderby $m.timestamp desc
| explode indicators.genericIndicators into gi
| filter gi.indicatorType == 'OLLY_SCHEDULED_TASK'
| choose caseId, caseNumber, title, status, priority,
gi.externalId as dedup_key, gi.metadata as source_metadata, createdAt
| orderby createdAt desc
```

To narrow to one task, reach into the metadata (`filter gi.metadata.taskId == '<id>'`) — but confirm the key name from a sample row first, since the shape is owned by the producer, not by this schema.

#### Case relationships (parent / child)

`relationship` — the case's place in a parent-child hierarchy. **Absent entirely for a standalone case**, so test for null before reading it.

| Shape | Meaning |
|---|---|
| `{"type": "PARENT"}` | This case is a parent. Its children are **not** inlined — find them by querying for cases whose `relationship.parentCaseId` is this `caseId`. |
| `{"type": "CHILD", "parentCaseId": "<uuid>"}` | This case is a child of that case. |

Note the discriminator is **`type`**, not `$type` — unlike `metadata.change`, `assignee` and `closedBy`, which use `$type`. This is the easiest thing to get wrong here.

Structural guarantees, which make these queries simpler than they look: a case has **at most one parent**, is **either a parent or a child but never both**, and a parent has **at most 100 children**. The hierarchy is therefore exactly one level deep and cannot contain cycles.

**A parent and its children in one result:**

```
source system/labs.cases.state_updates
| dedupeby caseId orderby $m.timestamp desc
| filter relationship != null
| filter caseId == '<parent-uuid>' || relationship.parentCaseId == '<parent-uuid>'
| choose caseId, caseNumber, title, status, priority, relationship.type as rel
```

**Linking is not visible as its own event.** A link emits a state-update on both cases with `metadata.trigger == 'caseUpdated'` and no `metadata.change`, so you cannot find "when was this linked" by filtering on a trigger. Detect links from the current `relationship` value, or by diffing it across rows ordered by `$m.timestamp`.

`muteStatus` is the related signal: linking a case as a child mutes it by default, and a child muted that way records a mute source of `AUTOMATIC_LINKING`.

#### Permutations & labels (routing / attribution)

`permutations` — the actual observed label combinations that produced this case. Each element of `permutations` is a **permutation-group**: a list of `{key, value, permutationIndex}` objects representing one real co-occurring set of key/value pairs.
Expand Down Expand Up @@ -223,6 +282,19 @@ Per-element fields:
#### Other fields

`aiSummary` is a pre-computed string. Treat it as one input among many — never the source of truth.

Also present on every row, and worth knowing because they are easy to miss:

| Field | Meaning |
|---|---|
| `caseUrl` | Deep link to the case in the console. Prefer it over building a URL by hand. |
| `labelsFlat` | The case's labels as flat `key=value` strings, which is usually easier to filter on than the nested `labels` map. |
| `muteStatus` | Whether notifications are suppressed, and why. A child case muted by linking records a source of `AUTOMATIC_LINKING`. |
| `resolutionCode` | The structured reason a case was resolved, alongside the free-text `resolutionDetails.resolutionReason`. |
| `lastReactivatedAt` | Set when a resolved case was reopened by a recurrence of the same indicator. Non-null means this case has fired more than once. |
| `ollyAnalysis` | Olly's stored analysis of the case, when one has been generated. |
| `schemaVersion` | Payload version. Check it before relying on a field this reference does not list. |

`$m.timestamp` is the **event timestamp** (when the state-update was emitted) and is what you order by. `createdAt` (or `$d.createdAt`) is the **case creation time**.

## Hard rules (ALWAYS apply)
Expand Down
Loading