Skip to content
Merged
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
46 changes: 46 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,36 @@ Cut on sight:
| "When nothing in the range carries the key, the tab falls back and says so: `Nothing in this range carries category…`" | "If you group by a key that isn't present in the selected range, the grouping falls back to event name." |
| "Journeys resolves it over whole sessions through a semi-join." | "In a funnel, `cart_value > 100` selects sessions that emitted at least one matching event." |

### Trust the reader

The reader is competent and has the product open. Anything they can see on screen, work out for themselves, or would never have assumed otherwise is not worth a sentence. Three habits that keep coming back:

**Don't describe what a control displays.** Give the instruction or the rule. The reader will read the button.

| Don't write | Write instead |
|---|---|
| "Click **Import**. The button shows the number it will create, for example **Import 4 alerts**." | "Click **Import**." |
| "It reads **None** when nothing matches, and `—` for an expired silence." | (cut it) |
| "Paste the JSON. The dialog checks it as you paste and lists the alerts it found by name." | "Paste the exported JSON." |
| "Either way a dialog shows the JSON. Click **Copy JSON**, then paste it into the destination." | "Copy the JSON into the destination workspace." |

**Don't announce a list before showing it.** A lead-in that only says "a list is coming" is throat-clearing. Delete it and let the list or table do the work.

| Don't write | Write instead |
|---|---|
| "It leaves out everything that can't survive the move." (above a table of what's dropped) | (cut it) |
| "The silence creator opens pre-filled with:" (above two bullets) | "The silence creator opens with matchers derived from the group and a binding to it." |

**Don't justify what the reader can infer.** State the fact and stop. Reasoning that only leads back to the fact is padding.

| Don't write | Write instead |
|---|---|
| "the rules import **paused**, so they don't fire against an account they can't read" | "the rules import **paused**" |
| "The rule's ID and folder are dropped, because both belong to the source workspace." | "Imported rules land ungrouped." |
| "The rules appear in the Alerts list right away." | (cut it) |

The test: delete the sentence and ask whether a competent reader does anything differently. If not, it stays deleted.

### Sentence shape

One idea per sentence, subject first, verb early. Any clause the reader has to unpack reads as machine writing, and so does a technical thing described in literary paraphrase.
Expand All @@ -68,6 +98,21 @@ Name things with the term the product and the reader both use. "Anything the eve

Plain does not mean cryptic. Cutting a sentence to four words that the reader then has to decode is a worse failure than the padding it replaced. Write the full sentence, then delete only what carries no meaning.

The maintainer rewrote this opening as the model for how a sentence should be built. Match it:

> **Drafted:** "Alert rules move between workspaces as JSON. Export a rule or a whole folder, then paste it into another workspace to run the same rules in staging and production, or to set up a new workspace from one you already have."
>
> **Rewritten:** "Alert rules can be exported as JSON and moved between workspaces. Export a single rule or an entire folder, then paste it into another workspace to reuse the same rules across staging and production, or quickly create a new workspace from an existing setup."

What changed, and what to copy:

- **Name the capability outright.** "can be exported as JSON and moved between workspaces" states both actions in the order the reader performs them. "move between workspaces as JSON" compresses them into a clause the reader has to unpack, and buries the verb ("export") they're searching for.
- **Keep full determiners.** "a single rule or an entire folder", not "a rule or a whole folder". Clipping articles and quantifiers reads as note-taking, not prose.
- **Use the verb that names the reader's goal.** "reuse the same rules across staging and production" and "create a new workspace", not the vaguer "run" and "set up".
- **No vague pronoun tails.** "from an existing setup", not "from one you already have". The same applies to "one", "ones", and "them" standing in for a noun the reader then has to resolve: "pick the destination's after importing" becomes "select a dashboard in the destination after importing".

Trimming fluff (the section above) and writing complete sentences are not in tension. Cut the sentence that carries nothing; write the sentences that remain in full.

Detail has a bar too: state the behavior, not its operator-by-operator mechanics. "Selecting more than one value on a field matches any of them" is the rule; "two `=` picks become **In**, two `!=` picks become **Not in**, and a second `>` replaces the first" is a spec dump.

### Tone (AGENTS.md §2 has the full word lists; these are the repeat offenders)
Expand All @@ -76,6 +121,7 @@ Detail has a bar too: state the behavior, not its operator-by-operator mechanics
- **No drama or voiceover:** rhetorical build-ups ("X is the answer"), trailing tags ("…, which it is"), figurative labels ("delivery vehicle"), cutesy asides ("the same treatment for your Lambda functions"). Write the plain fact.
- **No reflexive benefit-tails:** don't glue "…, so you can [vague upside]" onto every sentence. Keep it only when the cause and effect is real and specific.
- **No hype or filler:** powerful, seamless, robust, effortless; leverage/utilize → use, via → with, "in order to" → to, "simply/just/easily".
- **Jargon in context is fine; ornamental vocabulary is not.** `matcher`, `group-by key`, `eBPF`, and `alarm_id` are the real names for real things, so use them. What reads as AI is the decorative register around them: "the button narrows to **Retry**", "the reliable check", "everything that can't survive the move". Write the word a colleague would say out loud.

### Do this

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/docs/alerts/alert-groups.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Click any row to open the group detail page.
- **Title:** set by the first alert and can't be changed.
- **Run RCA / View RCA:** starts or opens a root-cause investigation for the group. See [Run RCA and View RCA](#run-rca-and-view-rca).
- **Ask KloudMate Assistant:** opens the assistant chat panel with the group's labels, state, alert and instance counts, and routing rule pre-loaded into the prompt, so you can start investigating without retyping context.
- **Silence this group:** opens the silence creator pre-filled with the group's labels as matchers, bound to the group so the silence expires when it resolves.
- **Silence this group:** opens the silence creator with matchers derived from the group, bound to the group so the silence lifts when the group resolves. The binding sets when the silence ends, not what it mutes: the silence covers any alert in the workspace whose labels match its matchers, group member or not. See [From an Alert Group](../silences/#from-an-alert-group).

### Firing instances

Expand Down
1 change: 1 addition & 0 deletions src/content/docs/docs/alerts/create-alerts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ From the **more options (⋯)** icon on any rule, you can:
- **Edit** the rule configuration
- **Duplicate** the rule
- **Move to folder** to move the rule into a different [folder](../folders/)
- **Export as JSON** to copy the rule into another workspace. See [Export and Import Alerts](../export-and-import/)
- **View state history** for the rule
- **Pause Evaluation** or **Pause Notifications**
- **Delete** the rule
Expand Down
63 changes: 63 additions & 0 deletions src/content/docs/docs/alerts/export-and-import.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
title: "Export and Import Alerts"
description: "Copy alert rules into another workspace by exporting them as JSON and pasting them in."
sidebar:
order: 2.8
---

Alert rules can be exported as JSON and moved between workspaces. Export a single rule or an entire folder, then paste it into another workspace to reuse the same rules across staging and production, or quickly create a new workspace from an existing setup.

Importing creates new rules, so it requires the same permission as creating an alert rule by hand.

## Export a rule or a folder

To export a single rule, open the **more options (⋯)** menu on its row and select **Export as JSON**.

To export an entire folder, switch the Alerts list to **Folder view**, open the folder header's kebab menu, and select **Export alerts as JSON**. The export includes every rule in the folder, including any that the search box or state filter is hiding.

## What is not copied

The export carries the rule's definition: name, description, queries, condition, evaluation and recovery periods, tags, and annotations. Some parts of a rule belong to the workspace it came from and are not copied:

- **The folder.** Imported rules arrive ungrouped.
- **The Dashboard and Panel links.** They point at a dashboard in the source workspace, so select a dashboard in the destination after importing.
- **Smart Alerts ownership.** A rule exported from [Smart Alerts](../smart-alerts/) imports as an ordinary rule, and the detector that created it no longer manages the copy.

A rule that inherited its evaluation interval, no-data state, or eval-error state from a folder continues to inherit after the import. Until it's moved into a folder in the destination workspace, it falls back to the system defaults. See [Folders](../folders/#inheritance-and-overrides).

A rule that was paused when it was exported stays paused after the import.

## Import rules into a workspace

1. Open **Alerts** in the destination workspace.
2. Click **Import JSON** and paste the exported JSON.
3. If the rules query AWS, select an account.
4. Click **Import**.

The import doesn't check for rules that already exist in the destination, so importing the same JSON twice creates a duplicate of every rule.

### AWS queries need an account in the destination

An AWS account connects to KloudMate exactly once, so the account an exported rule refers to will never exist in the destination workspace. Rules that query CloudWatch have to be pointed at an account in the workspace they're imported into:

- **One account in the workspace:** the rules are bound to it automatically.
- **Several accounts:** select which one to use before importing.
- **No account connected:** the rules are imported **paused**. Connect an account, point each rule at it, then resume evaluation.

Region, namespace, and dimensions are copied unchanged, so they still refer to resources in the source account. Review and update them after importing.

### Retry a partial import

An import that fails partway through keeps the rules it already created, and a retry sends only the ones that failed. The usual cause is the plan's alert rule limit, in which case the rules that fit are created and the rest are reported by name.

## Edit the JSON before importing

The export is plain text, so you can edit it before importing. Delete entries from the `alerts` array to import a subset, or rename rules to avoid name collisions with rules already in the destination. The import also accepts a bare array of alerts or a single alert object, so a fragment copied from a larger export works on its own.

An export created by a newer version of KloudMate can't be imported.

## Related

- [Creating Alerts](../create-alerts/): building a rule from scratch, from a template, or with AI.
- [Folders](../folders/): grouping imported rules and giving them shared defaults.
- [Smart Alerts](../smart-alerts/): curated detectors that create and maintain rules for you.
3 changes: 2 additions & 1 deletion src/content/docs/docs/alerts/folders.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ You create folders inline from the alert rule editor's **Folder** picker; there'

## Edit folder defaults

In Folder view, each folder header row has a kebab menu with **Edit folder** and **Delete folder**.
In Folder view, each folder header row has a kebab menu with **Edit folder**, **Export alerts as JSON**, and **Delete folder**.

**Edit folder** opens a dialog with the folder's settings:

Expand Down Expand Up @@ -80,3 +80,4 @@ A rule's folder identity flows into the alert's labels as `alarm_rule_folder_id`

- [Creating Alerts](../create-alerts/): picking a folder during rule creation.
- [Routing Rules](../routing-rules/): matching on folder labels.
- [Export and Import Alerts](../export-and-import/): exporting a folder's rules into another workspace.
1 change: 1 addition & 0 deletions src/content/docs/docs/alerts/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ For how these states transition, and how the pending duration, recovery period,
<CardGrid>
<LinkCard title="Create an alert" description="Walk through the alert creation form end to end." href="./create-alerts/" />
<LinkCard title="Smart Alerts" description="Turn on curated detectors and let KloudMate maintain the rules." href="./smart-alerts/" />
<LinkCard title="Export and import alerts" description="Copy alert rules into another workspace as JSON." href="./export-and-import/" />
<LinkCard title="Lifecycle & states" description="How alerts move through pending, firing, recovering, and resolve." href="./alert-lifecycle/" />
<LinkCard title="Annotations & severity" description="Add Liquid-templated context to your notifications." href="./annotations-and-severity/" />
<LinkCard title="Routing rules" description="Send the right alerts to the right channels." href="./routing-rules/" />
Expand Down
28 changes: 23 additions & 5 deletions src/content/docs/docs/alerts/silences.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Silences are grouped into **Active**, **Expired**, and **All** tabs.
| **Matchers** | Label conditions that pick which alerts the silence applies to. Same operator set as [Routing Rules](../routing-rules/#matchers): Equals, Not equals, Matches regex, Doesn't match regex. |
| **Expires at** | Date and time the silence stops. Capped at 30 days from creation. |
| **Reason** | Free-form text; recommended so others know why the silence exists. |
| **Scope** | Either **Group-bound** (auto-expires when a specific [Alert Group](../alert-groups/) resolves) or **Standalone** (lives until it expires). |
| **Ends** | **At expiry**, or **When the group resolves** for a silence bound to an [Alert Group](../alert-groups/). The binding sets when the silence ends, never which alerts it mutes. |

## Create a silence

Expand All @@ -40,17 +40,35 @@ Create a silence from the Silences page, an alert group, or an alert's Pause Not

### From an Alert Group

On any [Alert Group](../alert-groups/) detail page, click **Silence this group**. The silence creator opens pre-filled with:
On any [Alert Group](../alert-groups/) detail page, click **Silence this group**. The silence creator opens with matchers derived from the group and a binding to it.

- The group's labels populated as matchers.
- A binding to the group, so the silence stops automatically when the group resolves.
:::caution[A group binding sets when the silence ends, not what it mutes]
The binding does one thing: when the bound group resolves, the silence's expiry moves to that moment and it lifts early. Matching never looks at it, so the silence mutes every alert in the workspace whose labels match its matchers, group member or not.
:::

Save it; this creates a **Group-bound** silence.
**How the matchers are filled in.** They come from the routing rule's group-by keys, taking the value the group carries or the value every live member shares. A rule that groups by nothing falls back to `alarm_id`, so most groups pre-fill with a single matcher such as `alarm_id = alm_7f3c2b`.

**When the group's alerts share no label.** A silence is a set of label matchers that all have to hold, so it can't pick out an arbitrary list of alerts. A group whose alerts share nothing pre-fills no matchers. Instead, the form offers the labels those alerts do carry as one-click chips, each showing how many of the group's alerts it covers, such as `10/17`.

#### Adding every offered label doesn't scope the silence to the group

Building a silence out of the group's own labels fails in both directions.

- **It mutes less than the group.** An alert has to match every matcher, so adding a key that only some members carry drops the rest. On a 17-alert group, a folder label present on 10 of them takes the match from 17 down to 10, and the other 7 keep notifying.
- **It still mutes outside the group.** Two multi-valued keys accept every combination of the two, not just the pairs the group holds. Three rules across seven hosts makes 21 combinations. The group holds 17 alerts.

Use the **Alerts this silence will mute** preview on the create form instead. It counts every alert instance the current matchers would mute, before you save.

### From an alert's Pause Notifications

On an alert rule's **more options (⋯) → Pause Notifications**, KloudMate creates a silence scoped to that alert (an `alarm_id` matcher) with an expiry: the alert keeps evaluating, and only its notifications are suppressed. While paused, the rule is marked **Silenced**; view or end the pause from **Alerts → Silences**. Add matchers to narrow the pause to specific instances.

## See what a silence is muting

The Silences list carries an **Alerts muted** column: how many alert rules each active silence is muting. Expand a row to list those rules, each marked with a chip such as `3 firing` when its instances are firing. The silence detail page shows the same list, along with a link to the group the silence is bound to.

In the other direction, the **Silenced** badge links to the silence or maintenance window muting that alert. When more than one is muting it, the badge opens a menu listing all of them.

## Silence vs. maintenance window

Both features are a **notification gate, not a state change**: the alert still evaluates on its schedule and its state transitions are still recorded in history; only the notification is withheld. Silencing every firing instance of an alert group keeps the group **Open** (shown as **Muted**); it does **not** resolve the group, and muting never emits a *resolved* notification. The differences between the two are operational, not behavioral:
Expand Down
Loading